poll机制源码深入剖析

本文详细剖析了Linux内核中的poll机制,从poll调用方法、实现原理到源码实现,深入讲解了如何通过pollfd结构体、do_pollfd、__pollwait等函数将进程添加到等待队列并唤醒。通过对tcp_poll的分析,展示了数据就绪时如何唤醒等待进程的过程。

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

poll机制源码深入剖析

poll调用方法

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout)

各参数含义:pollfd为我们关心的fd数组,nfds是数组大小,timeout是等待的时间,如果是-1表示一直等待,如果是0表示立即返回。

返回值

  1. 大于0:表示数组fds中有socket描述符的状态发生变化,或可以读取、或可以写入、或出错。并且返回的值表示这些状态有变化的文件描述符的总数量;此时可以对fds数组进行遍历,以寻找那些revents不空的文件描述符,然后判断这个里面有哪些事件以读取数据。
  2. 等于0:表示没有socket描述符有状态变化,并且调用超时
  3. 小于0:此时表示有错误发生,此时全局变量errno保存错误码

struct pollfd结构如下:【在源码文件poll.h文件中】

struct pollfd {
   
	int fd;  //文件描述符
	short events; //关心事件
	short revents; //活跃事件
};

poll实现原理

poll是采用了一个单向链表来存储我们需要关心的pollfd结构,其中采用__put_user和copy_from_uer完成pollfd结构体数组的复制。从用户复制了数组以后将其构成链表,然后对链表中的每个fd都调用其对应的驱动程序中file_operation的poll方法,该poll方法会调用poll_wait将进程或者线程放到该文件描述符的等待队列上(一个文件描述符有一个等待队列)。此时没有真正睡眠,一直等到每个监听的文件描述符都添加完了以后,才会进入睡眠,也就是说每个fd的等待队列上都会有一个当前的进程,任何一个fd有数据,都会唤醒当前进程。如果有数据到来,就会唤醒进程。将事件复制到用户区。

poll源码实现

应用程序调用poll函数

当app应用程序调用poll函数,会陷入系统调用,从而进入sys_poll

//ufds:待检测的fd数组集合,nfds:待检测的fd数组大小,timeout超时时间
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
		int, timeout_msecs)
{
   
	struct timespec end_time, *to = NULL;
	int ret;
    //有一些计算时间的代码,得到超时的时间节点放到to里面
    
	ret = do_sys_poll(ufds, nfds, to);
    
    //有一些错误处理,暂时先忽略了
	if (ret == -EINTR) {
   
	}
	return ret;
}

真正工作的是 do_sys_poll(ufds, nfds, to);

重要的数据结构:

该结构是保存pollfds的链表,这个链表是按块分配的,其中next就是将各个链表块连接起来的。

struct poll_list {
   
	struct poll_list *next;
	int len;
	struct pollfd entries[0];
};
struct poll_wqueues {
   
	poll_table pt;
	struct poll_table_page * table;
	int error;
	int inline_index;
	struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
};

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);

typedef struct poll_table_struct {
   
	poll_queue_proc qproc;
} poll_table;

do_sys_poll复制用户区数据构建监听结构链表–>调用do_poll–>数据返回

//ufds:待检测的fd数组集合,nfds:待检测的fd数组大小,endtime超时的时间节点
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
		struct timespec *end_time)
{
   
	struct poll_wqueues table;
 	int err = -EFAULT, fdcount, len, size;
	long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
    //为了加快处理速度和提高系统性能,这里优先使用已经定好的一个栈空间,其大小为POLL_STACK_ALLOC,在我系统上,其值为256,大小为256个字节的栈空间转换为struct poll_list结构,以存储需要被检测的文件描述符
	struct poll_list *const head = (struct poll_list *)stack_pps;
    //walk存储了需要检测的文件描述符
 	struct poll_list *walk = head;
 	unsigned long todo = nfds;
    
	if (nfds > rlimit(RLIMIT_NOFILE))
		return -EINVAL;
//计算能存储多少个pollfd结构
	len = min_t(unsigned int, nfds, N_STACK_PPS);
	for (;;) {
   
		walk->next = NULL;
		walk->len = len;
		if (!len)
			break;

 //把用户空间的fds复制到walk链表
		
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值