Como os arquivos Linux “/ dev” são criados?

108

Existem arquivos especiais no Linux que não são realmente arquivos.

Os exemplos mais notáveis e claros estão na pasta dev , "arquivos" como:

  • /dev/null - Ignora qualquer coisa que você escreve no arquivo
  • /dev/random - Produz dados aleatórios em vez do conteúdo de um arquivo
  • /dev/tcp - Envia todos os dados que você escreve para este arquivo pela rede

Primeiro de tudo, qual é o nome desses tipos de "arquivos" que são realmente algum tipo de script ou binário disfarçado?

Em segundo lugar, como eles são criados? Esses arquivos estão embutidos no sistema no nível do kernel, ou existe uma maneira de criar um "arquivo mágico" por si mesmo (que tal um /dev/rickroll )?

    
por IQAndreas 06.11.2015 / 07:42

7 respostas

97

/dev/zero é um exemplo de "arquivo especial" - particularmente, um "nó de dispositivo". Normalmente, eles são criados pelo processo de instalação da distribuição, mas você pode totalmente criá-los por conta própria, se quiser.

Se você perguntar ls sobre /dev/zero :

# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5  Nov 5 09:34 /dev/zero

O "c" no início diz que este é um "dispositivo de caractere"; o outro tipo é "dispositivo de bloco" (impresso por ls como "b"). Mais ou menos, os dispositivos de acesso aleatório, como discos rígidos, tendem a ser dispositivos de bloco, enquanto itens sequenciais, como unidades de fita ou sua placa de som, tendem a ser dispositivos de caractere.

A parte "1, 5" é o "número do dispositivo principal" e o "número do dispositivo secundário".

Com essas informações, podemos usar o comando mknod para criar nosso próprio nó de dispositivo:

# mknod foobar c 1 5

Isso cria um novo arquivo chamado foobar , na pasta atual, que exatamente é a mesma coisa que /dev/zero . (É claro que você pode definir diferentes permissões se quiser.) Tudo o que "arquivo" realmente contém são os três itens acima - tipo de dispositivo, número maior, número menor. Você pode usar ls para procurar os códigos de outros dispositivos e recriá-los também. Quando você se cansar, basta usar rm para remover os nós de dispositivo que você acabou de criar.

Basicamente, o número principal informa ao kernel Linux com qual driver de dispositivo conversar, e o número menor informa ao driver de dispositivo de qual dispositivo você está falando. (Por exemplo, você provavelmente tem um controlador SATA, mas talvez vários discos rígidos sejam conectados a ele.)

Se você quiser inventar novos dispositivos que fazem algo novo ... bem, você precisará editar o código-fonte para o kernel Linux e compilar seu próprio kernel personalizado. Então não vamos fazer isso! :-) Mas você pode adicionar arquivos de dispositivos que duplicam aqueles que você já tem muito bem. Um sistema automatizado como o udev basicamente está apenas assistindo a eventos de dispositivos e chamando mknod / rm automaticamente. Nada mais mágico que isso.

Ainda existem outros tipos de arquivos especiais:

  • O Linux considera um diretório como um tipo especial de arquivo. (Normalmente você não pode abrir diretamente um diretório, mas se pudesse, você descobriria que é um arquivo normal que contém dados em um formato especial e diz ao kernel onde encontrar todos os arquivos naquele diretório.)

  • Um link simbólico é um arquivo especial. (Mas um link físico não é.) Você pode criar links simbólicos usando o comando ln -s . (Procure a manpage para isso.)

  • Há também uma coisa chamada "pipe nomeado" ou "FIFO" (fila de entrada e saída). Você pode criar um com mkfifo . Um FIFO é um arquivo mágico que pode ser aberto por dois programas ao mesmo tempo - uma leitura, uma escrita. Quando isso acontece, funciona como um tubo de shell normal. Mas você pode iniciar cada programa separadamente ...

Um arquivo que não é "especial" é chamado de "arquivo normal". Você ocasionalmente verá isso na documentação do Unix. Isso é o que significa; um arquivo que não é um nó de dispositivo ou um symlink ou qualquer outra coisa. Apenas um arquivo normal, todos os dias, sem propriedades mágicas.

    
por 06.11.2015 / 10:57
34

A maioria das entradas /dev são inodes de dispositivo de bloco ou inodes de dispositivo de caractere. A Wikipedia tem muitos detalhes sobre isso, o que não vou repetir.

