Linux epoll的用法
Linux epoll的用法
epollfd_create函數(shù)
#include <sys/epoll.h>int epoll_create (int __size)| __size | 此參數(shù)從Linux 2.6.8后就不再使用了,但必須設(shè)置成大于零的值 |
| >0 | 可用的epollfd |
| -1 | 調(diào)用失敗 |
epollfd_ctl函數(shù)
有了epollfd,我們需要將要檢測事件的fd綁定到這個epollfd上,或者修改或者移除,使用epollfd_ctl完成
int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event)| __epfd | 上文中的epollfd |
| __op | 操作類型1.(EPOLLFD_CTL_ADD)添加2.(EPOLLFD_CTL_MOD)修改3.(EPOLLFD_CTL_DEL)移除 |
| __fd | 需要被操作的描述符fd |
| epoll_event *__event | 這是一個epollfd_event結(jié)構(gòu)體地址,下文解釋 |
其中的epoll_data_t
typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;| 0 | 成功 |
| -1 | 失敗 |
epollfd_wait函數(shù)
int epoll_wait (int __epfd, struct epoll_event *__events,int __maxevents, int __timeout);| epoll_event *__events | 輸出參數(shù),在函數(shù)調(diào)用成功后,events中存放的是與就緒事件相關(guān)的epoll_event結(jié)構(gòu)體數(shù)組 |
| __maxevents | 上述數(shù)組中元素的個數(shù) |
| >0 | 有事件的fd的數(shù)量 |
| 0 | 超時 |
| -1 | 失敗 |
示例:
int main() {epoll_event epoll_events[1024];int n = epoll_wait(epollfd,epoll_events,1024,1000);if(n<0) {//被信號中斷if(errno == EINTR){//...}}else if(n ==0){//...}for (size_t i = 0; i < n; ++i) {if(epoll_events[i].events & EPOLLIN){//處理可讀事件} else if (epoll_events[i].events & EPOLLOUT){//處理可寫事件} else if (epoll_events[i].events & EPOLLERR){//處理出錯事件}} }poll與epoll_wait函數(shù)的區(qū)別
邊緣觸發(fā)模式(ET) 和 水平觸發(fā)模式 (LT)
水平觸發(fā)模式:一個事件只要有,就會一直觸發(fā)
邊緣觸發(fā)模式:一個事件從無到有才會觸發(fā)
想不出好例子,摘抄一個吧
水平觸發(fā)
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
兒子:媽媽,我今天花了200元買了個變形金剛。
媽媽:以后不要亂花錢。
兒子:媽媽,我今天買了好多好吃的,還剩下100元。
媽媽:用完了這些錢,我可不會再給你錢了。
兒子:媽媽,那100元我沒花,我攢起來了
媽媽:這才是明智的做法!
兒子:媽媽,那100元我還沒花,我還有錢的。
媽媽:嗯,繼續(xù)保持。
兒子:媽媽,我還有100元錢。
媽媽:…
接下來的情形就是沒完沒了了:只要兒子一直有錢,他就一直會向他的媽媽匯報。LT模式下,只要內(nèi)核緩沖區(qū)中還有未讀數(shù)據(jù),就會一直返回描述符的就緒狀態(tài),即不斷地喚醒應(yīng)用進程。在上面的例子中,兒子是緩沖區(qū),錢是數(shù)據(jù),媽媽則是應(yīng)用進程了解兒子的壓歲錢狀況(讀操作)。
邊緣觸發(fā)
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
(兒子使用壓歲錢購買了變形金剛和零食。)
兒子:
媽媽:兒子你倒是說話啊?壓歲錢呢?
這個就是ET模式,兒子只在第一次收到壓歲錢時通知媽媽,接下來兒子怎么把壓歲錢花掉并沒有通知媽媽。即兒子從沒錢變成有錢,需要通知媽媽,接下來錢變少了,則不會再通知媽媽了。在ET模式下, 緩沖區(qū)從不可讀變成可讀,會喚醒應(yīng)用進程,緩沖區(qū)數(shù)據(jù)變少的情況,則不會再喚醒應(yīng)用進程。
/** * 驗證epoll的LT與ET模式的區(qū)別,epoll_server.cpp* zhangyl 2019.04.01*/ #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> #include<fcntl.h> #include<sys/epoll.h> #include<poll.h> #include<iostream> #include<string.h> #include<vector> #include<errno.h> #include<iostream>int main() {//創(chuàng)建一個監(jiān)聽socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1){std::cout << "create listen socket error" << std::endl;return -1;}//設(shè)置重用ip地址和端口號int on = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));//將監(jiān)聽socker設(shè)置為非阻塞的int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if (fcntl(listenfd, F_SETFL, newSocketFlag) == -1){close(listenfd);std::cout << "set listenfd to nonblock error" << std::endl;return -1;}//初始化服務(wù)器地址struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1){std::cout << "bind listen socker error." << std::endl;close(listenfd);return -1;}//啟動監(jiān)聽if (listen(listenfd, SOMAXCONN) == -1){std::cout << "listen error." << std::endl;close(listenfd);return -1;}//創(chuàng)建epollfdint epollfd = epoll_create(1);if (epollfd == -1){std::cout << "create epollfd error." << std::endl;close(listenfd);return -1;}epoll_event listen_fd_event;listen_fd_event.data.fd = listenfd;listen_fd_event.events = EPOLLIN;//取消注釋掉這一行,則使用ET模式//listen_fd_event.events |= EPOLLET;//將監(jiān)聽sokcet綁定到epollfd上去if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &listen_fd_event) == -1){std::cout << "epoll_ctl error" << std::endl;close(listenfd);return -1;}int n;while (true){epoll_event epoll_events[1024];n = epoll_wait(epollfd, epoll_events, 1024, 1000);if (n < 0){//被信號中斷if (errno == EINTR) continue;//出錯,退出break;}else if (n == 0){//超時,繼續(xù)continue;}for (size_t i = 0; i < n; ++i){//事件可讀if (epoll_events[i].events & EPOLLIN){if (epoll_events[i].data.fd == listenfd){//偵聽socket,接受新連接struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);if (clientfd != -1){int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1){close(clientfd);std::cout << "set clientfd to nonblocking error." << std::endl;}else{epoll_event client_fd_event;client_fd_event.data.fd = clientfd;client_fd_event.events = EPOLLIN;//取消注釋這一行,則使用ET模式//client_fd_event.events |= EPOLLET; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event) != -1){std::cout << "new client accepted,clientfd: " << clientfd << std::endl;}else{std::cout << "add client fd to epollfd error" << std::endl;close(clientfd);}}}}else{std::cout << "client fd: " << epoll_events[i].data.fd << " recv data." << std::endl;//普通clientfdchar ch;//每次只收一個字節(jié)int m = recv(epoll_events[i].data.fd, &ch, 1, 0);if (m == 0){//對端關(guān)閉了連接,從epollfd上移除clientfdif (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1){std::cout << "client disconnected,clientfd:" << epoll_events[i].data.fd << std::endl;}close(epoll_events[i].data.fd);}else if (m < 0){//出錯if (errno != EWOULDBLOCK && errno != EINTR){if (epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1){std::cout << "client disconnected,clientfd:" << epoll_events[i].data.fd << std::endl;}close(epoll_events[i].data.fd);}}else{//正常收到數(shù)據(jù)std::cout << "recv from client:" << epoll_events[i].data.fd << ", " << ch << std::endl;}}}else if (epoll_events[i].events & POLLERR){// TODO 暫不處理}}}close(listenfd);return 0; }現(xiàn)在采用的是一個水平模式,只要有數(shù)據(jù)可讀,就會觸發(fā)事件
現(xiàn)在采用邊緣觸發(fā)模式
采用邊緣觸發(fā)模式,只有有新數(shù)據(jù)到來才會觸發(fā),所以就有了上面的現(xiàn)象
總結(jié)
以上是生活随笔為你收集整理的Linux epoll的用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MAC电脑常用效率工具推荐
- 下一篇: java为何重复调用方法_通过反射调用J