IO多路复用_Select & poll

来源:互联网 发布:mac beta 转 正式版 编辑:程序博客网 时间:2024/06/02 00:09

1.  IO多路复用含义:

IO 输入输出操作;

多路复用:若请求的IO操作阻塞,且它不是真正阻塞IO,而是让其中的一个函数等待,在这期间,IO 还能进行其他的操作。该函数有select() 和 poll()两个函数。

2. Selete()poll()函数:

设置程序中每一个所关心的文件描述符的条件、希望等待的时间等,selete()poll() 函数返回时内核会通知用户已经准备好的文件描述符的数量、已准备好的条件等。

通过使用selete()poll() 函数的返回结果,就可调用相应的IO处理函数;

2.1 Select()函数介绍:

#include <sys/types.h>

#include <sys/time.h>

#include <unistd.h>

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exeptfds, struct timeval *timeout);

参数说明:

函数参数

参数说明

备注

numfds

需要监视的文件描述符的最大值+1

 

readfds

select()监视的读文件描述符集合

 

writefds

监视的写文件描述符集合

 

exeptfds

监视的异常处理文件描述符集合

 

timeout

1) NULL ——永远等待,直到扑捉到信号;

2) 具体值——在等待的时间范围内,没有等到,则立即返回;

3) 0——从不等待,所有文件描述符立即返回

 

 

 

 

返回值

1) >0:成功,返回准备好的文件描述符的数目;

2) 0:超时;

3) -1:出错;

 

 

select() 文件描述符处理函数:

FD_ZERO(fd_set *set); 清除一个文件描述符集;

FD_SET(int fd, fd_set *set);  将文件描述符fd加入到文件描述符集合set

FD_CLR(int fd, fd_set *set); 将一个文件描述符fd从文件描述符集合中删除;

FD_ISSET(int fd, fd_set *set);

 

//timeout数据类型介绍:

struct timeval

{

long  tv_sec;

long  tv_usec;

}

 

2.2 为什么linux select函数的第一个参数总应该是fdmax + 1 ?

这就涉及到linux select第一个参数的含义: 待测试的描述集的总个数。 但要注意, 待测试的描述集总是从0, 1, 2, ...开始的。 所以, 假如你要检测的描述符为8, 9, 10, 那么系统实际也要监测0, 1, 2, 3, 4, 5, 6,  7,  此时真正待测试的描述符的个数为11个, 也就是max8, 9, 10) + 1

 

select 函数注意点:http://blog.csdn.net/jkazan/article/details/52529687

 

 

2.3 poll() 函数介绍:

#include <sys/types.h>

#include <poll.h>

int poll(struct pollfd *fds, int numfds, int timeout);

参数说明:

函数参数

参数说明

备注

fds

struct pollfd结构指针:描述需要对哪些文件的 哪种类型的操作进行监控;

struct pollfd  {

int    fd;

short  events;      //要监听的事件

short  revents;     //已发生的事件   

}

events成员描述主要有以下几类:

POLLIN;  POLLPRI;  POLLOUT;  POLLERR;  POLLHUP;  POLLNVAL;

 

numfds

要监听的文件描述符个数;

即第一个参数所指向的数组张哦功能的元素数目;

 

timeout

0值:  等待的ms数;

<0:      无限等待;

 

 

 

 

返回值

1) >0:成功,事件发生的pollfd结构的个数;

2) 0:超时;

3) -1:出错;

 

 

3. 实例验证:

通过调用select() 函数来监听3个终端的输入,并分别进行相应的处理。3个文件描述符分别是:一个标准输入,两个管道文件描述符;通过监视主程序的输入终端来实现程序控制,比如结束程序;两个管道文件描述符作为输入,主程序将两个管道读取的输入字符串写入到标准输出文件,即屏幕上。

在标准终端输入‘q’ 或者 ’Q’时,监控终端退出,或者60S超时退出。

 

实验运行结果如下:


 

创建两个段管道,用命令:mknod in1 mknod in2

管道、网络编程等,都有阻塞作用。

管道中的数据,在下一次select()函数启动的时候,会调用读取管道里面的数据。

3.1 select() 程序代码:

