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

22

Estoy tratando de entender los archivos especiales de personajes. De Wikipedia , entiendo que estos archivos "proporcionan una interfaz" para dispositivos que transmiten datos de un carácter a la vez. Tengo entendido que el sistema de alguna manera llama al dispositivo de caracteres en lugar de llamar al controlador del dispositivo directamente. Pero, ¿cómo proporciona el archivo esta interfaz? ¿Es un ejecutable que traduce la llamada al sistema? ¿Alguien puede explicar qué pasa?

bernie2436
fuente

Respuestas:

19

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 tuberías 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 teclado y 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 diferentes 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 /devnombrada dspcomo un dispositivo de personaje marcado mayor 14 menor 3.

(En versiones de Linux con una huella mínima significativamente más antigua, es /dev/posible 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 marcado como 'archivo especial de caracteres' con el número mayor / menor apropiado (por ejemplo, su reproductor de audio intenta enviar audio digital /dev/dsp), el núcleo sabe que estos datos deben se transmitirá a través del controlador al que está asociado el número mayor / menor presumiblemente dicho conductor sabe qué hacer con él a su vez.

Shadur
fuente
1
1. ¿Entonces los números mayores / menores son análogos a los puertos?
bernie2436
2. Entonces, cuando los programas acceden a cualquier archivo, el núcleo lee estas interfaces especiales para saber si el programa debería recibir interrupciones de un dispositivo en particular. Por ejemplo: si un programa abre un archivo de palabras, lee el archivo especial del dispositivo de caracteres para saber si el programa debe responder a la entrada del teclado.
bernie2436
1) Algo . Es la analogía de un hombre pobre, pero servirá.
Shadur
2
2) Te faltan tres o cuatro capas de abstracción allí. El programa en el que abre un archivo de texto sin saber ni importarle cuál es el dispositivo del teclado. La comunicación con el hardware subyacente se realiza a través del emulador de terminal (si está en modo consola) o mediante la capa de eventos X (si está en modo gráfico), cualquiera de los cuales escuchará el teclado y otras unidades y decidirá qué , en todo caso, para pasar al programa. Estoy resumiendo un sistema multicapa bastante complejo aquí; podrías hacer bien en leer sobre el sistema X Window en general.
Shadur
1
Tenga en cuenta también que, en algunos tipos de UN * X, hay archivos especiales de caracteres para dispositivos de almacenamiento; una lectura o una escritura en el archivo especial se convierte en una lectura o escritura en una secuencia de bloques en el dispositivo. (En las versiones recientes de FreeBSD, esas son las única archivos especiales para dispositivos de almacenamiento, no son ningún archivo especial de bloques.)
10

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

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

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

En el caso de un personaje especial, buscar y contar no se implementan ya que admiten una 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
Ignacio Vazquez-Abrams
fuente
6

file_operationsEjemplo ejecutable mínimo

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 syscall relacionado con el archivo
  • mknod <path> c <major> <minor> crea un dispositivo de personaje que usa esos file_operations
  • para dispositivos de caracteres que asignan dinámicamente números de dispositivo (la norma para evitar conflictos), encuentre el número con cat /proc/devices

character_device.ko módulo del núcleo:

#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 aguas arriba con repetitivo para ejecutarlo realmente:

