UNIX环境高级编程 - 文件I/O - 原子操作、复制、修改文件描述符
原子操作
- 追加到一个文件
- 追加一个文件时,不能通过
lseek到末尾然后write。要用O_APPEND选项打开文件,然后直接write。- 通过
lseek到末尾然后write时,如果多个进程同时执行这两个操作,则会引起竞争条件 - 通过
O_APPEND选项打开文件,然后直接write时,内核每一次在写操作之前,都会将进程的当前偏移量设置到文件的末尾,于是就不需要执行lseek定位操作
- 通过
pread/pwrite可以执行原子性的定位读/定位写O_CREAT|O_EXCL选项打开文件时,可以原子性的检查文件是否存在和创建文件这两个操作。
- 函数
pread和pwrite
#include <unistd.h>
ssize_t pread(int fd,void*buf,size_t nbytes,off_t offset);
ssize_t pwrite(int fd,const void*buf,size_t nbytes,off_t offset);
- 参数:
fd:打开的文件描述符buf:读出数据存放的缓冲区/ 写到文件的数据的缓冲区nbytes:预期读出/写入文件的字节数offset:从文件指定偏移量开始执行read/write
- 返回:
- 成功:读到的字节数/已写的字节数
- 失败: -1
调用pread相当于先调用lseek再调用read.但是调用pread时,无法中断其定位和读操作,并且不更新当前文件偏移量;调用pwrite相当于先调用lseek再调用write.但是调用pwrite时,无法中断其定位和写操作,并且不更新当前文件偏移量
其他操作
dup
使用dup/dup2复制一个现有的文件描述符:
#include <unistd.h>
int dup(int fd);
int dup2(int fd,int fd2);
- 参数:
fd:被复制的文件描述符(已被打开)fd2:指定的新的文件描述符(待生成)
- 返回值:
- 成功: 返回新的文件描述符
- 失败: 返回 -1
对于dup函数,返回的新的文件描述符一定是当前可用的文件描述符中最小的数字。
对于dup2函数:
- 如果
fd2已经是被打开的文件描述符且不等于fd,则先将其关闭,然后再打开(原子操作) - 如果
fd2等于fd,则直接返回fd2(也等于fd),而不作任何操作
任何情况下,这个返回的新的文明描述符与参数fd共享同一个文件表项(因此文件状态标志以及文件偏移量都会共享)。任何情况下,这个返回的新的文明描述符的close-on-exec标志总是被清除。
dup后的内核数据结构如下:

sync
UNIX操作系统在内核中设有缓冲区,大多数磁盘 I/O 都通过缓冲区进行。当用户程序想文件写入数据时,内核通常都首先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘。这种方式称为延迟写。
- 当内核需要重用缓冲区来存方其他数据时,它会把所有延迟写的数据库写入磁盘
- 也可以调用下列函数来显式的将延迟写的数据库写入磁盘
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);
-
参数:
fd:指定的打开的文件描述符
-
返回值:
- 成功:返回 0
- 失败: 返回 -1
区别:
sync:将所有修改过的块缓冲区排入写队列,然后返回,它并不等待时机写磁盘结束fsync:只对由fd指定的单个文件起作用,等待写磁盘操作结束才返回fdatasync:只对由fd指定的单个文件起作用,等待写磁盘操作结束才返回,但是它只影响文件的数据部分(fsync会同步更新文件的属性)
update守护进程会周期性的调用sync函数。命令sync也会调用sync函数

浙公网安备 33010602011771号