日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

物联网卡linux,Server Develop (六) Linux epoll总结

發(fā)布時(shí)間:2023/12/15 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 物联网卡linux,Server Develop (六) Linux epoll总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

epoll是Kernel 2.6后新加入的事件機(jī)制,在高并發(fā)條件下,遠(yuǎn)優(yōu)于select。epoll最大的好處在于它不會(huì)隨著監(jiān)聽fd數(shù)目的增長而降低效率。因?yàn)樵趦?nèi)核中的select實(shí)現(xiàn)中,它是采用輪詢來處理的,輪詢的fd數(shù)目越多,自然耗時(shí)越多。并且,在linux/posix_types.h頭文件有這樣的聲明:

#define __FD_SETSIZE 1024 //select最多同時(shí)監(jiān)聽1024個(gè)fd

當(dāng)然,可以通過修改頭文件再重編譯內(nèi)核來擴(kuò)大這個(gè)數(shù)目,但這似乎并不治本。

所以在Nginx中采用了epoll來實(shí)現(xiàn)其高并發(fā)特性。

工作方式LT(level triggered):水平觸發(fā),缺省方式,同時(shí)支持block和no-block socket,在這種做法中,內(nèi)核告訴我們一個(gè)文件描述符是否被就緒了,如果就緒了,你就可以對這個(gè)就緒的fd進(jìn)行IO操作。如果你不作任何操作,內(nèi)核還是會(huì)繼續(xù)通知你的,所以,這種模式編程出錯(cuò)的可能性較小。傳統(tǒng)的selectpoll都是這種模型的代表。

ET(edge-triggered):邊沿觸發(fā),高速工作方式,只支持no-block socket。在這種模式下,當(dāng)描述符從未就緒變?yōu)榫途w狀態(tài)時(shí),內(nèi)核通過epoll告訴你。然后它會(huì)假設(shè)你知道文件描述符已經(jīng)就緒,并且不會(huì)再為那個(gè)描述符發(fā)送更多的就緒通知,直到你做了某些操作導(dǎo)致那個(gè)文件描述符不再為就緒狀態(tài)了(比如:你在發(fā)送、接受或者接受請求,或者發(fā)送接受的數(shù)據(jù)少于一定量時(shí)導(dǎo)致了一個(gè)EWOULDBLOCK錯(cuò)誤)。但是請注意,如果一直不對這個(gè)fs做IO操作(從而導(dǎo)致它再次變成未就緒狀態(tài)),內(nèi)核不會(huì)發(fā)送更多的通知。

區(qū)別:LT事件不會(huì)丟棄,而是只要讀buffer里面有數(shù)據(jù)可以讓用戶讀取,則不斷的通知你。而ET則只在事件發(fā)生之時(shí)通知。

主要的數(shù)據(jù)結(jié)構(gòu)

epoll_event的結(jié)構(gòu)如下:

typedef union epoll_data {void *ptr;intfd; __uint32_t u32; __uint64_t u64;} epoll_data_t;//保存觸發(fā)事件的某個(gè)文件描述符相關(guān)的數(shù)據(jù)structepoll_event { __uint32_t events;/*epoll event*/epoll_data_t data;/*User data variable*/};

events表示感興趣的事件和被觸發(fā)的事件,可能的取值為:EPOLLIN對應(yīng)的文件描述符可以讀

EPOLLOUT對應(yīng)的文件描述符可以寫

EPOLLPRI對應(yīng)的文件描述符有緊急的數(shù)可讀

EPOLLERR對應(yīng)的文件描述符發(fā)生錯(cuò)誤

EPOLLHUP對應(yīng)的文件描述符被掛斷

EPOLLET將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對于水平觸發(fā)(Level Triggered)來說的

EPOLLONESHOT只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個(gè)socket的話,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里

操作函數(shù)

epoll的接口非常簡單,用三個(gè)相關(guān)函數(shù)來創(chuàng)建epoll句柄、注冊epoll事件以及等待事件的發(fā)生。

創(chuàng)建epoll句柄:

int epoll_create(intsize);//size表示內(nèi)核需要監(jiān)聽的數(shù)目

//return : epoll文件描述符

需要注意的是,當(dāng)創(chuàng)建好epoll句柄后,它就是會(huì)占用一個(gè)fd值(文件標(biāo)識(shí)符),在linux下如果查看/proc/進(jìn)程id/fd/,是能夠看到這個(gè)fd的,所以在使用完epoll后,必須調(diào)用close()關(guān)閉,否則可能導(dǎo)致fd被耗盡。

