「IO 模型」IO 多路复用技术实现对比1
做一个比较注重七寸认知的技术人!
Hello,大家好!我是七寸君。
IO 多路复用就是通过一种机制,一个进程可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
与多进程、多线程技术相比,IO 多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
目前支持 IO 多路复用的系统调用有 select、pselect、poll、epoll。但 select、pselect、poll、epoll 本质上都是同步 IO,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步 IO则无需自己负责进行读写,异步IO的实现会负责把数据从内核拷贝到用户空间。
epoll 跟 select 都能提供多路I/O复用的解决方案。在现在的 Linux 内核里有都能够支持,其中 epoll 是 Linux 所特有,而 select 则应该是 POSIX 所规定,一般操作系统均有实现。
select
基本原理
select 函数监视的文件描述符分3类,分别是 writefds、readfds、和 exceptfds。调用后 select 函数会同步阻塞,直到有描述符就绪(有数据 可读、可写、或者有 except),或者超时(timeout 指定等待时间,如果立即返回设为 null 即可),select 函数返回。当 select 函数返回后,可以通过遍历 fdset,来找到就绪的描述符。
基本流程
优劣对比
select 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select 的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。
select 本质上是通过设置或者检查存放 fd 标志位的数据结构来进行下一步处理。这样所带来的缺点是:
1. select 最大的缺陷就是单个进程所打开的 FD 是有一定限制的,它由FD_SETSIZE设置,默认值是 1024。
一般来说这个数目和系统内存关系很大,具体数目可以 cat /proc/sys/fs/file-max 察看。32位机默认是 1024个。64 位机默认是 2048.
2. 对 socket 进行扫描时是线性扫描,即采用轮询的方法,效率较低。
当套接字比较多的时候,每次 select() 都要通过遍历 FD_SETSIZE 个Socket 来完成调度,不管哪个 Socket 是活跃的,都遍历一遍。这会浪费很多 CPU 时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。
3. 需要维护一个用来存放大量 fd 的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
poll
基本原理
poll 本质上和 select 没有区别,都是将用户传入的数组拷贝到内核空间,然后查询每个 fd 对应的设备状态,如果设备就绪,则在设备等待队列中加入一项并继续遍历,如果遍历完所有 fd 后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历 fd。这个过程经历了多次无谓的遍历。
优劣对比
poll 没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
2. poll 还有一个特点是“水平触发”,如果报告了 fd 后,没有被处理,那么下次 poll 时会再次报告该 fd。
从上面看,select 和 poll 都需要在返回后,通过遍历文件描述符来获取已经就绪的 Socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
- EOF -
关注「七寸知架构」加星标,不错过技术那些事儿
❤️ 您的 点赞 和 在看 就是最大的支持❤️