返回顶部
首页 > 资讯 > 操作系统 >Linux I/O多路复用实例分析
  • 614
分享到

Linux I/O多路复用实例分析

2023-06-27 18:06:05 614人浏览 薄情痞子
摘要

本篇内容主要讲解“linux I/O多路复用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux I/O多路复用实例分析”吧!I/O多路复用通过一种机制,可以监视多个描述符,一旦某个

本篇内容主要讲解“linux I/O多路复用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux I/O多路复用实例分析”吧!

I/O多路复用通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。I/O 多路复用技术是为了解决进程或线程阻塞到某个I/O系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。

Linux I/O多路复用实例分析

I/O多路复用select

该函数准许进程指示内核等待多个事件中的任何一个发送,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒。

select函数
1.1 需要头文件
#include #include #include #include
1.2 声明和返回值

\1. 声明

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

\2. 返回值

成功:就绪描述符的数目,超时返回 0。

出错:-1。

1.3 功能

监视并等待多个文件描述符的属性变化(可读、可写或错误异常)。select()函数监视的文件描述符分 3 类,分别是writefds、readfds、和exceptfds。调用后select() 函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时( timeout 指定等待时间),函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到就绪的描述符。

1.4 参数

\1. nfds: 要监视的文件描述符的范围,一般取监视的描述符数的最大值+1,如这里写 10, 这样的话,描述符 0,1, 2 …… 9 都会被监视,在 Linux 上最大值一般为1024。

\2. readfd: 监视的可读描述符集合,只要有文件描述符即将进行读操作,这个文件描述符就存储到这。

\3. writefds: 监视的可写描述符集合。

\4. exceptfds: 监视的错误异常描述符集合。

\5. timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。 其timeval结构用于指定这段时间的秒数和微秒数。

