Como são criados os arquivos Linux "/ dev"?

112

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

Os exemplos mais notáveis ​​e claros disso estão na devpasta "arquivos", como:

  • /dev/null - Ignora qualquer coisa que você escreve no arquivo
  • /dev/random - Envia dados aleatórios em vez do conteúdo de um arquivo
  • /dev/tcp - Envia todos os dados que você escreve para esse 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?

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

IQAndreas
fonte
1
Não fazia ideia de como marcar essa pergunta, principalmente porque não sei o nome do que estou procurando. Sinta-se livre para editar em qualquer tag relevante.
IQAndreas #
15
BTW, essa é uma parte fundamental do design de sistemas operacionais unix e unix-like: (quase) tudo é um arquivo ou pode ser feito para parecer um arquivo.
cas 6/15
5
Veja também: mknod (2) man 2 mknod
RobertL 6/15
4
Estes são "nós do dispositivo". No entanto, os que você mencionou - diferentemente dos associados a discos, teclado, mouses, placas de áudio e outros dispositivos - são chamados de "pseudo-dispositivos", pois não são dispositivos "reais" e só existem no kernel. É possível criar novos, escrevendo um driver de dispositivo adequado e adicioná-lo ao kernel (por exemplo, um pseudo-dispositivo para monitorar alguma atividade no computador). Antes do diretório / dev existir no disco - hoje em dia é um sistema de arquivos virtual (do tipo devfs) criado pelo kernel.
Baard Kopperud
10
Todos os arquivos, mesmo os arquivos "reais", são artefatos de software. O software por trás de cada dispositivo, arquivo, soquete, arquivo especial, ou algo ainda a ser inventado fornece uma tabela de funções para lidar com open(), read(), close(), etc. Depois disso, cabe ao software
waltinator

Respostas:

101

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

Se você perguntar lssobre /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 informa que esse é um "dispositivo de caractere"; o outro tipo é "dispositivo de bloco" (impresso lscomo "b"). Muito grosso modo, dispositivos de acesso aleatório, como discos rígidos, tendem a ser dispositivos de bloco, enquanto coisas seqüenciais, como unidades de fita ou placa de som, tendem a ser dispositivos de caracteres.

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

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

# mknod foobar c 1 5

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

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

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

Ainda existem outros tipos de arquivos especiais:

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

  • Um link simbólico é um arquivo especial. (Mas um link físico não é.) Você pode criar links simbólicos usando o ln -scomando (Procure a página de manual 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 e uma escrita. Quando isso acontece, funciona como um tubo de concha normal. Mas você pode iniciar cada programa separadamente ...

Um arquivo que não é "especial" de forma alguma é chamado de "arquivo regular". Você ocasionalmente verá menção disso na documentação do Unix. É isso que significa; um arquivo que não é um nó de dispositivo ou um link simbólico ou qualquer outra coisa. Apenas um arquivo normal todos os dias sem propriedades mágicas.

MathematicsOrchid
fonte
4
Há também mais um tipo de arquivo especial, um soquete de domínio Unix vinculado ao sistema de arquivos.
Brian Bi
8
Se você quiser brincar mknod, corra cat /proc/devicespara ver os números principais de todos os drivers. O que nos leva a outro tipo de arquivo especial do /procsistema de arquivos ( esta resposta fala sobre isso).
Ugoren
8
Outros departamentos inventaram seus próprios arquivos especiais, por exemplo, o Solaris tinha portas .
Kevin
6
Nitpick menor: você não precisa recompilar o kernel para escrever um novo dispositivo de caractere / bloco :) crashcourse.ca/introduction-linux-kernel-programming/… Caso contrário, essa é uma resposta muito boa, +1!
Comandante Coentro Salamandra
1
@MathematicsOrchid: Uma etapa em que sua resposta está ausente (ou pelo menos está declarando implicitamente) é o fato de que esses arquivos especiais não são scripts ou binários de shell disfarçados (como a pergunta implicava), mas uma interface para acessar a funcionalidade presente no kernel do sistema operacional.
Dreamer
34

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

Mas o /dev/tcpque é mencionado na sua pergunta não é explicado por nenhuma das respostas existentes. /dev/tcpe /dev/udpsão diferentes da maioria das outras /deventradas. Os dispositivos de bloco e de caracteres são implementados pelo kernel, mas /dev/tcpe /dev/udpsão implementados em modo de usuário.

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

Isso é implementado no modo de usuário e apenas em alguns programas, como pode ser visto no exemplo a seguir, que demonstra a diferença entre permitir bashe cattentar 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 ksh93é que bashisso só fará essas conexões TCP com operadores de redirecionamento, e não nos outros lugares em que ele pode abrir arquivos como o sourceou .embutido.

Kasperd
fonte
Além disso, o GNU despertou gawkcasos semelhantes da mesma forma /inet{,4,6}/{tcp,udp}/$port/$remote/$rport, desde algo em torno de 2010 (não me lembro exatamente e não consigo encontrar notas de versão).
Dave_thompson_085
6
Na IMO, a melhor maneira de afirmar o ponto /dev/tcpé que NÃO é um arquivo. Nunca existe um arquivo chamado isso. A sintaxe de Bash para abrir soquetes usa a string /dev/tcp/addresscomo um nome de arquivo, mas chamá-la de "arquivo implementado no espaço do usuário" parece estranha. Interessante que kshconecta esses nomes de arquivos a tudo, não apenas redireciona. Isso está mais perto de "implementar um arquivo".
Peter Cordes
@ PeterCordes Eu acredito que o UWIN os configura como arquivos reais. e acho que o 3dfs faz o mesmo. lembre-se, bashapenas copiou esse comportamento, mas ele se origina em outro lugar.
mikeserv
19

