select()函数和poll()函数均是主要用来处理多路I/O复用的情况。比如一个服务器既想等待输入终端到来,又想等待若干个套接字有客户请求到达,这时候就需要借助select或者poll函数了。
1、select()函数
int select(int fdsp1, fd_set *readfds, fd_set *writefds, fd_set *errorfds, const struct timeval *timeout);
各个参数含义如下:
- int fdsp1:最大描述符值 + 1
- fd_set *readfds:对可读感兴趣的文件描述符集
- fd_set *writefds:对可写感兴趣的文件描述符集
- fd_set *errorfds:对出错感兴趣的文件描述符集
- struct timeval *timeout:超时时间(注意:对于linux系统,此参数没有const限制,每次select调用完毕timeout的值都被修改为剩余时间,而unix系统则不会改变timeout值)
select函数会在发生以下情况时返回:
- readfds集合中有文件描述符可读
- writefds集合中有文件描述符可写
- errorfds集合中有文件描述符遇到错误条件
- 指定的超时时间timeout到了
当select返回时,文件描述符集合将被修改以指示哪些个文件描述符正处于可读、可写或有错误状态。可以用FD_ISSET宏对描述符进行测试以找到状态变化的描述符。如果select因为超时而返回的话,所有的描述符集合都将被清空。
select函数返回状态发生变化的描述符总数。返回0意味着超时。失败则返回-1并设置errno。可能出现的错误有:EBADF(无效描述符)、EINTR(因终端而返回)、EINVAL(nfds或timeout取值错误)。
设置描述符集合通常用如下几个宏定义:
FD_ZERO(fd_set *fdset); /* clear all bits in fdset */ FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fd_set */ FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fd_set */ int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset? */
fd_set rset; FD_ZERO(&rset); /* initialize the set: all bits off */ FD_SET(1, &rset); /* turn on bit for fd 1 */ FD_SET(4, &rset); /* turn on bit for fd 4 */ FD_SET(5, &rset); /* turn on bit for fd 5 */
当select返回的时候,rset位都将被置0,除了那些有变化的fd位。
当发生如下情况时认为是可读的:
- socket的receive buffer中的字节数大于socket的receive buffer的low-water mark属性值。(low-water mark值类似于分水岭,当receive buffer中的字节数小于low-water mark值的时候,认为socket还不可读,只有当receive buffer中的字节数达到一定量的时候才认为socket可读)
- 连接半关闭(读关闭,即收到对端发来的FIN包)
- 发生变化的描述符是被动套接字,而连接的三路握手完成的数量大于0,即有新的TCP连接建立
- 描述符发生错误,如果调用read系统调用读套接字的话会返回-1。
当发生如下情况时认为是可写的:
- socket的send buffer中的字节数大于socket的send buffer的low-water mark属性值以及socket已经连接或者不需要连接(如UDP)。
- 写半连接关闭,调用write函数将产生SIGPIPE
- 描述符发生错误,如果调用write系统调用写套接字的话会返回-1。
注意:
select默认能处理的描述符数量是有上限的,为FD_SETSIZE的大小,1024,无法完成服务端大并发请求的处理。
对于timeout参数,如果置为NULL,则表示wait forever;若timeout->tv_sec = timeout->tv_usec = 0,则表示do not wait at all;否则指定等待时间。
如果使用select处理多个套接字,那么需要使用一个数组(也可以是其他结构)来记录各个描述符的状态。而使用poll则不需要,下面看poll函数。
2、poll()函数
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
各参数含义如下:
- struct pollfd *fdarray:一个结构体,用来保存各个描述符的相关状态。
- unsigned long nfds:fdarray数组的大小,即里面包含有效成员的数量。
- int timeout:设定的超时时间。(以毫秒为单位)
poll函数返回值及含义如下:
- -1:有错误产生
- 0:超时时间到,而且没有描述符有状态变化
- >0:有状态变化的描述符个数
着重讲fdarray数组,因为这是它和select()函数主要的不同的地方:
pollfd的结构如下:
struct pollfd { int fd; /* descriptor to check */ short events; /* events of interest on fd */ short revents; /* events that occured on fd */ };
其实poll()和select()函数要处理的问题是相同的,只不过是不同组织在几乎相同时刻同时推出的,因此才同时保留了下来。select()函数把可读描述符、可写描述符、错误描述符分在了三个集合里,这三个集合都是用bit位来标记一个描述符,一旦有若干个描述符状态发生变化,那么它将被置位,而其他没有发生变化的描述符的bit位将被clear,也就是说select()的readset、writeset、errorset是一个value-result类型,通过它们传值,而也通过它们返回结果。这样的一个坏处是每次重新select 的时候对集合必须重新赋值。而poll()函数则与select()采用的方式不同,它通过一个结构数组保存各个描述符的状态,每个结构体第一项fd代表描述符,第二项代表要监听的事件,也就是感兴趣的事件,而第三项代表poll()返回时描述符的返回状态。合法状态如下:
- POLLIN: 有普通数据或者优先数据可读
- POLLRDNORM: 有普通数据可读
- POLLRDBAND: 有优先数据可读
- POLLPRI: 有紧急数据可读
- POLLOUT: 有普通数据可写
- POLLWRNORM: 有普通数据可写
- POLLWRBAND: 有紧急数据可写
- POLLERR: 有错误发生
- POLLHUP: 有描述符挂起事件发生
- POLLNVAL: 描述符非法
对于POLLIN | POLLPRI等价与select()的可读事件;POLLOUT | POLLWRBAND等价与select()的可写事件;POLLIN 等价与POLLRDNORM | POLLRDBAND,而POLLOUT等价于POLLWRBAND。如果你对一个描述符的可读事件和可写事件以及错误等事件均感兴趣那么你应该都进行相应的设置。
对于timeout的设置如下:
- INFTIM: wait forever
- 0: return immediately, do not block
- >0: wait specified number of milliseconds
转自:http://www.cppblog.com/myjfm/archive/2011/10/26/159093.aspx
相关推荐
在进行"Unix网络编程 第6章 I/O复用:select和poll函数"的代码练习时,你可能会涉及到以下内容: 1. 创建并初始化`fd_set`或`pollfd`结构。 2. 使用`FD_SET()`或`pollfd`结构添加文件描述符。 3. 设置合适的超时时间...
select 和 poll 函数都是 Linux 中常用的系统调用,它们用于查询设备是否可读写或是否处于某种状态。在设备驱动程序中,select 和 poll 函数可以用于查询设备的状态,从而实现设备的异步输入/输出操作。
下面文章在这段时间内研究 select/poll/epoll的内核实现的一点心得体会:...这三种函数都需要设备驱动提供poll回调函数,对于套接字而言,他们是 tcp_poll,udp_poll和datagram_poll; 对于自己开发的设备驱动而言,是自
在Linux网络编程中,`select`和`poll`是两种常用的方法,用于处理多个套接字的并发I/O事件。这两个函数允许程序监控多个文件描述符(通常是套接字),以便在它们准备进行读写操作时及时响应。下面将详细探讨这两种...
### UNIX下SELECT和POLL模型的服务器程序设计论文解析 #### 概述 本文档是对一篇关于UNIX环境下SELECT和POLL模型在服务器程序设计中的应用的论文进行的深入解析。SELECT和POLL都是用于实现多路复用的技术,能够...
在Linux系统中,当面临需要同时管理大量网络连接或文件描述符时,`select`、`poll`和`epoll`是三种常见的I/O多路复用技术,它们允许程序在一个单独的线程中等待多个文件描述符的事件,提高了程序的效率和并发能力。...
第6章 I/O复用:select和poll函数 第7章 套接口选项 第8章 基本UDP套接口编程 第9章 基本SCTP套接口编程 第10章 SCTP客户/服务器程序例子 第11章 名字与地址转换 第3部分 高级套接口编程? 第12章 IPv4与IPv6...
《Select和POLL使用方法详解——高级字符设备驱动》 在软件开发中,尤其是在处理并发I/O操作时,Select和POLL是两种常用的机制,它们允许程序同时监控多个文件描述符,以等待某个或某些描述符的状态变化。本文将...
POLL函数是Linux系统提供的一种I/O多路复用机制,它比传统的select函数更为灵活和高效。在`server.c`和`client.c`源文件中,我们可以看到POLL函数的使用。POLL函数的核心是`poll()`系统调用,它允许程序监视多个文件...
基于Linux开发板的GPIO子系统,使用poll()函数监听io口的实时电平变化,使用示例: GpioApi ioTest = new GpioApi(this); ioTest->addOutIO(GpioApi::IO_C_0);//添加输出口 ioTest->addInIO(GpioApi::IO_G_11);//...
3. **状态检查**:`poll`函数会根据文件描述符当前的状态(如可读、可写等)返回一个比特掩码(`bitmask`),指示`select`函数哪些描述符已经准备好。 4. **等待队列操作**:如果没有文件描述符准备好,则进程会被挂...
`poll`是Linux系统提供的一种I/O多路复用机制,它是`select`函数的增强版,支持更多的文件描述符(FD),并且没有`select`对文件描述符数量的限制。`poll`函数允许开发者在一个单独的线程中监控多个文件描述符,以...
第6章 IO复用:select和poll函数 第7章 套接字选项 第8章 基本UDP套接字编程 第9章 基本SCTP套接字编程 第10章 SCTP客户/服务器程序例子 第11章 名字与地址转换 第三部分 第12章 IPv4与IPv6的互操作性 第13...
第6章 I/O复用:select和poll函数 第7章 套接字选项 第8章 基本UDP套接字编程 第9章 基本SCTP套接字编程 第10章 SCTP客户/服务器程序例子 第11章 名字与地址转换 第三部分 高级套接字编程 第12章 ...
【EPOLL - Linux 下的 Select 和 Poll 增强版】 EPOLL 是 Linux 内核提供的 I/O 多路复用技术,它是 Select 和 Poll 的更高效版本,特别是在处理大量并发连接时。EPOLL 提供了一种基于事件的异步 I/O 模型,能够...
第6章 I/O复用:select和poll函数 第7章 套接口选项 第8章 基本UDP套接口编程 第9章 基本SCTP套接口编程 第10章 SCTP客户/服务器程序例子 第11章 名字与地址转换 第3部分 高级套接口编程? 第12章 IPv4与IPv6的互操作...
多路复用I/O模型,如select和poll函数,可以让单个线程管理多个Socket。它们提供了一种机制,用于检测多个Socket的状态,当有数据可读或可写时,再进行相应操作。虽然提高了并发处理能力,但当Socket数量巨大时,...