LINUX非阻塞访问机制POLL SELECT EPOLL原理分析

24 篇文章 0 订阅
5 篇文章 0 订阅


LINUX非阻塞访问机制POLL SELECT EPOLL原理分析

相关源码版本:

LINUX内核源码版本:linux-3.0.86

UBOOT版本:uboot-2010.12.

Android系统源码版本:Android-5.0.2

 

Linux系统提供几种多种实现非阻塞访问机制(read write操作时不会阻塞,但对于POLL SELECT EPOLL的操作在驱动层代码也是不阻塞的,而是在系统调用sys_poll等下面有休眠功能):POLL SELECT EPOLL等机制;应用层和内核层的调用关系,既实现原理如下图:

A:Poll(API)--->__ppoll()[#define __NR_ppoll (__NR_SYSCALL_BASE+336)

]->---syscall(kernel )--->sys_ppoll--->do_sys_poll--->do_poll--->do_pollfd--->Poll(file_operations.poll)

 

B:Select(API)--->__pselect6[#define __NR_pselect6 (__NR_SYSCALL_BASE+335)

]->----syscall(kernel )--->sys_pselect6--->do_pselect--->do_select--->Poll((file_operations.poll)

 

C:Epoll([分三个函数:epoll_create epoll_ctlepoll_wait(api)->

->---syscall(kernel )--->Poll((file_operations.poll)[这种方式没有去分析  具体细节不清楚,这儿列出的调用方式也不一定对]

以上几种在本质上原理是一样的,本文着重分析B:Select(API)->----syscall(kernel )--->Poll((file_operations.poll)的实现原理。我们从应用层到内核层一步步分析处理过程来理解POLL机制,平常所说的POLL机制是内核驱动接口fops->poll函数是固定的来称它的,因为无论应用层是由SELECT还是POLL调用都到内核层再到驱动接口最后都是fops-poll函数,中间只是针对这两种函数的封装和一些差异化处理。

安桌层及应用层:

int select(int fd_count, fd_set* read_fds, fd_set* write_fds, fd_set* error_fds, timeval* tv)

下面分析他的每一个参数,理解了参数才能应用自如。

fd_count:要监控的所有文件描述符中最大那一个文件描述符加1

read_fds:要监控的可读的文件描述集。FD_SET宏来设置可读文件描述集。

write_fds:要监控的可写的文件描述集。FD_SET宏来设置可写文件描述集。

error_fds:要监控的有文件异常的描述集。FD_SET来设置它。

Tv:如果为NULL则一直休眠直到有第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为00毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 selecttimeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述

 

FD_ISSET:宏用于判断select函数执行之后,是否有可读可以写或异常文件。应用层根据这个宏来判断得到状态,继而继续处理相关逻辑。

 

下面网友分析的的fd_set原理,觉得写的很易懂,就直接COPY了感谢此大神。

 

 

内核层。

Sys_pselect6->do_pselect->core_sys_select->do_select->poll_initwait->f_op->poll进入驱动注册的poll函数 。每个poll函数里面都会调用poll_wait函数。它的作用就是用poll_initwait里面初始化的一个__pollwait函数来把当前进程挂载进poll_wait传进来的等待队列头中。流程是:poll_wait->pt->qproc=__pollwait----->

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,

poll_table *p)---->add_wait_queue(wait_address, &entry->wait);当前进程加入待待队列头中--->这样驱动函数中poll执行完之后返回内核层根据TV值来决定是否休眠。

然后驱动中根据状态来唤醒等待队列头中的等待队列。这样我们前面休眠的等待队列就可以根据驱动唤醒函数唤醒它了。状态满足了就唤醒继而继续执行,可以把状态返回给应用层。这样就实现了一个select函数监控了多个文件,也就是IO多路复用的体现。真正休眠是在do_select函数的

for (;;) {

mask = (*f_op->poll)(file, wait);

if (retval || timed_out || signal_pending(current))

break;

if (table.error) {

retval = table.error;

break;

 

}if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,

   to, slack))

timed_out = 1;

//执行完这个函数进入休眠,当有状态满足时唤醒。然后继续执行上面的for函数,再次进入poll函数,由于唤醒发生则里面一定有状态满足了,因此mask = (*f_op->poll)(file, wait);应该有满足条件的。上面BREAK处就会退出for循环了,然后就继续执行for循环后的代码然后把结果返回给应层,既监控层,这样就完成了一次监控,实现了的我们目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值