IO

阻塞IO

只要内核没有将数据准备好,系统的调用就会一直等待,所有的套接字,都是阻塞方式。
这里写图片描述

非阻塞式IO

如果内核还未将数据准备好, 系统调⽤用仍然会直接返回, 并且返回EWOULDBLOCK错误码.
非阻塞IO往往需要程序员循环的方式反复尝试读写⽂文件描述符, 这个过程称为轮询. 这对CPU来说是较⼤大的浪
费, ⼀一般只有特定场景下才使⽤用.
这里写图片描述

信号驱动IO

利用SIGIO信号通知程序进行IO操作,系统调用sigactio,若是内核没有准备好,则直接返回继续等待,直至内核
递交SIGIO信号准备就绪,系统开始读取。
这里写图片描述

IO多路转接

利用SELECT控制多个文件描述符,读取内核信息。
这里写图片描述

异步IO

由内核在数据拷⻉贝完成时, 通知应⽤用程序(⽽而信号驱动是告诉应⽤户序何时可以开始拷⻉贝数据).
这里写图片描述
任何IO过程中, 都包含两个步骤. 第一是等待, 第二是拷贝. 而且在实际的应用场景中, 等待消耗的时间往往都远远高于拷贝的时间. 让IO更高效, 最核心的办法就是让等待的时间尽量少.

fcntl

int fcntl(int fd, int cmd, … /* arg */ );

  • 复制⼀一个现有的描述符(cmd=F_DUPFD).
    用来查找大于或等于参数arg的最小且仍未使用的文件描述符,并且复制参数fd的文件描述符。执行成功则返回新复制的文件描述符。新描述符与fd共享同一文件表项,但是新描述符有它自己的一套文件描述符标志,其中FD_CLOEXEC文件描述符标志被清除。
  • 获得/设置⽂文件描述符标记(cmd=FGETFD 或 FSETFD).
    F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。
    F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
  • 获得/设置⽂文件状态标记(cmd=FGETFL 或 FSETFL).
    F_GETFL 取得文件描述符状态旗标,此旗标为open()的参数flags。
    F_SETFL 设置文件描述符状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。可以将⼀一个⽂文件描述符设置为非阻塞.——–>进而轮询读取数据
  • 获得/设置异步I/O所有权(cmd=FGETOWN 或 FSETOWN).
  • 获得/设置记录锁(cmd=FGETLK,FSETLK或F_SETLKW).
重定向dup、dup2
int dup(int oldfd);
int dup2(int oldfd, int newfd);

两个均为复制一个现存的文件的描述
两个函数的返回:若成功为新的文件描述,若出错为-1;
由dup返回的新文件描述符一定是当前可用文件描述中的最小数值。用dup2则可以用fd2参数指定新的描述符数值。如果fd2已经打开,则先关闭。若fd1=fd2,则dup2返回fd2,而不关闭它。通常使用这两个系统调用来重定向一个打开的文件描述符。
利⽤用了dup分配⽂文件描述符是从最⼩小的开始分配这样的机制, 来完成这个重定向. 相
⽐比之下, 是⽤用dup2更加优雅⼀一些.(通常用dup2)

select

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
用途
  • select系统调⽤用是⽤用来让我们的程序监视多个⽂文件描述符的状态变化的。
  • 程序会停在select这⾥里等待,直到被监视的⽂文件描述符有⼀一个或多个发⽣生了状态改变
参数
  • 参数nfds是需要监视的最大的文件描述符值+1。为什么文件描述符+1?例:文件描述符的有效范围是 0 到 MAX。MAX=63(0 — 63),所以每个进程最多可以打开 64=63+1 个文件。
  • rdset,wrset,exset分别对应于需要检测的可读⽂文件描述符的集合,可写⽂文件描述符的集 合及异常⽂文件描述符的集合
  • 参数timeout为结构timeval,⽤用来设置select()的等待时间
    参数timeout取值
  • NULL:则表⽰示select()没有timeout,select将⼀一直被阻塞,直到某个⽂文件描述符上发⽣生了事件
  • 0:仅检测描述符集合的状态,然后⽴立即返回,并不等待外部事件的发⽣生。
  • 特定的时间值:如果在指定的时间段⾥里没有事件发⽣生,select将超时返回。
关于fd_set描述

fd_set这个结构就是⼀一个整数数组, 更严格的说, 是⼀一个 “位图”. 使⽤用位图中对应的位来表⽰示要监视的⽂文件描述符。

void FD_CLR(int fd, fd_set *set); // ⽤用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); // ⽤用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); // ⽤用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // ⽤用来清除描述词组set的全部位
函数返回值
  • 执⾏行成功则返回⽂文件描述词状态已改变的个数
  • 如果返回0代表在描述词状态改变前已超过timeout时间,没有返回
  • 当有错误发⽣生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测。
    错误值可能为: * EBADF ⽂文件描述词为⽆无效的或该⽂文件已关闭 * EINTR 此调⽤用被信号所中断 * EINVAL参数n 为负值。 * ENOMEM 核⼼心内存不⾜足
