上一章我们说明了执行IO操作的基本函数,其中的讨论是围绕普通文件IO进行的-打开文件,读文件或写文件。本章将描述文件系统的其他特征和文件的性质。我们将从stat函数开始,逐个说明stat结构的每一个成员以了解文件的所有属性。在此过程中,我们将说明修改这些属性的各个函数(更改所有者,更改权限等),还将更详细地说明UNIX文件系统的结构以及符号链接。本章最后介绍对目录进行操作的各个函数,并且开发了一个以降序遍历目录层次结构的函数。
本章主要讨论4个stat函数以及它们的返回信息。
#include
int stat(const char *restrict pathname,struct stat *restrict buf);
int fstat(int fd,struct stat *buf);
int lstat(const char * restrict pathname,struct stat * restrict buf);
int fstatat(int fd,const char *restrict pathname,struct stat *restrict buf,int flag);
一旦给出pathname,stat函数将返回与此命名文件有关的信息结构。fstat函数获得已在描述符fd上打开文件的有关信息。lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用的文件的信息。
fstatat函数为一个相对于当前打开目录(由fd参数指向)的路径名返回文件统计信息。flag参数控制着是否跟随着一个符号链接。当AT_SYMLINK_NOFOLLOW标志被设置时,fstatat不会跟随符号链接,而是返回符号链接本身的信息。否则,在默认情况下,返回的是符号链接所指向的实际文件的信息。如果fd参数的值是AT_FDCWD,并且pathname参数是一个相对路径名,fstatat会计算相对于当前目录的pathname参数。如果pathname是一个绝对路径,fd参数就会被忽略。这两种情况下,根据flag的取值,fstatat的作用就跟stat或lstat一样。
第2个参数buf是一个指针,它指向一个我们必须提供的结构。函数来填充由buf指向的结构。结构的实际定义可能随具体实现有所不同,但基本形式是:
struct stat{
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* i-node number (serial number) */
dev_t st_dev; /* device number (file system) */
dev_t st_rdev; /* device number for special files */
nlink_t st_nlink; /* number of links */
uid_t st_uid; /* user ID of owner*/
gid_t st_gid; /* group ID of owner*/
off_t st_size; /* size in bytes,for regular files*/
struct timespec st_atime; /* time of last access */
struct timespec st_mtime; /* time of last modification */
struct timespec st_ctime; /* time of last file status change*/
blksize_t st_blksize; /* best I/O block size*/
blkcnt_t st_blocks; /* number of disk blocks allocated*/
}
timespec结构类型按照秒和纳秒定义了时间,至少包括下面字段:
time_t tv_sec;
long tv_nsec;
注意,stat结构中的大多数成员都是基本系统数据类型。我们将说明此结构的每个成员以了解文件属性。
使用stat函数最多的地方可能就是ls -l 命令,用其可以获得有关一个文件的所有信息。
至此我们已经介绍了两种不同的文件类型:普通文件和目录。UNIX系统的大多数文件是普通文件或目录,但是也有另外一些类型。文件类型包括如下几种:
(1)普通文件(regular file)。这是最常用的文件类型,这种文件包含了某种形式的数据。至于这种数据是文本还是二进制数据,对于UNIX内核而言并不区别。对普通文件内容的解释由处理该文件的应用程序进行。
一个值得注意的例外是二进制可执行文件。为了执行程序,内核必须理解其格式。所有二进制可执行文件都遵循一种标准化的格式,这种格式使内核能够确定程序文本和数据的加载位置。
(2)目录文件(directory file)。这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。对一个目录文件具有读权限的任一进程都可以读该目录的内容,但只有内核可以直接写目录文件。进程必须使用本章介绍的函数才能更改目录。
(3)块特殊文件(block special file)。这种类型的文件提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行。
(4)字符特殊文件(character special file)。这种类型的文件提供对设备不带缓冲的访问,每次访问长度可变。系统中所有设备要么是字符特殊文件,要么是块特殊文件。
(5)FIFO。这种类型的文件用于进程间通信,有时也称为命名管道。
(6)套接字。这种类型的文件用于进程间的网络通信。套接字可用于在一台宿主机上进程之间的非网络通信。
(7)符号链接(symbolic link)。这种类型的文件指向另一个文件。
文件类型信息包含在stat结构的st_mode成员中。可以用图4-1中的宏确定文件类型。这些宏的参数都是stat结构中的st_mode成员。

POSIX.1允许实现将进程间通信(IPC)对象(如消息队列和信号量等)说明为文件。图中4-2中的宏可用来从stat结构中确定IPC对象的类型。这些宏与图4-1中的不同,它们的参数并非st_mode,而是指向stat结构的指针。

POSIX.1允许实现将进程间通信(IPC)对象(如消息队列和信号量等)说明为文件。图中4-2中的宏可用来从stat结构中确定IPC对象的类型。这些宏与图4-1中的不同,它们的参数并非st_mode,而是指向stat结构的指针。
实例4-3程序取其命令行参数,然后针对每一个命令行参数打印其文件类型。
#include "apue.h"
int
main(int argc,char *argv[])
{
int i;
struct stat buf;
char *ptr;
for(i=1;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\n",ptr);
}
exit(0);
}
我们特地使用lstat函数一遍检测符号链接。
早期UNIX版本并不提供S_ISxxx宏,于是就需要将st_mode与屏蔽字S_IFMT进行逻辑"与"运算,然后与名为S_IFxxx的常量相比较。大多数系统在文件
#define S_ISDIR (mode) (((mode)&S_IFMT)==S_ISDIR)
我们说过,普通文件是最主要的文件类型,但是观察以下在一个给定的系统中各种文件的比例是很有意思的。图4-4显示了在一个单用户工作站Linux系统中的统计值和百分比。这些数据是由4.22节中的程序得到的。

与一个进程相关联的ID有6个或更多,如图4-5所示。

(1)实际用户ID和实际组ID标识我们究竟是谁。这两个字段在登陆时取自口令文件中的登录项。通常,在一个登录会话期间这些值并不改变,但是超级用户进程有方法改变它们。
(2)有效用户ID,有效组ID以及附属组ID决定了我们的文件访问权限,下一节对此进行说明。
(3)保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本,在8.11节中说明setuid函数时,将说明这两个保存值的作用。
通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。
每个文件有一个所有者和组所有者,所有者由stat结构中的st_uid指定,组所有者则由st_gid指定。
当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID,有效组ID通常是实际组ID。但是可以在文件模式字(st_mode)中设置一个特殊标志,其含义是"当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)"。与此相类似,在文件模式字中可以设置另一位,它将执行此文件的进程的有效组ID设置为文件的组所有者ID。在文件模式字中的这两位被称为设置用户iD位和设置组ID位。