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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

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

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

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

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

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

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

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

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

主要的數(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ā)事件的某個文件描述符相關(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ā)生錯誤

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

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

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

操作函數(shù)

epoll的接口非常簡單,用三個相關(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句柄后,它就是會占用一個fd值(文件標(biāo)識符),在linux下如果查看/proc/進(jìn)程id/fd/,是能夠看到這個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表示動作/*op可被表示為: EPOLL_CTL_ADD:注冊新的fd到epfd中; EPOLL_CTL_MOD:修改已經(jīng)注冊的fd的監(jiān)聽事件; EPOLL_CTL_DEL:從epfd中刪除一個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ù)

epoll工作流程

首先,需要調(diào)用epoll_create創(chuàng)建epoll,此后我們就可以進(jìn)行socket/bind/listen,然后調(diào)用epoll_ctl進(jìn)行注冊。接下來,就可以通過一個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)聽到某一個客戶端關(guān)閉,那么我就需要再次調(diào)用epoll_ctl把它從epoll監(jiān)聽事件中刪除。

實例

#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點點博客園博客,原文鏈接:http://www.cnblogs.com/coder2012/p/3143953.html,如需轉(zhuǎn)載請自行聯(lián)系原作者

總結(jié)

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

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