Ejemplos más complejos:

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente
Esto fue súper útil gracias! Solo una pregunta, ¿qué hace exactamente esto *off = 1;y por qué está configurado 1?
SilverSlash el
1
@SilverSlash ese valor se transfiere a través de múltiples readllamadas al mismo open(descriptor de archivo. El conductor puede hacer lo que quiera con él. La semántica habitual es contener el número de bytes leídos. Sin embargo, en este ejemplo, tenemos una semántica más simple: 0para la primera lectura, 1después de la primera lectura. Intenta ejecutarlo y pon un paso printk o GDB para depurarlo.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
4

"Carácter a la vez" es un nombre inapropiado (como lo es la idea de que los dispositivos de caracteres no son necesariamente compatibles con buscar y contar). De hecho, los dispositivos de "bloqueo a la vez" (es decir, estrictamente orientados a la grabación, como una unidad de cinta *) deben ser dispositivos de caracteres. También lo es la idea de que un dispositivo de caracteres debe ser necesariamente inescrutable: los controladores de dispositivos de caracteres definen una file_operationsestructura completa que es libre de definir llseek o no según si el dispositivo admite la operación. Los dispositivos de caracteres que la mayoría de las personas consideran ejemplos son nulos, urandom, dispositivos TTY, tarjeta de sonido, mouse, etc., que no se pueden buscar debido a los detalles de esos dispositivos, pero / dev / vcs, / dev / fb0 , y / dev / kmem también son dispositivos de caracteres y todos son buscables.

Como mencioné, un controlador de dispositivo de caracteres define una estructura de operaciones de archivo que tiene punteros de función para todas las operaciones que alguien quiera llamar en un archivo (buscar, leer, escribir, ioctl, etc.) y cada una de ellas se llama una vez cuando la llamada del sistema correspondiente se ejecuta con este archivo de dispositivo abierto. Y leer y escribir, por lo tanto, puede hacer lo que quiera con sus argumentos: puede negarse a aceptar una escritura que sea demasiado grande o solo escribir lo que cabe; solo puede leer los datos correspondientes a un registro en lugar del número total de bytes solicitado.

Entonces, ¿qué es un dispositivo de bloque? Básicamente, los dispositivos de bloque son unidades de disco. Ningún otro tipo de dispositivo (a excepción de las unidades de disco virtual , como ramdisk y loopback) es un dispositivo de bloque. Están integrados en el sistema de solicitud de E / S, la capa del sistema de archivos, el sistema de memoria intermedia / caché y el sistema de memoria virtual de una manera que los dispositivos de caracteres no lo son, incluso cuando está accediendo, por ejemplo, a / dev / sda desde un proceso de usuario . Incluso los "dispositivos en bruto" que la página menciona como excepción son los dispositivos de caracteres .

* Algunos sistemas UNIX implementaron lo que ahora se llama "modo de bloque fijo", que permite que el núcleo agrupe y divida las solicitudes de E / S para que se ajusten a los límites de bloque configurados de la misma manera que se hace para las unidades de disco, como un bloque dispositivo. Se necesita un dispositivo de caracteres para el "modo de bloque variable", que preserva los límites del bloque del programa del usuario, ya que una sola llamada de escritura (2) escribe un bloque y una sola llamada de lectura (2) devuelve un bloque. Dado que el cambio de modo se implementa ahora como un ioctl en lugar de un archivo de dispositivo separado, se utiliza un dispositivo de caracteres. Las unidades de cinta de registro variable son en su mayoría "no buscables" porque la búsqueda implica contar una cantidad de registros en lugar de una cantidad de bytes, y la operación de búsqueda nativa se implementa como un ioctl.

Aleatorio832
fuente
1

Los dispositivos de caracteres se pueden crear mediante módulos del núcleo (o el núcleo mismo). Cuando se crea un dispositivo, el creador proporciona punteros a las funciones que implementan manejan llamadas estándar como abrir, leer, etc. El kernel de Linux luego asocia esas funciones con el dispositivo de caracteres, por ejemplo, cuando una aplicación en modo de usuario llama a read () en un archivo de dispositivo de caracteres, dará como resultado una llamada al sistema y luego el núcleo enrutará esta llamada a una función de lectura especificada al crear el controlador. Hay un tutorial paso a paso en la creación de un dispositivo de caracteres aquí , puede crear un proyecto de ejemplo y el paso a través de él utilizando un depurador para comprender cómo se crea el objeto de dispositivo y cuando se invocan los manipuladores.

bazis
fuente