0基础学会 IO多路复用(Epoll的两种工作模式)

Epoll的工作模式

LT模式(水平模式)

假设委托内核检测读事件->检测fd的读缓冲区

读缓冲区与数据->epoll检测到了会给用户通知

a\用户不读数据,数据一直在缓冲区,epoll会一直通知

b\用户只读了一部分数据,epoll会通知

c\缓冲区的数据读完了,不通知

LT(level-triggered)是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作,如果你不作任何操作,内核还是会继续通知你。

ET模式(边沿出发)

假设委托内核检测读事件->检测fd的读缓冲区

读缓冲区有数据->epoll检测了会给用户通知

a\用户不读数据,数据一直在缓冲区,epoll下次检测的时候就不通知了

b\用户只读了一部分数据,epoll不通知

c\缓冲区的数据读完了,不通知

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你,然后它就会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是如果一直不对这个fd做IO操作,内核不会发送更多的通知了

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死

struct epoll_event{
    uint32_t events;
    epoll_data_t data;
};
常见的Epoll检测事件:
-EPOLLIN
-EPOLLOUT
-EPOLLET

 LT模式相关的代码

 1 #include <stdio.h>
  2 #include <arpa/inet.h>
  3 #include <sys/epoll.h>
  4 #include <unistd.h>
  5 #include <string.h>
  6 #include <stdlib.h>
  7 int main()
  8 {
  9         //1、socket
 10         int socketfd=socket(AF_INET,SOCK_STREAM,0);
 11         if(socketfd==-1)
 12         {
 13                 perror("socket");
 14                 exit(-1);
 15         }
 16         //2、bind绑定
 17         struct sockaddr_in saddr;
 18         saddr.sin_family=AF_INET;
 19         saddr.sin_port=htons(9999);
 20         saddr.sin_addr.s_addr=INADDR_ANY;
 21         int bindfd=bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
 22         //3、listen监听
 23         listen(socketfd,128);
 24         //调用epoll_create创建一个epoll实例
 25         int epfd=epoll_create(100);
 26         //将监听的文件描述符相关的检测信息添加到epoll实例中
 27         struct epoll_event epev;
 28         epev.events=EPOLLIN;
 29         epev.data.fd=socketfd;
 30         epoll_ctl(epfd,EPOLL_CTL_ADD,socketfd,&epev);//对epoll实例进行管理,添加文件描述符信息
 31         struct epoll_event epevs[1024];
 32         while(1)
 33         {
 34                 int ret=epoll_wait(epfd,epevs,1024,-1);//检测函数
 35                 //返回发送变化的文件描述符的个数
 36                 if(ret==-1)
 37                 {
 38                         perror("epoll_wait");
 39                         exit(-1);
 40                 }
 41                 printf("ret=%d\n",ret);
 42                 for(int i=0;i<ret;i++)
 43                 {
 44                         int curfd=epevs[i].data.fd;
 45                         if(curfd == socketfd)
 46                         {
 47                                 //监听的文件描述符有数据到达,有客户端连接
 48                                 struct sockaddr_in cliaddr;
 49                                 int len=sizeof(cliaddr);
 50                                 int cfd=accept(socketfd,(struct sockaddr *)&cliaddr,&len);
 51 
 52                                 epev.events=EPOLLIN;
 53                                 epev.data.fd=cfd;
 54                                 epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
 55 
 56                         }
 57                         else
 58                         {
 59                                 char buf[5]={0};
 60                                 int len=read(curfd,buf,sizeof(buf));
 61                                 if(len==-1)
 62                                 {
 63                                         perror("read");
 64                                         exit(-1);
 65                                 }
 66                                 else if(len==0)
 67                                 {
 68                                         printf("client closed...\n");
 69                                         epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
 70                                         close(curfd);
 71                                 }
 72                                 else if(len>0)
 73                                 {
 74                                         printf("read buf=%s\n",buf);
 75                                         write(curfd,buf,strlen(buf)+1);
 76                                 }
 77                         }
 78                 }
 79 
 80 
 81 
 82 
 83         }
 84 
 85         close(socketfd);
 86         close(epfd);
 87         return 0;
 88 }

