• 正文
    • 1. poll詳解
    • 2. epoll詳解
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

IO多路轉(zhuǎn)接技術(shù) | poll/epoll詳解

3小時(shí)前
182
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

1. poll詳解

函數(shù)原型

intpoll(struct?pollfd *fd,?nfds_t?nfds,?int?timeout);

函數(shù)參數(shù)

fd:數(shù)組的地址,struct pollfd all[120]; 其中struct pollfd結(jié)構(gòu)體如下

structpollfd {
int? ?fd; ? ? ? ??/* 文件描述符 */
short?events; ? ??/* 等待的事件 */
short?revents; ? ?/* 實(shí)際發(fā)生的事件 */
? ? };

結(jié)構(gòu)體紅各項(xiàng)含義如下:

文件描述符fd:表示要堅(jiān)持測(cè)的fd,通過(guò) open("a.txt", O_wronly | O_append); 獲得。

events:要等待的事件

revents:實(shí)際發(fā)生的事件,它是內(nèi)核給的反饋,在select的時(shí)候,會(huì)有一個(gè)備份來(lái)供內(nèi)核修改并傳出。

nfds:數(shù)組的最大長(zhǎng)度, 數(shù)組中最后一個(gè)使用的元素下標(biāo)+1

內(nèi)核會(huì)輪詢檢測(cè)fd數(shù)組的每個(gè)文件描述符

timeout:

1:永久阻塞

0:調(diào)用完成立即返回

>0:等待的時(shí)長(zhǎng)毫秒

函數(shù)返回值:IO發(fā)生變化的文件描述符的個(gè)數(shù)。

2. epoll詳解

(1)API介紹

intepoll_create(int?size);

函數(shù)功能:生成一個(gè)epoll專用的文件描述符,實(shí)際上就是生成一個(gè)epoll樹(shù)的根結(jié)點(diǎn)。

函數(shù)參數(shù):size,epoll樹(shù)上能掛的最大文件描述符數(shù)量。表示我想在這個(gè)樹(shù)節(jié)點(diǎn)上掛size個(gè)節(jié)點(diǎn),假如實(shí)際上的節(jié)點(diǎn)大于size的話epoll會(huì)自動(dòng)擴(kuò)展,所以這個(gè)大小可以隨便傳,不用太在意。但是這個(gè)擴(kuò)展也是有上限的,如果電腦內(nèi)存是1G,那么擴(kuò)展的上限是10萬(wàn)(2G就是20萬(wàn)。。。通過(guò)加內(nèi)存可以擴(kuò)大上限)。

函數(shù)返回值:函數(shù)返回值是樹(shù)的根節(jié)點(diǎn),在后面用到epft參數(shù)的時(shí)候,都是指這個(gè)返回值,也就是樹(shù)的根節(jié)點(diǎn)。

intepoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event *event);

函數(shù)功能:用于控制某個(gè)epoll文件描述符事件,可以注冊(cè)、修改、刪除。

函數(shù)參數(shù):

epfd:epoll_create()函數(shù)生成的專用文件描述符。

op:

EPOLL_CTL_ADD ? ? ? —— ?注冊(cè)

EPOLL_CTL_MOD ? ? ?—— ?修改

EPOLL_CTL_DEL ? ? ? ?—— ?刪除

fd:關(guān)聯(lián)的文件描述符

event:告訴內(nèi)核要監(jiān)聽(tīng)什么事件

EPOLLIN ? ? —— 讀

EPOLLOUT —— 寫

EPOLLERR ?—— 異常

structepoll_event {/* 該結(jié)構(gòu)體主要存放和fd有關(guān)的信息 */uint32_t? ? ?events; ? ? ? ? ? ? ? ? ? ? ? ? ?epoll_data_t?data;?? ? ? };
typedefunion epoll_data {void? ? ? ? ?*ptr;int? ? ? ? ? fd;uint32_t? ? ?u32;uint64_t? ? ?u64;? ? ? }?epoll_data_t;

epoll_data_t是一個(gè)聯(lián)合體union,四個(gè)成員共用同一塊內(nèi)存,也就是說(shuō)四個(gè)成員我們只能用一個(gè),一般情況下我們用fd,這個(gè)fd實(shí)際上就是epoll_ctl()函數(shù)的第三個(gè)參數(shù)fd。

