在本章中,我们将介绍Linux中的文件、目录以及相关操作。我们将学习创建、打开、读写和关闭文件。我们还将介绍如何处理目录(例如创建、扫描和删除目录)。在上一章讨论shell之后,现在,我们开始用C语言进行编程。
开始讨论Linux中对文件I/O的处理方法之前,我们先回顾一下与文件、目录和设备相关的概念。为了对文件和目录进行处理,我们需要用到系统调用(UNIX和Linux 中与Windows API 对应的概念),以及一套完整的库函数——标准I/O库(stdio)能够更有效的进行文件处理。
在本章的大部分内容中,我们将详细讨论处理文件和目录的各种调用。因此,本章的内容将涵盖:
l 文件和设备。
l 系统调用。
l 库函数。
l 文件访问的底层操作。
l 对文件进行管理。
l 标准I/O库。
l 格式化的输入和输出。
l 文件和目录的维护。
l 扫描目录。
l 错误及其处理。
l /proc文件系统。
l 高级论题:fcntl和mmap。
3.1 Linux文件结构
你可能会问;“为什么要在这里讨论文件结构呢?我早知道它了。”这么说吧,与UNIX一样,Linux环境中的文件具有特别重要的意义,因为它们为操作系统服务和设备提供了一个简单而统一的接口。在Linux中,一切(或几乎一切)都是文件。
这就意味着,通常程序完全可以像使用文件那样使用磁盘文件、串行口、打印机和其他设备。在本书后面的内容中,我们将介绍一些例外情况,比如第15章中的网络连接,但大多数情况下,你只需要使用五个基本的函数——open、close、read、write和ioctl。
目录也是文件,但它是一种特殊类型的文件。在现代的UNIX(包括Linux)版本中,即使是超级用户可能也不再被允许直接对目录进行写操作。正常情况下,所有用户都必须用上层的opendir/readdir接口来读取目录,而无需了解特定系统中目录实现的具体细节。我们将在本章的后面部分学习对目录本身进行操作的特殊函数。
可以这么说,Linux中的任何事物都可以用一个文件代表,或者可以通过特殊的文件进行操作。当然,它们会与我们熟悉的传统文件有一些细微的区别,但两者的基本原则是一致的。下面就让我们来看看刚才提到的特殊文件。
3.1.1 目录
文件,除了本身包含的内容以外,它还会有一个名字和一些属性,即“管理信息”,例如文件的创建/修改日期和它的访问权限等。这些属性被保存在文件的inode(节点)数据结构中,它是文件系统中的一个特殊的数据块,它同时还包含文件的长度和文件在磁盘上的存放位置。系统使用的是文件的inode编号,目录结构仅仅是出于方便人们使用的目的而给文件命名。
目录是文件,它用来保存其他文件的节点号和名字。目录文件中的每个数据项都指向某个文件的节点,删除文件名就等于删除与之对应的链接(文件的节点号可以通过ln -i命令查看)。你可以通过使用ln命令在不同的目录中创建指向同一个文件的链接。如果指向某个文件的链接数(即ls -l命令的输出中跟在访问权限后面的那个数字)变为零,就表示该节点以及其指向的数据不再被使用,磁盘上的相应位置就会被标记为可用空间。
文件被安排在目录中,目录中可能还包含子目录。这些构成了我们所熟悉的文件系统层次结构。用户(比如neil)通常会把自己的文件保存在主目录中,可能是目录/home/neil,该目录可以再进一步划分为电子邮件、商业信函、工具程序等子目录。注意,许多UNIX和Linux的shell命令都允许通过一个简单的符号,让用户能够直接进入自己的主目录,这就是波浪线符号~。要想进入他人的主目录,就键入~user(~加用户名)即可。如你所知,每个用户的主目录通常是一个上层目录的子目录,这个上层目录是专为此目的而创建的,在这个例子中,它就是/home目录。
注意,不幸的是,标准库函数不能识别文件名参数中的波浪线符号。
/home目录本身又是根目录/的一个子目录,根目录位于目录层次的最顶端,它在它的各级子目录中包含着系统中的所有文件。根目录中通常包含用于存放系统程序(二进制可执行文件)的/bin子目录、用于存放系统配置文件的/etc子目录和用于存放系统函数库的/lib子目录。代表物理设备并为这些设备提供接口的文件按照惯例会被放在/dev子目录中,图3-1显示了典型的Linux目录结构的一部分。关于Linux 文件系统布局的更多信息请参考Linux文件系统标准(Linux File System Standard),读者也可以通过man hier命令来获得关于目录结构的描述。
3.1.2 文件和设备
甚至硬件设备在Linux操作系统中通常也被表示(映射)为文件。例如,作为超级用户,你可以通过如下的命令将CD-ROM驱动器挂载为一个文件:
![]()
这个命令将CD-ROM设备(在开机引导时被加载为/dev/hdc)中的当前内容挂载为/mnt/cdrom目录下的文件结构。然后,你就可以像操作普通文件那样在CD-ROM的目录中漫游,只不过该目录中的内容是只读的。
UNIX和Linux中比较重要的设备文件有三个——/dev/console、/dev/tty和/dev/null。
1./dev/console
这个设备代表的是系统控制台。错误信息和诊断信息通常会被发送到这个设备。每个UNIX系统都会有一个指定的终端或显示屏用来接收控制台消息。过去,它可能是一台专用的打印终端。在现代的工作站和Linux上,它通常是“活跃”的虚拟控制台;而在X窗口系统中,它会是屏幕上一个特殊的控制台窗口。
2./dev/tty
如果一个进程有控制终端的话,那么特殊文件/dev/tty就是这个控制终端(键盘和显示屏,或键盘和窗口)的别名(逻辑设备)。例如,通过cron运行的进程就没有控制终端,所以它们不能打开/dev/tty。
在能够使用该设备文件的情况下,/dev/tty允许程序直接向用户输出信息,而不管用户具体使用的是哪种类型的伪终端或硬件终端。在标准输出被重定向时,这一功能非常有用。命令ls –R | more就是一个这样的例子,more程序需要提示用户进行键盘操作之后才能显示下一页内容。我们将在第5章中看到更多使用/dev/tty的例子。
注意,虽然/dev/console设备只有一个,但通过/dev/tty却能够访问许多不同的物理设备。
3./dev/null
这是空(null)设备。所有写向这个设备的输出都将被丢弃。而读这个设备会立刻返回一个文件尾标志,所以在cp命令里可以把它用做拷贝空文件的源文件。人们常把不需要的输出重定向到/dev/null。
创建空文件的另一个方法是使用touch <filename>命令,它的作用是改变文件的修改时间,如果指定名字的文件不存在,就创建一个新文件。但它并不会把有内容的文件变成空文件。
![]()
可以在/dev目录中找到的其他设备包括,硬盘和软盘、通信端口、磁带驱动器、CD-ROM、声卡以及一些代表系统内部工作状态的设备。甚至还有/dev/zero设备,它的作用是作为内容是null字节的源文件来创建零长度文件。访问其中的某些设备需要具有超级用户权限,普通用户不能通过编写程序来直接访问如硬盘这样的底层设备。设备文件的名字会随系统的不同而不同。通常,Linux发行版都提供了以超级用户身份运行的应用程序,用来管理那些以其他用户身份无法访问的设备,例如,用于挂载文件系统的mount命令。
设备可分为字符设备和块设备。两者区别在于访问设备时是否需要一次读写一整块。一般情况下,块设备是那些支持随机文件系统存取的设备,例如硬盘。
在本章中,我们将集中讨论磁盘文件和目录。我们将在第5章中讨论另一种设备——用户终端。







