select 与 poll

select 与 poll功能相似。select是berkeley发明,而poll是由贝尔实验室发明。


select 系统调用是用来让我们的程序监视多个文件描述符(file descrīptor)的状态变化的。
程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。

原理:

   1 获得所需要的文件描述符列表
   2 将此列表传给select
   3 select挂起知道任何一个文件描述符有数据到达
   4 select设置一个变量中的若干位,用来通知你哪一个文件描述符已经有输入的数据

select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组
元素都能与一打开的文件描述符(不管是Socket描述符,还是其他 文件或命名管道或设备
描述符)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状
态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读,

select函数原型如下:

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

函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。

struct timeval结构如下:

struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /* microseconds */
};

第2、3、4三个参数的类型是一样的: fd_set *,即我们在程序里要申明几个fd_set类型的变量,比如定义了rfds, wfds, efds。

另外关于fd_set类型的变量,还有一组标准的宏定义来处理此类变量:

FD_ZERO(fd_set *fdset):清空fdset与所有文件描述符的联系。

FD_SET(int fd, fd_set *fdset):建立文件描述符fd与fdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件描述符fd与fdset的联系。

FD_ISSET(int fd, fd_set *fdset):检查fd_set联系的文件描述符fd是否可读写,>0表示可读写。

(关于fd_set及相关宏的定义见/usr/include/sys/types.h)定义的这三个参数都是描述符的集合,

第一个rfds是用来保存这样的描述符的:当描述符的状态变成可读的时系统就会告诉select函数返回,
第二个wfds是指有描述符状态变成可写的时系统就会告诉select函数返回,
第三个参数efds是特殊情况,即描述符上有特殊情况发生时系统会告诉select函数返回。

下面以一个输入为例来说明:

int fd1, fd2; /* 在定义两个描述符*/

fd1 = socket(...); /* 创建socket连接*/

fd2 = open(“/dev/tyS0”,O_RDWR); /* 打开一个串口*/

FD_ZERO(&rfds); /* 用select函数之前先把集合清零 */

FD_SET(fd1, &rfds); /* 分别把2个描述符加入读监视集合里去 */

FD_SET(fd2, &rfds);

int maxfd = 0;

maxfd = (fd1>fd2?fd1:fd2)+1; /* 注意是最大值还要加1 */

ret = select(maxfd, &rfds, NULL, NULL, &tv); /*然后调用select函数*/

这样就可以使用一个开关语句(switch语句)来判断到底是哪一个输入源在输入数据。具体判断如下:

switch(ret){

case -1:perror("select");/* 这说明select函数出错 */

case 0:printf("超时\n"); /* 说明在设定的时间内,socket的状态没有发生变化 */

default:

if(FD_ISSET(fd1, &rfds)) 处理函数1();/*socket有数据来*/

if(FD_ISSET(fd2, &rfds)) 处理函数2();/*ttyS0有数据来*/

}

实例1 :用来循环读取键盘输入

#include
#include
#include
#include
#include
#include
int main ()
{
    int keyboard;
    int ret,i;
    char c;
    fd_set readfd;
    struct timeval timeout;
    keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
    assert(keyboard>0);
    while(1)
    {
        timeout.tv_sec=1;
        timeout.tv_usec=0;
        FD_ZERO(&readfd);
        FD_SET(keyboard,&readfd);
        ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
        if(FD_ISSET(keyboard,&readfd))
        {
            i=read(keyboard,&c,1);
            if('\n'==c)
            continue;
            printf("hehethe input is %c\n",c);
            if ('q'==c)
            break;
        }
    }
}

实例2:在标准输入读取9个字节数据。用select函数实现超时判断!

int main(int argc, char ** argv)
{
    char buf[10] = "";
    fd_set rdfds;//
    struct timeval tv; //store timeout
    int ret; // return val
    FD_ZERO(&rdfds); //clear rdfds
    FD_SET(1, &rdfds); //add stdin handle into rdfds
    tv.tv_sec = 3;
    tv.tv_usec = 500;
    ret = select(1 + 1, &rdfds, NULL, NULL, &tv);
    if(ret < 0)
    perror("\nselect");
    else if(ret == 0)
    printf("\ntimeout");
    else
    {
        printf("\nret=%d", ret);
    }

    if(FD_ISSET(1, &rdfds))
    {
        printf("\nreading");
        fread(buf, 9, 1, stdin); // read form stdin
    }
    // read(0, buf, 9); /* read from stdin */
    // fprintf(stdout, "%s\n", buf); /* write to stdout */
    write(1, buf, strlen(buf)); //write to stdout
    printf("\n%d\n", strlen(buf));
    return 0;
}

poll 接受一个指向结构'struct pollfd'列表的指针,其中包括了你想测试的文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前的结构在调用后将被填写并在事件发生后返回。在SVR4(可能更早的一些版本)中的 "poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待时间精确到毫秒 (但令人困惑的是等待时间的类型却是int),当等待时间为0时,poll()函数立即返回,-1则使poll()一直挂起直到一个指定事件发生。下面是pollfd的结构。
     struct pollfd {
         int fd;        /* 文件描述符 */
         short events;  /* 等待的事件 */
         short revents; /* 实际发生了的事件 */
     };
于select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定事件内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。
如果没有事件发生,revents会被清空,所以你不必多此一举。

实例1
   /* 检测两个文件描述符,分别为一般数据和高优先数据。如果事件发生
      则用相关描述符和优先度调用函数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:

/* multiplex_poll.c */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <poll.h>

#define MAX_BUFFER_SIZE   1024    /* 缓冲区大小*/
#define IN_FILES   3    /* 多路复用输入文件数目*/
#define TIME_DELAY   6000    /* 超时时间秒数 */
#define MAX(a, b)   ((a > b)?(a):(b))

int main(void)
{
struct pollfd fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read, maxfd;
/*首先按一定的权限打开两个源文件*/
fds[0].fd = 0;
if((fds[1].fd = open ("in1", O_RDONLY|O_NONBLOCK)) < 0)
{
   printf("Open in1 error\n");
   return 1;
}
   if((fds[2].fd = open ("in2", O_RDONLY|O_NONBLOCK)) < 0)
   {
    printf("Open in2 error\n");
   return 1;
}

/*取出两个文件描述符中的较大者*/
for (i = 0; i < IN_FILES; i++)
{
   fds[i].events = POLLIN;
}
while(fds[0].events || fds[1].events || fds[2].events)
{
   if (poll(fds, IN_FILES, TIME_DELAY) <= 0)
   {
    printf("Poll error\n");
    return 1;
   }
   for (i = 0; i< IN_FILES; i++)
   {
    if (fds[i].revents)
    {
     memset(buf, 0, MAX_BUFFER_SIZE);
     real_read = read(fds[i].fd, buf, MAX_BUFFER_SIZE);

     if (real_read < 0)
     {
      if (errno != EAGAIN)
      {
       return 1;
      }
     }
     else if (!real_read)
     {
      close(fds[i].fd);
      fds[i].events = 0;
     }
     else
     {
      if (i == 0)
      {
       if ((buf[0] == 'q') || (buf[0] == 'Q'))
       {
        return 1;
       }
      }
      else
      {
       buf[real_read] = '\0';
       printf("%s", buf);
      }
     } /* end of if real_read*/
    } /* end of if revents */
   } /* end of for */
} /*end of while */
exit(0);
}

posted @ 2009-11-11 10:34  jackyxm  阅读(2904)  评论(0)    收藏  举报