Mas /dev/tcp , mencionado em sua pergunta, não é explicado por nenhuma das respostas existentes. /dev/tcp e /dev/udp são diferentes da maioria das outras /dev entradas. Os dispositivos de bloco e caractere são implementados pelo kernel, mas /dev/tcp e /dev/udp são implementados no modo de usuário.

O shell bash é um programa que possui uma implementação de /dev/tcp e /dev/udp (copiada de ksh93 ). Quando você tenta abrir um caminho abaixo daqueles com operadores de redirecionamento bash, ele não executará uma chamada de sistema open comum. Em vez disso, o bash criará um soquete TCP e o conectará à porta especificada.

Isso é implementado no modo de usuário e somente em alguns programas, como pode ser visto no exemplo a seguir, que demonstra a diferença entre deixar bash e cat tentar abrir /dev/tcp/::1/22

$ cat /dev/tcp/::1/22
cat: /dev/tcp/::1/22: No such file or directory
$ cat < /dev/tcp/::1/22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3

A diferença com ksh93 é que bash só fará essas conexões TCP com operadores de redirecionamento, não nos outros lugares onde pode abrir arquivos como source ou . builtin.

    
por 06.11.2015 / 10:23
19

Além dos nós de dispositivos explicados em outras respostas (criados com mknod (2) ou fornecido por alguns devfs ), o Linux tem outros arquivos "mágicos" fornecidos por especial /proc/ /proc.5.html">proc(5) , leia sobre procfs ) e em /sys/ (leia sobre sysfs ).

Estes pseudo-arquivos (que aparecem como stat (2) - como arquivos comuns, não como dispositivos) são uma visão virtual fornecida pelo kernel; em particular, lendo de /proc/ (por exemplo, com cat /proc/$$/maps , ou por aberto (2) -ing /proc/self/status no seu programa) geralmente não envolve nenhuma E / S física do disco ou da rede, portanto é bastante rápido.

Para criar algum pseudo arquivo adicional em /proc/ , você geralmente deve escrever seu próprio módulo do kernel e carregá-lo ( veja, por exemplo, este ).

    
por 06.11.2015 / 08:38
13

Eles são chamados de nós de dispositivos e são criados manualmente com mknod ou automaticamente por udev . Eles são tipicamente interfaces semelhantes a arquivos para caracterizar ou bloquear dispositivos com drivers no kernel - por exemplo, discos são dispositivos de bloco, ttys e portas seriais etc são dispositivos de caractere.

Existem outros tipos de arquivos "especiais" também, incluindo pipes nomeados e fifos e sockets.

    
por 06.11.2015 / 07:56
9

Como outros usuários já explicaram em detalhes, arquivos especiais exigem código para fazer backup deles. No entanto, ninguém parece ter mencionado que o Linux fornece várias maneiras de escrever esse código no espaço do usuário:

A. FUSE (Sistema de arquivos no espaço de uso) permite que você escreva algo como /proc sem risco de travar o kernel e fazê-lo em um idioma / tempo de execução de sua escolha, como Ir , Node.js , Perl , PHP , Python , Ruby , Rust , etc. .

Ele também tem a vantagem de que os sistemas de arquivos FUSE podem ser montados sem sudo , porque eles são executados como o usuário que faz a montagem.

Aqui estão alguns exemplos de coisas que as pessoas escreveram usando o FUSE:

  • mp3fs (Visualize seus arquivos FLAC como arquivos MP3 que são criados dinamicamente quando você os copia / clica e os arrasta para o seu Leitor de MP3)
  • PyTagsFS (Visualize sua mídia em uma árvore de pastas virtuais criadas a partir das tags de metadados)
  • fuse-zip (monte arquivos Zip como pastas)
  • FuseISO (monte ISOs sem permissões de root)
  • iFUSE (Monte iDevices)
  • FuseDAV (monte os compartilhamentos WebDAV)
  • fuse-exfat (monte sistemas de arquivos formatados com exFAT)
  • ntfs-3g ( O driver Linux NTFS)

B. Se você deseja criar um dispositivo de entrada virtual como teclado, mouse, joystick, etc. (por exemplo, escrever um driver de espaço de usuário para um dispositivo USB com o qual você está falando usando libusb ), há uinput .

As ligações são mais difíceis de encontrar, mas eu sei que elas existem para Ir (somente para teclado), Python e Ruby (2) .

