Linux之I/O多路复用

 I/O多路复用:单个线程可以同时处理多个I/O;

一、select:成功则返回已准备好的文件描述符个数,超时则返回0,出错则返回-1;

(1)头文件包含:#include <sys/select.h>、<unistd.h>、<sys/time.h>;

(2)select返回后集合fdset中只有事件发生了的fd对应的位被置为1,其他位都被清空为0,所以需要依次用FD_ISSET来判断是否被置为1即事件是否发生;

  • int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr); 
  • int FD_ZERO(fd_set *fdset);---删除集合fdset中的所有文件描述符
  • int FD_SET(int fd, fd_set *fdset);---加入fd到集合fdset中
  • int FD_CLR(int fd, fd_set *fdset);---删除集合fdset中的fd
  • int FD_ISSET(int fd, fd_set *fdset);---判断fd为是否可读写
 1 #include <sys/select.h>
 2 #include <unistd.h>
 3 
 4 int main(int argc, char *argv[])
 5 {
 6     fd_set readset, writeset;
 7     int ret = 0;
 8     FD_ZERO(&readset);
 9     FD_ZERO(&writeset);
10     FD_SET(3, &readset);
11     FD_SET(5, &writeset);
12 
13     ret = select(6, &readset, &writeset, NULL, NULL);
14     switch (ret)
15     {
16     case -1: //error
17         break;
18     case 0: //timeout
19         break;
20     default: //success
21         if(FD_ISSET(3, &readset))
22         {
23             ;//read fd(3)
24         }
25         if(FD_ISSET(5, &writeset))
26         {
27             ;//write fd(5)
28         }
29         break;
30     }
31     return 0;
32 }
View Code

二、poll:成功则返回已准备好的文件描述符个数,超时则返回0,出错则返回-1;

(1)头文件包含:#include <poll.h>

(2)struct pollfd

  {

    int fd;

    short events; //input: interested event

    short revents; //output: occured event

  };

  • int poll(struct pollfd fds[], nfds_t nfds, int timeout);
 1 #include <poll.h>
 2 #define EVENTS_NUM 3
 3 
 4 int main(int argc, char *argv[])
 5 {
 6     struct pollfd pfds[EVENTS_NUM];
 7     int ret = 0;
 8     
 9     pfds[0].fd = 10;
10     pfds[0].events = POLLIN;
11     pfds[1].fd = 11;
12     pfds[1].events = POLLOUT;
13     pfds[2].fd = 12;
14     pfds[2].events = POLLERR;
15 
16     ret = poll(pfds, EVENTS_NUM, -1);
17     switch (ret)
18     {
19     case -1: //error
20         break;
21     case 0: //timeout
22         break;
23     default: //success
24         for(int i = 0; i < EVENTS_NUM; i++)
25         {
26             if(pfds[i].revents & POLLIN)
27             {
28                 //read pfds[i].fd
29             }
30             else if(pfds[i].revents & POLLOUT)
31             {
32                 //write pfds[i].fd
33             }
34             else if(pfds[i].revents & POLLERR)
35             {
36                 //handle pfds[i].fd
37             }
38         }
39         break;
40     }    
41     return 0;
42 }
View Code

三、epoll:成功则返回已准备好的文件描述符个数,超时则返回0,出错则返回-1;

(1)头文件包含:#include <sys/epoll.h>,epoll是一种当文件描述符的内核读缓冲区非空的时候发出可读signal通知进程;当内核写缓冲区不满的时候发出可写signal通知进程的机制,即事件驱动的机制;

(2)LT:水平触发(Level Triggered),如果对就绪的文件描述符fd不做操作,则内核会一直通知直到fd被处理为止;默认值传统的select/poll也采用该机制

(3)ET:边缘触发(Edge Triggerred),不管有没有对就绪的文件描述符fd做操作,内核之通知一次;

  • int epoll_create(int size);
  • int epoll_ctl(int efpd, int op, int fd, struct epoll_event *event);
  • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • int close(int epfd);
 1 #include <sys/epoll.h>
 2 #include <sys/eventfd.h> //eventfd,create a fd which is used to event-notify
 3 #define EVENT_NUM 3
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     int epfd, ret = 0;
 8     struct epoll_event tmpEvt, occuredEvts[20];  
 9 
10     epfd = epoll_create(256);
11     if(epfd < 0)
12     {
13         //error
14     }
15     for(int i = 0; i < EVENT_NUM; i++)
16     {
17         tmpEvt.data.fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
18         //tmpEvt.data.fd = i;
19         tmpEvt.events = EPOLLIN;
20         ret = epoll_ctl(epfd, EPOLL_CTL_ADD, i, &tmpEvt);
21         if(ret < 0)
22         {
23             //error
24         }
25     }
26     ret = epoll_wait(epfd, occuredEvts, EVENT_NUM, -1);
27     switch (ret)
28     {
29     case -1: //error
30         break;
31     case 0: //timeout
32         break;
33     default: //success
34         for(int i = 0; i < ret; i++) //traverse ret!!!!!!
35         {
36             if(occuredEvts[i].events & EPOLLIN)
37             {
38                 //read occuredEvts[i].data.fd
39             }
40             else if(occuredEvts[i].events & EPOLLOUT)
41             {
42                 //write occuredEvts[i].data.fd
43             }
44             else if(occuredEvts[i].events & EPOLLERR)
45             {
46                 //handle occuredEvts[i].data.fd
47             }
48         }
49         break;
50     }
51 
52     return 0;
53 }
View Code

四、select/poll/epoll之间的比较

1 select:需要将用户传入的集合拷贝到内核空间,然后遍历每个fd对应的设备状态,如果设备就绪就更新设备的状态并继续遍历;如果遍历完没有发现就绪的设备则挂起当前进程,直到有设备就绪或者超时,进程被唤醒后又要遍历每个fd;最大连接数1024或2048;
2 poll:和select机制类似,唯一不同的就是poll没有最大连接数的限制,因为poll内部是基于链表(传入数组头指针+长度)来存储的;

3 epoll:内核监听到某个fd就绪时,就会采用callback函数的机制把该fd放入到内核的就绪队列中,这样epoll_wait返回的时候就只需要遍历该就绪队列即可,而无需遍历所有的fd;内核用红黑树来管理传入的epfd即多个fd;

(1)没有最大连接数的限制,1G的内存可以监听10万个fd;

(2)当fd就绪时它只会遍历已经就绪的fd,时间复杂度O(1),而不会像select/poll那样线性遍历所有的fd,所以效率得到了提升;

(3)采用内存映射(mmap)技术,减少了内存拷贝的开销;

 

转载于:https://www.cnblogs.com/bo1990/p/11385977.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值