struct timeval{long tv_sec;   //secondslong tv_usec;  //microseconds};

timeout可以设置的值:

把该参数设置为空指针NULL。表示永远等待下去,当有一个描述字准备好I/O时才返回。

把该参数设置为指定了timeval结构中的秒数和微秒数的值。表示等待指定了超时时间,当超时后还没有描述字准备好I/O时直接返回。

把该参数设置为指定了timeval结构中的秒数和微秒数的值,而且秒数和微秒都为0。表示不检查描述字是否准备好I/O后立即返回,这称为轮询。

1.5 fd_set

fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:

1.  void FD_ZERO(fd_set *fdset);       //清空集合2.  void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中3.  void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除4.  int FD_ISSET(int fd, fd_set *fdset);   //检查集合中指定的文件描述符是否可以读写
select优点和缺点
2.1 优点

select()目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。

2.2 缺点

每次调用 select(),都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大,同时每次调用select()都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。

单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。

I/O多路复用poll

select()和poll()系统调用的本质一样,前者在BSD UNIX中引入的,后者在System V中引入的。poll()的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

poll函数

1.1 需要头文件

#include
1.2 声明和返回值

\1. 声明

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

\2. 返回值

成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0;

失败时,poll()返回 -1,并设置 errno 为下列值之一:

EBADF:一个或多个结构体中指定的文件描述符无效。

EFAULT:fds 指针指向的地址超出进程的地址空间。

EINTR:请求的事件之前产生一个信号,调用可以重新发起。

EINVAL:nfds参数超出 PLIMIT_NOFILE 值。

ENOMEM:可用内存不足,无法完成请求。

1.3 功能

监视并等待多个文件描述符的属性变化。

1.4 参数

\1. fds 不同与select()使用三个位图来表示三个 fdset 的方式,poll()使用一个pollfd的指针实现。一个pollfd 结构体数组,其中包括了你想测试的文件描述符和事件, 事件由结构中事件域 events 来确定,调用后实际发生的时间将被填写在结构体的revents 域。

struct pollfd{int fd;         //文件描述符short events;   //等待的事件short revents;  //实际发生了的事件};

fd 每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。

events:每个结构体的 events 域是监视该文件描述符的事件掩码,由用户来设置这个域。events 等待事件的掩码取值如下:

处理输入:

POLLIN 普通或优先级带数据可读

POLLRDNORM 普通数据可读

POLLRDBAND 优先级带数据可读

POLLPRI 高优先级数据可读

处理输出:

POLLOUT 普通或优先级带数据可写

POLLWRNORM 普通数据可写

POLLWRBAND 优先级带数据可写

处理错误:

POLLERR发生错误

POLLHUP发生挂起

POLLVAL 描述字不是一个打开的文件

poll() 处理三个级别的数据,普通normal,优先级带priority band,高优先级high priority,这些都是出于流的实现。

POLLIN | POLLPRI 等价于select()的读事件。

POLLOUT | POLLWRBAND等价于select() 的写事件。

POLLIN等价于POLLRDNORM | POLLRDBAND。

POLLOUT等价于POLLWRNORM。

例如,要同时监视一个文件描述符是否可读和可写,我们可以设置events为 POLLIN | POLLOUT。

revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在revents域中返回。每个结构体的 events 域是由用户来设置,告诉内核我们关注的是什么,而revents域是返回时内核设置的,以说明对该描述符发生了什么事件。

\2. nfds 用来指定第一个参数数组元素个数。

\3. timeout: 指定等待的毫秒数。

如果timeout设置为等待的毫秒数,无论I/O是否准备好,poll()都会返回。

如果timeout设置为 0时,poll() 函数立即返回。

如果timeout设置为 -1时,poll()一直阻塞到一个指定事件发生。

I/O多路复用epoll

epoll是在2.6内核中提出的,是之前的 select()和 poll()的增强版本。相对于 select()和 poll()来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

需要头文件
#include
声明
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);
epoll_create 函数

int epoll_create(int size); 3.1 功能

该函数生成一个 epoll 专用的文件描述符(创建一个 epoll 的句柄)。 3.2 参数

size 用来告诉内核这个监听的数目一共有多大,参数size并不是限制了 epoll 所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。

自从linux 2.6.8之后,size 参数是被忽略的,也就是说可以填只有大于0 的任意值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看 /proc/ 进程 id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

3.3 返回值

成功:epoll专用的文件描述符

失败:-1

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

4.1 功能

epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

4.2 参数

1. epfd epoll 专用的文件描述符,epoll_create()的返回值

2. op 表示动作,用三个宏来表示:

EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从 epfd 中删除一个 fd;

3. fd 需要监听的文件描述符

4. event 告诉内核要监听什么事件,struct epoll_event 结构如下:

// 保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)typedef uNIOn epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;// 感兴趣的事件和被触发的事件struct epoll_event {__uint32_t events; epoll_data_t data; };

events 可以是以下几个宏的集合:

EPOLLIN :表示对应的文件描述符可以读(包括对端 Socket 正常关闭);

EPOLLOUT:表示对应的文件描述符可以写;

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR:表示对应的文件描述符发生错误;

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET :将 EPOLL 设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个 socket 的话,需要再次把这个 socket 加入到 EPOLL 队列里

4.3 返回值

成功:0

失败:-1

epoll_wait函数

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

5.1 功能

等待事件的产生,收集在 epoll 监控的事件中已经发送的事件,类似于 select() 调用。

5.2 参数

1. epfd epoll 专用的文件描述符,epoll_create()的返回值

2. events 分配好的 epoll_event 结构体数组,epoll 将会把发生的事件赋值到events 数组中(events 不可以是空指针,内核只负责把数据复制到这个 events 数组中,不会去帮助我们在用户态中分配内存)。

3. maxevents maxevents 告之内核这个 events 有多大 。

4. timeout 超时时间。

如果timeout设置为等待的毫秒数,无论I/O是否准备好,都会返回。

如果timeout设置为 0时,函数立即返回。

如果timeout设置为 -1时,一直阻塞到一个指定事件发生。

5.3 返回值

成功:返回需要处理的事件数目,如返回 0 表示已超时。

失败:-1

LT模式与ET模式

epoll 对文件描述符的操作有两种模式:LT(level trigger)和 ET(edge trigger)。LT 模式是默认模式。

6.1 LT模式

当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。

6.2 ET模式

当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。

6.3 LT模式与ET模式比较

ET模式在很大程度上减少了epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

epoll 的优点

在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll()事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制(软件中断 ),迅速激活这个文件描述符,当进程调用 epoll_wait()时便得到通知。

监视的描述符数量不受限制,它所支持的 FD 上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在 1GB 内存的机器上大约是 10 万左右,具体数目可以 cat /proc/sys/fs/file-max 察看,一般来说这个数目和系统内存关系很大。select()的最大缺点就是进程打开的 fd 是有数量限制的。这对于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache 就是这样实现的),不过虽然 Linux 上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

I/O 的效率不会随着监视 fd 的数量的增长而下降。select(),poll() 实现需要自己不断轮询所有 fd 集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而 epoll 其实也需要调用 epoll_wait() 不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪 fd 放入就绪链表中,并唤醒在 epoll_wait() 中进入睡眠的进程。虽然都要睡眠和交替,但是 select() 和 poll() 在“醒着”的时候要遍历整个fd集合,而 epoll 在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的 CPU 时间。这就是回调机制带来的性能提升。

select(),poll() 每次调用都要把 fd 集合从用户态往内核态拷贝一次,而epoll只要一次拷贝,这也能节省不少的开销。

到此,相信大家对“Linux I/O多路复用实例分析”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Linux I/O多路复用实例分析

