Como o Unix controla o diretório de trabalho de um usuário ao navegar no sistema de arquivos?

29

Digamos que eu entre no shell de um sistema unix e comece a digitar os comandos. Inicialmente, começo no diretório inicial do meu usuário ~. Eu poderia de lá cdpara o diretório Documents.

O comando para alterar o diretório de trabalho aqui é muito simples de entender intuitivamente: o nó pai possui uma lista de nós filhos que ele pode acessar e, presumivelmente, usa uma variante (otimizada) de uma pesquisa para localizar a existência de um nó filho com o nomeie o usuário digitado e o diretório de trabalho será "alterado" para corresponder a isso - me corrija se eu estiver errado lá. Pode até ser mais simples que o shell simplesmente "ingênuo" tente acessar o diretório exatamente conforme os desejos do usuário e, quando o sistema de arquivos retornar algum tipo de erro, o shell exibirá uma resposta de acordo.

No entanto, estou interessado em saber como o mesmo processo funciona quando navego em um diretório, ou seja, para um pai ou pai.

Dada minha localização desconhecida, presumivelmente "cega" de Documents, um dos possivelmente muitos diretórios em toda a árvore do sistema de arquivos com esse nome, como o Unix determina onde eu devo ser colocado em seguida? Faz uma referência pwde examina isso? Se sim, como pwdacompanha o estado atual de navegação?

ReactingToAngularVues
fonte
1
Veja também Recursão de link simbólico - o que o torna "redefinido"? onde muito disso é explicado
Stéphane Chazelas

Respostas:

76

As outras respostas são simplificações excessivas, cada uma apresentando apenas partes da história, e estão erradas em alguns pontos.

duas maneiras pelas quais o diretório de trabalho é rastreado:

  • Para cada processo, na estrutura de dados do espaço do kernel que representa esse processo, o kernel armazena duas referências de vnode aos vnodes do diretório de trabalho e o diretório raiz desse processo. A referência anterior é definida pelas chamadas chdir()e do fchdir()sistema, e a última por chroot(). Pode-se vê-los indiretamente nos /procsistemas operacionais Linux ou através do fstatcomando no FreeBSD e similares:

    % fstat -p $$ | cabeça -n 5
    USUÁRIO CMD PID FD MOUNT INUM MODO SZ | DV R / W
    JdeBP zsh 92648 text / 24958 -r-xr-xr-x 702360 r
    JdeBP zsh 92648 ctty / dev 148 crw - w ---- pts / 4 rw
    JdeBP zsh 92648 wd / usr / home / JdeBP 4 drwxr-xr-x 124 r
    JdeBP zsh 92648 root / 4 drwxr-xr-x 35 r
    % 

    Quando a resolução do nome do caminho opera, ela começa em um ou outro daqueles vnodes referenciados, dependendo se o caminho é relativo ou absoluto. (Há uma família de …at()chamadas de sistema que permitem que a resolução do nome do caminho comece no vnode referenciado por um descritor de arquivo aberto (diretório) como uma terceira opção.)

    No microkernel Unices, a estrutura de dados está no espaço do aplicativo, mas o princípio de manter referências abertas a esses diretórios permanece o mesmo.

  • Internamente, dentro de shells como o shell Z, Korn, Bourne Again, C e Almquist, o shell também acompanha o diretório de trabalho usando a manipulação de strings de uma variável interna de strings. Faz isso sempre que houver motivo para ligar chdir().

    Se alguém mudar para um nome de caminho relativo, ele manipula a string para acrescentar esse nome. Se alguém mudar para um nome de caminho absoluto, ele substituirá a sequência pelo novo nome. Nos dois casos, ajusta a sequência para remover .e ..componentes e perseguir links simbólicos, substituindo-os pelos nomes vinculados. ( Aqui está o código do shell Z para isso , por exemplo).

    O nome na variável de cadeia interna é rastreado por uma variável de shell denominada PWD(ou cwdnos shells C). Isso é convencionalmente exportado como uma variável de ambiente (nomeada PWD) para programas gerados pelo shell.

Estes dois métodos de coisas rastreamento são reveladas pelos -Pe -Lopções para o cde pwdshell built-in comandos e pelas diferenças entre as conchas Built-in pwdcomandos e tanto o /bin/pwdcomando e o built-in pwdcomandos de coisas como (entre outros) VIM e NeoVIM.

