[转] select 与 poll

如何管理多个连接?
“我想同时监控一个以上的文件描述符(fd)/连接(connection)/流(stream),应该怎么办?”

使用 select() 或 poll() 函数。

注 意:select() 在BSD中被引入,而poll()是SysV STREAM流控制的产物。因此,这里就有了平台移植上的考虑:纯粹的BSD系统可 能仍然缺少poll(),而早一些的SVR3系统中可能没有select(),尽管在SVR4中将其加入。目前两者都是POSIX. 1g标准,(译者 注:因此在Linux上两者都存在)

select()和poll()本质上来讲做的是同一件事,只是完成的方法不一样。两者都通过检验一组文件描述符来检测是否有特定的时间将在上面发生并在一定的时间内等待其发生。

[重要事项:无论select()还是poll()都不对普通文件起很大效用,它们着重用于套接口(socket)、管道(pipe)、伪终端(pty)、终端设备(tty)和其他一些字符设备,但是这些操作都是系统相关(system-dependent)的。]

2.1.1. 我如何使用select()函数?
select()函数的接口主要是建立在一种叫‘fd_set‘类型的基础上。它(‘fd_set‘) 是一组文件描述符(fd)的集合。由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量:

fd_set set;
    FD_ZERO(&set);       /* 将set清零 */
    FD_SET(fd, &set);    /* 将fd加入set */
    FD_CLR(fd, &set);    /* 将fd从set中清除 */
    FD_ISSET(fd, &set);  /* 如果fd在set中则真 */
      
在 过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查 fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。*这个值是系 统相关的*,同时检查你的系统中的select() 的man手册。有一些系统对多于1024个文件描述符的支持有问题。[译者注: Linux就是这样 的系统!你会发现sizeof(fd_set)的结果是128(*8 = FD_SETSIZE=1024) 尽管很少你会遇到这种情况。]

select的基本接口十分简单:

int select(int nfds, fd_set *readset, fd_set *writeset,
               fd_set *exceptset, struct timeval *timeout);
      
其中:

nfds     
     需要检查的文件描述符个数,数值应该比是三组fd_set中最大数
     更大,而不是实际文件描述符的总数。
readset    
     用来检查可读性的一组文件描述符。
writeset
     用来检查可写性的一组文件描述符。
exceptset
     用来检查意外状态的文件描述符。(注:错误并不是意外状态)
timeout
     NULL指针代表无限等待,否则是指向timeval结构的指针,代表最
     长等待时间。(如果其中tv_sec和tv_usec都等于0, 则文件描述符
     的状态不被影响,但函数并不挂起)
      
函数将返回响应操作的对应操作文件描述符的总数,且三组数据均在恰当位置被修改,只有响应操作的那一些没有修改。接着应该用FD_ISSET宏来查找返回的文件描述符组。

这里是一个简单的测试单个文件描述符可读性的例子:

int isready(int fd)
     {
         int rc;
         fd_set fds;
         struct timeval tv;
    
         FD_ZERO(&fds);
         FD_SET(fd,&fds);
         tv.tv_sec = tv.tv_usec = 0;
    
 rc = select(fd+1, &fds, NULL, NULL, &tv);
         if (rc < 0)
           return -1;
    
         return FD_ISSET(fd,&fds) ? 1 : 0;
     }
      
当然如果我们把NULL指针作为fd_set传入的话,这就表示我们对这种操作的发生不感兴趣,但select() 还是会等待直到其发生或者超过等待时间。

[译 者注:在Linux中,timeout指的是程序在非sleep状态中度过的时间,而不是实际上过去的时间,这就会引起和非Linux平台移植上的时间不 等问题。移植问题还包括在System V风格中select()在函数退出前会把timeout设为未定义的 NULL状态,而在BSD中则不是这样, Linux在这点上遵从System V,因此在重复利用timeout指针问题上也应该注意。]

2.1.2. 我如何使用poll()?
poll ()接受一个指向结构‘struct pollfd‘列表的指针,其中包括了你想测试的文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前 的结构在调用后将被填写并在事件发生后返回。在SVR4(可能更早的一些版本)中的 "poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待 时间精确到毫秒 (但令人困惑的是等待时间的类型却是int),当等待时间为0时,poll()函数立即返回,-1则使poll()一直挂起直到一个指定 事件发生。下面是pollfd的结构。

struct pollfd {
         int fd;        /* 文件描述符 */
         short events;  /* 等待的事件 */
         short revents; /* 实际发生了的事件 */
     };
      
于select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定事件内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。

如果没有事件发生,revents会被清空,所以你不必多此一举。

这里是一个例子