本文链接: https://lsjlt.com/news/312363.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Linux I/O多路复用实例分析
    本篇内容主要讲解“Linux I/O多路复用实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux I/O多路复用实例分析”吧!I/O多路复用通过一种机制,可以监视多个描述符,一旦某个...
    99+
    2023-06-27
  • Python之I/O多路复用
    回顾Socket一、Socket起源:socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些so...
    99+
    2023-01-31
    多路 复用 Python
  • Java I/O API性能实例分析
    本篇内容主要讲解“Java I/O API性能实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java I/O API性能实例分析”吧!  一、概述   IO API的可伸缩性对Web应用...
    99+
    2023-06-03
  • linux系统中文件I/O的示例分析
    小编给大家分享一下linux系统中文件I/O的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!linux 文件I/O一,文件描述符对内核而言,所以打开的文件...
    99+
    2023-06-13
  • 实现NodeJS异步I/O的示例分析
    这篇文章主要介绍实现NodeJS异步I/O的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一.NodeJS概述:要学习一个语言或者平台,我们首先应该知道其定义,依据定义来扩展...
    99+
    2024-04-02
  • C语言I/O流设计实例分析
    这篇文章主要介绍了C语言I/O流设计实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C语言I/O流设计实例分析文章都会有所收获,下面我们一起来看看吧。前言文件的读取和写入...
    99+
    2024-04-02
  • Nodejs中异步I/O的示例分析
    小编给大家分享一下Nodejs中异步I/O的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!“异步”这个名词其实在Node之前就已经诞生了。但是在绝大多数高...
    99+
    2023-06-14
  • Java I/O 之File类的示例分析
    这篇文章主要为大家展示了“Java I/O 之File类的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java I/O 之File类的示例分析”这篇文章吧。File类Java使用Fil...
    99+
    2023-06-20
  • Node.js中非阻塞 I/O的示例分析
    这篇文章主要介绍了Node.js中非阻塞 I/O的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 说到 ...
    99+
    2024-04-02
  • Node.js中的非阻塞I/O举例分析
    这篇文章主要介绍“Node.js中的非阻塞I/O举例分析”,在日常操作中,相信很多人在Node.js中的非阻塞I/O举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”No...
    99+
    2024-04-02
  • Mysql Innodb中的Linux native异步I/O分析
    本篇内容主要讲解“Mysql Innodb中的Linux native异步I/O分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Mysql Innodb中的L...
    99+
    2024-04-02
  • JAVA NIO怎么构建I/O多路复用的请求模型
    这篇文章主要介绍“JAVA NIO怎么构建I/O多路复用的请求模型”,在日常操作中,相信很多人在JAVA NIO怎么构建I/O多路复用的请求模型问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JAVA NIO怎...
    99+
    2023-06-02
  • 如何用C写一个web服务器之I/O多路复用
    目录前言I/O模型select/poll/epollepoll介绍epoll_createepoll_ctlepoll_waitepoll行为代码实现整体处理逻辑设置epoll ET...
    99+
    2024-04-02
  • 怎么用C写一个web服务器之I/O多路复用
    小编给大家分享一下怎么用C写一个web服务器之I/O多路复用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言I/O模型接触过 socket 编程的同学应该都知道...
    99+
    2023-06-15
  • sql server性能调优 I/O开销的示例分析
    这篇文章主要介绍sql server性能调优 I/O开销的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一.概述IO 内存是sql server最重要的资源,数据从磁盘加载到...
    99+
    2024-04-02
  • Node.js的非阻塞I/O、异步与事件驱动实例分析
    今天小编给大家分享一下Node.js的非阻塞I/O、异步与事件驱动实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1、...
    99+
    2023-07-02
  • Linux服务器高I/O等待延迟问题查找的示例分析
    这篇文章主要介绍Linux服务器高I/O等待延迟问题查找的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!0. 首先是top查看一下系统状况 发现两个参数异常,一是平均负载高,一是cpu %wa一直在50%以上...
    99+
    2023-06-16
  • Node中对非阻塞I/O、事件循环的示例分析
    这篇文章给大家分享的是有关Node中对非阻塞I/O、事件循环的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Node.js的主要特点单线程、非阻塞I/O、事件驱动,这三个...
    99+
    2024-04-02
  • Linux多线程编程实例分析
    这篇文章主要讲解了“Linux多线程编程实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux多线程编程实例分析”吧!线程  线程是计算机中独立运...
    99+
    2024-04-02
  • Linux下路径的示例分析
    这篇文章主要介绍Linux下路径的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一般的情况下,我们在用shell调用的话,或者你通过什么什么方式调用你的应用程序的时候,注意你的此刻的路径就是你的被调用的程序的...
    99+
    2023-06-16
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作