如果我們想在epoll樹(shù)上掛載更多信息,而不僅僅是fd文件描述符的話,我們可以把更多信息封裝在結(jié)構(gòu)體中,并把該結(jié)構(gòu)體傳給epoll_data_t結(jié)構(gòu)體的ptr指針,這樣就可以在epoll樹(shù)上掛載和fd有關(guān)的更多信息。

structsockInfo? ? ? ? {int? ? ? ? ?fd;structsockaddr_inaddr;? ? ? ? };

比如說(shuō),要獲取發(fā)生變化的fd對(duì)應(yīng)的client的IP和port,就可以利用指針ptr,這樣的話聯(lián)合epoll_data_t中的fd就不能用了,我們把文件描述符傳給sockInfo的fd即可完成fd信息的掛載。

intepoll_wait(int?epfd,struct?epoll_event* events, ?/* 結(jié)構(gòu)體數(shù)組 */int?maxevents,int?timeout);

函數(shù)功能:等待IO事件發(fā)生(可以設(shè)置阻塞),epoll_wait()函數(shù)相當(dāng)于前面講的select()或poll()函數(shù),表示委托內(nèi)核去進(jìn)行檢測(cè)。epoll_event通過(guò)返回值和傳出參數(shù)events來(lái)實(shí)現(xiàn)把哪幾個(gè)fd發(fā)生變化告訴server進(jìn)程的目的。首先,每當(dāng)有fd變化,就把這個(gè)fd對(duì)應(yīng)的樹(shù)節(jié)點(diǎn)拷貝到events數(shù)組中,最后,有幾個(gè)fd變化,就返回幾。這樣只要根據(jù)返回值和參數(shù)events就可以遍歷出所有變化的fd以及相關(guān)信息。

函數(shù)參數(shù):

epfd:要檢測(cè)的句柄

events:用于回傳待處理事件的數(shù)組。它是一個(gè)傳出參數(shù),需要提前分配內(nèi)存,哪個(gè)fd發(fā)生變化了,就把哪個(gè)fd的樹(shù)節(jié)點(diǎn)(struct epoll_event)拷貝一份放到這個(gè)數(shù)組中。這樣epoll就能返回是哪個(gè)fd發(fā)生了變化。

maxevents:告訴內(nèi)核events的大小,因?yàn)閮?nèi)核要把發(fā)生變化的fd對(duì)應(yīng)的樹(shù)節(jié)點(diǎn)拷貝到數(shù)組中,所以要知道數(shù)組大小。

timeout:為超時(shí)時(shí)間

1:永久阻塞

0:立即返回

>0

函數(shù)返回值:有多少個(gè)fd發(fā)生了變化就返回幾(變化的fd信息存在events數(shù)組中)。

(2)epoll樹(shù)

(3)epoll模型

