本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。
UNIX系统的大多数文件是普通文件或目录,但是也有另外一些文件类型。文件类型包括如下几种:
(1)普通文件(regular file)。这是最常见的文件类型,这种文件包含了某种形式的数据。至于这种数据是文本还是二进制数据对于UNIX内核而言并无区别。对普通文件的解释由处理该文件的应用程序进行。
一个值得注意的例外是二进制可执行文件。为了执行程序,内核必须理解其格式。所有二进制可执行文件都遵循一种格式,这种格式使内核能够确定程序文本和数据的加载位置。
(2)目录文件(directory file)。这种文件包含了其他文件的名字以及指向这些文件有关信息的指针。对一个目录文件具有读权限的任一进程都可以读该目录的内容,但只有内核可以直接写目录文件。进程必须使用特定的函数才能更改目录。
(3)块特殊文件(block special file)。这种文件类型提供对设备(例如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。
(4)字符特殊文件(character special file)。这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
(5)FIFO。这种类型文件用于进程间通信,有时也将其称为命名管道(named pipe)。
(6)套接字(socket)。这种文件类型用于进程间的网络通信。套接字也可用于在一台宿主机上进程之间的非网络通信。
(7)符号链接(symbolic link)。这种文件类型指向另一个文件。
文件类型信息包含在stat结构的st_mode成员中。可以用表4-1中的宏确定文件类型。这些宏的参数都是stat结构中的st_mode成员。
表4-1 <sys/stat.h>中的文件类型宏
宏 | 文件类型 |
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 管道或FIFO |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字 |
POSIX.1允许实现将进程间通信(IPC)对象(例如,消息队列和信号量等)表示为文件。表4-2的宏用来确定IPC对象的类型。这些宏与表4-1中的不同,它们的参数并非st_mode而是指向stat结构的指针。
表4-2 <sys/stat.h>中的IPC类型宏
宏 | 对象的类型 |
S_TYPEISMQ() | 消息队列 |
S_TYPEISSEM() | 信号量 |
S_TYPEISSHM() | 共享存储对象 |
注:Linux系统并不将这些对象表示为文件。
程序清单4-1 对每个命令行参数打印文件类型
[root@localhost apue]# cat prog4-1.c #include "apue.h" int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for(i=0; i<argc; i++) { printf("%s: ", argv[i]); if(lstat(argv[i], &buf) < 0) { err_ret("lstat error"); continue; } if(S_ISREG(buf.st_mode)) ptr = "regular"; else if(S_ISDIR(buf.st_mode)) ptr = "directory"; else if(S_ISCHR(buf.st_mode)) ptr = "character special"; else if(S_ISBLK(buf.st_mode)) ptr = "block special"; else if(S_ISFIFO(buf.st_mode)) ptr = "fifo"; else if(S_ISLNK(buf.st_mode)) ptr = "symbolic link"; else if(S_ISSOCK(buf.st_mode)) ptr = "socket"; else ptr = "** unknown mode **"; printf("%s ", ptr); } exit(0); }
编译后运行该程序:
[root@localhost apue]# ./prog4-1 /etc/passwd /etc /dev/initctl /dev/log /dev/tty /dev/fd0 /dev/cdrom ./prog4-1: regular /etc/passwd: regular /etc: directory /dev/initctl: fifo /dev/log: socket /dev/tty: character special /dev/fd0: block special /dev/cdrom: symbolic link
我们特地使用了lstat函数而不是stat函数以便检测符号链接。如若使用了stat函数,则不会观察到符号链接。
为了在Linux系统上编译该程序,必须定义_GUN_SOURCE,这样就能包括S_ISSOCK宏的定义。(我使用的是2.6.18内核Linux,编译上述程序时,并没有自己定义_GUN_SOURCE,编译通过且运行成功。)
早期的UNIX系统版本并不提供S_ISxxx宏,于是就需要将st_mode与屏蔽字S_FIMT进行逻辑“与”运算,然后与名为S_IFxxx的常量相比较。大多数系统在文件<sys/stat.h>中定义了此屏蔽字和相关的常量。如若查看此文件,则可找到S_ISDIR宏定义为:
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)