高级IO
非阻塞IO
对于可能使进程永远阻塞的系统调用,如果不想进程一直阻塞,可以将系统调用设为非阻塞:
- open时指定O_NONBLOCK标志;
- fcntl对一个打开的描述符设置O_NONBLOCK标志;
这样,如果系统调用无法满足要求,会立刻返回并设定相应错误标志。
记录锁
记录锁保证不会有多个进程同时修改一个文件的同一区域,设置记录锁的POSIX方法是通过fcntl:
- cmd为F_GETLK, F_SETLK, F_SETLKW, arg是指向flock结构的指针;
flock结构如下:
1
2
3
4
5
6
7struct flock{
short l_type; // F_RDLCK, F_WRLCK, F_UNLCK
short l_whence; // SEEK_SET, SEEK_CUR, SEEK_END
off_t l_start; // 偏移量
off_t l_len; // 区域长度,0表示锁到EOF
pid_t l_pid; // 锁的持有者
}同一进程重复加锁会替换之前的锁;
- 锁与进程和文件都关联,进程终止锁全部释放,而即使描述符引用计数大于1,只要任何一个描述符关闭,描述符引用文件的锁全部释放,这是因为锁信息存放在v节点中,而锁信息并没有记录究竟是谁加的锁;
- fork不会继承锁,exec取决是否设置exec关闭标志;
- 在文件尾端加锁要注意;
IO复用
select
1 | nt select(int nfds, fd_set *readfds, fd_set *writefds, |
- nfds是最大fd加1;
- 每次调用要重新设置关心的描述符集;
- 描述符是否阻塞不影响select;
- timeout为NULL无限等待,等于0非阻塞;
- pselect时间更精确,可原子安装信号屏蔽字;
- select监听的描述符上限取决于fd_set类型的大小;
poll
1 | int poll(struct pollfd *fds, nfds_t nfds, int timeout); |
- 和select本质一样,都是轮询,只是接口不同,基本没有上限;
异步IO
和同步IO相比,异步IO会替永远替进程完成所有的IO操作,然后才通知进程,而无论是阻塞IO、非阻塞IO、IO复用、信号驱动IO,都只是通知可以进行IO操作的方式不同,进程还是要自己把数据取出或写入。
readv和writev
1 | ssize_t readv(int fd, const struct iovec *iov, int iovcnt); |
- 分散读、集中写。
存储映射IO
1 | void *mmap(void *addr, size_t length, int prot, |
- mmap将一个磁盘文件映射到进程内存中;
- addr为映射起始地址,通常为0,代表由系统选择地址;
- fd是被映射的文件描述符,offset是偏移量,length是映射长度;
- prot是对映射区的保护要求,不能和fd打开模式冲突:
- PROT_READ:可读;
- PROT_WRITE:可写;
- PROT_EXEC:可执行;
- PROT_NONE:不可访问;
- flags是属性:
- MAP_FIXED:映射地址必须是addr;
- MAP_SHARED:允许进程修改映射区;
- MAP_PRIVATE:对映射区的写操作会创建一个进程的副本;
映射区域如下:
- mprotect可以更改映射区权限;
- msync类似fsync;
- munmap解除映射;