热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

select的文件描述符集合:fd_set

在使用select时我们必定会使用到fd_set,那么fd_set究竟是什么呢?一、fd_set的相关介绍在网络编程中,经常用到selec系统调用来判断套接字上是否存在数据可

在使用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)) //每个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]内部的位置
*/


/*设置对应的bit*/
#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是否为1*/
#define __FD_ISSET(fd, fdsetp) ((((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] & (1<<((fd) & 31))) != 0)

/*memset bitmap*/
#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。

    /*n是最大文件描述符,来计算我们需要多大的内存*/
size = FDS_BYTES(n);

/*stack_fds是内核中所准备的一块连续的内存,以long为单位而不是以字节为单位*/
bits = stack_fds;

/*如果需要的内存大于内核准备的栈,则重新申请内存。除以6是因为有6个描述符集合*/
if (size > sizeof(stack_fds) / 6) {
/* Not enough space in on-stack array; must use kmalloc */
ret = -ENOMEM;
bits = kmalloc(6 * size, GFP_KERNEL);
if (!bits)
goto out_nofds;
}

/*申请了连续的内存后,平分到6个映射*/
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);

/*调用do_select函数对描述符集进行遍历*/
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;

/*开始进行遍历,这里遍历的步长是long大小
而且是in、out和err一起进行遍历
*/

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、out、err地址上的值,是long类型的*/
in = *inp++; out = *outp++; ex = *exp++;

/*进行相或,只要不为0就说明这块地址的描述符需要检查*/
all_bits = in | out | ex;

/*如果为0了,说明long长度的位图都是空的那就继续循环*/
if (all_bits == 0) {
i += BITS_PER_LONG;
continue;
}

/*在long长度的地址中进行细致检查,每个位进行查看*/
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);

/*通过poll函数获得设备状态掩码*/
mask = (*f_op->poll)(f.file, wait);
}
fdput(f);

/*是否是in集合的,并且状态发生变化*/
if ((mask & POLLIN_SET) && (in & bit)) {
res_in |= bit;
retval++;
wait->_qproc = NULL;
}

/*是否是out集合的,并且状态发生变化*/
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait->_qproc = NULL;
}

/*是否是err集合的,并且状态发生变化*/
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


推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
author-avatar
李子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有