socket就绪条件

读就绪

  • socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以⽆无阻塞的读该⽂文件描述符, 并且返回值⼤大于0;
  • socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;
  • 监听的socket上有新的连接请求;
  • socket上有未处理的错误;
    写就绪
  • socket内核中, 发送缓冲区中的可⽤用字节数(发送缓冲区的空闲位置大小), 大于等于低⽔水位标记SO_SNDLOWAT, 此时可以⽆无阻的写, 并且返回值⼤大于0;
  • socket的写操作被关闭(close或者shutdown). 对⼀一个写操作被关闭的socket进⾏行写操作, 会触发SIGPIPE信号;
  • socket使⽤用⾮非阻塞connect连接成功或失败之后;
  • socket上有未读取的错误;
    关于水位标记请看
    https://www.cnblogs.com/brucemengbm/p/7079961.html

??????????????异常就绪

socket上收到带外数据. 关于带外数据, 和TCP紧急模式相关(回忆TCP协议头中, 有⼀一个紧急指针的字段),

select特点
  • 可监控的⽂文件描述符个数取决与sizeof(fdset) 的值 . 我这边服务器上 sizeof(fdset)=512,每bit表⽰示⼀一个⽂文件描述符,则我服务器上⽀支持的最⼤大⽂文件描述符是512*8=4096.
  • 备注: fd_set的⼤大⼩小可以调整,可能涉及到重新编译内核
select 缺点
  • 每次调⽤用select, 都需要⼿手动设置fd集合, 从接⼝口使⽤用⾓角度来说也⾮非常不便.
  • 每次调⽤用select,都需要把fd集合从⽤用户态拷⻉贝到内核态,这个开销在fd很多时会很大
  • 同时每次调⽤用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select⽀支持的⽂文件描述符数量太小

epoll

是为处理⼤大批量句柄⽽而作了改进的poll,它⼏几乎具备了之前所说的⼀一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知⽅方法。

  • int epoll_create(int size);创建⼀一个epoll的句柄.⾃自linux2.6.8之后,size参数是被忽略的.⽤用完后, 必须调⽤用close()关闭
  • **int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    epoll的事件注册函数.
    第⼀一个参数是epoll_create()的返回值(epoll的句柄).
    第⼆二个参数表⽰示动作,⽤用三个宏来表⽰示.EPOLL_CTL_ADD :注册新的fd到epfd中;
    EPOLL_CTL_MOD :修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL :从epfd中删除⼀一个fd;
    第三个参数是需要监听的fd.
    第四个参数是告诉内核需要监听什么事.
    int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    收集在epoll监控的事件中已经发送的事件.
  • 参数events是分配好的epoll_event结构体数组.epoll将会把发⽣生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这
    个events数组中,不会去帮助我们在⽤用户态中分配内存).
  • maxevents告之内核这个events有多⼤大,这个 maxevents的值不能⼤大于创建epoll_create()时的size.
  • 参数timeout是超时时间 (毫秒,0会⽴立即返回,-1是永久阻塞).
  • 如果函数调⽤用成功,返回对应I/O上已准备好的⽂文件描述符数目,如返回0表⽰示已超时, 返回⼩小于0表⽰示函数失败.
epoll的使⽤用过程就是三部曲

调⽤用epoll_create创建⼀一个epoll句柄;
调⽤用epoll_ctl, 将要监控的⽂文件描述符进⾏行注册;
调⽤用epoll_wait, 等待⽂文件描述符就绪;

epoll的优点
  • ⽂文件描述符数目⽆无上限: 通过epoll_ctl()来注册⼀一个⽂文件描述符, 内核中使⽤用红⿊黑树的数据结构来- 管理所有需要监控的⽂文件描述符.

  • 基于事件的就绪通知⽅方式: ⼀一旦被监听的某个⽂文件描述符就绪, 内核会采⽤用类似于callback的回调机
    制, 迅速激活这个⽂文件描述符. 这样随着⽂文件描述符数量的增加, 也不会影响判定就绪的性能;

  • 维护就绪队列: 当⽂文件描述符就绪, 就会被放到内核中的⼀一个就绪队列中. 这样调⽤用epoll_wait获取就绪⽂文件描述符的时候, 只要取队列中的元素即可, 操作的时间复杂度是O(1);
  • 内存映射机制: 内核直接将就绪队列通过mmap的⽅方式映射到⽤用户态. 避免了拷⻉贝内存这样的额外性
    能开销.

select ,poll ,epoll区别
https://blog.csdn.net/jgm20475/article/details/81083529
https://blog.csdn.net/lixungogogo/article/details/52226501

惊群问题
https://blog.csdn.net/fsmiy/article/details/36873357

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值