侧边栏壁纸
博主头像
Haenu的Blog 博主等级

坚持学习,慢慢进步!

  • 累计撰写 35 篇文章
  • 累计创建 10 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录
OS

IO多路复用

Haenu
2024-08-15 / 0 评论 / 0 点赞 / 32 阅读 / 0 字

Socket

套接字。对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。例子:客户端将数据通过网线发送到服务端,客户端发送数据需要一个出口,服务端接收数据需要一个入口,这两个“口子”就是Socket。

FD

文件描述符,非负整数。“一切皆文件”,linux 中的一切资源都可以通过文件的方式访问和管理。而FD就类似文件的索引(符号),指向某个资源,内核(kernel)利用FD来访问和管理资源。

image-lhka.png

举个小例子

你是一个老师,让学生做作业,学生做完作业后收作业。同步阻塞:逐个收作业,先收A,再收B,接着是C、D,如果有一个学生还未做完则你会等到他写完,然后才继续收下一个。

同步非阻塞:逐个收作业,先收A,再收B,接着是C、D,如果有一个学生还未做完,则你会跳过该学生,继续去收下一个。

select/poll:学生写完了作业会举手,但是你不知道是谁举手,需要一个个的去询问

epoll:学生写完了作业会举手,你知道是谁举手,你直接去收作业

同步阻塞代码

image-1hbr.png

image-zwop.png

同步非阻塞代码

image-5kj9.png

read: 网卡->内核空间->用户空间

wirte: 用户空间->内和空间->网卡

image-jasf.png

select

知道就绪的个数 不知道具体的fd 需要挨个询问

image-qfll.png

elect 实现多路复用的方式是,将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。

所以,对于 select 这种方式,需要进行 2 次「遍历」文件描述符集合,一次是在内核态里,一个次是在用户态里 ,而且还会发生 2 次「拷贝」文件描述符集合,先从用户空间传入内核空间,由内核修改后,再传出到用户空间中。

select 使用固定长度的 BitsMap,表示文件描述符集合,而且所支持的文件描述符的个数是有限制的,在 Linux 系统中,由内核中的 FD_SETSIZE 限制, 默认最大值为 1024,只能监听 0~1023 的文件描述符。

poll

image-cuck.png

poll 不再用 BitsMap 来存储所关注的文件描述符,取而代之用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。

但是 poll 和 select 并没有太大的本质区别,都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长。

epoll

image-q9hq.png

bc44407dbb6f72fceeb205510063986f.png

1、epoll 在内核里使用「红黑树」来关注进程所有待检测的 Socket,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn),通过对这棵黑红树的管理,不需要像 select/poll 在每次操作时都传入整个 Socket 集合,减少了内核和用户空间大量的数据拷贝和内存分配。

2、epoll 使用事件驱动的机制,内核里维护了一个「链表」来记录就绪事件,只将有事件发生的 Socket 集合传递给应用程序,不需要像 select/poll 那样轮询扫描整个集合(包含有和无事件的 Socket ),大大提高了检测的效率。

  • 通过epoll_create创建epoll对象,此时epoll对象的内核结构包含就绪链表和红黑树,就绪队列是用于保存所有读写事件到来的socket。红黑树用于保存所有待检测的socket。
  • 通过 epoll_crt 将待检测的socket,加入到红黑树中,并注册一个事件回调函数,当有事件到来的之后,会调用这个回调函数,进而通知到 epoll 对象。
  • 调用 epoll_wait 等待事件的发生,当内核检测到事件发生后,调用该socket注册的回调函数,执行回调函数就能找到socket对应的epoll对象,然后会将事件加入到epoll对象的绪队列中,最后将就绪队列返回给应用层。
0

评论区