#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<string.h>#include<sys/socket.h>#include<arpa/inet.h>#include<ctype.h>#include<sys/epoll.h>
intmain(int?argc, constchar* argv[]){if(argc <?2)? ? {printf("eg: ./a.out portn");exit(1);? ? }structsockaddr_inserv_addr;socklen_t?serv_len =?sizeof(serv_addr);int?port =?atoi(argv[1]);?//字符串轉(zhuǎn)整形值
// 創(chuàng)建套接字int?lfd =?socket(AF_INET, SOCK_STREAM,?0);// 初始化服務(wù)器 sockaddr_in?memset(&serv_addr,?0, serv_len);? ? serv_addr.sin_family = AF_INET;?// 地址族?? ? serv_addr.sin_addr.s_addr =?htonl(INADDR_ANY);?// 監(jiān)聽(tīng)本機(jī)所有的IP? ? serv_addr.sin_port =?htons(port);?// 設(shè)置端口?// 綁定IP和端口? ??bind(lfd, (struct?sockaddr*)&serv_addr, serv_len);
// 設(shè)置同時(shí)監(jiān)聽(tīng)的最大個(gè)數(shù)? ??listen(lfd,?36);printf("Start accept ......n");
structsockaddr_inclient_addr;socklen_t?cli_len =?sizeof(client_addr);
// 創(chuàng)建epoll樹(shù)根節(jié)點(diǎn)int?epfd =?epoll_create(2000);// 初始化epoll樹(shù)structepoll_eventev;? ? ev.events = EPOLLIN;? ? ev.data.fd = lfd;? ??epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);//存放發(fā)生變化的fd對(duì)應(yīng)的樹(shù)節(jié)點(diǎn)structepoll_eventall[2000];while(1)? ? {// 使用epoll通知內(nèi)核fd 文件IO檢測(cè)int?ret =?epoll_wait(epfd, all,?sizeof(all)/sizeof(all[0]),?-1);
// 遍歷all數(shù)組中的前ret個(gè)元素 //ret表示有幾個(gè)變化的fd,變化的fd都存在all數(shù)組中for(int?i=0; i<ret; ++i)? ? ? ? {int?fd = all[i].data.fd;// 判斷是否有新連接if(fd == lfd)? ? ? ? ? ? {// 接受連接請(qǐng)求 // accept不阻塞,因?yàn)橐呀?jīng)有連接int?cfd =?accept(lfd, (struct?sockaddr*)&client_addr, &cli_len);if(cfd ==?-1)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ??perror("accept error");exit(1);? ? ? ? ? ? ? ? }// 將新得到的cfd掛到樹(shù)上structepoll_eventtemp;? ? ? ? ? ? ? ? temp.events = EPOLLIN;?//檢測(cè)cfd對(duì)應(yīng)的讀緩沖區(qū),是否有數(shù)據(jù)傳入? ? ? ? ? ? ? ? temp.data.fd = cfd;? ? ? ? ? ? ? ??epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
// 打印客戶端信息char?ip[64] = {0};printf("New Client IP: %s, Port: %dn",? ? ? ? ? ? ? ??inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip,?sizeof(ip)),? ? ? ? ? ? ? ? ? ? ? ??ntohs(client_addr.sin_port));
? ? ? ? ? ? }else? ? ? ? ? ? {// 處理已經(jīng)連接的客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù)if(!all[i].events & EPOLLIN)?//只處理讀事件? ? ? ? ? ? ? ? {continue;? ? ? ? ? ? ? ? }/*? ? ? ? ? ? ? ? 假如說(shuō)client發(fā)送過(guò)了100個(gè)數(shù)據(jù),也就是serve的read緩沖區(qū)有100個(gè)數(shù)據(jù),? ? ? ? ? ? ? ? 但是調(diào)用recv函數(shù)的時(shí)候只能讀50個(gè)數(shù)據(jù),而本次循環(huán)只調(diào)用了一次recv,? ? ? ? ? ? ? ? 那么只能下次循環(huán)再讀剩余的50個(gè)數(shù)據(jù),所以下次循環(huán)檢測(cè)的時(shí)候,? ? ? ? ? ? ? ? epoll_wait還是會(huì)返回,因?yàn)榫彌_區(qū)還是剩余數(shù)據(jù)。這就是水平觸發(fā)模式。? ? ? ? ? ? ? ? 這樣的話雖然client只發(fā)了1次,但是epoll_wait會(huì)通知兩次server去讀數(shù)據(jù)。? ? ? ? */
// 讀數(shù)據(jù)char?buf[1024] = {0};int?len =?recv(fd, buf,?sizeof(buf),?0);if(len ==?-1)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ??perror("recv error");exit(1);? ? ? ? ? ? ? ? }elseif(len ==?0)? ? ? ? ? ? ? ? {printf("client disconnected ....n");//close(fd);// fd從epoll樹(shù)上刪除? ? ? ? ? ? ? ? ? ? ret =?epoll_ctl(epfd, EPOLL_CTL_DEL, fd,?NULL);// 掛樹(shù)的時(shí)候需要ev,把ev掛在樹(shù)上刪除寫NULL就行了if(ret ==?-1)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ??perror("epoll_ctl del error");exit(1);? ? ? ? ? ? ? }? ? ? ? ? ? ? ?close(fd);? ? ? ? ? ? ? ? }else? ? ? ? ? ? ? ? {printf(" recv buf: %sn", buf);? ? ? ? ? ? ? ? ? ??write(fd, buf, len);? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? }
? ??close(lfd);return0;}

