摘要

    在学习 Redis 的过程中,发现 Redis 底层是复用了现成的 I/O多路复用模型(evport, epoll, kqueue, select),本篇博客就总结一下 Linux 内核中提供的三种模型。

select

fd_set

    select()函数主要是建立在fd_set类型的基础上的。fd_set 是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:

void FD_CLR(int fd, fd_set *set)    //置 fd_set 中 fd 对应的位为 0
int  FD_ISSET(int fd, fd_set *set)  //判断 fd 在 fd_set 对应的位是否为 1 
void FD_SET(int fd, fd_set *set)    //将 fd_set 中 fd 对应的位置 1
void FD_ZERO(fd_set *set)           //将 fd_set 中的所有位全部置 0

   首先先看一下 fd_set 的结构体:

// from linux-3.10, win 下定义可能不同,一般为 4096(FD_SETSIZE = 64, long long int 类型数组,数组大小为 64, 所以 64 * 64 = 4096)
// posix_types.h

#undef __FD_SETSIZE
#define __FD_SETSIZE    1024

typedef struct {
    unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} __kernel_fd_set;

    可以看出 fd_set 是一个结构体,该结构体包含了一个 unsigned long 类型的一个数组,数组大小为:1024/(8 * sizeof(long)), 在我的系统上(Ubuntu 16.04),sizeof(long) == sizeof(unsigned long) = 8。所以数组的大小为:1024/(8 * 8) = 16; 由于 fd_set 是以位来标识一个 fd 的,所以我们来计算这个数组表示的位数,数组中的每一个元素都是 long 类型,数组元素个数为 16, 所以得:bits = 8 * 8 * 16 = 1024(bit), 故可以标识 1024 个 fd。

    可以通过下面的代码在自己的 Linux 系统上跑一下,参考代码:

#include <sys/time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        printf("fd_set size = %d\n", sizeof(fd_set));
        printf("long type size: %d\n", sizeof(long));
        printf("unsigned long type size: %d\n", sizeof(unsigned long));
        return 0;
}
查看类型占用的字节数

相关文章: