apue笔记第三章文件IO
一、文件描述符。文件I/O的操作对象。非负整数。
0=STDIN_FILENO。1=STDOUT_FILENO。2=STDERR_FILENO。
二、open函数
#include <fcntl.h> int open(const char *pathname, int oflag, ... /* mode_t mode */); //返回值:若成功返回文件描述符,若出错返回-1。
oflag参数:
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
这三个常量必须包含一个且只能有一个。下面的可以用”|”运算选择添加。
O_APPEND 追加写
O_CREAT 如果文件不存在,则创建文件,此时需要第三个参数指定文件权限位。
O_EXCL 如果同时指定了O_CREAT而文件已经存在,则出错。如果不存在,则创建文件。
这样是测试文件是否存在和创建文件成为原子操作。
O_TRUNC 如果文件存在,且为只写或读写打开成功,则将文件长度截短为0。
O_NONBLOCK 非阻塞模式。
O_NOCTTY 若pathname为终端,则不将此设备作为此进程的控制终端。(?)
O_DSYNC 写操作等待物理I/O完成,若写操作不影响读取刚写入的数据,则不等待文件属性更新。
O_RSYNC 读操作等待物理I/O完成,直到任何对文件同一部分的写操作完成。
O_SYNC 写操作等待物理I/O完成,包括文件属性更新。
Linux中将O_DSYNC,O_RSYNC处理成和O_SYNC功能相同。
三、creat函数
#include <fcntl.h> int creat(const char *pathname, mode_t mode); //返回值:若成功返回文件描述符,若出错返回-1。
相当于 open(pathname, O_WRONLY | O_TRUNC | O_CREAT,mode);
creat的不足是只能以只读打开文件。
推荐使用open(pathname, O_RDWR | O_TRUNC | O_CREAT,mode);
四、close函数
#include <fcntl.h> int close(int filedes); //返回值:成功返回0,出错返回-1。
一个进程终止时,内核会关闭该进程打开的所以文件。
五、lseek函数
#include <unistd.h> off_t lseek(int filedes, off_t offset, int whence); //返回值:成功返回新的文件偏移量,出错返回-1.
whence参数:
SEEK_SET:=0,将文件偏移量设置为距文件开始处offest个字节。
SEEK_CUR:=1,将文件偏移量设置为距文件当前偏移量offset个自己,offest可正可负。
SEEK_END:=2,将文件偏移量设置为距文件结尾处offest个字节,offest可正可负。
测试文件是否可以设置偏移量
off_t currpos= lseek(filedes, 0, SEEK_CUR);
因为偏移量可能是赋值,所以判断出错的时候应该判断是否等于-1,而不是是否小于0。
当文件偏移量大于文件长度,那么下次写将加长文件长度并形成文件空洞。
六、read函数
#include <unistd.h> ssize_t read(int filedes, void *buf, size_t nbytes); //返回值:若成功返回读取的字节数,若已到文件末尾返回0,若出错返回-1.
七、write函数
#include <unistd.h> ssize_t write(int filedes, void *buf, size_t nbytes); //返回值:若成功返回已写字节数,出错返回-1.(返回值与nbytes不同,表示出错。)
八、I/O的效率
参考:http://www.ibm.com/developerworks/cn/linux/l-cn-directio/
九、文件共享
内核使用三种数据结构表示打开的文件。它们之间的关系决定了在文件共享方面一个进程对另一个的进程的影响。
a) 文件描述符:每个进程在进程表中有一个记录项,记录项中包含一个文件描述符表,可将其视为一个矢量,每个文件描述符占用一项。
- 文件描述符标志(close_on_exec)。
- 指向一个文件表项的指针。
b) 文件表项:内核为所有打开的文件维持一张文件表。
- 文件状态标志(读、写、添加、同步、非阻塞等)。
- 当前文件偏移量。
- 指向该文件v节点表项的指针。
c) v节点结构:每个打开的文件或设备都有一个v节点结构。
- 文件类型。
- 对此文件进行各种操作的函数的指针。
- 该文件的i节点。(文件所以者、文件长度、文件所在的设备、指向文件实际数据块在磁盘上所在位置的指针等)
这些信息是打开文件时从磁盘上读入内存的,所以有关文件的信息都是开始可供使用的。
Linux中没有v节点,而是使用通用的i节点,两者在概念上是一样的。
- 两个进程打开同一文件,每个进程都有自己的文件表项。
- lseek函数只修改了文件表项中的当前文件偏移量,没有进行任何I/O操作。
- dup函数使两个文件描述符指向同一个文件表项。
- fork()函数后,父子进程对每一个打开文件描述符共享同一文件表项。
![]()
十、原子操作
原子操作是为了解决多进程间文件共享时的同步问题。
- 调用open函数时,使用O_APPEND标志,使每次写时,定位到文件末尾。
- 调用open函数时,设置O_TRUNC和O_CREAT标志,使测试和创建文件成为原子操作。
- 使用pread和pwrite原子性执行seek和I/O。
#include <unistd.h> ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset); //返回值:若成功返回读到的字节数,到文件末尾返回0,出错返回-1. ssize_t pwrite(int filedes, void *buf, size_t nbytes, off_t offset); //返回值:若成功返回写入的字节数,出错返回-1.
- dup2函数使关闭原文件描述符和复制文件描述符成为原子操作。
十一、dup和dup2函数
用来复制一个现存的文件描述符。
#include <unistd.h> int dup(int filedes); int dup2(int filedes, int fiedes2); //返回值:成功返回新的文件描述符,出错返回-1.
dup的返回值一定是当前可用的文件描述符中的最小值。
dup2用filedes指定新描述符的数值,若filedes2已经打开,则先关闭。若filedes=filedes2则不关闭,直接返回filedes。
dup和dup2返回的新文件描述符和参数filedes共享一个文件表项。
dup(filedes);等效于fcntl(filedes, F_DUPFD, 0);
dup2(filedes,filedes2);等效于close(filedes2); fcntl(filedes,F_DUPFD, filedes2);前者为原子操作。
十二、sync 、fsync和fdatasync函数
延迟写:文件I/O不是直接对磁盘文件的操作,而是对内核中缓冲区高速缓存或页面高速缓存的操作。
#include <unistd.h> int fsync(int filedes); int fdatasync(int filedes); //返回值:成功返回0,出错返回-1. void sync(void);
sync只是将所有修改过的块缓冲区排入写队列,然后就返回。并不等待实际磁盘写操作完成。(通常守护进程update隔段时间(一般30s)调用一次sync)。
fsync只作用于filedes一个文件,且等待写操作完成。
fdatasync类似于fsync,但只影响文件的数据部分。fsync还会更新文件的属性。
十三、fcntl函数
#include <fcntl.h> int fcntl(int filedes, int cmd, ... /* int arg */); //返回值:若成功依赖于cmd,出错返回-1.
fcntl的五中功能:
复制文件描述符:cmd =F_DUPFD。
获得/设置文件描述符标志:cmd =F_GETFD或cmd = F_SETFD。
获得/设置文件状态标志:cmd =F_GETFL或cmd = F_SETFL。
获得/设置异步I/O所有权:cmd =F_GETOWN或cmd = F_SETOWN。
获得/设置记录锁:cmd =F_GETLK / F_SETLK / F_SETLKW。
F_DUPFD:复制文件描述符。返回新的文件描述符,大于或等于第三个参数中的最小值。与filedes共享文件表项。清除F_CLOEXEC标志。
F_GETFD:获得文件描述符标志。返回文件描述符标志,即F_CLOEXEC。
F_SETFD:设置文件描述符标志。新值为第三个参数。一般不是用F_CLOEXEC,使用0(默认,exec时不关闭)或1(exec是关闭)。
F_CLOEXEC参考:http://blog.csdn.net/ustc_dylan/article/details/6930189
F_GETFL:获得文件状态标志。判断三个访问状态标志(O_RDONLY,O_WRONLY,O_RDWR)时,因为他们互斥,所以需要与O_ACCMODE进行与运算后判断。
F_SETFL:设置文件状态标志。将文件状态设置为第三个参数的值。
F_GETOWN:
F_SETOWN:
F_GETLK:
F_SETLK:
F_SETLKW:
在修改文件描述符标志和文件状态标志时,应先读取现在的值,然后修改它,最后设置新标志位。
十四、ioctl函数
#include <unistd.h> #include <sys/ioctl.h> int ioctl(int filedes, int request, ...); //返回值:出错返回-1,成功返回其他值。
十五、/dev/fd
打开/dev/fd/n相当于复制文件描述符n。
fd =open(“/dev/fd/0”, mode);相当于fd = dup(0);
但是mode必须为原打开mode的子集,否则增加的访问模式不起作用。