Epoll多路I/O复用技术
来源:互联网 发布:犀牛软件模型立面 编辑:程序博客网 时间:2024/06/08 05:53
Epoll多路I/O复用技术
通常学习一个新的linux技术,我们应该看看man手册对其定义。
NAME epoll - I/O event notification facilitySYNOPSIS #include <sys/epoll.h>DESCRIPTION The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.
那么从man手册这段文字我们可以看出,epoll它是由linux另一套的并发处理方案poll演变过来的,它与poll相类似:能够监控多个文件描述符的I/O变化。在Linux中,一切皆文件(有部分不是)所以,任何一个连接,也有一个文件描述符(一般为int类型)来存放
重点:epoll比poll的优点:支持水平触发(level-triggered)和边沿触发(edge-triggered)两种方案
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。Level Triggered (LT) 水平触发只要有数据都会触发。
epoll所需的API函数:
epoll_create(2) creates an epoll instance and returns a file descriptor referring to that instance. (The more recent epoll_create1(2) extends the functionality of epoll_create(2).)
Interest in particular file descriptors is then registered via epoll_ctl(2). The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set.
epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.
1、创建epoll文件描述符
创建一个epoll句柄,参数size用来告诉内核监听的文件描述符的个数,跟内存大小有关。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
int epoll_create(int size); //size:监听数目
2、管理epoll中的文件描述符集合,增加、修改、删除
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
第一个参数是epoll_create()的返回值。
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);typedef union epoll_data{ void *ptr; int fd; uint32_t u32; uint64_t u64;} epoll_data_t;struct epoll_event{ uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */} __EPOLL_PACKED;
3、收集在epoll监控的事件中已经发送的事件(默认阻塞等待)
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);/* events:用来从内核得到事件的集合, maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size, timeout:是超时时间 -1:阻塞 0:立即返回,非阻塞 >0:指定微秒 返回值:成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1*/
epoll工作原理
1、epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
2、另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
epoll 服务端例子
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/wait.h>#include <sys/epoll.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>int main(){ int sock_server = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(10099); addr.sin_addr.s_addr = 0; int ret = bind(sock_server,(struct sockaddr*)&addr,sizeof(addr)); listen(sock_server,5); int epollfd = epoll_create(2); struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = sock_server; //吧sock_server加入eopll集合中 epoll_ctl(epollfd,EPOLL_CTL_ADD,sock_server,&ev); while(1) { struct epoll_event outev[8]; int ret = epoll_wait(epollfd,outev,8,1000); if (ret < 0) { if (errno == EINTR)//若被信号打断,则重新循环 continue; break; } if (ret > 0)//有被唤醒的文件描述符 { for(int i = 0 ; i<ret; i++) { int fd = outev[i].data.fd; if (fd == sock_server) { //若为socket的文件描述符,则用accept进行三次握手,建立连接 int newfd = accept(fd,NULL,NULL); //EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭) ev.events = EPOLLIN; ev.data.fd = newfd; //把新的fd加到epollfd中,继续等待下一次唤醒 epoll_ctl(epollfd,EPOLL_CTL_ADD,newfd,&ev); } else { //若不是socketfd,则为已经建立的连接,可以直接读取数据 char buf[1024]; int readlen = read(fd,buf,sizeof(buf)); if (readlen<=0) { //read<=0,证明已经没有数据,或者出错,关闭fd close(fd); } else { printf("read data is :%s\n",buf); } } } } } return 0;}
测试epoll的客户端
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/wait.h>#include <sys/epoll.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>int main(){ int fd = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(10099); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); int ret = connect(fd , (struct sockaddr*)&addr, sizeof(addr)); write(fd,"hello server",sizeof("hello server")); char buf[1024]; read(fd,buf,sizeof(buf)); printf("server:%s\n",buf); close(fd); return 0;}
测试结果我就暂时不贴图了,希望这次博客能给大家带来一点收获!
- Epoll多路I/O复用技术
- linux系统I/O复用技术之三:epoll()
- I/O复用之epoll
- I/O复用之epoll模型
- I/O复用之epoll简介
- epoll I/O复用模型
- I/O复用之epoll服务器
- 高级I/O复用技术:Epoll的使用及一个完整的C实例
- 高级I/O复用技术:Epoll的使用及一个完整的C实例
- 高级I/O复用技术:Epoll的使用及一个完整的C实例
- UNIX网络编程:I/O复用技术(select、poll、epoll)
- UNIX网络编程:I/O复用技术(select、poll、epoll)
- I/O多路复用技术之 - epoll
- 关于I/O多路复用技术 - Epoll剖析
- Linux I/O多路复用技术-epoll
- I/O复用技术
- I/O复用模型之epoll学习
- I/O复用之epoll系统调用
- 判断小B究竟是从N到M之间哪些方向看到据说的颜色,不同的字母则表示不同的颜色
- 放大器在Javascript中的实现
- 协方差矩阵与PCA深入原理剖析
- 【读书札记】小王子的感悟
- QGroundControl笔记 —— QGCToolbox
- Epoll多路I/O复用技术
- 字符串的引用类型当做值类型来处理
- 利用visual studio 2013 update 5 编译GDAL
- Android实战技巧:深入解析AsyncTask
- javascript网页特效——控制表单控件
- SDUT oj面向对象2-2 Time类的定义
- 安装python(x,y)记录
- MyEclipse 的快捷键
- 关于c++面试问题的一些总结