1. 简介
本文是上一篇笔记文件IO(1)的续写,接下来继续补充一些文件IO特性以及介绍剩下的一些函数。文件IO的读写效率与调用时传入的buf大小有关,也与打开的文件描述标志有关(O_SYNC和O_DSYNC ),影响效率IO效率的相关函数有sync、fsync和fdatasync。理解dup、dup2和fcntl函数的预前知识:
(1)每个进在进程表中都有一个记录项,记录项包含一张打开的文件描述符表,每个描述符占一项。与每个文件描述符相关联的是:
a. 文件描述符标志(close_on_exec)。
b. 指向一个文件表项的指针。
(2)内核为所有打开的文件维持一张文件表。每个文件表项包含:
a. 文件状态标志(读、写、追加、同步和非阻塞等)。
b. 当前文件的偏移量。
c. 指向该文件的V节点表项的指针。
(3)每个打开的文件(或设备)都有一个V节点(v-node)结构。V节点包含了文件类型和对此文件进行各种操作的函数指针。
对于dup和dup2来说,调用后复制了一个新的文件描述符表项,新旧文件描述符表项的指针指向同一张文件表;在父子进程中,子进程复制父进程的文件描述符表,父子进程相同的文件描述符表项的指针指向同一张文件表;两个独立进程打开同一文件都有自己独立的文件描述符表和文件表项,它们文件表项的V节点指针都指向同一个V节点。
2.函数原型
ssive_t pread(int fd, void *bug, size_t nbytes, off_t offset) | pread和pwite相当于调用lseek后调用read,但前者是一个原子操作,另一个重要区别是前者调用后不更新当前文件偏移量 返回值:若成功,返回读到的字节数;若出错,返回-1;若到达文件末尾,返回0 |
ssive_t pwrite(int fd, void *bug, size_t nbytes, off_t offset) | 返回值:若成功,返回已写入的字节数;若出错,返回-1 |
int dup(int fd) | 返回值:若成功,返回新的文件描述符;若出错,返回-1 |
int dup2(int fd, int fd2) | 如果fd2已经打开,则先将其关闭;若fd2等于fd,则返回fd2,而不关闭它 返回值:若成功,返回新的文件描述符(等于fd2);若出错,返回-1 |
int fsync(int fd) int fdatasync(int fd) | fsync等待写磁盘操作结束才返回(文件属性+数据),相当于文件描述符标志O_SYNC ;fdatasync只影响数据部分,相当于文件描述符标志O_DSYNC 返回值:若成功,0;若出错,返回-1 |
void sync(void) | sync只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘的操作结束。 |
int fcntl(int fd, int cmd, .../* int arg */) | (1)复制一个已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC) (2)获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD) (3)获取/设置文件状态标志(cmd=F_GETFL或F_SETFL) (4)获取/设置异步IO所有权(cmd=F_GETOWN或F_SETOWN) (3)获取/设置记录锁(cmd=F_GETLK、F_SETLK或F_SETLKW) 返回值,若成功,依赖于cmd;若出错,返回-1 |
int ioctl(int fd, int request, ...) | 使用ioctl函数去操纵一些前面IO函数难以完成的任务,比如控制字符终端设备 返回值,若出错,返回-1;若成功,返回其他值 |
3. 实例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
int main()
{
int fd0 = open("t.txt", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, (S_IRUSR|S_IWUSR)|(S_IRGRP|S_IWGRP)|(S_IROTH));
int fd = open("t.txt", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, (S_IRUSR|S_IWUSR)|(S_IRGRP|S_IWGRP)|(S_IROTH));
printf("fd0 = %d, O_CLOEXEC flag = %d\n",fd0, fcntl(fd0, F_GETFD));
printf("fd = %d, O_CLOEXEC flag = %d\n",fd, fcntl(fd, F_GETFD));
int fd2 = dup(fd);
printf("fd2 = %d, O_CLOEXEC flag = %d\n",fd2, fcntl(fd2, F_GETFD));
int fd3 = dup2(fd, fd);
printf("fd3 = %d, O_CLOEXEC flag = %d\n",fd3, fcntl(fd3, F_GETFD));
int fd4 = dup2(fd, fd0);
printf("fd4 = %d, O_CLOEXEC flag = %d\n",fd4, fcntl(fd4, F_GETFD));
write(fd, "fd1\n", 4);
write(fd2, "fd2\n", 4);
write(fd3, "fd3\n", 4);
write(fd4, "fd4\n", 4);
int stdout_backup = dup(STDOUT_FILENO);
write(stdout_backup,"hello backup\n", 13);
printf("hello original\n");
int re_stdout = dup2(fd, STDOUT_FILENO);
printf("write to t.txt\n");
printf("write to t.txt\n");
printf("write to t.txt\n");
write(stdout_backup, "**********\n", 11);
dup2(stdout_backup, re_stdout);
close(stdout_backup);
printf("write to stdout\n");
printf("write to stdout\n");
return 0;
}
4.小结
1. 理解dup和dup2函数的使用和区别,两函数(对于dup2:fd != fd2)返回文件描述符的标志O_CLOEXEC总是被清除,dup2函数可实现流的重定向,如把标准输出重定向到文本文件。
3. 理解影响文件IO效率的因素,文件描述符标志(O_SYNC和O_DSYNC )和相关联的函数(sync、fsync和fdatasync)
3. 理解fcntl函数的各个命令以及返回值,理解原子操作