Linux源码解析-poll机制
1.poll函数
关于poll函数具体是⼲什么的,以及什么情况下使⽤等参考我的其他博客
2.poll机制分析
常见系统调⽤⼀般对应内核中sys_函数名,⽐如我们想看poll机制,具体怎么查看源码呢?
1. 下载Soure Insight软件
2. 下载linux源码
3. 创建项⽬,导⼊源代码
4. Ctrl+Shift+F快捷键打开搜索窗⼝
5. 打钩ProjectWide
6.
7. 搜索sys_poll
8.
9. 点击Select.c即可进⼊相应源码部分
notes:
很多其他博客都说poll调⽤的是内核函数sys_poll,结果在现在较新linux版本源码select.c中却不到,较新linux版本中采⽤宏组合的⽅式来表⽰sys_poll,在select.c中,
我们应该查看的是SYSCALL_DEFINE3
/*
下列函数主要做了三件事
1. 调⽤了⼀个时间转换函数,根据传⼊的时间计算出了⼀个另⼀种格式的超时时间
2. 调⽤了do_sys_poll来完成主要⼯作(实现实际的轮询功能)
3. do_sys_poll被信号中断后的处理。如果do_sys_poll返回-EINTR,则意味着poll操作被信号打断,返回
ERESTART_RESTARTBLOCK,由⽤户注册的信号如果设置了SA_RESTART,则可以在处理完⽤户注册的信号处理程序后,重新调⽤。
*/
SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
long, timeout_msecs)
{
struct timespec end_time, *to = NULL;
int ret;
4k电影源代码if (timeout_msecs >= 0) {
to = &end_time;
poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC, //是⼀个时间转换函数,根据传⼊的时间参数计算超时时间
//存放⼊⼀个 struct timespec结构体实例to中
NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));
}
ret = do_sys_poll(ufds, nfds, to); //调⽤do_sys_poll完成主要⼯作(实现轮询功能)
if (ret == -EINTR) { //do_sys_poll被信号中断的处理
struct restart_block *restart_block;
restart_block = ¤t_thread_info()->restart_block;
restart_block->fn = do_restart_poll;
restart_block->poll.ufds = ufds;
restart_block->poll.nfds = nfds;
if (timeout_msecs >= 0) {
restart_block->poll.tv_sec = end_time.tv_sec;
restart_block->poll.tv_nsec = end_time.tv_nsec;
restart_block->poll.has_timeout = 1;
} else
restart_block->poll.has_timeout = 0;
ret = -ERESTART_RESTARTBLOCK;
}
return ret;
}
linux/poll.h
struct poll_wqueues {
poll_table pt;
struct poll_table_page *table;
struct task_struct *polling_task;
int triggered;
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; //其中就只有⼀个函数指针成员
} poll_table; //其中就只有⼀个函数指针成员
void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait); //设置poll_table结构中的qproc函数指针为__pollwait函数,
// 就是pwq->pt->qproc=__pollwait。这个函数是⼀个回调函数,基本上这种机制的实现,就是依靠回调函数了,⽤于存储回调函数的指针
pwq->polling_task = current; //调⽤poll_initwait时,其中的polling_task成员被赋值为当前进程的task_struct,也即current
pwq->triggered = 0;
pwq->error = 0;
pwq->table = NULL;
pwq->inline_index = 0;
}
/*
do_sys_poll中⾸先把⽤户空间的struct pollfd拷贝到内核空间的struct poll_list类型的链表中(具体是块连接的形式),这链表的头定义在栈空间,
⽽其他成员则通过kmalloc在内核空间动态分配。
创建⼀个struct poll_wqueues类型的挑选队列,并由poll_initwait初始化,接着调⽤do_poll进⼊循环遍历poll_list的操作
*/
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
struct timespec *end_time)
{
struct poll_wqueues table; //创建⼀个struct poll_wqueues类型的挑选队列
int err = -EFAULT, fdcount, len, size;
/* Allocate small arguments on the stack to save memory and be
faster - use long to make sure the buffer is aligned properly
on 64 bit archs to avoid unaligned access */
long stack_pps[POLL_STACK_ALLOC/sizeof(long)]; //为了加快处理速度和提⾼系统性能,这⾥优先使⽤已经定好的⼀个栈空间,
//其⼤⼩为POLL_STACK_ALLOC,栈空间转换为struct poll_list结构,以存储需要被检测的⽂件描述符
struct poll_list *const head = (struct poll_list *)stack_pps; //struct poll_list类型的指针指向这个栈空间,便于之后块连接
struct poll_list *walk = head;
unsigned long todo = nfds; //总共需要处理的⽂件描述符总数
if (nfds > rlimit(RLIMIT_NOFILE))
return -EINVAL;
len = min_t(unsigned int, nfds, N_STACK_PPS); //到nfds和N_STACK_PPS的较⼩者,
//N_STACK_PPS就是计算前⾯默认的固定栈⼤⼩能够存储多少个struct pollfd的
for (;;) {
walk->next = NULL;
walk->len = len;
if (!len)
break;
if (copy_from_user(walk->entries, ufds + nfds-todo, //重点,将⽤户空间的struct pollfd中的len个数据拷贝到内核空间walk->entries中
sizeof(struct pollfd) * walk->len))
goto out_fds;
todo -= walk->len;
if (!todo)
break;
/*POLLFD_PER_PAGE表⽰⼀页的内存能够存储多少个struct pollfd,可以计算⼀下,⼀页是4K,⽽struct pollfd的内存占⽤8个字节,
就是⼀页的内存可以将近存储512个描述符。如果在分配⼀页的内存之后,还不够nfds来⽤,没关系,循环不会退出的,
会再分配⼀个页,并且所有分配的块都被struct poll_list链接起来,上⾯可以看到,这个结构有⼀个next域,就是专门做这个的。
*/在这之后,就会形成⼀个以stack_pps存储空间为头,然后⼀页⼀页分配的内存为接点的链表,这个链表上就存储了poll调⽤时传⼊的所有的⽂件描述符。
len = min(todo, POLLFD_PER_PAGE);
size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;
walk = walk->next = kmalloc(size, GFP_KERNEL);
if (!walk) {
err = -ENOMEM;
goto out_fds;
}
}
poll_initwait(&table); //初始化挑选队列table,其中的polling_task成员被赋值为当前进程的task_struct,即current,回调函数指针设置为__pollwait
fdcount = do_poll(nfds, head, &table, end_time); //循环遍历poll_list链表,检测每个节点中的存储fd的数组,
//将链表上的所有struct pollfd中的revents的状态写⼊到⽤户空间
poll_freewait(&table);//释放
for (walk = head; walk; walk = walk->next) {
struct pollfd *fds = walk->entries;
int j;
for (j = 0; j < walk->len; j++, ufds++)
if (__put_user(fds[j].revents, &ufds->revents))
goto out_fds;
}
err = fdcount;
out_fds:
walk = head->next;
while (walk) {
struct poll_list *pos = walk;
walk = walk->next;
kfree(pos);
}
return err;
}
/*
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论