linux中fd_set的內部實現
阿新 • • 發佈:2019-01-23
一、在網路程式設計中,經常用到selec系統呼叫來判斷套接字上是否存在資料可讀,或者能否向一個套接字寫入資料。其原型為:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中,fd_set是一個socket集合,常用如下巨集來對fd_set進行操作:
1 2 3 4 |
FD_CLR(
s, *set) //從set中刪除控制代碼s;
FD_ISSET(
s, *set) //檢查控制代碼s是否存在與set中; FD_SET(
s, *set ) //把控制代碼s新增到set中;
FD_ZERO(
*set ) //把set佇列初始為空.
|
需要說明一點,在核心中,socket對應struct socket結構,但在返回給使用者空間之前,核心做了一個關聯:呼叫get_unused_fd_flags從當前程序中獲取一個可用的檔案描述符fd ,將struct socket結構關聯到該fd,並返回fd給使用者空間。所以在使用者空間中,socket為檔案描述符。另外,程序可以開啟的檔案數是有限制的,為1024,故socket的取值小於1024。
二、fd_set是如何實現的呢?
試想由你實現這樣一個集合,可以往裡新增任意0~1024之間的數(FD_SET操作),也可以將加入到集合中的數移除——移除一個(FD_CLR操作)或全部(FD_ZERO),你會如何實現?
一種比較好的思路是使用點陣圖bitmap,往集合了新增n時只需將第n個bit位置1,移除n時只需將第n個位元置為0,移除所有資料時,只需將所有bit置為0,可以通過memset操作來實現。fd_set的實現就是採用點陣圖bitmap(關於點陣圖可以參考《程式設計珠璣》第一章)。
其定義如下:
1 2 3 4 5 6 7 8 9 |
#define
__NFDBITS (8 * sizeof(unsigned long)) //每個ulong型可以表示多少個bit,
#define
__FD_SETSIZE 1024 //socket最大取值為1024 #define
__FDSET_LONGS (__FD_SETSIZE/__NFDBITS) //bitmap一共有1024個bit,共需要多少個ulong
typedef struct {
unsigned long fds_bits
[__FDSET_LONGS]; //用ulong陣列來表示bitmap
}
__kernel_fd_set;
typedef __kernel_fd_set
fd_set;
|
對應的操作如下:
1 2 3 4 5 6 7 |
//每個ulong為32位,可以表示32個bit。
//fd
>> 5 即 fd / 32,找到對應的ulong下標i;fd & 31 即fd % 32,找到在ulong[i]內部的位置
#define
__FD_SET(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] |= (1<<((fd) & 31))) //設定對應的bit
#define
__FD_CLR(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] &= ~(1<<((fd) & 31))) //清除對應的bit
#define
__FD_ISSET(fd, fdsetp) ((((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] & (1<<((fd) & 31))) != 0) //判斷對應的bit是否為1
#define
__FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof (*(fd_set *)(fdsetp)))) //memset bitmap
|