【Unix网络编程】FD_SET详解

本文详细解析了fd_set结构体和FD_SET宏在Linux套接字编程中的作用。fd_set用于存储文件描述符集,其内部由long int数组构成,FD_SET宏则用于设置指定文件描述符在集合中的状态。通过示例代码展示了FD_SET如何在内存中设置位,以及其在不同字节序机器上的效果。同时,文章还探讨了位操作的与操作在不同字节序下的等价性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

fd_set结构体

fd_set的定义

 typedef struct
  {
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
    # define __FDS_BITS(set) ((set)->__fds_bits)
  } fd_set;

可简化为

typedef struct{
       long int __fds_bits[1024/(8*sizeof(int long))];
   };

即其中只有一个字段为__fds_bits,它的类型是long int的数组,数组元素个数为1024/(8本机long的字节数),
而(8
本机long的字节数)是本机long占用的bit数,这样就保证了
__fds_bits的字节数必为1024(select可以监听的最大描述符数),而且用long存储

FD_SET宏

FD_SET宏

 #define __FD_SET(d, set) \
  ((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))

其展开为

((void)((set)->__fds_bits)[((d)/8 * (int) sizeof (long int))] |= ((long int) (1UL << ((d) % (8 * (int) sizeof (long int))))))

其中的宏都可以在sys/select.h中找到
这个宏操作如下:

  1. 获取到传入fd_set指针的__fds_bits的第(d/当前主机long的字节数)个元素,也就是第d个bit所在的那个long元素
  2. 计算出将1UL(即longint的1)左移(d%当前主机long的字节数)
  3. 最后将第一步的数字与第二步的数字进行或操作,得到结果

比如在64位机器上FG_SET(rset, 73)
假设原来rset的第二个long元素是
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

  1. 73/64 = 第二个元素
  2. 73/64 = 1UL左移9位,得到
    00000000 00000000 00000000 00000000 00000000 00000000 00000010 00000000
  3. 进行或操作,得到
    00000000 00000000 00000000 00000000 00000000 00000000 00000010 00000000
    这样就可以把第73位置的bit置为1(注意不是从低内存到高内存直接数的73)

对结果进行打印

运行程序

#include "stdio.h"
#include "sys/select.h"
int main(){
    fd_set rset;
    FD_ZERO(&rset);
    // 打印下此时的rset
    int fortimes = 1024/(int)(8*sizeof(long int));
    printf("now FD_ZERO the fd_set:\n");
    for (int i=0;i<fortimes;i++){
        printf("rset [%d] %lu\n", i, rset.__fds_bits[i]);
    }
    // 使用FD_SET
    FD_SET(0, &rset);
    FD_SET(73, &rset);
    FD_SET(1023, &rset);
    // 打印下此时的rset
    printf("now r_set after fd_set:\n");
    for (int i=0;i<fortimes;i++){
        printf("rset [%d] %lu\n", i, rset.__fds_bits[i]);
    }
}

得到结果
请添加图片描述

结果分析

1.在内存中,一个字节中bit的保存是按我们可读的方式进行保存的
如 1UL在小端机器保存为
00000001 00000000 00000000 00000000
我们可以通过这个程序证实

#include <stdio.h>
union ic{
    int a;
    char b[4];
};
int main(){
    union ic ic_obj;
    ic_obj.a = 1;
    for (int i=0;i<4;i++){
        printf("%d\n", ic_obj.b[i]);
    }
    // 对int左移
    ic_obj.b[0]<<=1;
    for (int i=0;i<4;i++){
        printf("%d\n", ic_obj.b[i]);
    }
}

请添加图片描述
2.对于与操作,无论大端序还是小端序,最后或出来的结果是一样的,因为一个机器的字节读取顺序是不变的.
比如int类型的1或上int类型的2
对于大端序机器
00000000 00000000 00000000 00000001 或
00000000 00000000 00000000 00000010 得
00000000 00000000 00000000 00000011 机器转为int是3
对于小端序机器
00000001 00000000 00000000 00000000 或
00000010 00000000 00000000 00000000 得
00000011 00000000 00000000 00000000 机器转int也是3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值