Saltar al contenido

¿Cómo funcionan el dispositivo de caracteres o los archivos especiales de caracteres?

Solución:

En realidad, son solo eso: interfaces. Codificados por un número “mayor” y “menor”, proporcionan un enlace al núcleo.

Vienen en dos sabores (bueno, tres, pero las canalizaciones con nombre están fuera del alcance de esta explicación por ahora): Dispositivos de caracteres y Dispositivos de bloque.

Los dispositivos de bloque tienden a ser dispositivos de almacenamiento, capaces de almacenar en búfer la salida y almacenar datos para su posterior recuperación.

Los dispositivos de caracteres son cosas como tarjetas de audio o gráficas, o dispositivos de entrada como el teclado y el mouse.

En cada caso, cuando el kernel carga el controlador correcto (ya sea en el momento del arranque o mediante programas como udev), escanea los distintos buses para ver si algún dispositivo manejado por ese controlador está realmente presente en el sistema. Si es así, configura un dispositivo que ‘escucha’ en el número mayor / menor apropiado.

(Por ejemplo, el procesador de señal digital de la primera tarjeta de audio encontrada por su sistema obtiene el par de números mayor / menor de 14/3; el segundo obtiene 14,35, etc.)

Depende de udev crear una entrada en /dev llamado dsp como un dispositivo de carácter marcado mayor 14 menor 3.

(En versiones de Linux significativamente más antiguas o con una huella mínima, /dev/ puede que no se cargue dinámicamente, sino que solo contenga todos los archivos de dispositivo posibles de forma estática).

Luego, cuando un programa de espacio de usuario intenta acceder a un archivo que está marcado como un ‘archivo especial de carácter’ con el número mayor / menor apropiado (por ejemplo, su reproductor de audio tratando de enviar audio digital a /dev/dsp ), el kernel sabe que estos datos deben transmitirse a través del controlador al que se adjunta el número mayor / menor; presumiblemente dicho conductor sabe qué hacer con él a su vez.

Cada archivo, dispositivo o de otro tipo, admite 6 operaciones básicas dentro del VFS:

  1. Abierto
  2. Cerrar
  3. Leer
  4. Escribir
  5. Buscar
  6. Contar

Además, los archivos de dispositivo admiten el control de E / S, que permite otras operaciones diversas no cubiertas por los primeros 6.

En el caso de un carácter especial, buscar y decir no se implementan ya que admiten un interfaz de transmisión. Es decir, leer o escribir directamente como se hace con la redirección en el shell:

echo 'foo' > /dev/some/char
sed ... < /dev/some/char

Mínimo ejecutable file_operations ejemplo

Una vez que vea un ejemplo mínimo, todo se vuelve obvio.

Las ideas clave son:

  • file_operations contiene las devoluciones de llamada para cada llamada de sistema relacionada con el archivo
  • mknod <path> c <major> <minor> crea un dispositivo de caracteres que utiliza esos file_operations
  • para dispositivos de caracteres que asignan dinámicamente números de dispositivo (la norma para evitar conflictos), busque el número con cat /proc/devices

character_device.ko módulo de kernel:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* register_chrdev, unregister_chrdev */
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include <uapi/linux/stat.h> /* S_IRUSR */

#define NAME "lkmc_character_device"

MODULE_LICENSE("GPL");

static int major;

static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    size_t ret;
    char kbuf[] = {'a', 'b', 'c', 'd'};

    ret = 0;
    if (*off == 0) {
        if (copy_to_user(buf, kbuf, sizeof(kbuf))) {
            ret = -EFAULT;
        } else {
            ret = sizeof(kbuf);
            *off = 1;
        }
    }
    return ret;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
};

static int myinit(void)
{
    major = register_chrdev(0, NAME, &fops);
    return 0;
}

static void myexit(void)
{
    unregister_chrdev(major, NAME);
}

module_init(myinit)
module_exit(myexit)

Programa de prueba de Userland:

insmod /character_device.ko
dev="lkmc_character_device"
major="$(grep "$dev" /proc/devices | cut -d ' ' -f 1)"
mknod "/dev/$dev" c "$major" 0
cat /dev/lkmc_character_device
# => abcd
rm /dev/lkmc_character_device
rmmod character_device

GitHub QEMU + Buildroot upstream con boilerplate para ejecutarlo:

  • https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/character_device.c
  • https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/rootfs_overlay/character_device.sh

Ejemplos más complejos:

  • read, write, lseek con un búfer interno de tamaño fijo y en debugfs en lugar de un dispositivo de caracteres: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/fops.c
  • poll: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.c
  • ioctl: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.c
  • anon_inode_getfd asocia un file_operations a un descriptor de archivo sin ningún archivo de sistema de archivos: https://stackoverflow.com/questions/4508998/what-is-anonymous-inode/44388030#44388030
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *