日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux epoll的用法

發布時間:2023/12/10 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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結構體地址,下文解釋
struct epoll_event {uint32_t events; /* 需要檢測fd事件標志 */epoll_data_t data; /* 用戶自定義的數據 */ }

其中的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的用法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。