第三章 文件I/O
本章主要讲述了5个常用函数 open
、read
、write
、lseek
、close
。
以及在多进程中共享文件所用到的 dup
、fcntl
、sync
、fsync
、ioctl
。
第三章 文件I/O
文件描述符
文件描述符 | 符号常量 | 含义 |
---|---|---|
0 | STDIN_FILENO | 标准输入 |
1 | STDOUT_FILENO | 标准输出 |
2 | STDERR_FILENO | 标准错误 |
函数 open 和 openat
1 |
|
open 和 openat 的区别
- path 是绝对路径名时, fd 参数被忽略
- path 参数指定的是相对路径名,fd 参数指出了相对路径名所在文件系统中的开始地址。
- path 参数指定的是相对路径名,fd 的参数具有特殊值
AT_FDCWD
。这时open 和 openat 函数类似。
TODO: TOCTTOU
函数 creat
1 |
|
操作系统中,每个进程的文件描述符不同。相互独立。
函数 close
可以用 close 函数关闭一个打开的文件。
1 |
|
函数 lseek
lseek 可以显示地为一个文件设置偏移量
1 |
|
whence 参数:
- SEEK_SET 从文件开始处偏移offset 字节
- SEEK_CUR 当前值加 offset, offset可正可负
- SEEK_END 从文件尾部偏移 offset 个字节, offset可正可负
一个问题:
1 | ➜ IO cat lseek_test.c |
空洞的文件里面还有填充着 1b01 3b03 0034
, 不知道这个是什么东西。
奇怪的 printf
函数 read
1 |
|
函数 write
1 |
|
I/O 的效率
缓冲区长度 32字节的时候已经足够好了
TODO: 自己做一下测试
文件共享
UNIX 支持不同进程间共享打开文件。
原子操作
不支持原子操作实例:
早期 UNIX不支持 O_APPEND 实现 append 操作需要:
1 | if (lseek(fd, OL, SEEK_END) < 0) /*position to EOF */ |
在多进程中,如果在 lseek 后发生了进程的切换,那么就有很大可能发生文件的写入覆盖错误。
函数 pread 与 pwrite
pread 与 pwrite 支持原子操作
1 |
|
PS. 不更新文件偏移量
创建文件
TODO: 坑 待填
函数 dup 和 dup2
1 |
|
实验:
1 | ➜ IO cat -n dup_test.c |
函数 sync,fsync 和 fdatasync
功能: 保持文件在高速缓存与磁盘同步,数据一致性。
1 |
|
- sync 将所有修改过的块缓冲区排入写队列,然后就返回
- fsync 当前 fd 的数据,及时写入磁盘,完成之后才会返回
- fdatasync 和fsync类似,不过还更新 文件属性
函数 fcntl
fcntl 函数可以改变一打开文件的属性。
1 |
|
fcntl函数有5种功能:
- 复制一个现有的描述符(cmd=F_DUPFD).
- 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
- 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
- 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
cmd 选项:
F_DUPFD 返回一个如下描述的(文件)描述符:
最小的大于或等于arg的一个可用的描述符
与原始操作符一样的某对象的引用
如果对象是文件(file)的话,返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset)
相同的访问模式(读,写或读/写)
相同的文件状态标志(如:两个文件描述符共享相同的状态标志)
与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用
F_GETFD 取得与文件描述符fd联合close-on-exec标志,类似FD_CLOEXEC.如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(), 否则如果通过exec运行的话,文件将被关闭(arg被忽略)
F_SETFD 设置close-on-exec旗标。该旗标以参数arg的FD_CLOEXEC位决定。
F_GETFL 取得fd的文件状态标志,如同下面的描述一样(arg被忽略)
F_SETFL 设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND, O_NONBLOCK,O_SYNC和O_ASYNC。
F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)
F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明,否则,arg将被认为是进程id
fcntl的返回值: 与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。
应用实例:
1 |
|
函数 ioctl
I/O 操作杂货箱
1 |
|
TODO: 后面会有详解,这里先不在详细说明
/dev/fd
文件标识符
MISC
关于lseek 与 append 的冲突
1
2
3
4
5
6
7
8
9
10
int main(){
int fd = open("./test", O_RDWR | O_APPEND, 0770);
printf("%d\n>>>",lseek(fd,-35, SEEK_END));
write(fd,"********",10);
return 0;
}
// 结果,只能把内容写到文件末尾2.fork 中 father 与 child 的运行顺序
- 先运行完 father 然后再运行 child 除非 father wait
- write 函数,不管数组是否越界,只是个写
dw文件I/O