Le altre risposte sono semplificazioni eccessive, ognuna delle quali presenta solo parti della storia e sono sbagliate su un paio di punti.
Esistono due modi in cui viene tracciata la directory di lavoro:
- Per ogni processo, nella struttura dei dati dello spazio kernel che rappresenta quel processo, il kernel memorizza due riferimenti vnode ai vnodi della directory di lavoro e alla directory principale per quel processo. Il primo riferimento è impostato dalle chiamate di sistema
chdir()
e fchdir()
, il secondo da chroot()
. Si possono vedere indirettamente nei /proc
sistemi operativi Linux o tramite il fstat
comando su FreeBSD e simili:% fstat -p $$ | head -n 5
UTENTE CMD PID FD MOUNT MODO INUM SZ | DV R / W
JdeBP zsh 92648 testo / 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 vnodi di riferimento, a seconda che il percorso sia relativo o assoluto. (Esiste una famiglia di …at()
chiamate di sistema che consente di iniziare la risoluzione del nome percorso nel vnode a cui fa riferimento un descrittore di file (directory) aperto come terza opzione.)
Nei microkernel Unices la struttura dei dati si trova nello spazio dell'applicazione, ma il principio di mantenere riferimenti aperti a queste directory rimane lo stesso.
- Internamente, all'interno di shell come la shell Z, Korn, Bourne Again, C e Almquist, la shell tiene inoltre traccia della directory di lavoro usando la manipolazione di stringa di una variabile di 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, sostituisce la stringa con il nuovo nome. In entrambi i casi, regola la stringa da rimuovere .
e i ..
componenti e insegue i collegamenti simbolici sostituendoli con i loro nomi collegati. ( Ecco il codice della shell Z per questo , per esempio.)
Il nome nella variabile di stringa interna è seguito da una variabile di shell denominata PWD
(o cwd
nelle shell C). Questo viene convenzionalmente esportato come variabile di ambiente (denominata PWD
) nei programmi generati dalla shell.
Questi due metodi di monitoraggio cose sono rivelate dalle -P
e -L
opzioni per l' cd
e pwd
shell built-in comandi, e le differenze tra i gusci built-in pwd
comandi e sia il /bin/pwd
comando e il built-in pwd
comandi di cose come (tra gli altri) 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 = / hello / there / bin / pwd -L)
/ Usr / home / JdeBP / a
%
Come puoi vedere: ottenere la directory di lavoro "logica" è una questione di esaminare la PWD
variabile shell (o variabile d'ambiente se non si è il programma shell); mentre ottenere la directory di lavoro "fisica" significa chiamare la getcwd()
funzione di libreria.
Il funzionamento del /bin/pwd
programma quando -L
viene utilizzata l' opzione è piuttosto sottile. Non può fidarsi del valore della PWD
variabile d'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 rendere la PWD
variabile d'ambiente tenere sempre traccia del nome della directory di lavoro. O qualcuno potrebbe fare quello che ho fatto lì.
Quindi ciò che fa è (come dice lo standard POSIX) verificare che il nome indicato in PWD
dia lo stesso risultato del nome .
, come si può vedere con una traccia di chiamata di 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, 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 'Nessun file o 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
una stringa che in effetti denomina la stessa directory, ma con un percorso diverso.
La getcwd()
funzione di libreria è un argomento a sé stante. Ma per decorare:
Navigare verso ..
è di nuovo un argomento a sé stante. Un altro vantaggio: sebbene le directory convenzionalmente (anche se, come già accennato, non sono necessarie) contengono un effettivo ..
nella struttura dei dati della directory su disco, il kernel tiene traccia della directory padre di ogni vnode di directory stesso e può quindi navigare nel ..
vnode di qualsiasi directory di lavoro. Ciò è in qualche modo complicato dal mountpoint e dai meccanismi di root modificati, che vanno oltre lo scopo di questa risposta.
A parte
Windows NT infatti fa una cosa simile. Esiste una singola directory di lavoro per processo, impostata dalla SetCurrentDirectory()
chiamata API e tracciata per processo dal kernel tramite un handle di file (interno) aperto in quella directory; e c'è una serie di variabili d'ambiente che i programmi Win32 (non solo gli interpreti dei comandi, ma tutti i programmi Win32) usano per tenere traccia dei nomi di più directory di lavoro (una per unità), accodandole o sovrascrivendole ogni volta che cambiano directory.
Convenzionalmente, diversamente dal caso dei sistemi operativi Unix e Linux, i programmi Win32 non mostrano queste variabili d'ambiente agli utenti. Tuttavia, a volte è possibile vederli in sottosistemi simili a Unix in esecuzione su Windows NT, nonché utilizzando i comandi degli interpreti di SET
comando in un modo particolare.
Ulteriori letture