文章插图
你是不是觉得这有些多路复用的意思?
但这和我们用多线程去将阻塞 IO 改造成看起来是非阻塞 IO 一样,这种遍历方式也只是我们用户自己想出的小把戏,每次遍历遇到 read 返回 -1 时仍然是一次浪费资源的系统调用 。
【IO多路复用的理解/演变过程】所以,还是得恳请操作系统老大 , 提供给我们一个有这样效果的函数,我们将一批文件描述符通过一次系统调用传给内核 , 由内核层去遍历(而不是在用户态调用 , 再陷入到内核态中去遍历),才能真正解决这个问题 。
selectselect 是操作系统提供的系统调用函数,通过它,我们可以把一个文件描述符的数组发给操作系统,让操作系统去遍历 , 确定哪个文件描述符可以读写, 然后告诉我们去处理:
文章插图
select系统调用的函数定义如下 。
- int select(
- int nfds,
- fd_set *readfds,
- fd_set *writefds,
- fd_set *exceptfds,
- struct timeval *timeout);
- // nfds:监控的文件描述符集里最大文件描述符加1
- // readfds:监控有读数据到达文件描述符集合 , 传入传出参数
- // writefds:监控写数据到达文件描述符集合 , 传入传出参数
- // exceptfds:监控异常发生达文件描述符集合, 传入传出参数
- // timeout:定时阻塞监控时间,3种情况
- //1.NULL,永远等下去
- //2.设置timeval , 等待固定时间
- //3.设置timeval里时间均为0,检查描述字后立即返回,轮询
服务端代码 , 这样来写 。首先一个线程不断接受客户端连接,并把 socket 文件描述符放到一个 list 里 。
- while(1) {
- connfd = accept(listenfd);
- fcntl(connfd, F_SETFL, O_NONBLOCK);
- fdlist.add(connfd);
- }
然后 , 另一个线程不再自己遍历,而是调用 select,将这批文件描述符 list 交给操作系统去遍历 。- while(1) {
- // 把一堆文件描述符 list 传给 select 函数
- // 有已就绪的文件描述符就返回,nready 表示有多少个就绪的
- nready = select(list);
- ...
- }
不过 , 当 select 函数返回后,用户依然需要遍历刚刚提交给操作系统的 list 。只不过,操作系统会将准备就绪的文件描述符做上标识 , 用户层将不会再有无意义的系统调用开销 。
- while(1) {
- nready = select(list);
- // 用户层依然要遍历,只不过少了很多无效的系统调用
- for(fd <-- fdlist) {
- if(fd != -1) {
- // 只读已就绪的文件描述符
- read(fd, buf);
- // 总共只有 nready 个已就绪描述符,不用过多遍历
- if(--nready == 0) break;
- }
- }
- }
可以看出几个细节:- select 调用需要传入 fd 数组 , 需要拷贝一份到内核,高并发场景下这样的拷贝消耗的资源是惊人的 。(可优化为不复制)
- select 在内核层仍然是通过遍历的方式检查文件描述符的就绪状态 , 是个同步过程,只不过无系统调用切换上下文的开销 。(内核层可优化为异步事件通知)
- select 仅仅返回可读文件描述符的个数,具体哪个可读还是要用户自己遍历 。(可优化为只返回给用户就绪的文件描述符 , 无需用户做无效的遍历)
文章插图
可以看到,这种方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + n 次就绪状态的文件描述符的 read 系统调用) 。
epollepoll 是最终的大 boss,它解决了 select 和 poll 的一些问题 。
还记得上面说的 select 的三个细节么?epoll 主要就是针对这三点进行了改进 。
- 内核中保存一份文件描述符集合,无需用户每次都重新传入 , 只需告诉内核修改的部分即可 。
- 内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒 。
- 内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合 。
推荐阅读
- iPhone8怎么刷机(苹果x强制恢复出厂)
- 超人怎么死的(超人怎么复活)
- 记一次多个Java Agent同时使用的类增强冲突问题及分析
- 光遇啵啵先祖裤子怎么搭配
- fastposter v2.10.0 简单易用的海报生成器
- 电脑鼠标箭头不动怎么解决(一键修复usb无法识别鼠标)
- 鼠标突然不动了怎么解决(鼠标失灵一秒恢复)
- 二代火影是在木叶几年死的(火影多少集复活木叶四影)
- .Net 7里的函数.Ctor和.CCtor是干啥用的呢?你知道吗
- 原神8.13复原械画部件之二图文攻略