Las otras respuestas son simplificaciones excesivas, cada una presenta solo partes de la historia, y están equivocadas en un par de puntos.
Hay dos formas en que se rastrea el directorio de trabajo:
- Para cada proceso, en la estructura de datos del espacio del kernel que representa ese proceso, el kernel almacena dos referencias de vnode a los vnodes del directorio de trabajo y el directorio raíz para ese proceso. La referencia anterior se establece mediante las llamadas al sistema
chdir()
y fchdir()
la segunda por chroot()
. Uno puede verlos indirectamente en /proc
los sistemas operativos Linux o mediante el fstat
comando en FreeBSD y similares:% fstat -p $$ | head -n 5
USUARIO CMD PID FD MONTAJE MODO INUM SZ | DV R / W
JdeBP zsh 92648 texto / 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
%
Cuando funciona la resolución de nombre de ruta, comienza en uno u otro de los vnodos referenciados, según si la ruta es relativa o absoluta. (Hay una familia de …at()
llamadas al sistema que permiten que la resolución de la ruta comience en el vnodo al que hace referencia un descriptor de archivo abierto (directorio) como tercera opción).
En microkernel Unices, la estructura de datos está en el espacio de aplicación, pero el principio de mantener referencias abiertas a estos directorios sigue siendo el mismo.
- Internamente, dentro de shells como el shell Z, Korn, Bourne Again, C y Almquist, el shell además realiza un seguimiento del directorio de trabajo mediante la manipulación de cadenas de una variable de cadena interna. Hace esto cada vez que tiene motivos para llamar
chdir()
.Si uno cambia a un nombre de ruta relativo, manipula la cadena para agregar ese nombre. Si uno cambia a un nombre de ruta absoluto, reemplaza la cadena con el nuevo nombre. En ambos casos, ajusta la cadena para eliminar .
y ..
componentes y para perseguir enlaces simbólicos reemplazándolos con sus nombres vinculados. ( Aquí está el código del shell Z para eso , por ejemplo).
El nombre en la variable de cadena interna es rastreado por una variable de shell llamada PWD
(o cwd
en los shells C). Esto se exporta convencionalmente como una variable de entorno (denominada PWD
) a los programas generados por el shell.
Estos dos métodos de cosas de seguimiento son reveladas por los -P
y las -L
opciones al cd
e pwd
incorporada en el shell comandos, y por las diferencias entre las conchas incorporados pwd
comandos y tanto el /bin/pwd
comando y el incorporado en pwd
los comandos de cosas como (entre otros) VIM y 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
%
Como puede ver: obtener el directorio de trabajo "lógico" es una cuestión de mirar la PWD
variable de shell (o la variable de entorno si no se trata del programa de shell); mientras que obtener el directorio de trabajo "físico" es cuestión de llamar a la getcwd()
función de biblioteca.
La operación del /bin/pwd
programa cuando -L
se usa la opción es algo sutil. No puede confiar en el valor de la PWD
variable de entorno que ha heredado. Después de todo, no es necesario que haya sido invocado por un shell y los programas que intervienen pueden no haber implementado el mecanismo del shell para hacer que la PWD
variable de entorno siempre rastree el nombre del directorio de trabajo. O alguien puede hacer lo que hice allí.
Entonces, lo que hace es (como dice el estándar POSIX) verificar que el nombre dado PWD
arroje lo mismo que el nombre .
, como se puede ver con un seguimiento de llamada del 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 'No
existe tal archivo o directorio' __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
%
Como puede ver: solo llama getcwd()
si detecta una falta de coincidencia; y se puede engañar estableciendo PWD
una cadena que de hecho nombre el mismo directorio, pero por una ruta diferente.
La getcwd()
función de biblioteca es un tema en sí mismo. Pero para precisar:
Navegar ..
nuevamente es un tema en sí mismo. Otro précis: aunque los directorios convencionalmente (aunque, como ya se mencionó, esto no es obligatorio) contienen una ..
estructura de datos de directorio real en el disco, el kernel rastrea el directorio padre de cada vnode de directorio y, por lo tanto, puede navegar al ..
vnode de cualquier directorio de trabajo. Esto es algo complicado por el punto de montaje y los mecanismos raíz modificados, que están más allá del alcance de esta respuesta.
Aparte
Windows NT, de hecho, hace algo similar. Hay un único directorio de trabajo por proceso, establecido por la SetCurrentDirectory()
llamada API y rastreado por proceso por el núcleo a través de un identificador de archivo abierto (interno) a ese directorio; y hay un conjunto de variables de entorno que los programas Win32 (no solo los intérpretes de comandos, sino todos los programas Win32) usan para rastrear los nombres de múltiples directorios de trabajo (uno por unidad), agregándolos o sobrescribiéndolos cada vez que cambian de directorio.
Convencionalmente, a diferencia del caso de los sistemas operativos Unix y Linux, los programas Win32 no muestran estas variables de entorno a los usuarios. Sin embargo, a veces se pueden ver en subsistemas similares a Unix que se ejecutan en Windows NT, así como al usar los comandos de los intérpretes de SET
comandos de una manera particular.
Otras lecturas