1. 程式人生 > >linux中fd_set的內部實現

linux中fd_set的內部實現

一、在網路程式設計中,經常用到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