Linux中的IO复用接口简介(文件监视?)
I/O复用是Linux中的I/O模型之一。所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞。
在Linux中,提供了select、poll、epoll三类接口来实现I/O复用。
select函数接口
select中主要就是一个select函数,用于监听指定事件的发生,原型如下:
1 |
#include<sys/select.h> |
其中各参数的含义如下:
maxfd:最大文件描述符加1,比它小的从0开始的描述符都将被监视,它的值不能超过系统中定义的FD_SETSIZE(通常是1024)。
rset,wset,eset:分别表示监视的读、写、错误的描述符位数组,通常是一个整数数组,每一个整数可以表示32个描述符是否被监视。需要注意的是这几个参数都是值-结果参数,在调用select后这几个参数将表示哪些描述符就绪了。通过以下几个宏可以很方便的操作fset数组:
1 |
void FD_ZERO(fd_set *fdset); |
timeout:超时时间,即select最长等待多久就返回,为NULL时表示等到有操作符准备就绪后才返回。该时间可以精确到微秒,其结构如下:
1 |
struct timeval{ |
描述符就绪条件
对于普通数据的读写,描述符就绪显而易见,但仍有一些特殊情况时描述符会读写就绪,UNP中对描述符的读写就绪条件进行了说明。1)满足以下4个条件时,描述符准备好读
a)套接字接收缓冲区中的数据字节数大于套接字接收缓冲区低水位标记的当前大小(默认为1),读将会返回大于0的数。
b)该连接的读半部关闭,读将会返回0。
c)套接字上有一个错误待处理,读将返回-1。
d)该套接字是一个监听套接字并且已完成连接数不为0。2)满足以下4个条件时,描述符准备好写
a)套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小(默认2048),写将会返回大于0的数。
b)该连接的写半部关闭,写将会返回EPIPE。
c)套接字上有一个错误待处理,写将返回-1。
d)使用非阻塞式connect的套接字建立有结果返回。
poll函数接口
poll中的主要函数也只有一个poll,与select作用类似,但参数有所不同,函数原型如下:
1 |
#include<poll.h> |
其中各参数的含义如下:
fdarray:是一个指向pollfd结构数组的指针,维护着描述符以及事件信息,该结构体是poll里比较核心的结构体,结构如下:
1 |
struct pollfd{ |
该结构体通过两个变量区分关注的事件和发生的事件,从而避免了使用值-结果参数。events和revents可选的标志位如下:
1 |
POLLIN //普通或优先级带数据可读 |
nfds:指定结构体数组中元素的个数。
timeout:每次调用poll最大等待的毫秒数,负值代表等待到直到有事件触发。
epoll函数接口
epoll主要有三个函数,函数原型如下:
1 |
#include <sys/epoll.h> |
epoll_create(int size)
size:能监听多少个描述符,返回一个epoll描述符。注意使用完epoll后要关闭该描述符。
epoll_ctl(int efd, int op, int fd, struct epoll_event *event)
efd:epoll_create返回的epoll描述符
op:表示动作,可以在以下三个宏里选择一个
1 |
EPOLL_CTL_ADD //注册新的fd到epoll中 |
fd:要监听的fd
event:告诉内核要监听什么事件,其结构如下:
1 |
typedef union epoll_data { |
其中events表示epoll事件,可选的标志位如下:
1 |
EPOLLIN //描述符可以读 |
而epoll_data_t使用了union来存储数据,用户可以使用data来存放一些关于该fd的额外内容。
标志位中比较特殊的是EPOLLET这个选项,这个选项将EPOLL设置为边缘触发模式,EPOLL有EPOLLET和EPOLLLT两种工作模式。
EPOLLLT(Level Triggered,水平触发模式):默认工作模式,支持block和no-block socket,内核通知你描述符事件后,如果不进行操作,会一直通知。
EPOLLET(Edge Triggered,边缘触发模式):高速工作模式,只支持no-block socket,只会在描述符状态由未就绪转为就绪时会通知一次,使用该模式时,如果程序编写的不够健全,是很容易出现问题的。
epoll_wait(int efd, struct epoll_event *events, int maxevents, int timeout);
该函数与select和poll函数的功能类似,监视指定事件的发生并返回给用户。
efd:epoll_create返回的opoll描述符。
events:用来从内核得到事件的集合。
maxevents:用来告知内核events数组的大小。
timeout:超时时间,-1将阻塞直到有事件发生,否则表示最多等待多少毫秒后函数就返回。
select,poll,epoll比较
select
- select能监控的描述符个数由内核中的FD_SETSIZE限制,仅为1024,这也是select最大的缺点,因为现在的服务器并发量远远不止1024。即使能重新编译内核改变FD_SETSIZE的值,但这并不能提高select的性能。
- 每次调用select都会线性扫描所有描述符的状态,在select结束后,用户也要线性扫描fd_set数组才知道哪些描述符准备就绪,等于说每次调用复杂度都是O(n)的,在并发量大的情况下,每次扫描都是相当耗时的,很有可能有未处理的连接等待超时。
- 每次调用select都要在用户空间和内核空间里进行内存复制fd描述符等信息。
poll
- poll使用pollfd结构来存储fd,突破了select中描述符数目的限制。
- 与select的后两点类似,poll仍然需要将pollfd数组拷贝到内核空间,之后依次扫描fd的状态,整体复杂度依然是O(n)的,在并发量大的情况下服务器性能会快速下降。
epoll
- epoll维护的描述符数目不受到限制,而且性能不会随着描述符数目的增加而下降。
- 服务器的特点是经常维护着大量连接,但其中某一时刻读写的操作符数量却不多。epoll先通过epoll_ctl注册一个描述符到内核中,并一直维护着而不像poll每次操作都将所有要监控的描述符传递给内核;在描述符读写就绪时,通过回掉函数将自己加入就绪队列中,之后epoll_wait返回该就绪队列。也就是说,epoll基本不做无用的操作,时间复杂度仅与活跃的客户端数有关,而不会随着描述符数目的增加而下降。
- epoll在传递内核与用户空间的消息时使用了内存共享,而不是内存拷贝,这也使得epoll的效率比poll和select更高。
程序示例
分别使用select,poll和epoll实现了简单的回显服务器程序,客户端使用select来实现。其中select和poll程序主要参考unp的实现,只是Demo程序,对一些异常情况没有进行处理。
客户端程序
使用select来监听终端输入和连接服务器的流输入,这样可以保证客户端不在某一个输入流上死等。
1 |
#include <sys/socket.h> |
select服务器
1 |
#include <sys/socket.h> |
poll服务器
1 |
#include <sys/socket.h> |
epoll服务器
回显服务器使用了ET高速模式。在该模式下,最好所有的操作都是非阻塞的,程序中套接字都设置为了non-socket,并且使用了缓冲区,在读到数据时先将数据存到缓冲区中,下次可写时才将数据从缓冲区写回客户端。
另外,在ET模式下,accept、read、write时都要使用循环直到读到EAGAIN才能说明没有数据了。
1 |
#include <sys/epoll.h> |
本文标题:Linux中的IO复用接口简介
文章作者:Vimer Su
发布时间:2013年11月19日 - 19时56分
最后更新:2016年06月04日 - 22时57分
原始链接:http://vimersu.win/blog/2013/11/19/linux-io-reuse-interface/
许可协议: "署名-非商用-相同方式共享 3.0" 转载请保留原文链接及作者。
Linux中的IO复用接口简介(文件监视?)的更多相关文章
- Linux网络编程-IO复用技术
IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...
- LINUX网络编程 IO 复用
参考<linux高性能服务器编程> LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下 于是就出现了selelct poll epoll等IO复用函数. 这 ...
- Linux基础(06)IO复用
在Windows文件指的就是普通的肉眼可见的文件 , 而Linux一切皆文件 https://blog.csdn.net/nan_nan_nan_nan_nan/article/details/812 ...
- 深入理解JAVA I/O系列六:Linux中的IO模型
IO模型 linux系统IO分为内核准备数据和将数据从内核拷贝到用户空间两个阶段. 这张图大致描述了数据从外部磁盘向运行中程序的内存中移动的过程. 用户空间.内核空间 现在操作系统都是采用虚拟存储器, ...
- 深入理解JAVA I/O系列六:Linux中的IO模型(转载的文章非常值得学习)
From:http://www.cnblogs.com/dongguacai/p/5770287.html IO模型 linux系统IO分为内核准备数据和将数据从内核拷贝到用户空间两个阶段. 这张图大 ...
- linux中的 IO端口映射和IO内存映射
参考自:http://blog.csdn.net/zyhorse2010/article/details/6590488 CPU地址空间 (一)地址的概念 1)物理地址:CPU地址总线传来的地址,由硬 ...
- imx6中iomux IO复用
IOMUX Controller (IOMUXC) IO多路复用控制器1.overviewThe IOMUX Controller (IOMUXC), together with the IOMUX, ...
- linux中/etc/passwd和/etc/shadow文件说明
/etc/passwd是用来存储登陆用户信息: [root@localhost test]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x ...
- 如何在Linux中使用rz/sz工具进行文件传输
在Linux中,使用rz/sz工具能够进行Linux和windows之间的文件传输,那么要如何使用rz/sz工具工具呢?下面小编就给大家介绍下Linux下如何使用rz/sz工具进行文件传输,一起来学习 ...
随机推荐
- CentOS 配置防火墙操作实例(启、停、开、闭端口)CentOS Linux-FTP/对外开放端口(接口)TomCat相关
链接地址:http://blog.csdn.net/jemlee2002/article/details/7042991 CentOS 配置防火墙操作实例(启.停.开.闭端口): 注:防火墙的基本操作 ...
- GET方式URL乱码问题解决
打开 tomcat/conf/server.xml 查找下面这部分,在最后增加一段代码就可以了. <Connector port="80" maxHttpHeaderSi ...
- 理解TCP为什么需要进行三次握手
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认: ...
- 組裝工廠設置IQC的目的
在電子組裝工廠的組織裡,一般都會有 IQC (Incoming Quality Control) 這個單位,台灣稱之為「入(進)料管控」,大陸稱之為「來料管控」,其最主要目的在攔檢所有買進材料是否符合 ...
- qutIm编译
官网:http://www.qutim.org/ 原文地址:http://wiki.qutim.org/en/building_from_git 依赖: Qt4-dev 4.7:http://qt-p ...
- 奇怪的haproxy 跳转
<pre name="code" class="html">奇怪的Haproxy 跳转: acl admin_req path_beg -i /ad ...
- poj1565---(数论)skew binary
/*将数字存储在数组中 #math.h strlen(a)=len sum=0 for(i=0;i<len;i++) sum+=a[i]*(pow(2,len-i)-1)*/ #include ...
- 解析微信node开发;拿token
获取token,本着两个原则, 1.先查询是否有token,有的话判断其时间是否与上一次请求时间差是否超过7100: a.不超过,直接用得到: b.超过,再获取刷新: 2.没有token获取刷新tok ...
- java 中解决中文乱码问题的方法(三法)
1. 重新定义. String str = "中文试试" ; str = new String(u.getBytes("iso-8859-1"),"u ...
- Effective JavaScript Item 36 实例状态仅仅保存在实例对象上
本系列作为EffectiveJavaScript的读书笔记. 一个类型的prototype和该类型的实例之间是"一对多"的关系.那么,须要确保实例相关的数据不会被错误地保存在pro ...