io多路复用的演进

一次网络通信的流程:

服务端进程:

//创建socket
int s = socket(AF_INET, SOCK_STREAM, 0);   
//绑定
bind(s, ...)
//监听
listen(s, ...)
//接受客户端连接
int c = accept(s, ...)
//接收客户端数据
recv(c, ...);
//将数据打印出来
printf(...)

················这个时候网卡传来了数据。·············

1.网卡作用?

将网接收到的网络数据写入内存中。

2.CPU是如何知道网卡接收到数据了呢?**

  • 当网卡把数据写入到内存中后,会发送一个中断信号,操作系统收到中断信号就知道有数据到来,通过网卡的中断处理程序数据。

3.进程阻塞为什么不占用cpu资源吗?

  • 是不占用cpu资源的。操作系统内核管理着工作队列,只有在工作队列中的进程才处于运行状态。当程序执行new Socket()服务端时,操作系统会创建一个由文件系统管理的sockfd,这个sockfd中有发送缓冲区,接收缓冲区,等待队列等变量。进程执行到阻塞方法的时候,该进程从工作队列中进入sockfd的等待队列中等待所以是不占用cpu资源的。

4.进程是如何唤醒的?

  • 当socket接收到数据后,操作系统将该socket等待队列上的进程重新放回到工作队列,该进程变成运行状态,继续执行代码。也由于socket的接收缓冲区已经有了数据,recv可以返回接收到的数据。
······································以上流程穿起来就是:·······································
  • 服务器进程进程在recv阻塞期间,计算机收到了网络数据,网卡负责将数据写入内存,完了之后通过中断信号告诉cpu有数据到达,cpu执行中断处理程序,将网络数据写入socket缓冲区,然后唤醒进程,将进程从sockfd的等待队列中移到就绪队列

5.操作系统如何知道网络数据对应于哪个socket?

  • 因为一个socket对应着一个端口号,而网络数据包中包含了ip和端口的信息,内核可以通过端口号找到对应的socket。当然,为了提高处理速度,操作系统会维护端口号到socket的索引结构,以快速读取。

6.传统的io只能通过创建线程去监听socket,如何同时高效地监视多个socket的数据? >>>>>>>> io多路复用

6.1 select
  • 流程:
    1. 首先将需要监听的fd记录到对应的读 写 异常 的fd_set中,置位操作。
    2. 调用select,传入三个fd_set,产生了一次fd_set在用户空间到内核空间的复制,内核对fd_set进行监听,当监听到某个fd事件就绪之后,将未就绪的位置为0,然后把fd_set拷贝回用户空间,然后用户遍历fd_set读取数据。

  • 本质:将进程A加入到所有要监听的sockfd事件等待队列中,只要有一个socket事件就绪,中断程序就会唤醒进程A,将进程A送入工作队列,进入数据处理流程,进程醒了之后,遍历sockfd,处理数据。

     缺点:
     1.fd_set默认大小是1024位,所以默认最大监听1024个socket
     2.fd_set每次都要重新添加fd,是不可重用的。
     3.用户态和内核态拷贝fd_set造成开销
     4.遍历就绪的fd_set是O(n)的
    
6.2poll:
  • 1.最关键的改进是使用了pollfd的一个描述事件的结构体,其他和select大同小异.

  • 2.使用struct 记录socketfd和感兴趣(注册)的事件,然后将poll将pollfd数组拷贝到内核空间,一样是由内核来监控pollfd的事件的就绪,就绪则填充revents变量。并拷贝回用户态,用户遍历pollfd数组来处理数据。

     优点:
     	1.使用pollfd结构体来记录fd信息,可以无限制的扩充pollfd数组。
     	2.每次重置的是pollfd中的revents  所以pollfd是可以重用的。
     缺点:同select 缺点3 4
    
6.3 epoll
  • 1.epoll_create 创建一个epfd 内核事件表,这个描述符用来保存应用进程需要监控哪些fd和对应类型的事件。

  • 2.epoll_ctl 用于添加或者移除监控的fd和事件类型。已注册的描述符在内核中会被维护在一棵红黑树上。

  • 3.epoll_wait 内核向epfd中的sockfd绑定一个回调函数,当监控的fd事件就绪时,就会调用回调函数把当前就绪的事件加入到就绪列表中,
    在epoll_wait返回的时候,内核会将就绪队列中的fd和事件类型返回给应用进程。

    ·····补充 epoll LT 模式和ET 模式 ·····

  • LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程并通知此事件。

  • ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll
    wait时,不会再次响应应用程序并通知此事件。

··································
本质:维护了一个就绪列表,能引用就绪的sockfd,这样进程醒了之后直接查就绪列表,处理数据
·································

epoll流程细节
  • 1.epoll_create 创建一个epfd 内核事件表,这个描述符用来保存应用进程需要监控哪些fd和对应类型的事件。此外内核要维护“就绪列表”等数据,“就绪列表”可以作为eventpoll的成员。

  • 2.创建epoll对象后,可以用epoll_ctl添加或删除所要监听的socket。内核会将epfd加入到监听的socket的等待队列中。当socket收到数据后,中断程序会操作eventpoll对象,而不是直接操作进程。

  • 3.接收到数据后,中断程序会给eventpoll的“就绪列表”添加socket引用,指向就绪的socket。当程序执行到epoll_wait时,如果rdlist已经引用了socket,那么epoll_wait直接返回,如果rdlist为空,阻塞进程。

    就绪列表的数据结构:双向链表—快速插入和修改
    程序可能随时调用epoll_ctl添加监视socket,也可能随时删除。当删除时,若该socket已经存放在就绪列表中,它也应该被移除。

索引结构: 红黑树
epoll需要监听socketfd,用红黑树来维护socketfd,搜索、插入和删除时间复杂度都是O(log(N)),效率较好。

···················

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值