在使用select时我们必定会使用到fd_set,那么fd_set究竟是什么呢?
一、fd_set的相关介绍
在网络编程中,经常用到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队列初始为空.
试想由你实现这样一个集合,可以往里添加任意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))
#define __FD_SETSIZE 1024
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
typedef __kernel_fd_set fd_set;
对应的操作如下:
#define __FD_SET(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] |= (1<<((fd) & 31)))
#define __FD_CLR(fd, fdsetp) (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] &= ~(1<<((fd) & 31)))
#define __FD_ISSET(fd, fdsetp) ((((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] & (1<<((fd) & 31))) != 0)
#define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof (*(fd_set *)(fdsetp))))
二、fd_set在内核中的使用
在文章select与poll的内核实现简述中我们说到select函数最终会遍历每个文件描述符,调用每个设备的poll函数。但是具体是如何遍历的,在这里我们将会详细说明。
在core_sys_select
函数中,申请了一段连续地址的内存,并将这块内存进行了6平分,分别对应文件描述符的in、out、err,以及返回时所用的res_in、res_out和err。
size = FDS_BYTES(n)
bits = stack_fds
if (size > sizeof(stack_fds) / 6) {
ret = -ENOMEM
bits = kmalloc(6 * size, GFP_KERNEL)
if (!bits)
goto out_nofds
}
fds.in = bits
fds.out = bits + size
fds.ex = bits + 2*size
fds.res_in = bits + 3*size
fds.res_out = bits + 4*size
fds.res_ex = bits + 5*size
if ((ret = get_fd_set(n, inp, fds.in)) ||
(ret = get_fd_set(n, outp, fds.out)) ||
(ret = get_fd_set(n, exp, fds.ex)))
goto out
zero_fd_set(n, fds.res_in)
zero_fd_set(n, fds.res_out)
zero_fd_set(n, fds.res_ex)
ret = do_select(n, &fds, end_time)
在do_select
中对每个描述符进行了遍历。
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
inp = fds->in; outp = fds->out; exp = fds->ex;
rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
for (i = 0; i unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
if (all_bits == 0) {
i += BITS_PER_LONG;
continue;
}
for (j = 0; j 1) {
struct fd f;
if (i >= n)
break;
if (!(bit & all_bits))
continue;
f = fdget(i);
if (f.file) {
const struct file_operations *f_op;
f_op = f.file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op->poll) {
wait_key_set(wait, in, out,
bit, busy_flag);
mask = (*f_op->poll)(f.file, wait);
}
fdput(f);
if ((mask & POLLIN_SET) && (in & bit)) {
res_in |= bit;
retval++;
wait->_qproc = NULL;
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait->_qproc = NULL;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
wait->_qproc = NULL;
}
}
}
if (res_in)
*rinp = res_in;
if (res_out)
*routp = res_out;
if (res_ex)
*rexp = res_ex;
参考文章:https://www.cnblogs.com/scope-beyound/p/3628217.html