% mkdir a; ln -sab 
% (cd b; pwd; / bin / pwd; printenv PWD)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / a
/ usr / home / JdeBP / b
% (cd b; pwd -P; / bin / pwd -P)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; pwd -L; / bin / pwd -L)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / b
% (cd -P b; pwd; / bin / pwd; printenv PWD)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; PWD = / olá / lá / bin / pwd -L)
/ usr / home / JdeBP / a
% 

Como você pode ver: obter o diretório de trabalho "lógico" é uma questão de olhar para a PWDvariável shell (ou variável de ambiente, se não for o programa shell); enquanto que obter o diretório de trabalho "físico" é uma questão de chamar a getcwd()função de biblioteca.

A operação do /bin/pwdprograma quando a -Lopção é usada é um pouco sutil. Ele não pode confiar no valor da PWDvariável de ambiente que herdou. Afinal, ele não precisa ter sido chamado por um shell e os programas intervenientes podem não ter implementado o mecanismo do shell de tornar a PWDvariável de ambiente sempre rastrear o nome do diretório de trabalho. Ou alguém pode fazer o que eu fiz lá.

Então, o que ele faz é (como o padrão POSIX diz) verificar se o nome fornecido PWDproduz a mesma coisa que o nome ., como pode ser visto em um rastreamento de chamada do sistema:

