本站总访问量 Unix环境高级编程-文件和目录 - Jerry的小站

Jerry Gao

上帝就是真理,真理就是上帝

文件和目录

描述文件系统的其他特征和文件的性质

函数 stat、fstat、fstatat和lstat

一旦给出pathname,stat函数将返回与此命名文件有关的信息结构。

fstat函数获得已在描述符fd上打开该文件的有关信息。

lstat返回该符号链接的有关信息,而不是由该链接引用的文件的信息

fstatat函数为一个相对于当前打开目录的路径名返回文件统计信息,当AT_SYMLINK_NOFOLLOW标志被设置在flag参数时,fstatat不回去跟随符号链接,而是去返回符号链接本身的值。在默认情况下,返回的是符号链接所指向的实际文件的信息。

第二个参数buf是一个指针,指向一个特定的结构体stat

文件类型

UNIX系统大多数文件包含以下几种类型:

  • 普通文件:文本或二进制数据等。二进制可执行文件必须遵循一种标准化的格式,使内核能够理解其格式。
  • 目录文件:这种文件包含了其他文件的名字和指向这些与文件有关信息的指针。对一个目录有读权限的进程可以读该目录的内容,但是只有内核才能直接写目录文件。
  • 块特殊文件:提供对设备带缓冲的访问,每次以固定长度进行。
  • 字符特殊文件。提供对设备不带缓冲的访问。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
  • FIFO:用于进程间通信,又是也命名为管道。
  • 套接字:用于进程之间的网络通信。
  • 符号链接:指向另一个文件。

文件类型信息包含在stat结构的st_mode成员中。

image-20200816144255355

POSIX.1允许将进程间通信对象(如信号量或消息队列)说明为文件

设置用户ID和组ID

image-20200816151050506

第一个框在登陆时取自口令文件中的登录项。

第二个框决定访问权限。

第三个框在执行一个程序时包含了有效用户ID和有效组ID的副本。

通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。

设置用户ID位(S_ISUID):在文件模式字(st_mode)中指定,含义是,当执行此文件时,将进程有效用户ID设置为文件所有者的ID。

设置用户组位(S_ISGID):在文件模式字中指定,含义是,当执行此文件是,将进程有效用户ID设置为文件所有者的ID。

所以,当文件所有者是超级用户,而且设置了该文件的设置用户ID位,当该文件由一个进程执行时,该进程具有超级用户权限。例如passwd命令允许用户改变其口令,通常是将用户的新口令写入/etc/passwd或者/etc/shadow中,只有超级用户才拥有对此文件的写权限,这时候需要设置用户ID功能。

文件访问权限

image-20200816152550521

chmod命令用u表示用户,用g表示组,用o表示其他

权限的访问规则是:

  • 用名字打开任意类型的文件时,对该名字中包含的每一个目录,包括可能隐含的当前工作目录都应该具有执行权限。
    • 目录的读权限和执行权限的意义不同。读权限允许我们读目录,获得所有文件名的列表,但是没有执行权限我们无法进入这个目录。
  • 读权限决定我们是否能够打开现有文件进行读操作。
  • 写权限决定我们是否能够打开现有文件进行写操作。
  • 为了在open函数中对一个文件指定O_TRUNC标志,必须具有写权限。
  • 为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限。
  • 为了删除目录下的一个文件,必须对该目录下具有写权限和执行权限。对文件本身不需要读写权限。
  • 如果用7和exec函数中任何一个执行某文件,必须对该文件具有执行权限,文件必须是一个普通文件。

进程每次打开、创建或删除一个文件时,内核进行文件访问权限测试。

新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID,组ID允许实现下列选项之一:

  • 新文件组ID可以是进程的有效组ID
  • 新文件的组ID可以是所在目录的组ID

函数 access 和 faccessat

当用open函数打开一个文件,内核以进程的有效用户ID和有效用户组ID进行权限测试。但当一个进程可能通过设置用户ID和设置用户组ID以超级用户权限运行时,仍可能想验证其实际用户能否访问一个给定的文件。access和faccessat用来进行访问权限测试。

函数umask

为文件模式创建屏蔽字

函数 chmod、fchmod和fchmodat

chmod函数在指定的文件上进行操作

fchmod对已打开的文件进行操作

