深入理解Linux内核之IO多路复用上

目录

Linux代码结构看网络通信

Linux下的IO复用编程

文件描述符FD

select

poll

epoll

select、poll、epoll的比较

1、支持一个进程所能打开的最大连接数

2、FD剧增后带来的IO效率问题

3、消息传递方式

总结


Linux代码结构看网络通信

         

        Linux内核的源码包含的东西很多,在Linux的源代码中,网络设备驱动对应的逻辑位于 driver/net/ethernet,其中intel系列网卡的驱动在driver/net/ethernet/intel目录下。协议栈模块代码位于kernel和net目录。

        其中net目录中包含Linux内核的网络协议栈的代码。子目录ipv4和 ipv6 为 TCP/IP 协议栈的 IPv4 和 IPv6 的实现,主要包含了 TCP、UDP、IP协议的代码,还有ARP协议、ICMP 协议、IGMP 协议代码实现,以及如 proc、ioctl等控制相关的代码。

       站在网络通信的角度,源代码组织的表现形式如下:

            

       网络协议栈是由若干个层组成的,网络数据的流程主要是指在协议栈的各个层之间的传递。 一个TCP服务器的流程按照建立 socket()函数,绑定地址端口 bind()函数,侦听端口 listen() 函数,接收连接 accept()函数,发送数据 send()函数,接收数据 recv()函数,关闭 socket()函数的顺序来进行。与此对应内核的处理过程也是按照此顺序进行的,网络数据在内核中的处理过程主要是在网卡和协议栈之间进行:从网卡接收数据,交给协议栈处理;协议栈将需要发送的数据通过网络发出去。

       数据的流向主要有两种。应用层输出数据时,数据按照自上而下的顺序,依次通过应用 API 层、协议层和接口层。当有数据到达的时候,自下而上依次通过接口层、协议层和应用API 层的方式,在内核层传递。

              

       应用层 Socket 的初始化、绑定(bind)和销毁是通过调用内核层的 socket()函数进行资源的申 请和销毁的。

        发送数据的时候,将数据由应用 API 层传递给协议层,协议层在 UDP 层添加 UDP 的首部、 TCP 层添加 TCP 的首部、IP 层添加 IP 的首部,接口层的网卡则添加以太网相关的信息后,通过网卡的发送程序发送到网络上。

       接收数据的过程是一个相反的过程,当有数据到来的时候,网卡的中断处理程序将数据从以太网网卡的 FIFO 对列中接收到内核,传递给协议层,协议层在 IP 层剥离 IP 的首部、UDP 层剥离 UDP 的首部、TCP 层剥离 TCP 的首部后传递给应用 API 层,应用 API 层查询 socket 的标识后,将数据送给用户层匹配的 socket。


Linux下的IO复用编程

       select,poll,epoll 都是 IO 多路复用的机制。I/O 多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但 select,poll,epoll 本质上都是同步 I/O,因为他们都需要在读写事件就绪后自己负责进行读写,并等待读写完成。

文件描述符FD

       在 Linux 操作系统中,可以将一切都看作是文件,包括普通文件,目录文件,所有一切均抽象 成文件,提供了统一的接口,方便应用程序调用。

       文件描述符:File descriptor,简称 fd,当应用程序请求内核打开/新建一个文件时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其 fd 本质上就是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于 UNIX、Linux 这样的操作系统。

       系统为了维护文件描述符建立了 3 个表:进程级的文件描述符表、系统级的文件描述符表、文件系统的 i-node 表。所谓进程级的文件描述符表,指操作系统为每一个进程维护了一个文件描述符表,该表的索引值都从从 0 开始的,所以在不同的进程中可以看到相同的文件描述符,这种情况下相同的文件描述符可能指向同一个实际文件,也可能指向不同的实际文件。


select

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval
*timeout);

       select 函数监视的文件描述符分 3 类,分别是 writefds、readfds、和 exceptfds。调用后 select 函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有 except),或者超时 (timeout 指定等待时间,如果立即返回设为 null 即可),函数返回。当 select 函数返回后,可以 通过遍历 fdset,来找到就绪的描述符。

       select 目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select 的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,监视太多文件会造成效率的降低。


