Como funcionam os arquivos especiais de caracteres ou de caracteres?

21

Estou tentando entender os arquivos especiais do personagem. De wikipedia , eu entendo que esses arquivos "fornecer uma interface" para dispositivos que transmitem dados, um caractere por vez. Meu entendimento é que o sistema de alguma forma chama o dispositivo de caracteres em vez de chamar o driver de dispositivo diretamente. Mas como o arquivo fornece essa interface? É um executável que traduz a chamada do sistema? Alguém pode explicar o que está acontecendo?

    
por bernie2436 03.05.2012 / 16:53

5 respostas

18

Eles são na verdade apenas isso - interfaces. Codificados por um número "maior" e "menor", eles fornecem um gancho para o kernel.

Eles vêm em dois tipos (bem, três, mas os pipes nomeados estão fora do escopo desta explicação por enquanto): Dispositivos de Caracteres e Dispositivos de Bloco.

Dispositivos de bloco tendem a ser dispositivos de armazenamento, capazes de armazenar em buffer a saída e armazenar dados para recuperação posterior.

Dispositivos de caractere são coisas como placas de áudio ou gráficas ou dispositivos de entrada, como teclado e mouse.

Em cada caso, quando o kernel carrega o driver correto (no momento da inicialização ou através de programas como udev ) Ele verifica os vários barramentos para ver se algum dispositivo manipulado por esse driver está realmente presente no sistema. Em caso afirmativo, configura um dispositivo que "escuta" o número principal / secundário apropriado.

(Por exemplo, o Processador de Sinal Digital da primeira placa de áudio encontrada pelo seu sistema recebe o maior / menor par de números de 14/3; o segundo, 14,35, etc.)

Cabe ao udev criar uma entrada em /dev named dsp como um dispositivo de caractere marcado como major 14 minor 3.

(Em versões significativamente mais antigas ou de tamanho mínimo do Linux, /dev/ pode não ser carregado dinamicamente, mas apenas conter todos os arquivos de dispositivos estaticamente possíveis.)

Então, quando um programa userspace tenta acessar um arquivo marcado como 'arquivo especial de caractere' com o número principal / menor apropriado (por exemplo, seu reprodutor de áudio tentando enviar áudio digital para /dev/dsp ), o kernel sabe que esses dados precisam ser transmitidos através do driver que o número maior / menor está anexado; presumivelmente disse motorista sabe o que fazer com ele por sua vez.

    
por 03.05.2012 / 17:09
10

Todos os arquivos, dispositivos ou outros, suportam 6 operações básicas dentro do VFS:

  1. Abrir
  2. Fechar
  3. Ler
  4. Escrever
  5. Buscar
  6. Diga

Além disso, os arquivos de dispositivos suportam o Controle de E / S, o que permite outras operações diversas não cobertas pelos primeiros 6.

No caso de um caracter especial, seek e tell não são implementados, pois suportam uma interface de streaming . Ou seja, lendo ou escrevendo diretamente, como é feito com o redirecionamento no shell:

echo 'foo' > /dev/some/char
sed ... < /dev/some/char
    
por 03.05.2012 / 17:27
5

Minimal runnable file_operations example

Depois de ver um exemplo mínimo, tudo fica óbvio.

As principais ideias são:

  • file_operations contém os retornos de chamada para cada arquivo relacionado syscall
  • mknod path c major minor cria um dispositivo de caractere que usa esses file_operations
  • para dispositivos de caracteres que alocam dinamicamente números de dispositivos (a norma para evitar conflitos), encontre o número com cat /proc/devices

character_device.ko módulo do 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 teste 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 com clichê para executá-lo:

Exemplos mais complexos:

  • read , write , lseek com um buffer interno de tamanho fixo e em debugfs em vez de um dispositivo de caractere: link
  • poll : link
  • ioctl : link
  • anon_inode_getfd associa um file_operations a um descritor de arquivo sem nenhum arquivo do sistema de arquivos: link
por 18.06.2017 / 10:30
3

"Personagem de cada vez" é um equívoco (como é a idéia de que os dispositivos de caráter necessariamente não suportam a busca e a fala). Na verdade, dispositivos "bloco de cada vez" (isto é, estritamente voltados para registros, como uma unidade de fita *) devem ser dispositivos de caractere. Assim é a idéia de que um dispositivo de caractere deve necessariamente ser imperceptível - os drivers de dispositivo de caractere definem uma estrutura file_operations completa que é livre para definir ou não, dependendo se o dispositivo suporta a operação. Os dispositivos de caractere que a maioria das pessoas considera como exemplos são null, urandom, dispositivos TTY, placa de som, mouse, etc ... que são todos indecifráveis por causa das especificidades do que esses dispositivos são, mas / dev / vcs, / dev / fb0 e / dev / kmem também são dispositivos de caracteres e todos são procuráveis.

Como mencionei, um driver de dispositivo de caractere define uma estrutura file_operations que possui ponteiros de função para todas as operações que alguém pode querer chamar em um arquivo - seek, read, write, ioctl, etc - e cada um é chamado uma vez quando a chamada de sistema correspondente é executada com este arquivo de dispositivo aberto. E ler e escrever pode, portanto, fazer o que quiser com seus argumentos - pode se recusar a aceitar uma escrita que seja muito grande ou apenas escrever o que se encaixa; ele pode ler apenas os dados correspondentes a um registro em vez de todo o número de bytes solicitado.

Então, o que é um dispositivo de bloco? Basicamente, os dispositivos de bloco são unidades de disco. Nenhum outro tipo de dispositivo (exceto para unidades de disco virtual , como ramdisk e loopback) é um dispositivo de bloco. Eles estão integrados no sistema de solicitação de E / S, na camada do sistema de arquivos, no sistema de buffer / cache e no sistema de memória virtual de maneira que os dispositivos de caracteres não são mesmo quando você está acessando, por exemplo / dev / sda de um processo do usuário . Mesmo os "dispositivos brutos" que a página menciona como uma exceção são dispositivos de caractere .

* Alguns sistemas UNIX implementaram o que agora é chamado de "modo de bloco fixo" - que permite que o grupo de núcleos e as solicitações de E / S divididas ajustem os limites de bloco configurados da mesma maneira que é feito para unidades de disco - como um dispositivo de bloco. Um dispositivo de caractere é necessário para o "modo de bloco variável", que preserva os limites de bloco do programa do usuário, pois uma chamada de gravação única (2) grava um bloco e uma chamada de leitura única (2) retorna um bloco. Como a troca de modo é implementada agora como um arquivo de dispositivo ioctl em vez de separado, um dispositivo de caractere é usado. Unidades de fita de registro variável são na maioria das vezes "não-procuráveis" porque a busca envolve a contagem de vários registros em vez de um número de bytes, e a operação de busca nativa é implementada como um ioctl.

    
por 30.03.2017 / 18:28
1

Dispositivos de caracteres podem ser criados pelos módulos do kernel (ou pelo próprio kernel). Quando um dispositivo é criado, o criador fornece ponteiros para funções que implementam chamadas padrão como abrir, ler, etc. O kernel do Linux associa essas funções com o dispositivo de caractere, por exemplo, quando um aplicativo de modo de usuário chama o read () função em um arquivo de dispositivo de caractere, isso resultará em um syscall e, em seguida, o kernel roteará essa chamada para uma função de leitura especificada ao criar o driver. Há um tutorial passo-a-passo sobre como criar um dispositivo de caracteres aqui , você pode criar um projeto de amostra e a etapa ele usando um depurador para entender como o objeto de dispositivo é criado e quando os manipuladores são chamados.

    
por 25.02.2014 / 03:06

Tags