溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么處理Accept出現(xiàn)Emfile的問題

發(fā)布時(shí)間:2021-10-22 15:16:21 來源:億速云 閱讀:327 作者:小新 欄目:系統(tǒng)運(yùn)維

這篇文章主要介紹了怎么處理Accept出現(xiàn)Emfile的問題,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

通常情況下,服務(wù)端調(diào)用 accept 函數(shù)會(huì)返回一個(gè)新的文件描述符,用于和客戶端之間的數(shù)據(jù)傳輸

服務(wù)器的開發(fā)中,有時(shí)會(huì)遇到這種情況:當(dāng)調(diào)用 accept 函數(shù)接受客戶端連接,函數(shù)返回失敗,對(duì)應(yīng)的錯(cuò)誤碼是 EMFILE,  它表示當(dāng)前進(jìn)程打開的文件描述符已達(dá)上限,此時(shí),服務(wù)器不能再接受客戶端連接

當(dāng)遇到上述問題,怎么合理的處理呢,下面就來分析一下

建立連接的流程

先簡單回顧下客戶端和服務(wù)器建立連接的流程,具體的如下圖所示:

怎么處理Accept出現(xiàn)Emfile的問題

1. 客戶端發(fā)起 SYN 請(qǐng)求

2. 服務(wù)器收到客戶端的 SYN 請(qǐng)求后,內(nèi)核把連接放入半連接隊(duì)列,同時(shí)給客戶端返回一個(gè) SYN + ACK

3. 客戶端向服務(wù)器返回一個(gè)確認(rèn)的 ACK, 服務(wù)器收到本次 ACK  之后,三次握手完成,同時(shí),內(nèi)核把連接從半連接隊(duì)列中移除,創(chuàng)建新完全連接,加入到全連接隊(duì)列中

4. 應(yīng)用層調(diào)用 accept 函數(shù)從全連接隊(duì)列中取出連接

上面的第 1、第 2、第 3 步是 TCP 的三次握手,它是由內(nèi)核中TCP協(xié)議完成的, 第 4 步是應(yīng)用層調(diào)用 accept 接口

在 epoll 中的問題

epoll 是 Linux中IO多路復(fù)用模型,在服務(wù)器的開發(fā)中有廣泛的應(yīng)用,下面就以 epoll 為例來詳細(xì)說明

服務(wù)器端創(chuàng)建偵聽文件描述符 listenfd 之后, 向 epoll 注冊讀事件

當(dāng) epoll 檢測到 listenfd 上有讀事件發(fā)生,會(huì)立即通知應(yīng)用層,應(yīng)用層調(diào)用 accept  接受新連接,而此時(shí)進(jìn)程打開的文件描述符數(shù)量已經(jīng)達(dá)到上限了,所以每次 accept 都是失敗的

這里會(huì)出現(xiàn)以下幾個(gè)問題

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 由于 每次 accept 都失敗了,相當(dāng)于 listenfd 上的可讀事件沒有處理,epoll 會(huì)不停的觸發(fā) listenfd  上的可讀事件,應(yīng)用層也就會(huì)不停的調(diào)用 accept,然后又出現(xiàn) accept 調(diào)用失敗,如此這般不停的執(zhí)行無效的循環(huán),白白浪費(fèi)了CPU的資源

  3. 上面提到服務(wù)器在不停的執(zhí)行無效的循環(huán), 將會(huì)引發(fā)另一個(gè)問題,如果此時(shí)有新客戶端連接到來,建立連接的過程會(huì)很慢

前面說的 epoll 默認(rèn)是使用了水平觸發(fā)模式,如果使用垂直觸發(fā)模式會(huì)出現(xiàn)什么問題呢?

垂直觸發(fā)模式下,listenfd 從無讀事件狀態(tài)到有讀事件狀態(tài)時(shí),才會(huì)通知到應(yīng)用層,在應(yīng)用層處理完 listenfd 上所有的讀事件之前,epoll  不會(huì)再通知應(yīng)用層