fchmodat:

  • pathname参数为绝对路径,忽略fd
  • fd的取值为AT_FDCWD而pathname为相对路径

为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者该文件必须具有超级用户权限。

chmod只是更新i节点最近一次被修改的时间,而按照系统默认方式,ls -l只是列出最后修改文件内容的时间。

黏着位

S_ISVTX

交换区:Linux为了提高读写效率与速度,会将文件在内存中进行缓存,这部分缓存就是Cache Memory。即使程序运行结束,Cache Memory也不会自动释放。这会导致Linxu系统在频繁读写文件后,物理内存不够用,这时候需要将物理内存释放出来,这些释放的空间被临时保存在Swap空间中,等到程序需要运行时,才将文件从Swap分区中恢复到内存中,系统总是在物理内存不够的时候,才进行Swap交换。

当文件设置了S_ISVTX时,程序第一次被运行,终止时,程序正文副本依旧被保存在交换区中。比如说文本编译程序和C语言编译器。

现如今使用了虚拟内存技术不再需要设置黏着位。

如果对一个目录设置了黏着位,只有对该目录具有写权限的用户且满足下列条件之一才能删除或重命名该文件:

  • 拥有此文件
  • 拥有此目录
  • 是超级用户

在目录/tmp和/var/tmp设置了黏着位,但是用户并不能随意删除别人的文件。

函数chown、fchown、fchownat和lchown

改变用户ID和组ID

文件中的空洞

空洞是由所设置的偏移量超过文件尾端,并写入了某些操作所造成的。

文件会由于使用了若干块磁盘块以存放指向实际数据块的各个指针

文件截断

使用truncate命令进行截断

如果设置的length长度比原来大,会创建一个文件空洞

文件系统

我们可以把一个磁盘分区,每个分区可以包含一个文件系统

每个i节点都有一个链接计数,其值是指向i节点的目录项数,当链接计数减少至0时,才可删除该文件。解除对一个文件的计数并不总意味着释放该文件占用的数据块的原因。删除一个目录项的函数是unlink不是delete。

nlink_t这种链接类型被称为硬链接

符号链接的实际内容是包含了该符号链接所指向文件的名字

i节点中包含文件类型、文件权限访问位、文件长度、指向文件数据块的指针等。

只有两项重要数据存放在目录项:文件名和i节点编号。

目录项中i节点编号指向同一文件系统中的相应的i节点,所以一个目录项不能指向另一个文件系统中的i节点。所以命令ln不能跨越文件系统。

文件重命名实际上是构造一个指向现有i节点的新目录项,并删除老目录项。链接计数不会改变,文件内容无需移动。mv命令通常的操作方式。

任何一个叶目录(不包含任何目录的目录)的链接计数总是2,一个是包含此目录的目录项,一个是该目录中的.项。在父目录中的每个子目录都使得该父目录链接计数增加1。

函数 link、linkat、unlink、unlinkat和remove

创建一个指向现有文件的链接时link或linkat

unlink用于删除一个现有的目录项

当链接计数达到0可以删除这个文件,当删除另一个文件时如果有进程打开了该文件,其内容也不能删除。

内核首先检查打开文件的进程个数,如果计数达到0,内核再去检查其链接计数,如果链接计数也是0,那么就删除这个文件。

函数rename和renameat

符号链接

符号链接与硬链接不同,硬链接直接指向i节点

mkdir rmdir

如果调用rmdir使目录链接计数为0,并且也没有进程打开此目录,则释放此目录占用的空间。如果链接计数为0时仍有进程打开此目录,则在函数返回前删除最后一个目录链接及.和..项。且在此目录中不能再创建新文件。

读目录

有访问权限的都可以读目录,但是只有内核能够写目录。一个目录的写权限位和执行权限位决定能否创建文件及删除文件,但是并不表示能够写目录本身。

chdir、fchdir、getcwd

chdir用来改变工作目录(/etc/passwd中第六个字段)

shell当前工作目录并不会随着程序调用而改变,shell应该直接调用chdir函数,cd命令内建在shell当中

设备特殊文件

每个文件系统所在的存储设备都由其主次设备号表示,主设备号标识设备驱动程序,有时为与其通信的外设板,次设备号标识特定的子设备。

评论