Como o Linux diferencia entre arquivos reais e não-existentes (ex: device)?

28

Esta é uma pergunta de baixo nível, e eu entendo que talvez não seja o melhor lugar para perguntar. Mas, parecia mais apropriado que qualquer outro site da SE, então aqui vai.

Eu sei que no sistema de arquivos Linux, alguns arquivos existem , por exemplo: /usr/bin/bash é aquele que existe. No entanto, (tanto quanto eu entendo), alguns também não existem realmente como tal e são mais virtual arquivos, por exemplo: /dev/sda , /proc/cpuinfo , etc. Minhas perguntas são (eles são dois, mas intimamente relacionados para serem questões separadas):

  • Como o kernel do Linux determina se esses arquivos são reais (e, portanto, os lêem do disco) ou não quando um comando de leitura (ou algo semelhante) é emitido?
  • Se o arquivo não for real: como exemplo, uma leitura de /dev/random retornará dados aleatórios e uma leitura de /dev/null retornará EOF . Como funcionam os dados a serem lidos nesse arquivo virtual (e, portanto, o que fazer quando / se os dados forem gravados no arquivo virtual também) - existe algum tipo de mapa com ponteiros para separar comandos de leitura / gravação apropriados para cada arquivo, ou mesmo para o diretório virtual em si? Então, uma entrada para /dev/null poderia simplesmente retornar um EOF .
por Joe 20.11.2015 / 15:01

5 respostas

25

Existem basicamente dois tipos diferentes de coisas aqui:

  1. Sistemas de arquivos normais, que armazenam arquivos em diretórios com dados e metadados, de maneira familiar (incluindo links, links físicos e assim por diante). Estes são frequentemente, mas não sempre, suportados por um dispositivo de bloco para armazenamento persistente (um tmpfs vive apenas na RAM, mas é de outra forma idêntico a um sistema de arquivos normal). A semântica destes é familiar; ler, escrever, renomear e assim por diante, tudo funciona da maneira que você espera.
  2. Sistemas de arquivos virtuais, de vários tipos. /proc e /sys são exemplos aqui, assim como sistemas de arquivos customizados do FUSE como sshfs ou ifuse . Há muito mais diversidade neles, porque na verdade eles apenas se referem a um sistema de arquivos com semânticas que são de alguma forma 'customizadas'. Assim, quando você lê a partir de um arquivo em /proc , você não está realmente acessando uma parte específica de dados que foi armazenada por outra coisa escrita anteriormente, como em um sistema de arquivos normal. Você está basicamente fazendo uma chamada de kernel, solicitando algumas informações geradas on-the-fly. E esse código pode fazer o que quiser, já que é apenas alguma função que implementa read semântica. Assim, você tem o comportamento estranho dos arquivos em /proc , como por exemplo, fingir ser symlinks quando eles não são realmente.

A chave é que /dev é, na verdade, um dos primeiros tipos. É normal nas distribuições modernas ter /dev algo como um tmpfs, mas em sistemas mais antigos, era normal que fosse um diretório simples no disco, sem nenhum atributo especial. A chave é que os arquivos em /dev são nós de dispositivo, um tipo de arquivo especial semelhante a FIFOs ou soquetes Unix; um nó de dispositivo tem um número maior e menor, e ler ou gravá-los é fazer uma chamada para um driver de kernel, da mesma forma que ler ou escrever um FIFO está chamando o kernel para armazenar em buffer sua saída em um pipe. Este driver pode fazer o que quiser, mas geralmente toca hardware de alguma forma, por ex. para acessar um disco rígido ou reproduzir som nos alto-falantes.

Para responder às perguntas originais:

  1. Existem duas questões relevantes para saber se o 'arquivo existe' ou não; Estes são se o arquivo do nó do dispositivo literalmente existe, e se o código do kernel é significativo. O primeiro é resolvido como qualquer coisa em um sistema de arquivos normal. Sistemas modernos usam udev ou algo parecido para assistir a eventos de hardware e automaticamente criar e destruir os nós de dispositivos sob /dev de acordo. Mas sistemas mais antigos, ou construções customizadas leves, podem ter todos os seus nós de dispositivo literalmente no disco, criados antes do tempo. Enquanto isso, quando você lê esses arquivos, você está fazendo uma chamada para o código do kernel que é determinado pelos números maiores e menores do dispositivo; Se isso não for razoável (por exemplo, você está tentando ler um dispositivo de bloco que não existe), você só receberá algum tipo de erro de E / S.

  2. A maneira como funciona o código do kernel a ser chamado para qual arquivo de dispositivo varia. Para sistemas de arquivos virtuais como /proc , eles implementam suas próprias funções read e write ; o kernel apenas chama esse código dependendo de qual ponto de montagem ele está, e a implementação do sistema de arquivos cuida do resto. Para arquivos de dispositivos, ele é enviado com base nos números principais e secundários do dispositivo.

por 20.11.2015 / 17:23
17

Aqui está uma lista de arquivos de /dev/sda1 no meu servidor Arch Linux quase atualizado:

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

Portanto, a entrada de diretório em /dev/ para sda tem um número de inode, 1294. É um arquivo real no disco.

Veja onde o tamanho do arquivo geralmente aparece. "8, 1" aparece em seu lugar. Este é um número de dispositivo maior e menor. Observe também o 'b' nas permissões de arquivo.

O arquivo /usr/include/ext2fs/ext2_fs.h contém este (fragmento) C struct:

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

Essa estrutura nos mostra a estrutura em disco do inode de um arquivo. Muita coisa interessante está nessa estrutura; dê uma boa olhada nisso.

