第十四章-高级I/O

高级I/O包括:非阻塞I/O、记录锁、I/O多路复用(select和poll)、异步I/O、readv、writev函数以及存储映射函数(mmap)。

一、非阻塞I/O

对于一个给定的描述符,有两种为其指定非阻塞I/O的方法。
(1)如果调用open获得描述符,则可指定O_NONBLOCK标志
(2)对已经打开的描述符,可调用fcntl,由该函数打开O_NONBLOCK文件标志
对于非阻塞情况下返回的errno为EAGAIN或EWOULDBLOCK(在较新版本的UNIX下,两者等价),表示由于资源限制/不满足条件返回的错误,即再试一次。
比如调用read时,暂时无数据可读;调用write时,缓冲区暂时没有空余空间;都会直接返回,并设置errno。

二、记录锁

记录锁(record locking)的功能是:当一个进程正在读或修改文件的某个部分时,使用记录锁空余阻止其他进程修改同一文件区。

#include <fcntl.h>
int fcntl(int fd, int cmd, .../* struct flock* flockptr */);
//成功,返回值依赖于cmd;失败,返回-1

对于记录锁,cmd是F_GETLK, F_SETLK, F_SETLKW,第三个参数时指向flock结构的指针

三、I/O多路转接

3.1 函数select和pselect

传给select的参数告诉内核:

  • 关心的描述符
  • 对于每个描述符所关心的条件(读?写?异常?)
  • 愿意等待的时间

从select返回时,内核告诉我们:

  • 已准备好的描述符的总数量
  • 对于各个条件,分别有哪些描述符已准备好
#include <sys/select.h>
int select(int maxdpl, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* tvptr);
//返回准备就绪的描述符数量;超时,返回0;出错,返回-1

(1)最后一个参数 tvptr,指定愿意等待的时间(s/μs),三种情况:

  • tvptr == NULL:永远等待,直到捕捉到信号或有描述符准备好
  • tvptr->tv_sec == 0 && tvptr->tv_usec == 0 :不等待,测试所有指定描述符并立即返回
  • tvptr->tv_sec != 0 && tvptr->tv_usec != 0 :等待指定的时间

(2)中间三个参数是指向 描述符集(fd_set) 的指针。每个描述符集存储在一个fd_set数据类型中,它可以为每一个可能的描述符保持一位。我们可以认为它只是一个很大的字节数组。
在这里插入图片描述
这三个参数中若为空指针,则表示对相应条件并不关心。
(3)参数maxfdp1意思是“最大描述符编号值加1”
在三个描述符集中找出最大的描述符编号,然后+1;也可将其设为FD_SETSIZE,这是<sys/select.h>中的一个常量,它指定最大描述符数(经常是1024)。

POSIX也定义了一个select的变体,称为pselect

3.2 函数poll

poll函数类似于select,但是程序接口有所不同

#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
//返回准备就绪的描述符数量;超时,返回0;出错,返回-1

与select不同,poll不是为每一个条件(可读、可写、异常)构造一个描述符集,而是构造一个pollfd结构的数组,每个数组元素指定一个描述编号以及我们对该描述符感兴趣的条件:

struct pollfd{
	int fd;
	short events;
	short revents;
};

fdarray数组中的元素由nfds指定。
应将events成员设置为图中所示值的一个或几个;返回时,revents成员由内核设置,用于说明描述符发生了哪些事情。
注:poll没有更改events成员,而select修改其参数以指示哪一个描述符已准备好了
在这里插入图片描述
当一个描述符被挂断(POLLHUP)后,就不能再写该描述符,但仍可以从该描述符中获取数据。
与select一样,一个描述符是否阻塞不会影响poll是否阻塞。

四、异步I/O

select和poll可以实现异步形式的通知。信号机制提供了一种以异步形式通知某种事件已发生的方法。

4.1 System V异步I/O
4.2 BSD异步I/O
4.3 POSIX异步I/O

五、函数readv和writev

readv和writev用于在一次函数中读、写多个非连续缓冲区。有时将其称为散布读(scatter read)和聚集写(gather write)

#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec* iov, int iocnt);
ssize_t writev(int fd, const struct iovec* iov, int iocnt);
//成功,返回已读或已写的字节数;出错,返回-1

struct iovec{
	void* iov_base; //starting address of buffer
	size_t iov_len; //size of buffer
};

iov数组中的元素数由iocvnt指定。
在这里插入图片描述
wirtev按顺序从缓冲区中输出数据;readv将读入的数据按顺序散布到缓冲区中。readv总是先填满一个缓冲区,再填写下一个。

六、函数readn和writen

通常,在读写一个管道、网络设备或终端时,会发生未读完,或未写完等等。
调用readn或writen读、写指定的N字节数据,并处理返回值可能小于要求值的情况。其原理就是按需多次调用read和write,直至读、写了N字节数据

#include "apue.h"
ssize_t readn(int fd, void* buf, size_t nbytes);
ssize_t writen(int fd, void* buf, size_t nbytes);

七、存储映射I/O(mmap)

存储映射I/O(memeory-mapped I/O)能将一个磁盘文件映射到存储空间的一个缓冲区上。对缓冲区的操作就相当于对文件操作,这样就可以在不使用read和write的情况下执行I/O

#include <sys/mman.h>
void* mmap(void* addr, size_t len, int port, int flag, int fd, off_t off);
//成功,返回映射区地址;出错,返回MAP_FAILED
  • addr用于指定映射存储区的起始地址。通常将其设为0,表示由系统选择起始地址

  • fd指定要被映射文件的描述符(需先打开)。len参数是映射的字节数,off是要映射字节在文件中的起始偏移量

  • port指定映射存储区的保护要求:
    在这里插入图片描述

  • flag
    (1)MAP_FIXED
    (2)MAP_SHARED
    (3)MAP_PRIVATE
    在这里插入图片描述

当进程终止时,会自动解除存储映射区的映射,或者直接调用munmap函数也可以解除映射区。

int munmap(void* addr, size_t len);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值