epoll維護(hù)的紅黑樹(shù)是存在一個(gè)共享內(nèi)存中,內(nèi)核和用戶都可以通過(guò)操作這個(gè)共享內(nèi)存來(lái)操作樹(shù),不需要內(nèi)核態(tài)和用戶態(tài)的切換,也不需要兩種狀態(tài)之間的數(shù)據(jù)拷貝,所以效率更高。

(4)epoll的三種工作模式

水平觸發(fā)模式 ? - (根據(jù)讀來(lái)解釋)

只要fd對(duì)應(yīng)的緩沖區(qū)有數(shù)據(jù),epoll_wait就會(huì)返回

返回的次數(shù)與發(fā)送數(shù)據(jù)的次數(shù)沒(méi)有關(guān)系

epoll默認(rèn)的工作模式

邊沿觸發(fā)模式 - ET

fd - 默認(rèn)阻塞屬性

客戶端給server發(fā)數(shù)據(jù):

發(fā)一次數(shù)據(jù)server 的 epoll_wait就返回一次

不在乎數(shù)據(jù)是否讀完

如果讀不完,如何把數(shù)據(jù)全部讀出來(lái)?

while(recv());

數(shù)據(jù)讀完之后recv會(huì)阻塞

解決阻塞問(wèn)題 —— 設(shè)置非阻塞fd

對(duì)于epoll_wait()來(lái)說(shuō),epoll_wait 調(diào)用次數(shù)越多, 系統(tǒng)的開(kāi)銷越大。

水平觸發(fā)模式會(huì)多次返回,只要server的read緩沖區(qū)有數(shù)據(jù),epoll_wait就返回,也就會(huì)通知server去讀數(shù)據(jù),那么在循環(huán)檢測(cè)的時(shí)候,只要server的read緩沖區(qū)有數(shù)據(jù),epoll_wait就會(huì)多次調(diào)用,多次返回,并通知server去讀數(shù)據(jù);假如說(shuō)client發(fā)送過(guò)了100個(gè)數(shù)據(jù),也就是serve的read緩沖區(qū)有100個(gè)數(shù)據(jù),但是調(diào)用recv函數(shù)的時(shí)候只能讀50個(gè)數(shù)據(jù),而本次循環(huán)只調(diào)用了一次recv,那么只能下次循環(huán)再讀剩余的50個(gè)數(shù)據(jù),所以下次循環(huán)檢測(cè)的時(shí)候,epoll_wait還是會(huì)返回,因?yàn)榫彌_區(qū)還是剩余數(shù)據(jù)。這就是水平觸發(fā)模式。這樣的話雖然client只發(fā)了1次,但是epoll_wait會(huì)通知兩次server去讀數(shù)據(jù)。

—— (printf函數(shù)是標(biāo)準(zhǔn)C庫(kù)函數(shù),C庫(kù)函數(shù)都有一個(gè)默認(rèn)緩沖區(qū),printf的大小是8K。printf函數(shù)是行緩沖,使用printf函數(shù)的時(shí)候,如果不加 n 會(huì)默認(rèn)等到寫滿的時(shí)候才打印內(nèi)容,加 n 會(huì)強(qiáng)制把緩沖區(qū)的內(nèi)容打印出來(lái)。另外 表示結(jié)束,不加 就會(huì)一直輸出直到遇到 ,用write(STDOUT_FILENO)替代printf函數(shù)就可以解決這些問(wèn)題。)

邊沿觸發(fā)模式,client發(fā)一次數(shù)據(jù)epoll_wait只返回一次,也就只讀一次,這樣的話server的read緩沖區(qū)可能會(huì)有很多數(shù)據(jù)堆積,server讀數(shù)據(jù)的時(shí)候可能讀到的是上一次剩余的數(shù)據(jù),并且只有client發(fā)的時(shí)候,epoll_wait才會(huì)通知server去讀數(shù)據(jù),邊沿觸發(fā)模式盡可能減少了epoll_wait的調(diào)用次數(shù),缺點(diǎn)是數(shù)據(jù)有可能讀不完導(dǎo)致堆積;

邊沿非阻塞觸發(fā)

效率最高

如何設(shè)置非阻塞

open()

設(shè)置flags

必須 O_WDRW | O_NONBLOCK

終端文件: /dev/tty

 

fcntl

int flag = fcntl(fd, F_GETFL);

flag |= O_NONBLOCK;