% ln -sac 
% (cd b; truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / b", { mode = drwxr-xr-x, inode = 120932, tamanho = 2, tamanho do bloco = 131072}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, tamanho = 2, tamanho do bloco = 131072}) = 0 (0x0)
/ usr / home / JdeBP / b
% (cd b; PWD = / usr / local / etc truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / local / etc" , {mode = drwxr-xr-x, inode = 14835, tamanho = 158, tamanho do bloco = 10240}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, size = 2 , tamanho do bloco = 131072}) = 0 (0x0)
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / hello / there truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ hello / there", 0x7fffffffefe730) ERR # 2 'Não 
existe esse arquivo ou diretório' __getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / usr / home / JdeBP / c truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / c ", {mode = drwxr-xr-x, inode = 120932, tamanho = 2, tamanho do bloco = 131072}) = 0 (0x0) 
stat (". ", {Mode = drwxr-xr-x, inode = 120932 , tamanho = 2, tamanho do bloco = 131072}) = 0 (0x0)
/ usr / home / JdeBP / c
%

Como você pode ver: ele só chama getcwd()se detectar uma incompatibilidade; e pode ser enganado definindo PWDuma cadeia que de fato nomeia o mesmo diretório, mas por uma rota diferente.

A getcwd()função de biblioteca é um assunto em si. Mas précis:

  • Originalmente, era puramente uma função de biblioteca, que criava um nome de caminho do diretório ativo de volta à raiz, tentando repetidamente procurar o diretório ativo no ..diretório. Ele parou quando atingiu um loop em que ..era o mesmo que seu diretório de trabalho ou quando ocorreu um erro ao tentar abrir a próxima ... Seria muitas chamadas de sistema ocultas.
  • Atualmente a situação é um pouco mais complexa. No FreeBSD, por exemplo (isso também é válido para outros sistemas operacionais), é uma chamada de sistema verdadeira, como você pode ver no rastreamento de chamadas de sistema fornecido anteriormente. Todo o percurso do diretório ativo vnode até a raiz é feito em uma única chamada do sistema, que tira proveito de coisas como o acesso direto do código do modo kernel ao cache de entrada do diretório para fazer as pesquisas de componentes do nome do caminho com muito mais eficiência.

    No entanto, observe que mesmo no FreeBSD e nesses outros sistemas operacionais, o kernel não controla o diretório de trabalho com uma string.

Navegar para ..é novamente um assunto em si. Outra característica: embora os diretórios convencionalmente (embora, como já mencionado, isso não seja necessário) contenham um real ..na estrutura de dados do diretório no disco, o kernel rastreia o diretório pai de cada diretório do próprio nó e, portanto, pode navegar para o ..nó de qualquer diretório de trabalho. Isso é um pouco complicado pelo ponto de montagem e pelos mecanismos raiz alterados, que estão além do escopo desta resposta.

a parte, de lado

Windows NT, de fato, faz uma coisa semelhante. Há um único diretório de trabalho por processo, definido pela SetCurrentDirectory()chamada da API e rastreado por processo pelo kernel por meio de um identificador de arquivo aberto (interno) para esse diretório; e há um conjunto de variáveis ​​de ambiente que os programas Win32 (não apenas os intérpretes de comando, mas todos os programas Win32) usam para rastrear os nomes de vários diretórios de trabalho (um por unidade), anexando-os ou substituindo-os sempre que eles mudam de diretório.

Convencionalmente, diferentemente do caso dos sistemas operacionais Unix e Linux, os programas Win32 não exibem essas variáveis ​​de ambiente para os usuários. Às vezes, é possível vê-los em subsistemas do tipo Unix em execução no Windows NT, bem como usando os comandos dos intérpretes de SETcomando de uma maneira específica.

Leitura adicional

JdeBP
fonte
1
Isso é muito mais do que eu esperava. Obrigado e muito obrigado pela leitura adicional!
ReactingToAngularVues
doc.cat-v.org/plan_9/4th_edition/papers/lexnames fala sobre alguns dos problemas ..no contexto do Plan9,
icarus
@JdeBP: Talvez esteja faltando alguma coisa. Você diz: “Internamente, dentro de…, bash,… e…, o shell também controla o diretório de trabalho usando a manipulação de string de uma variável interna de string. …, Ajusta a string para remover .e ..componentes e perseguir links simbólicos, substituindo-os por seus nomes vinculados. … O nome na variável interna da string é rastreado por uma variável do shell chamada PWD… ”(ênfase adicionada). ... (continua)
G-Man diz 'Restabelecer Monica' em
(Continua)… Mas seu exemplo mostra PWD= …/bapós um cd bcomando, mesmo que bseja um link simbólico para a- para que o shell não “persiga” o a -> blink. Você deturpou ou interpretou mal?
G-Man diz 'Reinstate Monica'
Eu simplesmente encobri um ponto lateral e apontei para o código para obter detalhes. Veja os manuais dos vários reservatórios para saber quando e como eles decidem perseguir links simbólicos ou não. O shell Z chama facilmente sua opção de shell que é uma parte da fórmula de decisão CHASE_LINKS,.
JdeBP # 8/18
1

O kernel não controla os nomes de diretório ou arquivo; um arquivo ou diretório é representado no kernel por um par inode / dispositivo. Chamadas de sistema, como chdir(), open(), etc. tomar um caminho como parâmetro, que pode ser absoluta (por exemplo /etc/passwd), ou relativos ao diretório atual (exemplos: Documents, ..). Quando um processo é executado chdir("Documents"), é feita uma pesquisa Documentsno diretório de trabalho atual e o diretório de trabalho do processo é atualizado para se referir a esse diretório. Da perspectiva do kernel, não há nada de especial no nome "..", é apenas uma convenção no sistema de arquivos que ..se refere ao diretório pai.

A getcwd()função não é uma chamada de sistema, mas uma função de biblioteca que precisa ir até o diretório raiz, registrando os nomes dos componentes do caminho no caminho.

Johan Myréen
fonte
0

Curiosamente, tradicionalmente cd ..é muito, muito mais simples do que pwd. Diretórios nomeados ..são colocados explicitamente no sistema de arquivos. O sistema controla o dispositivo / inode do diretório atual; portanto, cd ..ou com mais precisão, a chamada do sistema chdir("..")implica procurar o nome ".." no arquivo pertencente ao inode do diretório atual e alterar o dispositivo / inode do diretório atual para o diretório valor encontrado lá.

pwd(com mais precisão /bin/pwd) segue os ..links sucessivamente e lê os respectivos diretórios até encontrar o inode de onde veio, montando a lista desses nomes em ordem inversa até atingir o diretório raiz (principalmente sem conter uma ..entrada).

Agora, este é o comportamento básico de baixo nível original. Em pwdvez disso, os comandos shell reais contam com uma variedade de técnicas que armazenam em cache o nome do caminho atual. Mas, no fundo, é apenas o seu inode que é realmente conhecido. Isso implica que, uma vez que os links simbólicos sejam usados ​​para navegar nos diretórios, as noções atuais de nome do diretório de trabalho do shell atual e do sistema /bin/pwdpoderão divergir.


fonte