poll

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

       不同与 select 使用三个位图来表示三个 fdset 的方式,poll 使用一个 pollfd 的指针实现。 pollfd 结构包含了要监视的 event 和发生的 event,不再使用 select“参数-值”传递的方式。同时,pollfd 并没有最大数量限制(但是数量过大后性能也是会下降)。和select函数一样,poll返回后,需要轮询 pollfd 来获取就绪的描述符。


epoll

       epoll 是在 2.6 内核中提出的,是之前的 select 和 poll 的增强版本。相对于 select 和 poll 来说,可以看到 epoll 做了更细致的分解,包含了三个方法,使用上更加灵活。

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

int epoll_create(int size);

       创建一个 epoll 的句柄,size 用来告诉内核这个监听的数目一共有多大,这个参数不同于 select()中的第一个参数,给出最大监听的 fd+1 的值,参数 size 并不是限制了epoll 所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。当创建好 epoll 句 柄后,它就会占用一个 fd 值,在 linux 下如果查看/proc/进程 id/fd/,是能够看到这个 fd 的, 所以在使用完 epoll 后,必须调用 close()关闭,否则可能导致 fd 被耗尽。 可以理解为对应于JDK NIO 编程里的 selector = Selector.open();

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

函数是对指定描述符 fd 执行 op 操作。 epfd:是 epoll_create()的返回值。 op:表示 op 操作,用三个宏来表示:添加 EPOLL_CTL_ADD,删除 EPOLL_CTL_DEL,修 改 EPOLL_CTL_MOD。分别添加、删除和修改对 fd 的监听事件。 fd:是需要监听的 fd(文件描述符) epoll_event:是告诉内核需要监听什么事,有具体的宏可以使用,比如 EPOLLIN :表示对应的文件描述符可以读(包括对端 SOCKET 正常关闭)。EPOLLOUT:表示对应的文件描述符可以写。可以理解为对应于 JDK NIO 编程里的socketChannel.register();

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

       等待 epfd 上的 io 事件,最多返回 maxevents 个事件。参数 events 用来从内核得到事件的集合,maxevents 告之内核这个 events 有多大,这 个 maxevents 的值不能大于创建 epoll_create()时的 size,参数 timeout 是超时时间(毫秒,0 会立即返回,-1 将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目, 如返回 0 表示已超时。可以理解为对应于JDK NIO 编程里的 selector.select();


select、poll、epoll的比较

       select,poll,epoll都是操作系统实现 IO 多路复用的机制。通过这种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

三种机制的区别如下:

1、支持一个进程所能打开的最大连接数

select:

       单个进程所能打开的最大连接数有 FD_SETSIZE 宏定义,其大小是 32 个整数的大小(在32 位的机器上,大小就是32*32,同理 64 位机器上FD_SETSIZE为32*64),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响。

poll:

       poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。

epoll:

       连接数基本上只受限于机器的内存大小。


2、FD剧增后带来的IO效率问题

select:

       因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll:同上。

epoll:

       因为 epoll 内核中实现是根据每个 fd 上的 callback函数来实现的,只有活跃的 socket 才会主动调用 callback,所以在活跃 socket 较少的情况下,使用 epoll 没有前面两者的线性下降的性能问题,但是所有 socket 都很活跃的情况下,可能会有性能问题。


3、消息传递方式

select:

         内核需要将消息传递到用户空间,都需要内核拷贝动作。

poll:同上。

epoll:

         epoll通过内核和用户空间共享一块内存来实现的。


总结

在选择 select,poll,epoll 时要根据具体的使用场合以及这三种方式的自身特点。

1、表面上看 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和 poll 的性能可能比 epoll 好,毕竟 epoll 的通知机制需要很多函数回调。

2、select 低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/450888.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NAS系统折腾记 – Emby搭建家庭多媒体服务器

Emby简介 Emby是一款优秀的媒体服务器软件,致力于为用户提供丰富的多媒体体验。通过Emby,您可以方便地在家庭内的各种设备上观看您喜爱的电影、电视剧和其他视频内容。而且,Emby还具备强大的媒体管理功能,让您的影视资源井然有序…

小米平板6获取root权限教程

1. 绑定账号 1> 打开"设置-我的设备-全部参数-连续点击MIUI版本按钮",直到提示已打开开发者模式( p s : 这里需要重点关注红框平板型号和 M I U I 版本,例如我这里平板型号是 X i a o m i P a d 6 , M I U I 版本是 14.0.10 &am…

C/C++ 回调函数 callback 异步编程

一、C语言的回调函数 1.小试牛刀 #include <iostream> using namespace std; #include <memory> #include <stdlib.h>int add(int a, int b) {return a b; }void test01() {// 函数指针可以指向任何类型的函数&#xff0c;只要函数的参数列表和返回值类型…

政安晨:示例演绎Python的列表

列表和你可以用它们做的事&#xff1a;包括索引、切片和对象变动 (变异-Mutation) 。 列表 在Python中&#xff0c;列表表示有序的值序列。以下是如何创建列表的示例&#xff1a; primes [2, 3, 5, 7] 我们可以将其他类型的元素放在列表中&#xff1a; planets [Mercury…

股票交易

这里尝试利用单调队列优化&#xff0c;这里不好直接用单调队列的原因是因为(以买为例)\(-ap[i]*k_1\)不是只与下标有关的 所以解决方案一&#xff1a;我们将下标变成一个整体&#xff0c;再把后面的代价换掉然后将与下标无关的直接提出去 解决方案二&#xff1a;利用“蚯蚓”那…

深入了解 Ansible:全面掌握自动化 IT 环境的利器

本文以详尽的篇幅介绍了 Ansible 的方方面面&#xff0c;旨在帮助读者从入门到精通。无论您是初学者还是有一定经验的 Ansible 用户&#xff0c;都可以在本文中找到对应的内容&#xff0c;加深对 Ansible 的理解和应用。愿本文能成为您在 Ansible 自动化旅程中的良师益友&#…

自学网安-IIS服务器

部署环境&#xff1a;win2003 配置环境&#xff1a;winxp ip&#xff1a;10.1.1.2 win2003 ip&#xff1a;10.1.1.1 开始安装 双击“应用程序服务器” 双击“Internet 信息服务&#xff08;IIS&#xff09;” 勾选万维网服务&#xff0c;确定然后下一步 查看端口号;netstat …

Fink CDC数据同步(三)Flink集成Hive

1 目的 持久化元数据 Flink利用Hive的MetaStore作为持久化的Catalog&#xff0c;我们可通过HiveCatalog将不同会话中的 Flink元数据存储到Hive Metastore 中。 利用 Flink 来读写 Hive 的表 Flink打通了与Hive的集成&#xff0c;如同使用SparkSQL或者Impala操作Hive中的数据…

阻塞队列(超详细易懂)

目录 一、阻塞队列 1.阻塞队列概述 2.生产者消费者模型 3.阻塞队列的作用 4.标准库中的阻塞队列类 5.例子&#xff1a;简单生产者消费者模型 二、阻塞队列模拟实现 1.实现循环队列&#xff08;可跳过&#xff09; 1.1简述环形队列 1.2代码实现 2.实现阻塞队列 2.1实…

[基础IO]文件描述符{重定向/perror/磁盘结构/inode/软硬链接}

文章目录 1. 再识重定向2.浅谈perror()3.初始文件系统4.软硬链接 1. 再识重定向 图解./sf > file.txt 2>&1 1中内容拷贝给2 使得2指向file 再学一个 把file的内容传给cat cat拿到后再给file2 2.浅谈perror() open()接口调用失败返回-1,并且错误码errno被适当的设置,…

回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测(SE注意力机制)

回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现CPO-CNN-LSTM-Attention冠豪猪优化卷积长短期记忆神经网络注意力机制多变量回归预测&#xff08;SE注…

Linux下grep命令详解

grep #文件内容过滤显示 #在指定的普通文件中查找并显示含有指定字符串的行&#xff0c;也可与管道符一起使用格式&#xff1a; grep-参数 查找条件 文件名 参数&#xff1a; 示例&#xff1a; [rootnode1 ~]# grep -n "root" /etc/passwd # -n&a…