https://blog.csdn.net/qwert55kai/article/details/124901367
https://www.zhihu.com/question/486578358/answer/2126762689
https://zhuanlan.zhihu.com/p/366365883 : 这个原理讲的最通俗易懂
结合 https://zhuanlan.zhihu.com/p/533713185?utm_id=0
同步阻塞IO:https://mp.weixin.qq.com/s/cIcw0S-Q8pBl1-WYN0UwnA
一.同步阻塞网络 IO
- 会阻塞的方法
accept() : 接收连接
recv() : 接收数据
数据包抵达网卡,网卡产生一个硬中断通知CPU,cpu拿到数据处理完(cpu就是简单处理了一下)会触发软中断,把数据放到对应的socket里面 并唤醒该socket对应的用户进程
(每次一个进程专门为了等一个 socket 上的数据就得被从 CPU 上拿下来。然后再换上另一个进程。等到数据 ready 了,睡眠的进程又会被唤醒。总共两次进程上下文切换开销,根据之前的测试来看,每一次切换大约是 3-5 us(微秒)左右。如果是网络 IO 密集型的应用的话,CPU 就不停地做进程切换这种无用功,这种切换很消耗资源。
在服务端角色上,这种模式完全没办法使用。因为这种简单模型里的 socket 和进程是一对一的。我们现在要在单台机器上承载成千上万,甚至十几、上百万的用户连接请求。如果用上面的方式,那就得为每个用户请求都创建一个进程。相信你在无论多原始的服务器网络编程里,都没见过有人这么干吧。)
二. 非同步阻塞: IO多路复用
IO多路复用:简单理解就是一个进程监听多个socket
IO多路复用方案有 select、poll、epoll,性能最好的就是epoll
- epoll 对象三个重要字段 : epoll对象 就是 eventpoll内核对象
wq(等待队列链表): https://zhuanlan.zhihu.com/p/533713185?utm_id=0
rdllist (就绪链表) : https://www.jianshu.com/p/dd72cceaabc1
接收到某个文件描述符过来数据时,那么内核将该节点插入到就绪链表里面。
当 epoll_wait接收到消息,并且将数据拷贝到用户空间,清空就绪链表。
如果为LT模式,且必须该节点确实有事件未处理,那么就会把该节点重新放入到刚刚删除掉的且刚准备好的就绪链表,epoll_wait马上返回
rbr(保存了所有socket的红黑树):红黑树存储所监控的文件描述符的节点数据
2.Socket的等待队列是干嘛的?
https://www.cnblogs.com/aiwz/p/6333250.html
https://blog.csdn.net/weixin_61631200/article/details/125806161 : 搜索:这个socket对象包含了发送缓冲区
Socket的等待队列主要存放的是等待socket事件的进程。当进程执行到创建socket的语句时,操作系统会创建一个由文件系统管理的socket对象,这个socket对象包含了发送缓冲区、接收缓冲区、等待队列等成员。当进程需要等待某个socket事件(如数据接收、连接等)时,它会被添加到等待队列中。
等待队列中的进程会一直处于等待状态,直到所等待的事件发生。例如,当socket接收到数据时,操作系统会将等待队列上的进程重新放回到工作队列中,该进程变成运行状态,继续执行代码。
需要注意的是,等待队列中的进程并不会一直占用CPU资源,而是让出CPU资源给其他进程或线程运行。这种机制可以避免不必要的CPU资源浪费,并提高系统的效率。
- epoll 会把 ep_poll_callback 函数设置为 socket 等待队列里,触发事件的回调函数,这样就把socket 和 epoll 关联上了: 软中断将数据收到 socket 的接收队列后,会通过注册的这个 ep_poll_callback 函数来回调,进而通知到 epoll 对象。
系统层面,socket也是交给了epoll管理的
- epoll_wait
epoll_wait 做的事情不复杂,当它被调用时它观察 eventpoll->rdllist 链表里有没有数据即可。有数据就返回,没有数据就创建一个等待队列项,将其添加到 eventpoll 的等待队列上,然后把自己阻塞掉就完事。
注:这里是添加到eventpoll 的等待l队列里
在 ep_poll_callback 根据等待任务队列项上的额外的 base 指针可以找到 epitem, 进而也可以找到 eventpoll对象。
首先它做的第一件事就是把自己的 epitem 添加到 epoll 的就绪队列中。
接着它又会查看 eventpoll 对象上的等待队列里是否有等待项(epoll_wait 执行的时候会设置)。
如果没执行软中断的事情就做完了。如果有等待项,那就查找到等待项里设置的回调函数。
网友评论