fcntl(fd, F_SETFL, flag);

如何將緩沖區(qū)的全部數(shù)據(jù)都讀出?

while(recv() >?0)? ? ?{? ? ??printf();? ? ?}

當(dāng)緩沖區(qū)數(shù)據(jù)讀完之后, 返回值是否為0?

阻塞狀態(tài)

數(shù)據(jù)讀完之后,recv阻塞

非阻塞狀態(tài)

強(qiáng)行讀了一個(gè)沒(méi)有數(shù)據(jù)的緩沖區(qū)(fd),數(shù)據(jù)已經(jīng)被讀完了,因?yàn)槭欠亲枞?,所以在while循環(huán)中recv還要繼續(xù)讀,導(dǎo)致返回-1

判斷 errno == EAGAIN

示例

#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<string.h>#include<sys/socket.h>#include<arpa/inet.h>#include<ctype.h>#include<sys/epoll.h>#include<fcntl.h>#include<errno.h>
intmain(int?argc, constchar* argv[]){if(argc <?2)? ? {printf("eg: ./a.out portn");exit(1);? ? }structsockaddr_inserv_addr;socklen_t?serv_len =?sizeof(serv_addr);int?port =?atoi(argv[1]);
// 創(chuàng)建套接字int?lfd =?socket(AF_INET, SOCK_STREAM,?0);// 初始化服務(wù)器 sockaddr_in?memset(&serv_addr,?0, serv_len);? ? serv_addr.sin_family = AF_INET; ? ? ? ? ? ? ? ? ??// 地址族?? ? serv_addr.sin_addr.s_addr =?htonl(INADDR_ANY); ? ?// 監(jiān)聽(tīng)本機(jī)所有的IP? ? serv_addr.sin_port =?htons(port); ? ? ? ? ? ?// 設(shè)置端口?// 綁定IP和端口? ??bind(lfd, (struct?sockaddr*)&serv_addr, serv_len);
// 設(shè)置同時(shí)監(jiān)聽(tīng)的最大個(gè)數(shù)? ??listen(lfd,?36);printf("Start accept ......n");
structsockaddr_inclient_addr;socklen_t?cli_len =?sizeof(client_addr);
// 創(chuàng)建epoll樹(shù)根節(jié)點(diǎn)int?epfd =?epoll_create(2000);// 初始化epoll樹(shù)structepoll_eventev;
// 設(shè)置邊沿觸發(fā)? ? ev.events = EPOLLIN;?//監(jiān)聽(tīng)的文件描述符沒(méi)必要邊沿觸發(fā),主要是通信的cfd? ? ev.data.fd = lfd;? ??epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
structepoll_eventall[2000];while(1)? ? {// 使用epoll通知內(nèi)核fd 文件IO檢測(cè)int?ret =?epoll_wait(epfd, all,?sizeof(all)/sizeof(all[0]),?-1);printf("================== epoll_wait =============n");
// 遍歷all數(shù)組中的前ret個(gè)元素for(int?i=0; i<ret; ++i)? ? ? ? {int?fd = all[i].data.fd;// 判斷是否有新連接if(fd == lfd)? ? ? ? ? ? {// 接受連接請(qǐng)求int?cfd =?accept(lfd, (struct?sockaddr*)&client_addr, &cli_len);if(cfd ==?-1)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ??perror("accept error");exit(1);? ? ? ? ? ? ? ? }// 設(shè)置文件cfd為非阻塞模式int?flag =?fcntl(cfd, F_GETFL);? ? ? ? ? ? ? ? flag |= O_NONBLOCK;? ? ? ? ? ? ? ??fcntl(cfd, F_SETFL, flag);
// 將新得到的cfd掛到樹(shù)上structepoll_eventtemp;// 設(shè)置邊沿觸發(fā)? ? ? ? ? ? ? ? temp.events = EPOLLIN | EPOLLET;? ? ? ? ? ? ? ? temp.data.fd = cfd;? ? ? ? ? ? ? ??epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
// 打印客戶端信息char?ip[64] = {0};printf("New Client IP: %s, Port: %dn",? ? ? ? ? ? ? ??inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip,?sizeof(ip)),? ? ? ? ? ? ? ??ntohs(client_addr.sin_port));
? ? ? ? ? ? }else? ? ? ? ? ? {// 處理已經(jīng)連接的客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù)if(!all[i].events & EPOLLIN)?? ? ? ? ? ? ? ? {continue;? ? ? ? ? ? ? ? }
// 讀數(shù)據(jù)char?buf[5] = {0};int?len;// 循環(huán)讀數(shù)據(jù)while( (len =?recv(fd, buf,?sizeof(buf),?0)) >?0?)? ? ? ? ? ? ? ? {// 數(shù)據(jù)打印到終端//不要用printf,因?yàn)閜rintf如果找不到  n 字符會(huì)出現(xiàn)亂碼,打印不出來(lái)等問(wèn)題? ? ? ? ? ? ? ? ? ??write(STDOUT_FILENO, buf, len);// 發(fā)送給客戶端? ? ? ? ? ? ? ? ? ??send(fd, buf, len,?0);? ? ? ? ? ? ? ? }if(len ==?0)? ? ? ? ? ? ? ? {printf("客戶端斷開(kāi)了連接n");? ? ? ? ? ? ? ? ? ? ret =?epoll_ctl(epfd, EPOLL_CTL_DEL, fd,?NULL);if(ret ==?-1)? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ??perror("epoll_ctl - del error");exit(1);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ??close(fd);? ? ? ? ? ? ? ? }elseif(len ==?-1)? ? ? ? ? ? ? ? {//數(shù)據(jù)已經(jīng)被讀完了,因?yàn)槭欠亲枞?,所以在while循環(huán)中recv還要繼續(xù)讀,導(dǎo)致返回-1if(errno == EAGAIN)? ? ? ? ? ? ? ? ? ? {printf("緩沖區(qū)數(shù)據(jù)已經(jīng)讀完n");? ? ? ? ? ? ? ? ? ? }else? ? ? ? ? ? ? ? ? ? {//這才是真正的recv錯(cuò)誤printf("recv error----n");exit(1);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }#if?0if(len ==?-1)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ??perror("recv error");exit(1);? ? ? ? ? ? ? ? }elseif(len ==?0)? ? ? ? ? ? ? ? {printf("client disconnected ....n");// fd從epoll樹(shù)上刪除? ? ? ? ? ? ? ? ? ? ret =?epoll_ctl(epfd, EPOLL_CTL_DEL, fd,?NULL);if(ret ==?-1)? ? ? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? ? ??perror("epoll_ctl - del error");exit(1);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ??close(fd);
? ? ? ? ? ? ? ? }else? ? ? ? ? ? ? ? {// printf(" recv buf: %sn", buf);? ? ? ? ? ? ? ? ? ??write(STDOUT_FILENO, buf, len);? ? ? ? ? ? ? ? ? ??write(fd, buf, len);? ? ? ? ? ? ? ? }#endif? ? ? ? ? ? }? ? ? ? }? ? }
? ??close(lfd);return0;}

5)文件描述符1024限制