/* 检测两个文件描述符,分别为一般数据和高优先数据。如果事件发生
      则用相关描述符和优先度调用函数handler(),无时间限制等待,直到
      错误发生或描述符挂起。*/
   
   #include <stdlib.h>
   #include <stdio.h>
  
   #include <sys/types.h>
   #include <stropts.h>
   #include <poll.h>
  
   #include <unistd.h>
   #include <errno.h>
   #include <string.h>
  
   #define NORMAL_DATA 1
   #define HIPRI_DATA 2
  
   int poll_two_normal(int fd1,int fd2)
   {
       struct pollfd poll_list[2];
       int retval;
  
       poll_list[0].fd = fd1;
       poll_list[1].fd = fd2;
       poll_list[0].events = POLLIN|POLLPRI;
       poll_list[1].events = POLLIN|POLLPRI;
  
       while(1)
       {
           retval = poll(poll_list,(unsigned long)2,-1);
           /* retval 总是大于0或为-1,因为我们在阻塞中工作 */
  
           if(retval < 0)
           {
               fprintf(stderr,"poll错误: %s/n",strerror(errno));
               return -1;
           }
    
           if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
              ((poll_list[0].revents&POLLERR) == POLLERR) ||
              ((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
              ((poll_list[1].revents&POLLHUP) == POLLHUP) ||
              ((poll_list[1].revents&POLLERR) == POLLERR) ||
              ((poll_list[1].revents&POLLNVAL) == POLLNVAL))
             return 0;
  
           if((poll_list[0].revents&POLLIN) == POLLIN)
             handle(poll_list[0].fd,NORMAL_DATA);
           if((poll_list[0].revents&POLLPRI) == POLLPRI)
             handle(poll_list[0].fd,HIPRI_DATA);
           if((poll_list[1].revents&POLLIN) == POLLIN)
             handle(poll_list[1].fd,NORMAL_DATA);
           if((poll_list[1].revents&POLLPRI) == POLLPRI)
             handle(poll_list[1].fd,HIPRI_DATA);
       }
   }
      
2.1.3. 我是否可以同时使用SysV IPC和select()/poll()?
*不能。* (除非在AIX上,因为它用一个无比奇怪的方法来实现这种组合)

一般来说,同时使用select()或poll()和SysV 消息队列会带来许多麻烦。SysV IPC的对象并不是用文件描述符来处理的,所以它们不能被传递给select()和 poll()。这里有几种解决方法,其粗暴程度各不相同:

完全放弃使用SysV IPC。 :-)

用fork(),然后让子进程来处理SysV IPC,然后用管道或套接口和父进程 说话。父进程则使用select()。

同上,但让子进程用select(),然后和父亲用消息队列交流。

安排进程发送消息给你,在发送消息后再发送一个信号。*警告*:要做好 这个并不简单,非常容易写出会丢失消息或引起死锁的程序。

另外还有其他方法。

时间: 2024-11-18 20:11:02

[转] select 与 poll的相关文章

Linux中的select,poll,epoll模型

Linux中的 select,poll,epoll 都是IO多路复用的机制. select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作.select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一.select的一个缺点在于单个进程能够监视的文件描

select、poll、epoll之间的区别总结[整理]

select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间.关于这三种IO多路复用的用法,前面三篇总结写的很清楚,并用服务器回射echo程序进行了测试.

socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较

1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步:      所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:      异步的概念和同步相对.当c端一个异步过程调用发出后,调

Linux IO模式及 select、poll、epoll详解

注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念: - 用户空间和内核空间 - 进程切换 - 进程的阻塞 - 文件描述符 - 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32

select、poll、epoll之间的区别总结[转]

  原文链接:http://www.cnblogs.com/Anker/p/3265058.html select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到

select,poll,epoll

1. Epoll是何方神圣? Epoll可是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select相似,其实都I/O多路复用技术而已,并没有什么神秘的. 其实在Linux下设计并发网络程序,向来不缺少方法,比如典型的Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,以及select模型和poll模型,那为何还要再引入Epoll这个东东呢?那还是有得说说的-

python之IO多路复用(二)——select、poll、epoll详解

select,poll,epoll都是IO多路复用的机制.I/O多路复用就是通过一种机制使一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的 异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间.   sellect.poll.epoll三者的区别 : select通过一个sel

(转)Linux IO模式及 select、poll、epoll详解

本文为转载,并作了部门调整.修改. [原文出处:https://segmentfault.com/a/1190000003063859] 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念: 用户空间和内核空间 进程切换 进程的阻塞 文件描述符 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚

select、poll、epoll之间的区别总结

这个问题面试经常会被问道,赶紧总结一下 select.poll.epoll都是IO多路复用的机制,先是监听多个文件描述符FD,一旦某个FD就绪,就可以进行相应的读写操作.但是select.poll.epoll本质都是同步I/O,他们都需要在读写事件就绪之后自己负责读写,即这个读写过程是阻塞的 1 select/poll select缺点: [1]每次调用select都需要把fd从用户态拷贝到内核态,开销比较大 [2]每次都需要在内核遍历传入的fd [3]select支持文件数量比较小,默认是10

Select、Poll与Epoll比较

(1)select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作. select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一. select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修