I/O复用

发布于:2024-08-12 ⋅ 阅读:(164) ⋅ 点赞:(0)

        I/O复用使得程序能够同时监听多个文件描述符,这对提高程序的性能至关重要。

系统函数调用

select系统调用

       #include <sys/select.h>

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

参数: 

  •  nfds:参数类型为int,指被监听的所有文件描述符总数 。它通常被设置为select监听的所有文件描述符中的最大值+1,因为文件描述符是从0开始计数的。
  • readfds,writefds,exceptfds:分别指向 可读,可写和异常等事件对应的文件描述符集合。通过这三个参数传入自己想要监控的文件描述符。select调用返回后,内核将修改他们来通知应用程序那些文件描述符已经就绪。
  • timeout :timeout用来设置select函数的超时调用时间。

返回值

成功返回就绪(可读可写异常)文件描述符总数,在规定时间内没有就绪,就返回0:调用失败返回-1并设置errno;如果在等待时间内接收到信号,则立即返回-1,并设置errno为EINTR。 


fd_set结构体类型 

        fd_set结构体仅包含一个整型数组,该数组中的每一个元素的每一个比特位标记一个文件描述符。里面通过一个宏(FD_SETSIZE),来限制select能同时处理文件的总量

timeout结构体类型

         采用timeval结构体指针,是因为内核将修改它以告诉应用程序select等待了多久,但是select调用失败后,返回的这个值是不确定的

下面就是select系统调用的简单使用

       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/select.h>

       int main(void)
       {
           fd_set rfds;
           struct timeval tv;
           int retval;
           //下面是监控输入文件描述符 fd = 0
           /* Watch stdin (fd 0) to see when it has input. */

           FD_ZERO(&rfds);//将rfds类型的变量的所有比特位置为0
           FD_SET(0, &rfds);//设置rfds上面的比特位

           /* Wait up to five seconds. *///设置超时时间

           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);//监控的文件描述符是0 填入是就是1
           /* Don't rely on the value of tv now! */

           if (retval == -1)
               perror("select()");
           else if (retval)//成功返回就绪文件描述符的总数
               printf("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true. */
           else
               printf("No data within five seconds.\n");

           exit(EXIT_SUCCESS);
       }

上面代码是监控输入文件描述符,在五秒时间内,如果没有输入,就会返回0:有数据就会返回1,因为只监控了一个文件描述符,所以返回1。 

         你也可以进行循环监控。

 

poll系统调用

       #include <poll.h>

       int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:

fds:fds参数就是一个pollfd结构体类型数组,后面会讲。

nfds:指定被监听事件集合大小(typedef unsigned long int nfds_t)就是长整型

timeout:参数类型为int,单位为毫秒,指定poll的超时时间。当为-1时,poll将永远阻塞(相当于卡住了,没就绪就不返回),直到发生某个事件;为0时,poll将立即返回(非阻塞(大白话:就是管你就不就绪,立刻返回))

返回值:

成功返回就绪(可读可写异常)文件描述符总数,在规定时间内没有就绪,就返回0:调用失败返回-1并设置errno;如果在等待时间内接收到信号,则立即返回-1,并设置errno为EINTR。


struct pollfd 结构体

  • fd:你要监听的文件描述符
  • events:告诉poll你要监听fd上的那些事件
  • revent:由内核修改,通知应用程序fd上实际发生了那些事件。 

poll事件监控类型

 

poll简单示例代码 

       #include <poll.h>
       #include <fcntl.h>
       #include <sys/types.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       int main(int argc, char *argv[])
       {
           int nfds, num_open_fds;
           struct pollfd *pfds;

           if (argc < 2) {
              fprintf(stderr, "Usage: %s file...\n", argv[0]);
              exit(EXIT_FAILURE);
           }

           num_open_fds = nfds = argc - 1;
           pfds = (struct pollfd*)malloc(nfds*sizeof(struct pollfd));
           if (pfds == NULL)
               errExit("malloc");

           /* Open each file on command line, and add it 'pfds' array */

           for (int j = 0; j < nfds; j++) {
               pfds[j].fd = open(argv[j + 1], O_RDONLY);

               if (pfds[j].fd == -1)
               {
                 printf("111");
                   errExit("open");
               }

               printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

               pfds[j].events = POLLIN;//注册可读事件

           }

           /* Keep calling poll() as long as at least one file descriptor is
              open */

           while (num_open_fds > 0) {
               int ready;

               printf("About to poll()\n");
               ready = poll(pfds, nfds, -1);
               //ready = poll(pfds, nfds, -1);
               if (ready == -1)
                   errExit("poll");

               printf("Ready: %d\n", ready);

               /* Deal with array returned by poll() */

               for (int j = 0; j < nfds; j++) {
                   char buf[10];

                   if (pfds[j].revents != 0) {
                       printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                               (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                               (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                               (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                       if (pfds[j].revents & POLLIN) {
                           ssize_t s = read(pfds[j].fd, buf, sizeof(buf));
                           if (s == -1)
                               errExit("read");
                           printf("    read %zd bytes: %.*s\n",
                                   s, (int) s, buf);
                       } else {                /* POLLERR | POLLHUP */
                           printf("    closing fd %d\n", pfds[j].fd);
                           if (close(pfds[j].fd) == -1)
                               errExit("close");
                           num_open_fds--;
                       }
                   }
               }
           }

           printf("All file descriptors closed; bye\n");
           exit(EXIT_SUCCESS);
       }

 

 

        上述代码,我给的是一个文件test1,由于代码里面时死循环,并且文件一直都是可读的,所以就一直循环,最后ctrl+c进行终止。,但是你给个目录就只打印一次。

epoll系统调用

         epoll系统调用和上面两系统调用有所不同,他有三个函数接口。

epoll_create

       #include <sys/epoll.h>

       int epoll_create(int size);

 

epoll_ ctl

       #include <sys/epoll.h>

       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

 epoll_wait

       #include <sys/epoll.h>

       int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

 


网站公告

今日签到

点亮在社区的每一天
去签到