预计阅读本页时间:-
10.6.2 Linux的文件系统调用
许多系统调用与文件和文件系统有关。在本节中,首先研究对单个文件进行操作的系统调用,之后我们会研究针对目录和文件系统的系统调用。要创建一个文件时,可以使用creat系统调用。(曾经有人问Ken Thompson,如果给他一次重新发明UNIX的机会,他会做什么不同事情,他回答说他要把这个系统调用的拼写改成create,而不是现在的creat。)这个系统调用的参数是文件名和保护模式。于是
fd=creat("abc",mode);
创建了一个名为abc的文件,并根据mode设置文件的保护位。这些保护位决定了用户访问文件的权限及方式。在下文将会具体讨论。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
creat系统调用不仅创建了一个新文件,还以写的方式打开了这个文件。为了使以后的系统调用能够访问这个文件,creat成功时返回一个非负整数,这个非负整数叫做文件描述符,也就是例子中的fd。如果creat作用在一个已经存在的文件上,那么该文件的文件长度会被截短为0,它的内容会被丢弃。通过设置合适的参数,open系统调用也能创建文件。
现在我们继续讨论图10-27列出的主要的文件系统调用。为了读或写一个已经存在的文件,必须使用open系统调用打开这个文件。它的参数是要打开文件的文件名以及打开方式:只读、只写或两者。此外,也可以指定不同的选项。和creat一样,open返回一个文件描述符,可用来进行读写。然后可以使用close系统调用来关闭文件,它使得文件描述符可以被后来的creat或open使用。creat和open系统调用总是返回未被使用的最小数值的文件描述符。

当一个程序以标准方式运行时,文件描述符0、1、2已经分别用于标准输入、标准输出和标准错误。通过这种方式,一个过滤器,比如sort程序,可以从文件描述符0读取输入,输出到文件描述符1,而不需要关心这些文件是什么。这种机制能够有效是因为shell在程序启动之前就设置好了它们的值。
毫无疑问,最常使用的文件系统调用是read和write。它们每个都有三个参数:文件描述符(标明要读写的文件)、缓冲区地址(给出数据存放的位置或者读取数据的位置),长度(给出要传输的数据的字节数)。这些就是全部了。这种设计非常简单,一个典型的调用方法是:
n=read(fd,buffer,nbytes);
虽然几乎所有程序都是顺序读写文件的,但是一些程序需要能够从文件的任何位置随机地读写文件。每个文件都有一个指针指向文件当前的读写位置。当顺序地读写文件时,这个指针指向将要读写的字节。如果文件位置指针最初指向4096,在读取了1024个字节后,它会自动地指向第5120个字节。lseek系统调用可以改变位置指针的值,所以之后的read和write可以从文件的任何位置开始读写,甚至是超出文件的结尾。这个系统调用叫做lseek,是为了避免与seek冲突,其中后者以前在16位计算机上用于查找,现在已经不使用了。
lseek有三个参数:第一个是文件描述符,第二个是文件读写位置,第三个表明读写位置是相对于文件开头、当前位置还是文件尾。lseek的返回值是当读写位置改变后的绝对位置。有点讽刺的是,lseek是惟一一个从不会引起实际的磁盘寻道的文件系统调用,因为它所做的只是修改了内存中的一个值(文件读写位置)。
对于每个文件,Linux记录了它的文件类型(普通文件、目录、特殊文件)、大小、最后一次修改时间和其他信息。程序可以使用stat系统调用来查看这些信息,stat的第一个参数是文件名,第二个参数是指向获取的文件信息将要存放的结构的指针,该结构的各个域如图10-28所示。系统调用fstat的作用和stat一样,惟一不同的是,fstat针对一个打开的文件(文件名可能未知)进行操作,而不是一个路径名。

pipe系统调用用来创建一个shell管线。它创建了一种伪文件(pseudo-file),用于缓冲管线通信的数据,并给缓冲区的读写都返回文件描述符。以下面的管线为例:
sort<in|head-30
在执行sort的进程中,文件描述符1(标准输出)被设置为写入管道,执行head的进程中,文件描述符0(标准输入)被设置为从管道读取。通过这种方式,sort只是从文件描述符0(被设置为文件in)读取,写入到文件描述符1(管道),甚至不会觉察到它们已经被重定向了。如果它们没有被重定向,sort将会自动从键盘读取数据,而后输出到显示器(默认设备)。同样地,当head从文件描述符0中读取数据时,它读取到的是sort写入到管道缓冲区中的数据,head甚至不知道自己使用了管道。这个例子清晰地表明了一个简单的概念(重定向)和一个简单的实现(文件描述符0和1)如何实现一个强大的工具(以任意方式连接程序,而不需要去修改它们)。
图10-27列举的最后一个系统调用是fcntl。fcntl用于加锁和解锁文件,应用共享锁和互斥锁,或者是执行一些文件相关的其他操作。
现在我们开始关注与目录及文件系统整体更加相关,而不是仅和单个文件有关的系统调用,图10-29列举了一些这样的系统调用。可以使用mkdir和rmdir创建和删除目录,但需要注意:只有目录为空时才可以将其删除。

如图10-24所示,创建一个指向已有文件的链接时创建了一个目录项(directory entry)。系统调用link用于创建链接,它的参数是已有文件的文件名和链接的名称,使用unlink可以删除目录项。当文件的最后一个链接被删除时,这个文件会被自动删除。对于一个没有被链接的文件,对其使用unlink也会让它从目录中消失。
使用chdir系统调用可以改变工作目录,工作目录的改变会影响到相对路径名的解释。