Exemplos de uso de entrada de dados no mundo real incluem:

  • G15Daemon (driver do Linux para o LCD e teclas de jogos nos teclados de jogos Logitech G15)
  • ds4drv (Driver para controladores Sony DualShock 4)
  • xboxdrv (driver alternativo do controlador XBox 360 e Linux equivalente a x360ce jogos tão mal desenhados como Runner2: Future Legend of Rhythm Alien pode pensar que eles estão falando com um controlador XBox real quando não estão)
  • Os antigos drivers Wiimote, como cwiid , que eram necessários antes que alguém finalmente escrevesse um driver de kernel Wiimote para que o suporte estivesse disponível por padrão .

C. Para dispositivos de caracteres genéricos, há CUSE (dispositivos de caractere no USErspace). É muito menos popular embora.

O único usuário da CUSE API do qual estou ciente é o mesmo programa que motivou sua criação: osspd , que implementa /dev/dsp , /dev/adsp e /dev/mixer (a API de áudio do OSS) no userspace para que eles possam ser roteados por meio do PulseAudio ou dmix.

A única ligação CUSE que consegui encontrar é cusepy , que não é atualizada desde 2010.

D. Talvez você não precise de um novo arquivo especial.

Por exemplo, você pode abrir comunicações cruas com qualquer dispositivo USB usando libusb (Lista de ligações na página) e, em seguida, comunicar com outros programas através de algum outro mecanismo (sockets TCP / UDP, leitura / escrita de stdin / stdout ou ficheiros regulares no disco, etc.).

    
por 08.11.2015 / 08:11
6

O livro Linux Device Drivers (altamente recomendado) explica isso em detalhes, e até mesmo você criou um módulo de kernel que faz isso como exemplo, mas em poucas palavras, cada driver de dispositivo tem funções específicas que são chamadas quando um arquivo é aberto, fechado, lido, escrito, etc. Os arquivos "especiais" apenas fazem algo especial dentro dessas funções, em vez de acessar o arquivo. hardware de armazenamento em um disco.

Por exemplo, a função de gravação para /dev/null não faz nada, ignorando os bytes. A função de leitura para /dev/random retorna um número aleatório.

    
por 07.11.2015 / 18:30
1

mount -t devtmpfs

Também é interessante ver que em sistemas modernos, /dev é normalmente um tipo de sistema de arquivos que pode ser montado onde você quiser. Ubuntu 16.04:

mkdir d
sudo mount -t devtmpfs none d
head -c 10 d/random
sudo umount d

Isso é ativado por CONFIG_DEVTMPFS=y e permite que o próprio kernel crie e destrua arquivos de dispositivos conforme necessário.

CONFIG_DEVTMPFS_MOUNT=y

Esta opção faz com que o kernel monte automaticamente o devtmpfs em /dev .

drivers/base/Kconfig documentos:

config DEVTMPFS_MOUNT
    bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs"
    depends on DEVTMPFS
    help
      This will instruct the kernel to automatically mount the
      devtmpfs filesystem at /dev, directly after the kernel has
      mounted the root filesystem. The behavior can be overridden
      with the commandline parameter: devtmpfs.mount=0|1.
      This option does not affect initramfs based booting, here
      the devtmpfs filesystem always needs to be mounted manually
      after the rootfs is mounted.
      With this option enabled, it allows to bring up a system in
      rescue mode with init=/bin/sh, even when the /dev directory
      on the rootfs is completely empty.

file_operations

Finalmente, você deve criar seu próprio módulo de kernel de dispositivo de caractere para ver exatamente o que está acontecendo.

Este é um exemplo mínimo executável: Entendendo o caractere arquivos de dispositivos (ou caracteres especiais)

O passo mais importante é configurar a file_operations struct, por exemplo:

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

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

que contém ponteiros de função que são chamados para cada chamada de sistema relacionada a arquivos.

Torna-se então óbvio que você sobrescreve essas chamadas de sistema relacionadas a arquivos para fazer o que quiser, e é assim que o kernel implementa dispositivos como /dev/zero .

Crie entradas /dev automaticamente sem mknod

O mistério final é como o kernel cria automaticamente /dev entradas.

O mecanismo pode ser observado fazendo um módulo do kernel que faz isso você mesmo, como mostrado em: link e se resume a uma chamada device_create .

    
por 06.08.2017 / 14:31