Além dos nós do dispositivo explicados em outras respostas (criadas com o mknod (2) ou fornecidas por alguns devfs ), o Linux possui outros arquivos "mágicos" fornecidos por sistemas de arquivos virtuais especiais , especialmente em /proc/(consulte proc (5) , leia sobre procfs ) e em /sys/(leia sobre sysfs ).

Esses pseudo arquivos (que aparecem -eg para stat (2) - como arquivos comuns, não como dispositivos) são uma visão virtual fornecida pelo kernel; em particular, a leitura de /proc/(por exemplo, com cat /proc/$$/mapsou por open (2) ing /proc/self/statusem seu programa), geralmente não envolvem qualquer física I / O de disco ou rede, para que é bastante rápido.

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

Basile Starynkevitch
fonte
3
AFAIK as informações sobre extensão / processo estão desatualizadas. Embora tecnicamente possível, / proc (ou melhor, procfs) deve conter apenas informações sobre processos em execução. Todos os outros pseudo arquivos, incluindo aqueles que contêm informações de tempo de execução ou opções de configuração para o kernel, devem entrar em / sys (sysfs). Ainda existem alguns pseudo arquivos não relacionados ao processo em / proc (por exemplo, meminfo, cpuinfo) por motivos de compatibilidade, mas novos pseudo arquivos devem entrar no sysfs.
Dreamer
13

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

Também existem outros tipos de arquivos "especiais", incluindo pipes nomeados, fifos e soquetes.

cas
fonte
9

Como outros usuários já explicaram detalhadamente, arquivos especiais requerem 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:

R. O FUSE (sistema de arquivos no USErspace) permite escrever algo como /procsem risco de travar o kernel e fazê-lo em um idioma / tempo de execução de sua escolha, como Go , 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 sudoporque são executados como o usuário que está montando.

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

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

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

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

Exemplos de uso de entrada do mundo real incluem:

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

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

O único usuário da API CUSE que estou pessoalmente ciente é o mesmo programa que levou a sua criação: osspd , que implementa /dev/dsp, /dev/adspe /dev/mixer(a API de áudio OSS) no espaço do usuário para que possam ser encaminhados através PulseAudio ou dmix.

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

D. Você pode não precisar de um novo arquivo especial.

Por exemplo, você pode abrir a comunicação bruta com qualquer dispositivo USB usando libusb (Lista de ligações na página) e depois se comunicar com outros programas através de algum outro mecanismo (soquetes TCP / UDP, leitura / gravação de stdin / stdout ou arquivos regulares no disco etc.).

ssokolow
fonte
1
cusepy pode não ter sido atualizado há algum tempo (na verdade, nunca foi atualizado; possui apenas uma confirmação!), mas depois de escrever um dispositivo de caractere usando cusepy há algumas semanas, posso confirmar que ainda funciona bem. Faltam algumas funções relacionadas à implementação poll, mas como o cusepy usa ctypes e as ligações são geradas automaticamente com base nos arquivos de cabeçalho C, corrigir todas as funções ausentes é apenas uma questão de adicionar o nome da função desejada à lista de funções exportadas no setup.py.
Aleksi Torhamo
1
Outro exemplo interessante do uso do FUSE é o sshfs . Ele permite que você navegue no sistema de arquivos remoto como se fosse local usando a conexão SSH abaixo.
Mr. Deathless
@ Mr.Deathless Sim. Na verdade, eu uso isso e pretendia mencioná-lo, mas eu esqueci.
ssokolow
6

O livro Linux Device Drivers (altamente recomendado) explica isso em detalhes, e você ainda cria um módulo do 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 , leia, escreva etc. Os arquivos "especiais" fazem algo especial nessas funções, em vez de acessar o hardware de armazenamento em um disco.

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

Karl Bielefeldt
fonte
1

mount -t devtmpfs

Também é interessante ver que, em sistemas modernos, /devnormalmente é do 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 CONFIG_DEVTMPFS=ye permite que o próprio kernel crie e destrua arquivos de dispositivo, conforme necessário.

CONFIG_DEVTMPFS_MOUNT=y

Esta opção ativa o devtmpfs de montagem automática do kernel /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

Por fim, você deve criar seu próprio módulo do kernel do dispositivo de caracteres para ver exatamente o que está acontecendo.

Aqui está um exemplo mínimo executável: Noções básicas sobre arquivos de dispositivo de caractere (ou caractere especial)

O passo mais importante é configurar a file_operationsestrutura, 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 do sistema relacionada a arquivos.

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

Crie /deventradas automaticamente semmknod

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

O mecanismo pode ser observado criando um módulo do kernel que faz isso sozinho, como mostrado em: https://stackoverflow.com/questions/5970595/how-to-create-a-device-node-from-the-init-module- code-of-a-linux-kernel-module / 45531867 # 45531867 e se resume a uma device_createchamada.

Ciro Santilli adicionou uma nova foto
fonte
No OpenBSD, existe um script MAKEDEV que simplifica um pouco isso, veja man.openbsd.org/MAKEDEV.8 Não sei por que o Linux não o possui, exceto que é muito mais complicado. Talvez as peças possam ser adaptadas. Você pode dizer MKNOD tty por exemplo e ele lida com os detalhes.
Alan Corey