對(duì)于select來(lái)說(shuō),無(wú)法突破文件描述符1024上限,因?yàn)閟elect是通過(guò)數(shù)組實(shí)現(xiàn)的。poll和epoll可以突破1024限制,poll是內(nèi)部鏈表實(shí)現(xiàn),而epoll是紅黑樹(shù)實(shí)現(xiàn)。

查看受計(jì)算機(jī)硬件限制的文件描述符上限可以通過(guò)下面命令

cat?/proc/sys/fs/file-max

同樣,我們也可以通過(guò)修改配置文件來(lái)修改這個(gè)上限,但是,我們?cè)诔绦蛑性O(shè)置的時(shí)候不能超過(guò)硬件限制的上限

vim /etc/security/limits.conf

- soft ?nofile ? ?8000 ? ? ?—— 也可以通過(guò)命令ulimit -n 2000來(lái)修改為2000

- hard ?nofile ? 8000 ? ? ?—— 硬件資源限制

修改后重啟系統(tǒng)即可起效。

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

Linux、C、C++、Python、Matlab,機(jī)器人運(yùn)動(dòng)控制、多機(jī)器人協(xié)作,智能優(yōu)化算法,貝葉斯濾波與卡爾曼濾波估計(jì)、多傳感器信息融合,機(jī)器學(xué)習(xí),人工智能。