导航文件系统时,Unix如何跟踪用户的工作目录?


29

假设我登录到UNIX系统上的Shell并开始点击命令。我最初从用户的主目录开始~。我可能会从那里cd到目录Documents

此处更改工作目录的命令非常直观直观:父节点具有可以访问的子节点列表,并且大概使用搜索的(优化)变体来定位子节点的存在。输入用户输入的名称,然后“更改”工作目录以匹配此名称-如果我在那里输入错误,请更正我。甚至可能更简单,shell只是简单地“天真”地尝试完全按照用户的意愿尝试访问目录,并且当文件系统返回某种类型的错误时,shell会相应地显示响应。

但是,我感兴趣的是,当我向上浏览目录时,即到父级或父级的父级,相同的过程是如何工作的。

给定我未知的(可能是“盲目的”位置)Documents,即整个文件系统树中许多具有该名称的目录之一,Unix如何确定下一步应该放置的位置?它是否参考pwd并检查了?如果是,如何pwd跟踪当前的导航状态?


Answers:


76

其他答案是过于简单化,每个答案仅代表故事的一部分,在两点上都是错误的。

跟踪工作目录有两种方式:

  • 对于每个进程,在代表该进程的内核空间数据结构中,内核将两个vnode引用存储到该进程的工作目录和根目录的vnode。前一个参考由chdir()fchdir()系统调用设置,后者由设置chroot()。可以/proc在Linux操作系统上或通过fstatFreeBSD等上的命令间接看到它们:

    %fstat -p $$ |头-n 5
    用户CMD PID FD安装INUM模式SZ | DV读/写
    JdeBP zsh 92648文本/ 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根/ 4 drwxr-xr-x 35 r
    

    路径名解析操作时,根据路径是相对还是绝对,它从这些引用的vnode中的一个或另一个开始。(有一系列…at()系统调用,它们允许路径名解析从打开的(目录)文件描述符所引用的vnode开始,作为第三个选项。)

    在微内核Unices中,数据结构位于应用程序空间中,但是保留对这些目录的开放引用的原理仍然相同。

  • 在内部,在诸如Z,Korn,Bourne Again,C和Almquist外壳之类的外壳中,该外壳使用内部字符串变量的字符串操作来跟踪工作目录。只要有原因调用,它就会执行此操作chdir()

    如果更改为相对路径名,它将操纵字符串以追加该名称。如果更改为绝对路径名,它将用新名称替换字符串。在这两种情况下,它都会调整字符串以删除...组成部分,并追逐符号链接,并用其链接名称替换它们。(例如,这是Z shell的代码)。

    内部字符串变量中的名称由名为(或在C shell中)的shell变量跟踪。通常,它作为环境变量(名为)导出到shell生成的程序中。PWDcwdPWD

这两种跟踪事物的方法由-Pand 和shell内置命令的and -L选项cd以及pwdshell的内置命令与诸如此类(以及其他)之类pwd/bin/pwd命令和内置命令之间的差异揭示。pwdVIM和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 /那里/ bin / pwd -L)
/ usr / home / JdeBP / a

如您所见:获取“逻辑”工作目录只是查看PWDshell变量(如果不是shell程序,则查看环境变量)。而获取“物理”工作目录仅是调用getcwd()库函数。

使用/bin/pwd-L选项时程序的操作有些微妙。它不能信任PWD已继承的环境变量的值。毕竟,它不必由外壳程序调用,并且介入程序也可以不实施外壳程序机制,使PWD环境变量始终跟踪工作目录的名称。或者有人可以做我刚才在那里所做的事情。

因此,它的作用是(如POSIX标准所说)检查输入的名称是否与name PWD相同.,这在系统调用跟踪中可以看到:

%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桁架/ 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 /有桁架/ bin / pwd -L 3>&1 1>&2 2>&3 | grep -E'^ stat | __getcwd')
stat(“ / hello / there”,0x7fffffffe730)错误#2'没有这样的文件或目录' 
__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

如您所见:它仅getcwd()在检测到不匹配时才调用;可以通过设置PWD一个确实确实命名相同目录但使用不同路由的字符串来欺骗它。

