既然說是用select踩到的坑,那麼就先直接貼一段使用select的**上來瞅一下:
bool socketaction(int fd, const char* buf, size_t len, uint64_t milli_expire)
if(fd_isset(fd, &rd_set)) ;
ret = read(fd, rd_buf, sizeof(rd_buf));
if(ret < 0) return false;
printf("%s\n", rd_buf);
} else if(fd_isset(fd, &wt_set))
return true;
}上面的**著實很簡單,只是針對某乙個socket fd進行讀寫操作,從邏輯上來說應該是沒有任何問題的。然而這個在實際的使用過程中確實會出現問題,我們程式中封裝了socket的操作,如上**的方式使用了select。在出現大量的socket連線時,會出現宕機現象,並且宕機生成的core檔案的堆疊也莫名其妙的被破壞了。遇到這個情況時,我直接被震驚了,因為仔細檢查了**確實沒有發現任何問題,為什麼每次都是連線數達到快1500的時候就出現宕機呢?
剛開始以為是其他的邏輯出了問題,於是仔細檢查了其他的邏輯確實也沒有發現存在任何問題,並且從堆疊被破壞的情況上分析,也懷疑是某個使用**現了陣列越界訪問引發的。但在整個邏輯中使用的都是stl裡的容器,沒有申請陣列,出現讀寫越界著實無法理解。
此後只能懷疑底層封裝的問題,在一步步嘗試注釋**的過程中,發現select使用的地方出現了問題。那麼問題究竟在**呢?
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
在select的使用manual中,select的定義和nfds的說明如上所示,也只是說明了select的nfds是最大的檔案描述符+1,在實際的使用過程中也確實有這麼使用,好像也確實沒有問題。然而,不止這麼簡單,select的檔案描述符的最大值實際是有乙個潛在規則的,那就是select的最大檔案描述符最大是1024,該值在centor os系統中,定義在檔案/usr/include/sys/select.**件中,如下:
78 /* maximum number of file descriptors in `fd_set'. */
79 #define fd_setsize __fd_setsize
而__fd_setsize則定義在:/usr/include/bits/typesizes.h 中,如下:
62 /* number of descriptors that can fit in an `fd_set'. */
63 #define __fd_setsize 1024
這下可以理解了吧,在socket大於1024時,檔案描述符自然就大於1024,則在使用select時訪問的大小就實際超越了系統中對於select的支援,因此出現讀寫越界,造成程式的宕機。
這次所踩的坑非常隱晦,但也說明了乙個問題,在使用系統介面時需要慎重。雖然可能使用方法並沒有錯誤,但也要在使用的時候多注意其「潛規則」。
多路復用 select
在之前寫過一篇五種i o模型,感興趣的可以去看一下,今天主要講其中的一種,那就是i o多路復用。因為i o多路復用可以使乙個程序同時處理多個連線。這對提高程式的效能至關重要。對於io復用的概念與理解在上文說的挺清楚了。本文主要說實現io復用的系統呼叫。在linux下,實現io復用的系統呼叫主要有三個...
select多路復用
在某些場景下我們需要同時從多個通道接收資料。通道在接收資料時,如果沒有資料可以接收發生阻塞。你也許會寫出 使用遍歷的方式來實現 for 這種方式雖然可以實現從多個channel接收值的需求,但是執行效能會差很多。為了應對這種場景,go內建了select關鍵字,可以同時響應多個通道的操作。select...
select 函式 多路復用
select 的機制中提供一fd set的資料結構,實際上是一long型別的陣列,每乙個陣列元素都能與一開啟的檔案控制代碼 不管是socket控制代碼,還是其他 檔案或命名管道或裝置控制代碼 建立聯絡,建立聯絡的工作由程式設計師完成,當呼叫select 時,由核心根據io狀態修改fd set的內容,...