ET模式相关的代码

 1 #include <stdio.h>
  2 #include <errno.h>
  3 #include <fcntl.h>
  4 #include <arpa/inet.h>
  5 #include <sys/epoll.h>
  6 #include <unistd.h>
  7 #include <string.h>
  8 #include <stdlib.h>
  9 int main()
 10 {
 11         //1、socket
 12         int socketfd=socket(AF_INET,SOCK_STREAM,0);
 13         if(socketfd==-1)
 14         {
 15                 perror("socket");
 16                 exit(-1);
 17         }
 18         //2、bind绑定
 19         struct sockaddr_in saddr;
 20         saddr.sin_family=AF_INET;
 21         saddr.sin_port=htons(9999);
 22         saddr.sin_addr.s_addr=INADDR_ANY;
 23         int bindfd=bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
 24         //3、listen监听
 25         listen(socketfd,128);
 26         //调用epoll_create创建一个epoll实例
 27         int epfd=epoll_create(100);
 28         //将监听的文件描述符相关的检测信息添加到epoll实例中
 29         struct epoll_event epev;
 30         epev.events=EPOLLIN;
 31         epev.data.fd=socketfd;
 32         epoll_ctl(epfd,EPOLL_CTL_ADD,socketfd,&epev);//对epoll实例进行管理,添加文件描述符信息
 33         struct epoll_event epevs[1024];
 34         while(1)
 35         {
 36                 int ret=epoll_wait(epfd,epevs,1024,-1);//检测函数
 37                 //返回发送变化的文件描述符的个数
 38                 if(ret==-1)
 39                 {
 40                         perror("epoll_wait");
 41                         exit(-1);
 42                 }
 43                 printf("ret=%d\n",ret);
 44                 for(int i=0;i<ret;i++)
 45                 {
 46                         int curfd=epevs[i].data.fd;
 47                         if(curfd == socketfd)
 48                         {
 49                                 //监听的文件描述符有数据到达,有客户端连接
 50                                 struct sockaddr_in cliaddr;
 51                                 int len=sizeof(cliaddr);
 52                                 int cfd=accept(socketfd,(struct sockaddr *)&cliaddr,&len);
 53                                 //设置cfd属性非阻塞
 54                                 int flag=fcntl(cfd,F_GETFL);
 55                                 flag | O_NONBLOCK;
 56                                 fcntl(cfd,F_SETFL,flag);
 57 
 58                                 epev.events=EPOLLIN | EPOLLET;//设置边沿出发
 59                                 epev.data.fd=cfd;
 60                                 epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
 61 
 62                         }
 63                         else
 64                         {
 65                                 if(epevs[i].events & EPOLLOUT)
 66                                 {
 67                                         continue;
 68                                 }
 69 
 70 
 71                                 //循环读取所有数据
 72                                 char buf[5]={0};
 73                                 int len=0;
 74                                 while((len=read(curfd,&buf,sizeof(buf)))>0)
 75                                 {
 76                                 //      printf("recv:%s\n",buf);
 77                                         write(STDOUT_FILENO,buf,len);
 78                                         write(curfd,buf,len);
 79                                 }
 80                                 if(len==-1)
 81                                 {
 82                                         if(errno==EAGAIN)
 83                                         {
 84                                                 printf("data over...");
 85                                         }
 86                                         else
 87                                         {
 88                                                 perror("read");
 89                                                 exit(-1);
 90                                         }
 91                                 }
 92                                 else if(len==0)
 93                                 {
 94                                         printf("client closed...\n");
 95                                 }
 96                         }
 97                 }
 98 
 99 
100 
101 
102         }
103 
104         close(socketfd);
105         close(epfd);
106         return 0;
107 }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值