getcwd()库函数是在自己的权利的主体。但是要注意:

  • 最初,它纯粹是一个库函数,它通过反复尝试在目录中查找工作目录来建立从工作目录到根目录的路径名..。当到达..与工作目录相同的循环时,或者尝试打开下一个目录时出错时,它停止..了。这将是很多隐藏的系统调用。
  • 如今,情况略为复杂。例如,在FreeBSD上(对于其他操作系统也是如此),这一个真实的系统调用,如您在前面给出的系统调用跟踪中所看到的。从工作目录vnode到根的所有遍历都是在单个系统调用中完成的,它利用诸如内核模式代码直接访问目录条目缓存之类的优势来更有效地进行路径名组件查找。

    但是,请注意,即使在FreeBSD和其他操作系统上,内核也不会使用字符串来跟踪工作目录。

导航到..本身又是一个主题。另一个原则:尽管目录通常(尽管已经暗示,但这不是必需的)..在磁盘上的目录数据结构中包含实际目录,但内核会跟踪每个目录vnode本身的父目录,因此可以导航到..任何vnode的vnode工作目录。挂载点和更改的根机制使此操作有些复杂,这超出了此答案的范围。

在旁边

Windows NT实际上做了类似的事情。每个进程只有一个工作目录,由SetCurrentDirectory()API调用设置,并由内核通过该目录的(内部)打开文件句柄对每个进程进行跟踪;Win32程序(不仅仅是命令解释器,而是所有 Win32程序)使用一组环境变量来跟踪多个工作目录的名称(每个驱动器一个),并在它们更改目录时追加或覆盖它们。

通常,与Unix和Linux操作系统不同,Win32程序不会向用户显示这些环境变量。但是,有时人们可以在运行于Windows NT的类Unix子系统中看到它们,也可以通过SET以特定方式使用命令解释器的命令来看到它们。

进一步阅读


1
这远远超出了我的预期。谢谢,并感谢您的进一步阅读!
ReactingToAngularVues

doc.cat-v.org/plan_9/4th_edition/papers/lexnames有关的一些问题会谈..中的Plan9的背景下,
伊卡洛斯

@JdeBP:也许我缺少了一些东西。您说:“在内部,……,bash,……和……内部,shell 使用内部字符串变量的字符串操作来跟踪工作目录。…,它会调整字符串以删除...组成部分,并追逐符号链接,并将其替换为链接名称。…内部字符串变量中的名称由名为PWD…” 的shell变量跟踪(添加了强调)。…(续)
G-Man说'Reinstate Monica''Mar

(续)…但是您的示例在命令后显示PWD= ,即使它是指向的符号链接,因此外壳程序也不会“追逐”该链接。您是否陈述错误,或者我误读了?…/bcd bbaa -> b
G-Man说'Resstate Monica''Mar

我只是简单介绍了一个要点,并向您指出了代码的详细信息。有关何时以及如何决定追逐符号链接的信息,请参见各个shell的手册。Z shell方便地调用其shell选项,该选项是决策公式的一部分CHASE_LINKS
JdeBP

1

内核不跟踪目录或文件名。文件或目录在内核中由一个索引节点/设备对表示。系统调用(例如chdir()open()等)将路径作为参数,该路径可以是绝对路径(例如/etc/passwd),也可以是相对于当前目录的路径(例如:Documents..)。流程执行时chdir("Documents"),将Documents在当前工作目录中进行查找,并且流程的工作目录将更新为引用该目录。从内核的角度来看,名称“ ..”没有什么特别的,它只是文件系统中..引用父目录的约定。

getcwd()函数不是系统调用,而是库函数,它必须一直工作到根目录,并记录路径中路径组件的名称。


0

有趣的是,从传统上讲,cd ..它要简单得多pwd。命名..的目录显式放置在文件系统中。系统会跟踪当前目录的设备/索引节点,因此cd ..或更准确地说,系统调用chdir("..")只需要在属于当前目录的索引节点的文件中查找名称“ ..”,并将当前目录的设备/索引节点更改为在那里找到价值。

pwd(更准确地说/bin/pwd..依次跟踪链接并读取相应的目录,直到找到它来自的inode,然后反汇编这些名称的列表,直到到达根目录(尤其是不包含..条目)。

现在,这是原始的低级基本行为。实际的shell命令pwd而是依靠多种技术来缓存当前路径名。但实际上,只有它的inode才是真正已知的。这意味着一旦将符号链接用于目录导航,则当前shell和系统的当前工作目录名称概念/bin/pwd可能会有所不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.