Supponiamo che io acceda a una shell su un sistema Unix e inizi a eliminare i comandi. Inizialmente inizio nella directory home del mio utente ~
. Potrei da lì cd
fino alla directory Documents
.
Il comando per cambiare directory di lavoro qui è molto semplice da capire intuitivamente:il nodo padre ha un elenco di nodi figli a cui può accedere e presumibilmente usa una variante (ottimizzata) di una ricerca per individuare l'esistenza di un nodo figlio con il nominare l'utente inserito e la directory di lavoro viene quindi "modificata" in modo che corrisponda a questo - correggimi se sbaglio lì. Potrebbe anche essere più semplice che la shell semplicemente "ingenuamente" tenti di accedere alla directory esattamente secondo i desideri dell'utente e quando il file system restituisce un qualche tipo di errore, la shell mostra una risposta di conseguenza.
Ciò che mi interessa, tuttavia, è come funziona lo stesso processo quando esploro una directory, ad esempio verso un genitore o il genitore di un genitore.
Data la mia posizione sconosciuta, presumibilmente "cieca" di Documents
, una delle possibilmente molte directory nell'intero albero del file system con quel nome, in che modo Unix determina dove dovrei essere posizionato dopo? Fa riferimento a pwd
ed esaminarlo? Se sì, come funziona pwd
monitorare lo stato di navigazione corrente?
Risposta accettata:
Le altre risposte sono semplificazioni eccessive, ciascuna presenta solo parti della storia e sono sbagliate su un paio di punti.
Ce ne sono due modi in cui viene tracciata la directory di lavoro:
- Per ogni processo, nella struttura dati dello spazio kernel che rappresenta quel processo, il kernel memorizza due riferimenti vnode ai vnode della directory di lavoro e alla directory root per quel processo. Il primo riferimento è impostato da
chdir()
efchdir()
chiamate di sistema, quest'ultimo dachroot()
. Si possono vedere indirettamente in/proc
su sistemi operativi Linux o tramitefstat
comando su FreeBSD e simili:% fstat -p $$|head -n 5 USER CMD PID FD MOUNT INUM MODE 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 la risoluzione del percorso è attiva, inizia dall'uno o dall'altro di quei vnode di riferimento, a seconda che il percorso sia relativo o assoluto. (Esiste una famiglia di
…at()
chiamate di sistema che consentono alla risoluzione del percorso di iniziare nel vnode a cui fa riferimento un descrittore di file aperto (directory) come terza opzione.)Nel microkernel Unices la struttura dei dati è nello spazio delle applicazioni, ma il principio di mantenere i riferimenti aperti a queste directory rimane lo stesso.
- Internamente, all'interno di shell come Z, Korn, Bourne Again, C e Almquist, la shell in aggiunta tiene traccia della directory di lavoro usando la manipolazione delle stringhe di una variabile stringa interna. Lo fa ogni volta che ha motivo di chiamare
chdir()
.Se si cambia in un percorso relativo, manipola la stringa per aggiungere quel nome. Se si cambia in un percorso assoluto, si sostituisce la stringa con il nuovo nome. In entrambi i casi, regola la stringa per rimuovere
.
e..
componenti e per inseguire i collegamenti simbolici sostituendoli con i loro nomi collegati. (Ecco il codice della shell Z per questo, ad esempio.)Il nome nella variabile stringa interna è tracciato da una variabile shell denominato
PWD
(ocwd
nelle conchiglie C). Questo viene convenzionalmente esportato come una variabile di ambiente (denominataPWD
) ai programmi generati dalla shell.
Questi due metodi per tracciare le cose sono rivelati dal -P
e -L
opzioni al cd
e pwd
comandi incorporati della shell e dalle differenze tra pwd
incorporata nelle shell comandi e sia il /bin/pwd
comando e il pwd
integrato comandi di cose come (tra gli altri) VIM e NeoVIM.
% mkdir a ; ln -s a b % (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=/hello/there /bin/pwd -L) /usr/home/JdeBP/a %Correlati:Alla ricerca di un editor di file GUI alternativo con supporto per file di grandi dimensioni?
Come puoi vedere:ottenere la directory di lavoro “logica” è questione di guardare il PWD
variabile shell (o variabile d'ambiente se non si tratta del programma shell); mentre per ottenere la directory di lavoro "fisica" si tratta di chiamare getcwd()
funzione libreria.
Il funzionamento del /bin/pwd
programma quando -L
l'opzione è usata è alquanto sottile. non può fidarsi il valore del PWD
variabile di ambiente che ha ereditato. Dopotutto, non è necessario che sia stato invocato da una shell e i programmi intermedi potrebbero non aver implementato il meccanismo della shell per creare il PWD
la variabile di ambiente tiene sempre traccia del nome della directory di lavoro. Oppure qualcuno potrebbe fare quello che ho fatto io proprio lì.
Quindi quello che fa è (come dice lo standard POSIX) controllare che il nome dato in PWD
restituisce la stessa cosa del nome .
, come si può vedere con una traccia di chiamata di sistema:
% ln -s a c % (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,size=2,blksize=131072 }) = 0 (0x0) stat(".",{ mode=drwxr-xr-x ,inode=120932,size=2,blksize=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,size=158,blksize=10240 }) = 0 (0x0) stat(".",{ mode=drwxr-xr-x ,inode=120932,size=2,blksize=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",0x7fffffffe730) ERR#2 'No such file or directory' __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,size=2,blksize=131072 }) = 0 (0x0) stat(".",{ mode=drwxr-xr-x ,inode=120932,size=2,blksize=131072 }) = 0 (0x0) /usr/home/JdeBP/c %
Come puoi vedere:chiama solo getcwd()
se rileva una mancata corrispondenza; e può essere ingannato impostando PWD
a una stringa che in effetti denomina la stessa directory, ma con un percorso diverso.
Il getcwd()
la funzione di biblioteca è un argomento a sé stante. Ma per la precisione:
- In origine era puramente una funzione di libreria, che creava un percorso dalla directory di lavoro fino alla radice tentando ripetutamente di cercare la directory di lavoro nel
..
directory. Si è fermato quando ha raggiunto un loop in cui..
era la stessa della sua directory di lavoro o quando si è verificato un errore durante il tentativo di aprire il successivo..
su. Sarebbero molte chiamate di sistema nascoste. - Oggi la situazione è leggermente più complessa. Su FreeBSD, per esempio (questo vale anche per altri sistemi operativi), è una vera chiamata di sistema, come puoi vedere nella traccia della chiamata di sistema fornita in precedenza. Tutto l'attraversamento dalla directory di lavoro vnode fino a root viene eseguito in una singola chiamata di sistema, che sfrutta cose come l'accesso diretto del codice in modalità kernel alla cache delle voci di directory per eseguire le ricerche dei componenti del percorso in modo molto più efficiente.
Tuttavia, nota che anche su FreeBSD e quegli altri sistemi operativi il kernel non tieni traccia della directory di lavoro con una stringa.
Passando a ..
è di nuovo un soggetto a sé stante. Un'altra precisazione:sebbene le directory convenzionalmente (sebbene, come già accennato, questo non richiesto) contengono un ..
effettivo nella struttura dei dati della directory su disco, il kernel tiene traccia della directory padre di ciascuna directory vnode stessa e può quindi passare al ..
vnode di qualsiasi directory di lavoro. Questo è in qualche modo complicato dal punto di montaggio e dai meccanismi di root modificati, che esulano dallo scopo di questa risposta.
A parte
Windows NT infatti fa una cosa simile. Esiste una singola directory di lavoro per processo, impostata da SetCurrentDirectory()
Chiamata API e tracciata per processo dal kernel tramite un handle di file aperto (interno) in quella directory; e c'è un insieme di variabili d'ambiente che Win32 programma (non solo gli interpreti dei comandi, ma tutti Win32) utilizzano per tenere traccia dei nomi di più directory di lavoro (una per unità), aggiungendole o sovrascrivendole ogni volta che cambiano directory.
Convenzionalmente, a differenza dei sistemi operativi Unix e Linux, i programmi Win32 non mostrano queste variabili di ambiente agli utenti. Tuttavia, a volte è possibile vederli in sottosistemi simili a Unix in esecuzione su Windows NT, nonché utilizzando il SET
degli interpreti dei comandi comandi in un modo particolare.
Ulteriori letture
- “
pwd
“. Le specifiche di Open Group Base Edizione 7. IEEE 1003.1:2008. Il gruppo aperto. 2016. - "Risoluzione del percorso". Le specifiche di Open Group Base Edizione 7. IEEE 1003.1:2008. Il gruppo aperto. 2016.
- https://askubuntu.com/a/636001/43344
- Come vengono aperti i file in unix?
- a cosa serve l'inode, in FreeBSD o Solaris
- Strana variabile d'ambiente!::=::in Cygwin
- Perché CDPATH non funziona come documentato nei manuali?
- Come posso impostare zsh per utilizzare percorsi fisici?
- Andare in una directory collegata da un link