O que são diretórios, se tudo no Linux é um arquivo?

17

Muitas vezes, os iniciantes ouvem uma frase "Tudo é um arquivo no Linux / Unix". No entanto, quais são os diretórios então? Como eles são diferentes dos arquivos?

Sergiy Kolodyazhnyy
fonte

Respostas:

22

Nota: originalmente isso foi escrito para dar suporte à minha resposta para Por que o diretório atual no lscomando é identificado como vinculado a si mesmo? mas senti que esse é um tópico que merece ser autônomo e, portanto, essas perguntas e respostas .

Entendendo o sistema de arquivos e arquivos Unix / Linux: Tudo é um inode

Essencialmente, um diretório é apenas um arquivo especial, que contém a lista de entradas e seu ID.

Antes de começarmos a discussão, é importante fazer uma distinção entre alguns termos e entender o que os diretórios e arquivos realmente representam. Você pode ter ouvido a expressão "Tudo é um arquivo" para Unix / Linux. Bem, o que os usuários geralmente entendem como arquivo é o seguinte: /etc/passwd- Um objeto com um caminho e um nome. Na realidade, um nome (seja um diretório ou arquivo, ou qualquer outra coisa) é apenas uma sequência de texto - uma propriedade do objeto real. Esse objeto é chamado inode ou número I e armazenado em disco na tabela de inodes. Os programas abertos também têm tabelas de inodes, mas essa não é a nossa preocupação no momento.

A noção de diretório do Unix é como Ken Thompson disse em uma entrevista de 1989 :

... E então alguns desses arquivos eram diretórios que apenas continham nome e número I.

Uma observação interessante pode ser feita a partir da palestra de Dennis Ritchie em 1972 que

"... o diretório, na verdade, não passa de um arquivo, mas seu conteúdo é controlado pelo sistema e o conteúdo é o nome de outros arquivos. (Um diretório às vezes é chamado de catálogo em outros sistemas.)"

... mas não há menção de inodes em nenhum lugar da conversa. No entanto, o manual de 1971 sobre os format of directoriesestados:

O fato de um arquivo ser um diretório é indicado por um pouco na palavra sinalizada de sua entrada no nó i.

As entradas do diretório têm 10 bytes. A primeira palavra é o nó i do arquivo representado pela entrada, se não for zero; se zero, a entrada está vazia.

Então está lá desde o começo.

O emparelhamento de diretório e inode também é explicado em Como as estruturas de diretório são armazenadas no sistema de arquivos UNIX? . um diretório em si é uma estrutura de dados, mais especificamente: uma lista de objetos (arquivos e números de inode) apontando para listas sobre esses objetos (permissões, tipo, proprietário, tamanho, etc.). Portanto, cada diretório contém seu próprio número de inode e, em seguida, nomes de arquivos e seus números de inode. O mais famoso é o inode # 2, que é o /diretório . (Observe que, embora isso /deve ; ou seja, um inode seja único em seu próprio sistema de arquivos, mas com vários sistemas de arquivos conectados, você possui inodes não exclusivos). o diagrama emprestado da pergunta vinculada provavelmente explica de forma mais sucinta:/run são sistemas de arquivos virtuais, assim que uma vez que são pastas de raiz para o seu sistema de arquivos, eles também têm inode 2

Diretório-iNode-Block

Todas essas informações armazenadas no inode podem ser acessadas através de stat()chamadas do sistema, conforme Linux man 7 inode:

Cada arquivo possui um inode contendo metadados sobre o arquivo. Um aplicativo pode recuperar esses metadados usando stat (2) (ou chamadas relacionadas), que retorna uma estrutura stat, ou statx (2), que retorna uma estrutura statx.

É possível acessar um arquivo apenas sabendo seu número de inode ( ref1 , ref2 )? Em algumas implementações do Unix, é possível, mas ignora as verificações de permissão e acesso; portanto, no Linux, não é implementado, e você precisa percorrer a árvore do sistema de arquivos (por find <DIR> -inum 1234exemplo, para obter um nome de arquivo e seu inode correspondente).

No nível do código-fonte, ele é definido na fonte do kernel Linux e também é adotado por muitos sistemas de arquivos que funcionam em sistemas operacionais Unix / Linux, incluindo sistemas de arquivos ext3 e ext4 (padrão do Ubuntu). O interessante: com os dados sendo apenas blocos de informações, o Linux realmente possui a função inode_init_always que pode determinar se um inode é um pipe ( inode->i_pipe). Sim, soquetes e tubulações também são tecnicamente arquivos - arquivos anônimos, que podem não ter um nome de arquivo no disco. FIFOs e soquetes de domínio Unix têm nomes de arquivos no sistema de arquivos.

