linux中fd_set的內部實現

原文鏈接:http://www.cnblogs.com/scope-beyound/p/3628217.html

一、在網絡編程中,經常用到selec系統調用來判斷套接字上是否存在數據可讀,或者能否向一個套接字寫入數據。其原型爲:

  int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

其中,fd_set是一個socket集合,常用如下宏來對fd_set進行操作:

FD_CLR( s, *set)    //從set中刪除句柄s;

FD_ISSET( s, *set)  //檢查句柄s是否存在與set中;

FD_SET( s, *set )   //把句柄s添加到set中;

FD_ZERO( *set )     //把set隊列初始爲空.

二、fd_set是如何實現的呢?需要說明一點,在內核中,socket對應struct socket結構,但在返回給用戶空間之前,內核做了一個關聯:調用get_unused_fd_flags從當前進程中獲取一個可用的文件描述符fd ,將struct socket結構關聯到該fd,並返回fd給用戶空間。所以在用戶空間中,socket爲文件描述符。另外,進程可以打開的文件數是有限制的,爲1024,故socket的取值小於1024。

試想由你實現這樣一個集合,可以往裏添加任意0~1024之間的數(FD_SET操作),也可以將加入到集合中的數移除——移除一個(FD_CLR操作)或全部(FD_ZERO),你會如何實現?

一種比較好的思路是使用位圖bitmap,往集合了添加n時只需將第n個bit位置1,移除n時只需將第n個比特置爲0,移除所有數據時,只需將所有bit置爲0,可以通過memset操作來實現。fd_set的實現就是採用位圖bitmap(關於位圖可以參考《編程珠璣》第一章)。

其定義如下:

#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;

對應的操作如下:

//每個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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章