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錯誤。如下圖:
(注意:udp套接口有發送緩沖區的大小,并且可以通過so_sndbuf套接口選項修改。不過它僅僅是寫到套接口的udp資料報的大小,因為
udp是不可靠的,它不必儲存應用程序的資料拷貝,是以無需一個真正的發送緩沖區。)上面的代碼已經設定了足夠大的發送緩沖區大小。
在sendto發送>=128k大小的消息時,傳回enobufs錯誤。
我懷疑是否是sendto()的原因,我改用sendmsg(),未果還是傳回這個錯誤。
有人說是:“發送消息太頻繁,間隔太短”。其實項目中發送消息根本就不頻繁,背着死馬當活馬醫,未果還是傳回這個錯誤。
嘗試修改/proc/sys/net/core下面的各種相關選項,如
未果,還是傳回這個錯誤。(其它路徑下的相關選項也試了,不行)
?我無從下手了,不知道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的錯誤。核心版本為:
/proc/slabinfo的資訊如下,跟上面的有些差異:
kmalloc的最大限制是8192k,故我們運作上述程式沒有問題!
原來都是核心惹得禍阿,害我困惑那麼久!!!baidu和google都沒有找到原因,是以分享此文,以警惕後者。