Linux通信总结——阻塞/非阻塞、同步/异步、IO模型

1.阻塞/非阻塞,同步/异步
  • 一次典型的IO包括两个阶段:数据就绪和数据读写
  • 数据就绪阶段:阻塞和非阻塞
    • 阻塞:数据还没到达之前,调用IO方法的线程会挂起(并不占用CPU资源)
    • 非阻塞:不会改变线程的状态,会通过返回值判断缓冲区是否有数据传来 int size = recv(sockfd, buf, 1024, 0);
      • size = -1,读取出错(但是要特殊判断下列3种情况:EINTR,EAGAIN,EWOULDBLOCK)
      • size = 0,读取到数据末尾,或者对方连接关闭
      • size > 0,读取到多少数据
    • 在处理IO的时候,阻塞和非阻塞都是同步IO,只有使用了特殊API的才是异步IO。
  • 数据读写阶段:同步和异步
    • 同步:A向B请求调用网络IO接口时,数据的读写都是由请求方A自己完成
    • 异步:...,A向B传入请求的事件以及事件发生时通知的方式,A就可以处理其他业务逻辑了,当B监听到事件处理完成之后,会用事先约定好的通知方式,通知A处理结果。
      • Linux异步IO接口 aio_read(),aio_write()
    • 同步的意思就是当调用一个I/O syscall后,直到有确定结果返回后,应用程序都是被阻塞的内核态,时间片被换出,完全卡住不能运行。异步就是调用完syscall后,不存在任何阻塞内核态,应用程序继续执行直到时间片用完。
    • 全异步控制非常困难并且要求很高,现在常用的是同步非阻塞,这里面也是使用同步调用read/write,但是fd是非阻塞的,这时候调用这两个syscall只会短暂的切换到内核态如果没有数据会立刻返回给应用EAGAIN,这样应用可以选择等一会再来一次,或者等待EPOLL的事件之后再来一次。这种方式被广泛用于现在主流的各种服务器的网络socket操作,反向代理等等软件上比如nginx。为什么只有socket操作用会比较好呢,因为网络操作底层kernel都是在处理buffer,小规模的操作并不会导致太大的延迟,所以返回都比较快,不会造成很大延迟。
    • 需要注意这时候调用read的时候也会产生上下文切换,这时候严格说也是阻塞的,但是阻塞时间非常短。
    • 全异步的aio_read不存在这个短暂时间,效率更高,但是也更难控制,因为需要自己处理流程和buffer的问题,非常难搞。这个一般用于即使较小的规模的I/O操作也可能造成很大延时的操作,典型的就是磁盘读写。例子就是nginx的读文件的操作。
 
2.IO模型
  • 阻塞IO
    • 调用者调用了某个函数,等待这个函数返回,期间什么都不做,不停检查这个函数有没有返回,必须等该函数返回才能进行下一步动作。
 
(阻塞IO)
  • 非阻塞IO
    • 每隔一段时间去检测IO时间是否就绪,没有就绪就可以进行其他操作。
    • 非阻塞IO执行系统调用总是立即返回,不管事件是否已经发生,若没发生则返回-1,此时根据errno区分两种情况,对于accept、recv和send,事件未发生时,errno被设置为EAGAIN。
(非阻塞IO)
  • IO复用
    • select、poll、epoll函数实现,这些函数也会使进程阻塞,但是和阻塞IO不同的是,这些函数可以同时阻塞多个IO操作。
    • 可以同时对多个读操作、写操作的IO函数进行检测,直到有数据可读或可写时,才真正调用IO操作函数。
(IO复用)
  • 信号驱动IO
    • 注册新号处理函数,进程继续运行并不阻塞,当IO事件就绪时,进程受到SIGIO新号,然后处理IO事件。
    • 内核在第一个阶段是异步,在第二个阶段是同步。
    • 与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断的轮询检查,减少了系统API的调用次数,提高了效率。
(信号驱动IO)
  • 异步IO
    • LInux中,调用aio_read函数告诉内核描述字缓冲区指针和缓冲区大小 、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
(异步IO)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2021-08-09 20:37  萌新的学习之路  阅读(303)  评论(0编辑  收藏  举报