从stat函数开始逐个说明stat结构的每一个成员,以了解文件的所有属性。
1.函数stat,fstat,fstatat和lstat
1 #include <sys/stat.h> 2 int stat(const char * restrict pathname, struct stat * restrict buf); 3 int fstat(int fd, struct stat *buf); 4 int lstat(const char * restrict pathname, struct stat *restrict buf); 5 int fstatat(int fd, const char * restrict pathname, struct stat * restrict buf, int flag);
成功的话返回0,失败的话返回-1
stat返回与pathname命名文件有关的信息结构
fstat获得在描述符上打开的文件的有关的信息
lstat返回符号链接的有关的信息,而不是符号链接引用的文件的信息
fstatat为一个相对于当前打开目录的路径名返回文件统计信息
2.文件类型
1)普通文件regular file
文本和二进制的数据对于内核来说没有区别,但是一个例外是二进制的可执行文件,为了让内核能够确定程序文本和数据的加载位置,二进制可执行文件都遵循一种标准化的格式
2)目录文件directory file
这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针
对一个目录具有读权限的任一进程都可以读该目录的内容,但只有内核可以直接写目录文件
3)块特殊文件block special file
这种类型的文件提供对设备带缓冲的访问,每次访问以固定长度为单位进行
4)字符特殊文件character special file
这种类型的文件提供对设备不带缓冲的访问,每次访问的长度可变,系统中的所有的设备要么是字符特殊文件,要么是块特殊文件。
5)FIFO
也称为管道named pipe用于进程间通信
6)套接字socket
用于进程间的网络通信,也可以用于在一台宿主机上进程之间的非网络通信
7)符号链接symbolic link
1 //fig4.3.c 2 #include "apue.h" 3 int main(int argc, char *argv[]) 4 { 5 int i; 6 struct stat buf; 7 char *ptr; 8 9 for (i = 1; i < argc; i++) 10 { 11 printf("%s: ", argv[i]);//依次打印数组 12 if (lstat(argv[i], &buf) < 0) //返回文件信息 13 { 14 err_ret("lstat error"); 15 continue; 16 } 17 if (S_ISREG(buf.st_mode))//buf是个指针,st_mode是stat结构的成员 18 ptr = "regular";//分别使用宏来确定文件的类型 19 else if (S_ISDIR(buf.st_mode)) 20 ptr = "directory"; 21 else if (S_ISCHR(buf.st_mode)) 22 ptr = "character special"; 23 else if (S_ISBLK(buf.st_mode)) 24 ptr = "block special"; 25 else if (S_ISFIFO(buf.st_mode)) 26 ptr = "fifo"; 27 else if (S_ISLNK(buf.st_mode)) 28 ptr = "symbolic link"; 29 else if (S_ISSOCK(buf.st_mode)) 30 ptr = "socket"; 31 else 32 ptr = "** unknown mode **"; 33 printf("%s\n", ptr); 34 } 35 exit(0); 36 }
运行情况:
1 jason@t61:~/c_program/apue.3e/figlinks$ cp fig4.3 fig4.3.c 2 jason@t61:~/c_program/apue.3e/figlinks$ ./a.out /etc/passwd /etc /dev/log /dev/tty\ 3 > /var/lib/oprofile/opd_pipe /dev/sr0 /dev/cdrom 4 /etc/passwd: regular 5 /etc: directory 6 /dev/log: symbolic link 7 /dev/tty: character special 8 /var/lib/oprofile/opd_pipe: lstat error: No such file or directory 9 /dev/sr0: block special 10 /dev/cdrom: symbolic link
3.设置用户id和组id
与一个进程相关联的id有六个或者更多
实际用户id
实际组id 标识我们实际上究竟是谁,一个登陆会话期间这些值不改变,但是超级用户进程可以改变它
有效用户id
有效组id
附属组id 决定了我们的文件的访问权限
保存的设置用户id
保存的设置组id 由exec函数保存
通常情况下有效id就是实际id
若文件所有者是root而且设置了文件的设置用户id那么当程序由一个进程执行时,进程具有超级用户权限,不管执行此文件的进程的实际用户id是什么
设置用户id和设置组id都保存在文件的st_mode中,可以分别使用S_ISUID和S_ISGID测试
4.文件访问权限
所有的文件类型都有访问权限
每个文件有9个访问权限位
rwxrwxrwx
u g o
5.函数access和faccessat
1 #include <unistd.h> 2 int access(const char *pathname, int mode); 3 int faccessat(int fd, const char * pathname, int mode, int flag);
按照实际用户id和实际组id进行访问权限的测试
成功返回0,出错返回-1
mode
F_OK 是否存在
R_OK 测试读权限
W_OK 测试写权限
X_OK 测试执行权限
faccessat在下面两种情况下和access是相同的
1)pathname参数为绝对路径
2)fd参数取值为AT_FDCWD而pathname参数为相对路径
否则的话,faccessat计算相对于打开目录(由fd指向)的pathname
flag设置为AT_EACCESS的话,访问检查用的是调用进程的有效用户id和有效组id不是实际的用户id和实际组id
6.函数umask
1 #include <sys/stat.h> 2 mode_t umask(mode_t cmask);
返回屏蔽字
1 fig4.9.c 2 #include "apue.h" 3 #include <fcntl.h> 4 5 #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) 6 7 int 8 main(void) 9 { 10 umask(0);//创建屏蔽字 11 if (creat("foo", RWRWRW) < 0)//创建一个权限为rw-rw-rw-的文件foo 12 err_sys("creat error for foo"); 13 umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);//改变屏蔽字 14 if (creat("bar", RWRWRW) < 0)//创建一个权限为rw-------的文件bar 15 err_sys("creat error for bar"); 16 exit(0); 17 }
运行效果:
1 jason@t61:~/c_program/apue.3e/figlinks$ gcc fig4.9.c -lapue 2 jason@t61:~/c_program/apue.3e/figlinks$ umask 3 0002 4 jason@t61:~/c_program/apue.3e/figlinks$ ./a.out 5 jason@t61:~/c_program/apue.3e/figlinks$ ls -l foo bar 6 -rw------- 1 jason jason 0 6月 25 10:59 bar 7 -rw-rw-rw- 1 jason jason 0 6月 25 10:59 foo
7.函数chmod,fchmod和fchmodat
改变现有文件的访问权限
1 #include <sys/stat.h> 2 int chmod(const char * pathname, mode_t mode); 3 int fchmod(int fd, mode_t mode); 4 int fchmodat(int fd,const char * pathname, mode_t mode, int flag);
chmod函数在指定的文件上进行操作
fchmod函数对已打开的文件进行操作
fchmodat计算相对于打开目录的pathname
为了改变一个文件的权限位,进程的有效用户id必须等于文件的所有者id或者该进程必须具有超级用户权限
8.粘着位sticky bit
要是一个目录设置了粘着位,只有对该目录具有写权限的用户并满足下列条件之一才能删除或重命名该目录下的文件
拥有此文件
拥有此目录
是超级用户
例如/tmp和/var/tmp是设置粘着位的典型,任何用户都可以在这两个目录中创建文件,任一用户对这两个目录的权限通常是rwx但是用户应当不能删除或者重命名属于其他人的文件,为此在这两个目录的文件模式中都设置了粘着位
9.函数chown,fchown,fchownat和lchown
1 #include <unistd.h> 2 int chown(const char * pathname, uid_t owner, gid_t group); 3 int fchown(int fd, uid_t owner, gid_t group); 4 int fchownat(int fd, const char * pathname, uid_t owner, gid_t group, int flag); 5 int lchown(const char *pathname, uid_t owner, gid_t group);
成功返回0,失败返回-1
10.文件长度
stat结构成员st_size表示以字节为单位的文件的长度
这个字段只对普通文件,目录文件和符号链接才有意义
普通文件的长度可以是0,在开始读这种文件的时候,得到end-of-file指示
目录文件长度是一个数的整数倍
符号链接文件长度是文件名中的实际字节数
11.文件截断
1 #include <unistd.h> 2 int truncate(const char *pathname, off_t length); 3 int ftruncate(int fd,off_t length);
成功的话返回0,失败的话返回-1
要是原始长度小于length那么会在文件中间形成空洞
12.函数link,linkat,unlink,unlinkat,remove
任何一个文件可以有多个目录项指向其i节点,创建一个指向现有文件的链接的方法是使用link函数或者linkat函数
1 #include <unistd.h> 2 int link(const char * existingpath, const char * newpath); 3 int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
成功返回0失败返回-1
flag设置为AT_SYMLINK_FOLLOW时,创建指向符号链接目标的链接,要是这个标志清除了,创建指向符号链接本身的链接。
创建目录和增加链接数应该是一个原子操作。
删除一个现有的目录项使用unlink函数
1 #include <unistd.h> 2 int unlink(const char * pathname); 3 int unlink(int fd, const char * pathname, int flag);
成功返回0,失败返回-1
为了解除文件的链接,必须对包含该目录项的目录具有写和执行权限。
关闭一个文件时,内核先去检查打开文件的进程个数,如果这个计数达到0,再区检查其链接个数,如果计数也是0的话,就删除该文件的内容。
也可以使用remove函数解除一个文件或者目录的链接
对文件:remove=unlink
对目录:remove=rmdir
1 #include <stdio.h> 2 int remove (const char * pathname);
13.函数rename 和renameat
1 #include <stdio.h> 2 int rename(const char * oldname, const char * newname); 3 int renameat(int oldfd, const char * oldname, int newfd, const char *newnam);
对文件和目录进行重命名的函数
成功的时候返回0失败的时候返回-1
14.创建和读取符号链接
符号链接是对一个文件的间接指针
1 #include <unistd.h> 2 int symlink(const char * actualpath, const char * sympath); 3 int symlonkat(const char * actualpath, int fd, const char * sympath);
创建一个符号链接,成功的话返回0,出错的时候返回-1
1 #include <unistd.h> 2 ssize_t readlink(const char * restrict pathname, char * restrict buf, size_t bufsize); 3 ssize_t raedlinkat(int fd, const char * restrict pathname, char * restrict buf, size_t bufsize);
这两个函数可以打开链接本身,并读该链接中的名字,若成功,返回读取的字节数,失败返回-1
15.文件的时间
每个文件维护三个时间段
st_atim 文件数据的最后访问时间
st_mtim 文件数据的最后修改时间
st_ctim i节点状态的最后更改时间
16函数futimens,utimensat和utimes
futimens和utimensat函数可指定纳秒级精度的时间戳
用到的数据结构是和stat函数族相同的timespec结构
1 #include <sys/stat.h> 2 int futimens(int fd, const struct timespec times[2]); 3 int utimensat(int fd, const char * path, const struct timespec times[2], int flag);
更改一个文件的访问和修改时间,成功的时候返回0失败的时候返回-1
这两个函数times数组参数的第一个元素包含访问时间,第二个元素包含修改时间。
17.函数mkdir,mkdirat和rmdir
1 #include <sys/stat.h> 2 int mkdir(const char * pathname, mode_t mode); 3 int mkdirat(int fd, const char * pathname, mode_t mode);
这两个函数可以创建目录,成功的返回值是0,失败的返回值是-1
1 #include <unistd.h> 2 int rmdir(const char * pathname);
删除一个空目录,成功的时候返回值是0,失败的时候返回值是-1
空目录是只包含.和..的目录。
18.读目录
对某个目录具有访问权限的任一用户都可以读该目录,但是为了防止混乱的发生,只有内核才能写目录。
一个目录的写权限和执行权限决定了在该目录中是否创建新文件以及删除文件,它并不表示能否写目录本身。
目录的实际格式依赖于系统实现和文件系统的设计。
1 #include <dirent.h> 2 DIR * opendir(const char * pathname); 3 DIR * fdopendir(int fd);//提供一种方法,可以把打开文件描述符转换成目录处理函数需要的DIR结构
成功的话,返回指针,失败,返回NULL
1 struct dirent * readdir(DIR * dp); 2 成功的话,返回指针,若在目录尾或者出错返回NULL 3 void rewinddir(DIR * dp); 4 int closedir(DIR * dp); 5 成功返回0,出错返回-1 6 long telldir(DIR * dp); 7 返回值,与dp关联的目录中的当前位置 8 void seekdir(DIR * dp, long loc);
DIR是个内部结构,上面的函数的这个内部结构保存当前正在被读的目录的有关信息
19.函数chdir,fchdir和getpwd
1 #include <unistd.h> 2 int chdir(const char * pathname); 3 int fchdir(int fd);
更改当前的工作目录,成功返回0,失败返回-1
2015年06月25日 星期四 20时45分44秒