epoll事件注冊函數(shù):

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)//epfd是epoll_create()的返回值//op表示動(dòng)作/*op可被表示為: EPOLL_CTL_ADD:注冊新的fd到epfd中; EPOLL_CTL_MOD:修改已經(jīng)注冊的fd的監(jiān)聽事件; EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;*///fd是需要監(jiān)聽的fd//event是內(nèi)核需要監(jiān)聽的事件

等待事件發(fā)生函數(shù):

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, inttimeout)//epfd是函數(shù)返回值//events是內(nèi)核監(jiān)聽事件的集合//maxevents是epoll_wait可以處理的連接事件的最大限度值//timeout是超時(shí)時(shí)間

//返回值:請求數(shù)

epoll工作流程

首先,需要調(diào)用epoll_create創(chuàng)建epoll,此后我們就可以進(jìn)行socket/bind/listen,然后調(diào)用epoll_ctl進(jìn)行注冊。接下來,就可以通過一個(gè)while(1)循環(huán)調(diào)用epoll_wait來等待事件的發(fā)生,然后循環(huán)查看接收到的事件并進(jìn)行處理。如果事件是sever的socketfd我們就要進(jìn)行accept,并且把接收到client的socketfd加入到要監(jiān)聽的事件中。如果在監(jiān)聽過程中,需要修改操作方式(讀/寫),可以調(diào)用epoll_ctl來重新修改。如果監(jiān)聽到某一個(gè)客戶端關(guān)閉,那么我就需要再次調(diào)用epoll_ctl把它從epoll監(jiān)聽事件中刪除。

實(shí)例

#include #include#include#include#include#include#include#include#include#include#includevoid setnonblocking(intsockfd){intopts; opts=fcntl(sockfd, F_GETFL);if(opts < 0){ perror("fcntl1 error!"); exit(1); } opts= opts |O_NONBLOCK;if(fcntl(sockfd, F_SETFL, opts) < 0){ perror("fcntl2 error!"); exit(1); }}intmain(){intfd;inton;intrs;intlen;intconn;char buffer[100];intflag1, flag2;structsockaddr_in serv_addr, clt_addr;structtimeval timeout;inti;intnfds;intepfd;intnewfd;structepoll_event ev;struct epoll_event events[20]; fd= socket(AF_INET, SOCK_STREAM, 0); on= 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,&on, sizeof(on)); timeout.tv_sec= 5; timeout.tv_usec= 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); bzero(&serv_addr, sizeof(structsockaddr_in)); serv_addr.sin_family=AF_INET; serv_addr.sin_port= htons(9090); serv_addr.sin_addr.s_addr=INADDR_ANY; rs= bind(fd, (struct sockaddr*)(&serv_addr), sizeof(structsockaddr));if(rs < 0){ perror(""); close(fd);return -1; } setnonblocking(fd); epfd= epoll_create(100); ev.data.fd=fd; ev.events= EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, fd,&ev); rs= listen(fd, 5);if(rs < 0){ perror(""); close(fd);return -1; } len= sizeof(structsockaddr);for(;;){ nfds= epoll_wait(epfd, events, 20, 500);for(i = 0; i < nfds; ++i){if(events[i].data.fd ==fd){ conn= accept(fd, (struct sockaddr*)(&clt_addr), (unsigned int*)(&len)); setnonblocking(conn); ev.data.fd=conn; ev.events= EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, conn,&ev); }else if(events[i].events &EPOLLIN){if((newfd = events[i].data.fd) < 0)continue; bzero(buffer,sizeof(buffer)); flag1= recv(newfd, buffer, 100, 0); printf("recv: %s", buffer); printf("recv return: %d", flag1); ev.data.fd=newfd; ev.events= EPOLLOUT|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, newfd,&ev); }else if(events[i].events &EPOLLOUT){if((newfd = events[i].data.fd) < 0)continue; bzero(buffer,sizeof(buffer)); strcpy(buffer,"ACK"); flag2= send(newfd, buffer, sizeof(buffer), 0); printf("recv return: %d", flag2); ev.data.fd=newfd; ev.events= EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, newfd,&ev); } } } close(fd);return 0;}

本文轉(zhuǎn)自cococo點(diǎn)點(diǎn)博客園博客,原文鏈接:http://www.cnblogs.com/coder2012/p/3143953.html,如需轉(zhuǎn)載請自行聯(lián)系原作者

總結(jié)

以上是生活随笔為你收集整理的物联网卡linux,Server Develop (六) Linux epoll总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。