轮询

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

  使用非阻塞 I/O 的应用程序通常会使用 select() 和 poll() 系统调用查询是否可对设备进行无阻塞的访问。这两个系统调用最终会引发设备驱动中的 poll() 函数被执行。

  • numfds:此值是需要检查的号码最高的文件描述符加 1 
  • readfds:被 select 监视的读文件描述符集合
  • writefds:被 select 监视的写文件描述符集合
  • exceptfds:被 select 监视的异常处理的文件描述符集合
  • timeout:指向 struct timeval 类型的指针,可使 select 在等待 timeout 时间后若没有文件描述符准本好则返回
1 struct timeval {
2     int tv_sec; /**/
3     int tv_usec; /* 微秒 */    
4 };

  下列操作来设置、清除、判断文件描述符集合

1 FD_ZERO(fd_set *set);    /* 清除一个文件描述符集合 */
2 FD_SET(int fd, fd_set *set);    /* 将一个文件描述符加入文件描述符集合中 */
3 FD_CLR(int fd, fd_set *set);    /* 将一个文件描述符从文件描述符集合中清除 */
4 FD_ISSET(int fd, fd_set *set);    /* 判断文件描述符是否被置位 */
 1 #include ...
 2 
 3 #define        FIFO_CLEAR  0x01
 4 #define        BUFFER_LEN    20
 5 
 6 main()
 7 {
 8     int fd, num;
 9     char rd_ch(BUFFER_LEN);
10     fd_set rfds,wfds;    /* 读/写文件描述符集 */
11     
12     /* 以非阻塞方式打开 /dev/xxx  设备文件 */
13     fd = open("/dev/xxx", O_RDONLY | O_NONBLOCK);
14     if(fd != -1)
15     {
16         /* FIFO 清0 */
17         if(ioctl(fd, FIFO_CLEAR, 0) < 0)
18         {
19             prinf("ioctl command failed
");
20         }
21         
22         while(1)
23         {
24             FD_ZERO(&rfds);
25             FD_ZERO(&wfds);
26             FD_SET(fd, &rfds);
27             FD_SET(fd, &wfds);
28             
29             select(fd + 1, &rfds, &wfds, NULL, NULL);
30             /* 数据可获得 */
31             if(FD_ISSET(fd, &rfds))
32                 prinf("Poll monitor: can be read
");
33             
34             /* 数据可写入 */
35             if(FD_ISSET(fd, &wfds))
36                 prinf("Poll monitor: can be write
");
37         }
38     }
39     else
40     {
41         prinf("Device open failure
");
42     }
43 }

 1.1 多路复用 select()

  

  第一次对 n 个文件进行 select 的时候,若任何一个文件满足要求,select 就直接返回;第二次再进行 select 的时候,没有文件满足读写要求,select 进程阻塞且睡眠。

  由于调用 select 的时候,每个驱动的  poll() 接口都会被调用到,实际上执行 select 的进程被挂到了每个驱动的等待队列上,可以被任何一个驱动唤醒,如果 FDn 变得可读写,select 返回

  当多路复用的文件数量庞大,IO流量频繁的时候,一般不太适用使用 select 和 poll ,此情况下,这两个函数表现的性能较差,应使用  epoll。

  epoll  的最大好处是不会随着 fd 的数目增长而降低效率。

1 /* poll 函数原型 */
2 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
1 /* 创建一个 epoll 句柄,size 告诉内核要监听多少个 fd */
2 /* 注:当创建 epoll 句柄后,它本身也会占用一个 fd,所以在使用完 epoll 后,必须调用 close() 关闭 */
3 int epoll_creat(int size);
1 /* 告诉内核要监听什么类型的事件 
2  * 第一个参数是 epoll_creat 的返回值
3  * 第二个参数表示动作,第三个参数是需要监听的 fd,第四个参数是告诉内核需要监听的事件类型
4  */
5 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  第二个参数的动作包含:

  

  struct epoll_event 结构体为:

  

  • event 可以是以下几个宏的或:
    • EPOLLIN:表示对应的文件描述符可读
    • EPOLLOUT:表示对应的文件描述符可写
    • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里表示的是有 socket 带外数据到来)
    • EPOLLERR:表示对应的文件描述符发生错误
    • EPOLLHUP:表示对应的文件描述符被挂断
    • EPOLLET:将 epoll 设为边缘触发模式,  
    • EPOLLONESHOT:意味着一次性监听,当监听完这次事件之后,如果还需要继续监听这个 fd 的话,需要再次把这个 fd 加入到 epoll 队列中

  

1 /* 等待事件的产生,用来从内核得到事件的集合,maxevents 告诉内核本次最多收多少事件,maxevent<= 创建 epoll_creat() 时的size 
2  * timeout 为超时时间(以毫秒为单位,0意味着立即返回,-1意味着永久等待)
3  * 返回值是需要处理的事件数目,若返回0,表示已超时
4  */
5 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

  

原文地址:https://www.cnblogs.com/kele-dad/p/8747628.html