此为第四章笔记。讲了好多的API啊,根本记不住,烦死了啊。对于文件系统那一部分我觉得我还是需要再看几遍的,有一点难度。
函数 stat、fstat、fstatat和lstat
函数原型分别如下
1 2 3 4 5 6
| #include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf); int fstae(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);
|
这里使用 stat
做一个实验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include <stdio.h> #include <stdlib.h>
int main(int argc, char *argv[]) { struct stat sb;
if (argc != 2) { fprintf(stderr, "Usage: %s <pathname>\n", argv[0]); }
if (stat(argv[1], &sb) == -1) { perror("stat"); exit(EXIT_FAILURE); }
printf("File type:\t"); switch (sb.st_mode & S_IFMT) { case S_IFDIR: printf("directionary\n"); break; case S_IFREG: printf("regular file\n"); break; default: printf("unknown\n"); break; }
printf("Preferred I/O block size: %ld bytes\n", (long)sb.st_blksize); printf("File size: %lld bytes\n", (long long)sb.st_size); printf("Blocks allocated: %lld\n", (long long) sb.st_blocks);
printf("Last status change: %s", ctime(&sb.st_ctime)); printf("Last file access: %s", ctime(&sb.st_atime)); printf("Last file modification: %s", ctime(&sb.st_mtime));
exit(EXIT_SUCCESS); }
|
1 2 3 4 5 6 7 8
| > ./a.out ls_test.c File type: regular file Preferred I/O block size: 4096 bytes File size: 1008 bytes Blocks allocated: 8 Last status change: Wed Aug 21 08:53:08 2019 Last file access: Wed Aug 21 08:53:14 2019 Last file modification: Wed Aug 21 08:53:08 2019
|
这个函数可以获取文件的大部分状态信息,并存放至结构体 stat
中。全部的信息如下
当然,还有另外一个方式判断文件类型:使用其预定义的宏(PDFp96)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #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_ISLNK(buf.st_mode)) ptr = "symbolic link"; else ptr = "** unknown node **";
printf("%s\n", ptr); }
exit(EXIT_SUCCESS); }
|
1 2 3 4
| > ./a.out /dev/cdrom /etc/passwd /etc /dev/cdrom: symbolic link /etc/passwd: regular /etc: directory
|
其实宏中定义就像第一种方法
1 2 3
| #define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
|
和进程相关的ID
文件访问权限
每个文件有9个访问权限位,常量定义在 <sys/stat.h>
- 用户
u
S_IRUSER
读
S_IWUSER
写
S_IXUSER
执行
- 组
g
S_IRGRP
读
S_IWGRP
写
S_IXGRP
执行
- 其他
o
S_IROTH
读
S_IWOTH
写
S_IXOTH
执行
可以使用系统调用 access
或 faccessat
来验证当前用户能否访问
1 2 3 4
| #include <unistd.h>
int access(const char *pathname, int mode); int faccessat(int fd, const char *pathname, int mode, int flag);
|
mode 列表如下
mode |
说明 |
R_OK |
测试读权限 |
W_OK |
测试写权限 |
X_OK |
测试执行权限 |
第二的函数的 flag
若为 AT_EACCESS
,则测试的是调用进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID
第二个函数在下列几种情况下和第一个函数作用相同
pathname
为绝对路径
fd
为 AT_FDCWD
当前工作路径、pathname
为相对路径
否则其计算的是相对于打开目录( fd
参数指向)的 pathname
使用屏蔽词umask
通过设置屏蔽词来关闭文件 mode
中的相应位
系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdlib.h> #include <fcntl.h> #include <apue.h>
#define RWRWRW (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
int main(int argc, char *argv[]) { umask(0);
if (creat("foo", RWRWRW) < 0) err_sys("creat error for foo");
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (creat("bar", RWRWRW) < 0) err_sys("creat error for bar");
exit(EXIT_SUCCESS); }
|
执行此程序后,发现 bar
文件的全部和设置的并不一致,这是因为设置了 umask
1 2
| -rw------- 1 parallels parallels 0 8月 21 13:45 bar -rw-rw-rw- 1 parallels parallels 0 8月 21 13:45 foo
|
命令行用法
umask的文件访问位
屏蔽位 |
含义 |
用户 |
- |
|
0400 |
读 |
0200 |
写 |
0100 |
执行 |
组 |
- |
|
0040 |
读 |
0020 |
写 |
0010 |
执行 |
其他 |
- |
|
0004 |
读 |
0002 |
写 |
0001 |
执行 |
1 2 3 4 5 6
| > umask 002
> umask 027 > umask -S u=rwx,g=rx,o=
|
常用的umask组合
002
阻止其他用户写入你的文件
022
阻止其他用户以及同组用户写入你的文件
027
阻止同组用户写入你的文件,以及其他用户读、写或执行你的文件
粘着位
如果执行如下指令
输出的权限位列表中有一个 t
,这个就是 粘着位 (sticky bit)
。对于 /tmp
:
- 任何用户都可以在其中创建文件。任一用户对此目录的权限都是读、写和执行。
- 任何用户(除了root)都不可以删除或重命名属于其他人的文件。
命令行创建
1 2 3
| chmod o+t tmp chmod +t tmp chmod 1757 tmp
|
代码创建 S_ISVTX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdlib.h> #include <sys/stat.h> #include <apue.h>
int main(int argc, char *argv[]) { struct stat statbuf; char* dirname = "tmp";
if (stat(dirname, &statbuf) < 0) err_sys("stat error for %s", dirname);
if (chmod(dirname, (statbuf.st_mode | S_ISVTX)) < 0) err_sys("chmod error for %s", dirname);
exit(EXIT_SUCCESS); }
|
文件系统
PDFp111
使用 unlink
使用 unlink
来确保程序的临时文件在退出的时候可以彻底删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <apue.h> #include <fcntl.h> #include <stdlib.h>
int main(int argc, char *argv[]) { char *filename = "output.file"; if (open (filename, O_RDWR) < 0) err_sys("open error");
if (unlink(filename) < 0) err_sys("unlink error");
printf("file unlinked\n");
sleep(15); printf("done\n"); exit(EXIT_SUCCESS); }
|
程序执行时文件立刻被删除,但是占用空间是在进程退出时删除的。
文件的时间
字段 |
说明 |
例子 |
ls 选项 |
st_atim |
最后访问时间 |
read |
-u |
st_mtim |
最后修改时间 |
write |
默认 |
st_ctim |
i节点状态的最后更改时间 |
chmod, chown |
-c |
处理文件时间的函数 futimens
、utimensat
以及 utimes
需要传入的 timespec
数组中,第一个数组元素为访问时间,第二个数组元素为修改时间,分别对应 stat
结构体中的 st_atim
和 st_mtim
。
下列代码对文件信息进行截断,同时不改变其访问和修改日期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <apue.h> #include <fcntl.h> #include <stdlib.h>
int main(int argc, char *argv[]) { int i,fd; struct stat statbuf; struct timespec times[2];
for (i = 1; i < argc; i++) { if (stat(argv[i], &statbuf) < 0) { err_ret("%s: stat error", argv[i]); continue; } if ((fd = open(argv[i], O_RDWR | O_TRUNC)) < 0) { err_ret("%s: open error", argv[i]); continue; } times[0] = statbuf.st_atim; times[1] = statbuf.st_mtim; if (futimens(fd, times) < 0) { err_ret("%s: futimens error", argv[i]); } close(fd); } exit(EXIT_SUCCESS); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| > ls -lu a.txt -rw-rw-r-- 1 parallels parallels 23 8月 21 18:04 a.txt
> ls -l a.txt -rw-rw-r-- 1 parallels parallels 23 8月 21 18:03 a.txt
> ./a.out a.txt
> ls -lu a.txt -rw-rw-r-- 1 parallels parallels 0 8月 21 18:04 a.txt
> ls -l a.txt -rw-rw-r-- 1 parallels parallels 23 8月 21 18:03 a.txt
> ls -lc a.txt # 这里节点状态信息修改时间改变了 -rw-rw-r-- 1 parallels parallels 0 8月 21 18:05 a.txt
|
当前的工作路径
使用函数 chdir
或 fchdir
,定义在 <unistd.h>
中
文件名以及路径的最大长度设置在 <limits.h>
文件中
1 2
| #define NAME_MAX 255 #define PATH_MAX 4096
|
修改当前的路径,并打印其绝对路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <apue.h> #include <stdlib.h> #include <unistd.h>
int main(int argc, char *argv[]) { char *ptr; size_t size;
if (chdir("/usr/libexec/docker") < 0) { err_sys("chdir failed"); }
ptr = path_alloc(&size);
if (getcwd(ptr, size) == NULL) { err_sys("getcwd failed"); }
printf("cwd = %s\n", ptr);
exit(EXIT_SUCCESS); }
|
识别设备编号
每个文件系统所在的存储设备都由其 主、次设备号 表示。设备号所用的数据类型是基本系统数据类型 dev_t
。 主设备号
标识 设备驱动程序
或 与其通信的外设板
。 次设备号
标识 特定的子设备
。
通过宏 major
和 minor
来访问主、次设备号。对于Linux,这两个宏定义在头文件 <sys/sysmacros.h>
中。
st_dev
为文件系统的设备号,包含 文件名
及与其对应的 i节点
。 只有 字符特殊文件
和 块特殊文件
才有 st_rdev
值,此值包含实际设备的设备号。
下列函数能打印设备号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <apue.h> #include <stdlib.h> #include <stat.h> #include <sys/sysmacros.h>
int main(int argc, char *argv[]) { int i; struct stat buf;
for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (stat(argv[i], &buf) < 0) { err_ret("stat error"); continue; } printf("dev = %d/%d", major(buf.st_dev), minor(buf.st_dev));
if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) { printf(" (%s) rdev = %d/%d", (S_ISCHR(buf.st_mode)) ? "character" : "block", major(buf.st_rdev), minor(buf.st_rdev)); }
printf("\n"); }
exit(EXIT_SUCCESS); }
|
编译执行后有如下输出
1 2 3
| > ./a.out /home/parallels /dev/tty /home/parallels: dev = 8/1 /dev/tty: dev = 0/6 (character) rdev = 5/0
|