天天看點

(unix domain socket)使用udp發送>=128K的消息會報ENOBUFS的錯誤1、Unix domain socket簡介2、問題描述4、我的嘗試5、最終原因及解決辦法(都是核心惹得禍!!)

unix域協定并不是一個實際的協定族,而是在單個主機上執行客戶/伺服器通信的一種方法,所用api于在不同主機上執行客戶/伺服器通信所有的

api(套接字api,如af_inet、af_inet6等類型的api)相同。unix域協定可以視為是程序之間本地通信ipc的一種。

unix域提供兩類套接口:位元組流套接口(類似tcp)和資料報套接口(類似udp)。使用unix域套接口的理由有三:

unix域套接口往往比位于同一主機的tcp套接口快出一倍。

unix域套接口可用于在同一主機上的不同程序之間傳遞描述字。

unix域套接口把客戶的憑證(使用者id和使用者組id)提供給伺服器,進而實作能夠提供額外的安全檢查措施。

unix域中用域辨別客戶和伺服器的協定位址是普通檔案系統中的路徑名(類比:ipv4協定的位址由一個32位位址和一個16位端口号構成,ipv6協定的位址由一個128位位址和16位端口号構成。)。

簡單介紹了unix域套接口之後,進入主題——描述我碰到的問題。由于unix域套接口用于本機間程序通信比網絡套接口效率高,因為它是不經過協定

棧的!在項目中選擇了unix域的資料報套接口。在使用過程中碰到了如下,問題:發送<128k的消息時,客戶、程序可以正常收發消息;發

送>=128k的消息時,發送端(sendto)傳回enobufs的錯誤。

伺服器的代碼如下:

用戶端的代碼如下:

3、可能碰到的另外一個問題

如果你沒有設定足夠大的發送緩沖區大小,你很有可能碰到emsgsize的錯誤!因為應用程式寫了一個大于套機口發送緩沖區大小的資料報,核心報emsgsize錯誤。如下圖:

(unix domain socket)使用udp發送>=128K的消息會報ENOBUFS的錯誤1、Unix domain socket簡介2、問題描述4、我的嘗試5、最終原因及解決辦法(都是核心惹得禍!!)

(注意:udp套接口有發送緩沖區的大小,并且可以通過so_sndbuf套接口選項修改。不過它僅僅是寫到套接口的udp資料報的大小,因為

udp是不可靠的,它不必儲存應用程序的資料拷貝,是以無需一個真正的發送緩沖區。)上面的代碼已經設定了足夠大的發送緩沖區大小。

在sendto發送>=128k大小的消息時,傳回enobufs錯誤。

我懷疑是否是sendto()的原因,我改用sendmsg(),未果還是傳回這個錯誤。

有人說是:“發送消息太頻繁,間隔太短”。其實項目中發送消息根本就不頻繁,背着死馬當活馬醫,未果還是傳回這個錯誤。

嘗試修改/proc/sys/net/core下面的各種相關選項,如

(unix domain socket)使用udp發送>=128K的消息會報ENOBUFS的錯誤1、Unix domain socket簡介2、問題描述4、我的嘗試5、最終原因及解決辦法(都是核心惹得禍!!)

未果,還是傳回這個錯誤。(其它路徑下的相關選項也試了,不行)

?我無從下手了,不知道128k的這個限制在哪?既然“no buffer space available”,我怎樣給他空間?

至此,我實在沒有辦法了,不知道如何解決!但是從錯誤enobufs的說明:

enobufs means there is no sufficient memory available and the

system(kernel)  can not allocate any more. application will usually

retry the operation when it detects this error from a system call since

it indicates there is a transient resource shortage. it is the operating

system that refuses the resource request from the listener. the virtual

memory allocation routine of the os will determine if a swap can

be made to disk of a real memory segment thereby allowing the listener

access to some more real memory.

可以看出一些端倪,這肯定跟記憶體配置設定有關!而且限制在配置設定128k就失敗!利用socket進行程序間的通信,需要經過linux核心:程序1

将資料寫到核心,程序2從核心讀取資料。核心必須申請一個空間來存放資料包!實際上,socket發送資料包時,需要從slab中申請一塊cache存放

資料包。

在2.6.21核心中(這就是我們公司伺服器的核心版本),slab配置設定器最大支援的size為128k(詳情可見/proc/slabinfo)。

在2.6.31核心中,slab配置設定器最大支援的size大小為32m。

是以2.6.21核心上,發送大于128k的資料包時,kmalloc()會失敗,并傳回no buffer的錯誤。建議:對于本地程序通信,可以使用其它的ipc方式,進行資料通信,如shm、pipe等。

找出了原因,可以采用以下方式來解決該問題:

更新核心,或修改核心的這個限制。

改用unix 域udp套接口為unix域tcp套接口(最終我們采用的方式)。

改用其它的ipc方式(這個涉及到太多的修改,故我們放棄使用)。

附/proc/slabinfo 資訊:size-131072即128k的限制!

我在ubuntu 10.10上測試,不會報enobufs的錯誤。核心版本為:

(unix domain socket)使用udp發送>=128K的消息會報ENOBUFS的錯誤1、Unix domain socket簡介2、問題描述4、我的嘗試5、最終原因及解決辦法(都是核心惹得禍!!)

/proc/slabinfo的資訊如下,跟上面的有些差異:

(unix domain socket)使用udp發送>=128K的消息會報ENOBUFS的錯誤1、Unix domain socket簡介2、問題描述4、我的嘗試5、最終原因及解決辦法(都是核心惹得禍!!)

kmalloc的最大限制是8192k,故我們運作上述程式沒有問題!

原來都是核心惹得禍阿,害我困惑那麼久!!!baidu和google都沒有找到原因,是以分享此文,以警惕後者。

繼續閱讀