Os dados em si podem ser únicos, mas os números dos inodes não são únicos. Se tivermos um link físico para foo chamado foobar, isso também apontará para o inode 123. Esse próprio inode contém informações sobre quais blocos reais de espaço em disco são ocupados por esse inode. E é tecnicamente assim que você pode estar .sendo vinculado ao nome do arquivo do diretório. Bem, quase: você não pode criar hardlinks para diretórios no Linux , mas os sistemas de arquivos podem permitir hard links para diretórios de uma maneira muito disciplinada, o que faz uma restrição de ter apenas .e ..como hard links.

Árvore de diretórios

Os sistemas de arquivos implementam uma árvore de diretórios como uma das estruturas de dados da árvore. Em particular,

  • ext3 e ext4 usam HTree
  • xfs usa árvore B +
  • O zfs usa árvore de hash

O ponto principal aqui é que os próprios diretórios são nós em uma árvore e os subdiretórios são nós filhos, com cada filho tendo um link para o nó pai. Portanto, para um link de diretório, a contagem de inodes é no mínimo 2 para um diretório simples (link para o nome do diretório /home/example/e link para self /home/example/.) e cada subdiretório adicional é um link / nó extra:

# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24

O diagrama encontrado na página do curso de Ian D. Allen mostra um diagrama muito claro e simplificado:

WRONG - names on things      RIGHT - names above things
=======================      ==========================

    R O O T            --->         [etc,bin,home]   <-- ROOT directory
   /   |   \                         /    |      \
etc   bin   home       --->  [passwd]  [ls,rm]  [abcd0001]
 |   /   \    \                 |      /    \       |
 |  ls   rm  abcd0001  --->     |  <data>  <data>  [.bashrc]
 |               |              |                   |
passwd       .bashrc   --->  <data>                <data>

A única coisa incorreta no diagrama RIGHT é que os arquivos não são tecnicamente considerados como estando na própria árvore de diretórios: A adição de um arquivo não afeta a contagem de links:

$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4

Acessando diretórios como se fossem arquivos

Para citar Linus Torvalds :

O ponto principal de "tudo é um arquivo" não é que você tenha algum nome de arquivo aleatório (na verdade, soquetes e tubulações mostram que "arquivo" e "nome do arquivo" não têm nada a ver um com o outro), mas o fato de que você pode usar ferramentas para operar em coisas diferentes.

Considerando que um diretório é apenas um caso especial de um arquivo, naturalmente deve haver APIs que nos permitam abri- los / ler / escrever / fechá- los de maneira semelhante aos arquivos regulares.

É aí que a dirent.hbiblioteca C entra em ação , que define a direntestrutura, que você pode encontrar no man 3 readdir :

   struct dirent {
       ino_t          d_ino;       /* Inode number */
       off_t          d_off;       /* Not an offset; see below */
       unsigned short d_reclen;    /* Length of this record */
       unsigned char  d_type;      /* Type of file; not supported
                                      by all filesystem types */
       char           d_name[256]; /* Null-terminated filename */
   };

Assim, no seu código C você precisa definir struct dirent *entry_pe, quando abrirmos um diretório opendir()e começarmos a lê-lo readdir(), armazenaremos cada item nessa entry_pestrutura. Obviamente, cada item conterá os campos definidos no modelo para direntmostrado acima.

O exemplo prático de como isso funciona pode ser encontrado na minha resposta em Como listar arquivos e seus números de inode no diretório de trabalho atual .

Observe que o manual do POSIX no fdopen declara que "[as entradas de diretório para ponto e ponto-ponto são opcionais" e os estados do manual readdir struct dirent são necessários apenas para ter d_namee d_inocampos.

Nota sobre "gravação" em diretórios: a gravação em um diretório está modificando sua "lista" de entradas. Portanto, a criação ou remoção de um arquivo está diretamente associada às permissões de gravação de diretório e a adição / remoção de arquivos é a operação de gravação no referido diretório.

Sergiy Kolodyazhnyy
fonte
2
Recuso-me a aceitar soquetes são arquivos;) "Tudo é acessível como um arquivo" seria mais preciso?
Rinzwind 10/09
@Rinzwind Bem, a frase "tudo é acessível como um arquivo" é precisa. Arquivos regulares têm open()e read()soquetes têm connect()e read()também. O que seria mais preciso é que "arquivo" seja realmente "dados" organizados armazenados no disco ou na memória, e alguns arquivos são anônimos - eles não têm nome de arquivo. Geralmente, os usuários pensam nos arquivos em termos desse ícone na área de trabalho, mas essa não é a única coisa que existe. Veja também unix.stackexchange.com/a/116616/85039
Sergiy Kolodyazhnyy 10/09/18
Bem, a questão era mais sobre se um diretório era um arquivo. E isso é. Os soquetes podem quase ser uma pergunta separada junto com os tubos nomeados FIFO.
WinEunuuchs2Unix
Bem, eu tenho uma resposta sobre tubos até agora: askubuntu.com/a/1074550/295286 Talvez FIFOs será o próximo
Sergiy Kolodyazhnyy