Linux epoll的用法
Linux epoll的用法
epollfd_create函數
#include <sys/epoll.h>int epoll_create (int __size)| __size | 此參數從Linux 2.6.8后就不再使用了,但必須設置成大于零的值 |
| >0 | 可用的epollfd |
| -1 | 調用失敗 |
epollfd_ctl函數
有了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結構體地址,下文解釋 |
其中的epoll_data_t
typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;| 0 | 成功 |
| -1 | 失敗 |
epollfd_wait函數
int epoll_wait (int __epfd, struct epoll_event *__events,int __maxevents, int __timeout);| epoll_event *__events | 輸出參數,在函數調用成功后,events中存放的是與就緒事件相關的epoll_event結構體數組 |
| __maxevents | 上述數組中元素的個數 |
| >0 | 有事件的fd的數量 |
| 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函數的區別
邊緣觸發模式(ET) 和 水平觸發模式 (LT)
水平觸發模式:一個事件只要有,就會一直觸發
邊緣觸發模式:一個事件從無到有才會觸發
想不出好例子,摘抄一個吧
水平觸發
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
兒子:媽媽,我今天花了200元買了個變形金剛。
媽媽:以后不要亂花錢。
兒子:媽媽,我今天買了好多好吃的,還剩下100元。
媽媽:用完了這些錢,我可不會再給你錢了。
兒子:媽媽,那100元我沒花,我攢起來了
媽媽:這才是明智的做法!
兒子:媽媽,那100元我還沒花,我還有錢的。
媽媽:嗯,繼續保持。
兒子:媽媽,我還有100元錢。
媽媽:…
接下來的情形就是沒完沒了了:只要兒子一直有錢,他就一直會向他的媽媽匯報。LT模式下,只要內核緩沖區中還有未讀數據,就會一直返回描述符的就緒狀態,即不斷地喚醒應用進程。在上面的例子中,兒子是緩沖區,錢是數據,媽媽則是應用進程了解兒子的壓歲錢狀況(讀操作)。
邊緣觸發
兒子:媽媽,我收到了500元的壓歲錢。
媽媽:嗯,省著點花。
(兒子使用壓歲錢購買了變形金剛和零食。)
兒子:
媽媽:兒子你倒是說話啊?壓歲錢呢?
這個就是ET模式,兒子只在第一次收到壓歲錢時通知媽媽,接下來兒子怎么把壓歲錢花掉并沒有通知媽媽。即兒子從沒錢變成有錢,需要通知媽媽,接下來錢變少了,則不會再通知媽媽了。在ET模式下, 緩沖區從不可讀變成可讀,會喚醒應用進程,緩沖區數據變少的情況,則不會再喚醒應用進程。
/** * 驗證epoll的LT與ET模式的區別,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() {//創建一個監聽socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1){std::cout << "create listen socket error" << std::endl;return -1;}//設置重用ip地址和端口號int on = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));//將監聽socker設置為非阻塞的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;}//初始化服務器地址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;}//啟動監聽if (listen(listenfd, SOMAXCONN) == -1){std::cout << "listen error." << std::endl;close(listenfd);return -1;}//創建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;//將監聽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){//超時,繼續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;//每次只收一個字節int m = recv(epoll_events[i].data.fd, &ch, 1, 0);if (m == 0){//對端關閉了連接,從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{//正常收到數據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; }現在采用的是一個水平模式,只要有數據可讀,就會觸發事件
現在采用邊緣觸發模式
采用邊緣觸發模式,只有有新數據到來才會觸發,所以就有了上面的現象
總結
以上是生活随笔為你收集整理的Linux epoll的用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MAC电脑常用效率工具推荐
- 下一篇: linux快速php,Linux 下的这