#include <fcntl.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <time.h>#include <errno.h>#include <string.h>#define MAX_BUFF_SIZE   1024#define IN_FILES        3#define TIME_DELAY      60#define MAX(a, b)       ((a > b) ? (a) : (b))int main(void){        int fds[IN_FILES];        char buff[MAX_BUFF_SIZE];        int i, res, real_read, maxfd;        struct timeval tv;        fd_set inset, tmp_inset;        fds[0] = 0;        if( (fds[1] = open("in1", O_RDONLY | O_NONBLOCK)) < 0 )        {       printf("Open in1 is error!\n");                return 1;        }        if( (fds[2] = open("in2", O_RDONLY | O_NONBLOCK)) < 0)        {       printf("Open in2 is error!\n");                return 1;        }        maxfd = MAX( MAX(fds[0], fds[1]), fds[2]);        FD_ZERO(&inset);        for(i = 0; i < IN_FILES; i++)        {                FD_SET(fds[i], &inset);        }        FD_SET(0, &inset);        tv.tv_sec = TIME_DELAY;        tv.tv_usec = 0;        while( FD_ISSET(fds[0], &inset) || FD_ISSET(fds[1], &inset) || FD_ISSET(fds[2], &inset) )        {                tmp_inset = inset;                //printf("tmp_inset = %d\n", tmp_inset);                res = select( maxfd+1, &tmp_inset, NULL, NULL, &tv);                printf("select return res = %d,准备好的fd数目\n", res);                switch(res)                {                        case -1:{                                        printf("Select error\n");                                        return 1;                                }                                break;                        case  0:{                                        printf("Time out\n");                                        return 1;                                }                                break;                        default:{for(i = 0; i < IN_FILES; i++){        if( FD_ISSET(fds[i], &tmp_inset) )        {                memset(buff, 0, MAX_BUFF_SIZE);                real_read = read(fds[i], buff, MAX_BUFF_SIZE);                if(real_read < 0)                {       if(errno != EAGAIN)                                return 1;                }                else if( !real_read )                {       close(fds[i]);                        FD_CLR(fds[i], &inset);                }                else                {                        if( i == 0 )                        {                                if( (buff[0] = 'q') || (buff[0] == 'Q') )                                                return 1;                        }                        else                        {                                buff[real_read] = '\0';                                printf("Read Data is %s\n", buff);                        }                }        }}                        }                }        }}

select() 函数缺点:

1)内核必须检查多余的文件描述符;

2)每次调用select()函数之后,必须重置被监听的文件描述符集;

3)可监听的文件描述符受限;

 

4. poll()函数实例:



代码实现:

#include <fcntl.h>      2 #include <stdio.h>      3 #include <unistd.h>      4 #include <stdlib.h>      5 #include <string.h>      6 #include <time.h>      7 #include <errno.h>      8 #include <poll.h>      9      10 #define MAX_BUFFSIZE    1024     11 #define IN_FILES        3     12 #define TIME_DELAY      100     13 #define MAX(a, b)       ((a > b) ? (a) : (b))     14      15 int main(void)     16 {     17      18         struct pollfd fds[IN_FILES];     19         char buff[MAX_BUFFSIZE];     20         int i, res, real_read, maxfd;     21      22         fds[0].fd = 0;     23  24         if( (fds[1].fd = open("in1", O_RDONLY | O_NONBLOCK)) < 0)     25         {       printf("Open in1 Error\n");     26                 return 1;     27         }     28      29         if( (fds[2].fd = open("in2", O_RDONLY | O_NONBLOCK)) < 0)     30         {       printf("Open in2 Error\n");     31                 return 1;     32         }     33      34         //1. 初始化所要监听的文件描述符的事件     35         for( i=0; i<IN_FILES; i++)     36         {     37                 fds[i].events = POLLIN;     38         }     39      40         //2. 循环检测所监听的文件描述符的事件类型的事件是否发生:if( fds[i].revents )     41         while( fds[0].events || fds[1].events || fds[2].events )     42         {     43                 //poll()函数,返回值为fds结构体的个数,即发生的文件描述>符的个数     44                 if( (res = poll(fds, IN_FILES, 0)) < 0)     45                 {       printf("Poll error\n");     46                         return 1;     47                 }     48         printf("1) poll fun returned is %d\n", res);     49         for(i=0; i<IN_FILES; i++)     50         {     51         printf("2) %d fds.fd is happened!\n",fds[i].fd);     52      53         //特别注意,该判断切勿遗漏:     revents表示已发生的事件;  events表示需要监听的事件的事件类型初始化; 54         if(fds[i].revents)     55         {     56                 printf("3) %d fds.fd is happened!\n",fds[i].fd);     57                 memset(buff, 0, MAX_BUFFSIZE);     58                 real_read = read(fds[i].fd, buff, MAX_BUFFSIZE);     59                 if(real_read < 0)     60                 {       if(errno != EAGAIN)     61                                 return 1;     62                 }     63                 else if( !real_read )     64                 {     65                         close(fds[i].fd);     66                         fds[i].events = 0;     67                 }     68                 else     69                 {     70                         //控制终端输入 q 或者 Q 时,退出程序运行;     71                         if( 0 == i )     72                         {     73                                 if( (buff[0] == 'q') || (buff[0] == 'Q') )     74                                         return 1;     75                         }     76                         else     77                         {     78                                 buff[real_read] = '\0';     79                                 printf("read data are %s\n", buff);     80                         }     81                 }     82         }  83         }     84         }     85         exit(0);     86 }     87      88 




原创粉丝点击