O elemento i_mode de struct ext2_inode tem 16 bits e usa apenas 9 para o usuário / grupo / outro, permissões de leitura / gravação / execução e outros 3 para setuid, setgid e sticky. Ele tem 4 bits para diferenciar entre os tipos "arquivo simples", "link", "diretório", "pipe nomeado", "soquete da família Unix" e "dispositivo de bloco".

O kernel do Linux pode seguir o algoritmo de pesquisa de diretório usual e, em seguida, tomar uma decisão com base nas permissões e sinalizadores no elemento i_mode . Para 'b', bloquear arquivos de dispositivo, ele pode localizar os números de dispositivos maiores e menores e, tradicionalmente, usar o número do dispositivo principal para procurar um ponteiro para alguma função do kernel (um driver de dispositivo) que lida com discos. O número menor do dispositivo geralmente é usado como, digamos, o número do dispositivo de barramento SCSI ou o número do dispositivo EIDE ou algo assim.

Algumas outras decisões sobre como lidar com um arquivo como /proc/cpuinfo são feitas com base no tipo de sistema de arquivos. Se você fizer um:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

você pode ver que /proc tem o tipo de sistema de arquivos "proc". A leitura de um arquivo em /proc faz com que o kernel faça algo diferente com base no tipo do sistema de arquivos, assim como abrir um arquivo em um sistema de arquivos ReiserFS ou DOS faria com que o kernel usasse funções diferentes para localizar arquivos e localizasse dados dos arquivos.

    
por 20.11.2015 / 17:11
7

No final do dia, são todos arquivos do Unix, essa é a beleza da abstração.

A maneira como os arquivos são manipulados pelo kernel, agora é uma história diferente.

/ proc e atualmente / dev e / run (aka / var / run) são sistemas de arquivos virtuais na RAM. / proc é uma interface / janelas para variáveis e estruturas do kernel.

Eu recomendo ler o link do Kernel Linux e os drivers de dispositivo do Linux, terceira edição link .

Eu também gostei do Design e Implementação do link

do Sistema Operacional do FreeBSD

Veja a página relevante que pertence à sua pergunta.

link

    
por 20.11.2015 / 15:07
5

Além das respostas de @RuiFRibeiro e @ BruceEdiger, a distinção que você faz não é exatamente a distinção que o kernel faz. Na verdade, você tem vários tipos de arquivos: arquivos regulares, diretórios, links simbólicos, dispositivos, soquetes (e eu sempre esqueço alguns, então não tentarei fazer uma lista completa). Você pode ter as informações sobre o tipo de um arquivo com ls : é o primeiro caractere na linha. Por exemplo:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

O 'b' no início sinaliza que este arquivo é um dispositivo de bloco. Um traço, significa um arquivo regular, 'l' um link simbólico e assim por diante. Essas informações são armazenadas nos metadados do arquivo e podem ser acessadas pela chamada do sistema stat , por exemplo, para que o kernel possa ler diferentemente um arquivo e um link simbólico, por exemplo.

Em seguida, você faz outra distinção entre "arquivos reais", como /bin/bash e "arquivos virtuais", como /proc/cpuinfo , mas ls , como arquivos regulares, então a diferença é de outro tipo:

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

O que acontece é que eles pertencem a diferentes sistemas de arquivos. /proc é o ponto de montagem de um pseudo sistema de arquivos procfs , enquanto /bin/bash está em um sistema de arquivos de disco regular. Quando o Linux abre um arquivo (ele o faz de forma diferente dependendo do sistema de arquivos), ele preenche uma estrutura de dados file que tem, entre outros atributos, uma estrutura de vários ponteiros de função que descrevem como usar esse arquivo. Portanto, ele pode implementar comportamentos distintos para diferentes tipos de arquivos.

Por exemplo, estas são as operações anunciadas por /proc/meminfo :

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

Se você observar a definição de meminfo_proc_open , verá que essa função preenche um buffer na memória com as informações retornadas pela função meminfo_proc_show , cuja tarefa é coletar dados sobre o uso da memória. Esta informação pode então ser lida normalmente. Cada vez que você abre o arquivo, a função meminfo_proc_open é chamada e as informações sobre a memória são atualizadas.

    
por 20.11.2015 / 17:16
3

Todos os arquivos em um sistema de arquivos são "reais" no sentido de permitirem E / S de arquivo. Quando você abre um arquivo, o kernel cria um descritor de arquivo, que é um objeto (no sentido de programação orientada a objetos) que age como um arquivo. Se você ler o arquivo, o descritor de arquivo executará seu método de leitura, que por sua vez solicitará ao sistema de arquivos (sysfs, ext4, nfs, etc.) dados do arquivo. Os sistemas de arquivos apresentam uma interface uniforme para userspace e sabem o que fazer para lidar com leituras e gravações. Os sistemas de arquivos, por sua vez, solicitam que outras camadas manipulem suas solicitações. Para um arquivo regular em um sistema de arquivos ext4, isso envolverá pesquisas em estruturas de dados do sistema de arquivos (que podem envolver leituras de disco) e, eventualmente, uma leitura do disco (ou cache) para copiar dados no buffer de leitura. Para um arquivo em digamos sysfs, geralmente apenas sprintf () é algo para o buffer. Para um nó de block dev, ele pedirá ao driver do disco para ler alguns blocos e copiá-los no buffer (os números principal e secundário informam ao sistema de arquivos para qual driver fazer solicitações).

    
por 20.11.2015 / 18:51