linux epoll,poll,select
程序员文章站
2023-01-01 14:16:20
epoll函数用法,还有点poll和select 1,LT的epoll是select和poll函数的改进版。 特点是,读完缓冲区后,如果缓冲区还有内容的话,epoll_wait函数还会返回,直到把缓冲区全部读完。 2,ET的epoll(阻塞) 特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll ......
epoll函数用法,还有点poll和select
1,lt的epoll是select和poll函数的改进版。
特点是,读完缓冲区后,如果缓冲区还有内容的话,epoll_wait函数还会返回,直到把缓冲区全部读完。
2,et的epoll(阻塞)
特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。估计有的读者朋友会想到用while去读,但是有个致命的问题,因为文件描述符是阻塞的,所以当全部读完后,进程就会阻塞在recv函数那里,就不能够再处理别的连接了。
3,et的epoll(非阻塞),效率最高的使用方法。
特点是,读完缓冲区后,不管缓冲区还有没有内容,epoll_wait函数都不会再返回,直到对端再一次发送信息过来。但是可以事先用fcntl把文件描述符设置成非阻塞的方式,让后用while一直去读,当全部读完后,recv函数也不会阻塞。
et的epoll(非阻塞)的例子:
#include <stdio.h> #include <sys/epoll.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> int main(int argc, char** argv){ int port = atoi(argv[1]); int lfd = socket(af_inet, sock_stream, 0); struct sockaddr_in addr; addr.sin_family = af_inet; addr.sin_port = htons(port); addr.sin_addr.s_addr = inaddr_any; bind(lfd, (struct sockaddr*)&addr, sizeof(addr)); listen(lfd, 5); int efd = epoll_create(10); struct epoll_event re; re.events = epollin; re.data.fd = lfd; epoll_ctl(efd, epoll_ctl_add, lfd, &re); struct epoll_event events[100]; while(1){ int ret = epoll_wait(efd, events, 100, -1); printf("======================wait=======\n"); if(ret == -1){ perror("epoll_wait"); exit(1); } for(int i = 0; i < ret; ++i){ if(events[i].data.fd == lfd){ int cfd = accept(lfd, null, null); int flags = fcntl(cfd, f_getfl); flags |= o_nonblock; fcntl(cfd, f_setfl, flags); struct epoll_event re; re.events = epollin | epollet; re.data.fd = cfd; epoll_ctl(efd, epoll_ctl_add, cfd, &re); break; } char buf[3]; int ret; while((ret = recv(events[i].data.fd, buf, sizeof buf, 0)) > 0){ write(stdout_fileno, buf, ret); } if(ret == 0){ epoll_ctl(efd, epoll_ctl_del, events[i].data.fd, null); close(events[i].data.fd); printf("client disconnet\n"); } else if(ret == -1 && errno == eagain){ printf("read over\n"); } } } }
poll函数例子:
#include <stdio.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char** argv){ int port = atoi(argv[1]); int lfd = socket(af_inet, sock_stream, 0); struct sockaddr_in addr; addr.sin_family = af_inet; addr.sin_port = htons(port); addr.sin_addr.s_addr = inaddr_any; bind(lfd, (struct sockaddr*)&addr, sizeof(addr)); listen(lfd, 5); struct pollfd pfd[1024]; for(int i = 0; i < 1024; ++i){ pfd[i].fd = -1; } pfd[0].fd = lfd; pfd[0].events = pollin; nfds_t maxfd = 0; while(1){ int ret = poll(pfd, maxfd + 1, -1); printf("--------------poll------\n"); if(pfd[0].revents & pollin){ int cfd = accept(lfd, null, null); for(int i = 0; i < 1024; ++i){ if(pfd[i].fd == -1){ pfd[i].fd = cfd; pfd[i].events = pollin; maxfd++; break; } } continue; } for(int i = 0; i <= maxfd; ++i){ if(pfd[i].revents & pollin){ char buf[64]; int ret = recv(pfd[i].fd, buf, sizeof buf, 0); if(ret == 0){ pfd[i].fd = -1; close(pfd[i].fd); printf("client is disconnet\n"); } else{ write(stdout_fileno, buf, ret); } } } } }
通过对比epoll和poll的例子可以看出来:
- epoll不需要事先决定数组的大小。poll需要。
- epoll内部是用红黑树实现的效率,不会随着连接的增多,而明显的变低。poll是用链表实现的,所以性能随着连接的增多而降低。poll还不能在windows下使用。epoll是跨平台的。
- 顺便说下,select是用数组实现的,数组的大小由内核代码写死了,就是1024,所以想增大,只能重新编译内核。但是select是在跨平台的。
c/c++ 学习互助qq群:877684253
本人微信:xiaoshitou5854
上一篇: MongoDB 操作数据库
推荐阅读
-
python实现Linux异步epoll代码
-
linux使用select实现精确定时器详解
-
Python—IO多路复用之select模块详解(select、poll、epoll之间的区别)
-
IO多路复用(二) -- select、poll、epoll实现TCP反射程序
-
linux epoll,poll,select
-
IO复用之select poll epoll的总结(推荐)
-
Linux IO多路复用之epoll网络编程
-
使用epoll实现聊天室功能,同时比较epoll和select的异同
-
【原创】Linux select/poll机制原理分析
-
Linux shell select菜单选择实现代码