也就是說,應(yīng)用層收到 listenfd 上讀事件通知之后,需要把 listenfd 上所有的讀事件全部處理完,下次listenfd  上再有讀事件時(shí),才會(huì)通知應(yīng)用層

回到 accept 的問題上,在垂直觸發(fā)模式下,當(dāng) epoll 通知應(yīng)用層 listenfd 上有可讀事件時(shí),應(yīng)用層調(diào)用 accept,  由于此時(shí)進(jìn)程打開的文件描述符數(shù)量已經(jīng)達(dá)到上限了,所以 accept 調(diào)用失敗

也即 listenfd 上的可讀事件還沒有處理,在應(yīng)用層處理完 listenfd 上可讀事件之前,epoll 不會(huì)再通知應(yīng)用層 listenfd  上有可讀事件

如果在應(yīng)用層處理完 listenfd 上可讀事件之前,有新的客戶端連接到來,這個(gè)時(shí)候 epoll 是不會(huì)通知應(yīng)用層 listenfd  上有可讀事件,這會(huì)導(dǎo)致一個(gè)嚴(yán)重的問題:accept 只要出現(xiàn)了 EMFILE的錯(cuò)誤碼,就再也無法接受客戶端的連接了

所以,當(dāng)出現(xiàn) EMFILE 時(shí),不管使用 epoll 的水平觸發(fā)模式還是垂直觸發(fā)模式都會(huì)存在問題

如何解決

EMFILE 表示進(jìn)程打開的文件描述符數(shù)量達(dá)到上限了,可以把這個(gè)值調(diào)大些,但這治標(biāo)不治本

本來系統(tǒng)設(shè)置文件描述符數(shù)量上限是為了限制進(jìn)程對(duì)系統(tǒng)資源的過度占用,況且,這個(gè)值調(diào)整到多大合適呢,總不能無限大吧,所以調(diào)整上限值的方式不是最合適的方式

accept 成功時(shí)會(huì)返回一個(gè)新的文件描述符,如果此時(shí)進(jìn)程打開的文件描述符數(shù)量已經(jīng)達(dá)到上限了,就會(huì)返回失敗

假如此時(shí)能關(guān)閉一個(gè)空閑的文件描述符,讓出一個(gè)名額,再調(diào)用 accept 就會(huì)創(chuàng)建成功,這種方式具體的處理步驟如下:

1、事先準(zhǔn)備一個(gè)空閑的文件描述符 idlefd,相當(dāng)于先占一個(gè)"坑"位

2、調(diào)用 close 關(guān)閉 idlefd,關(guān)閉之后,進(jìn)程就會(huì)獲得一個(gè)文件描述符名額

3、再次調(diào)用 accept 函數(shù), 此時(shí)就會(huì)返回新的文件描述符 clientfd, 立刻調(diào)用 close 函數(shù),關(guān)閉 clientfd

4、重新創(chuàng)建空閑文件描述符 idlefd,重新占領(lǐng) "坑" 位,再出現(xiàn)這種情況的時(shí)候又可以使用

由于測試代碼比較長,這里就不貼了,感興趣可以通過文末的方式獲取,下面是處理 EMFILE 的偽代碼:

int ret = accept( listenfd, (struct sockaddr*)&addr, sizeof(addr) );  if (-1 == ret) {   if ( errno == EMFILE )   {      //關(guān)閉空閑文件描述符,釋放 "坑"位      close(idlefd);            //接受 clientfd      clientfd = accept( listenfd, nullptr, nullptr);      //關(guān)閉 clientfd,防止一直觸發(fā) listenfd 上的可讀事件      close(clientfd);            //重新占領(lǐng) "坑"位      idlefd = ::open("/dev/null", O_RDONLY | O_CLOEXEC);   } }

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“怎么處理Accept出現(xiàn)Emfile的問題”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI