`
totoxian
  • 浏览: 1074510 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

可以睡眠的poll

 
阅读更多
<!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:PunctuationKerning /> <w:DrawingGridVerticalSpacing>7.8 磅</w:DrawingGridVerticalSpacing> <w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery> <w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery> <w:ValidateAgainstSchemas /> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:Compatibility> <w:SpaceForUL /> <w:BalanceSingleByteDoubleByteWidth /> <w:DoNotLeaveBackslashAlone /> <w:ULTrailSpace /> <w:DoNotExpandShiftReturn /> <w:AdjustLineHeightInTable /> <w:BreakWrappedTables /> <w:SnapToGridInCell /> <w:WrapTextWithPunct /> <w:UseAsianBreakRules /> <w:DontGrowAutofit /> <w:UseFELayout /> </w:Compatibility> <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> </w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" LatentStyleCount="156"> </w:LatentStyles> </xml><![endif]--><!--[if !mso]> <object classid="clsid:38481807-CA0E-42D2-BF39-B33AF135CC4D" id=ieooui> </object> <mce:style><! st1/:*{behavior:url(#ieooui) } -->

file_operations中有许多的回调函数,正是这些回调函数实现了vfsvfs提供了一个机制,这些回调函数提供了不同的策略,等于说实现了vfs,照理说这些函数不应该有任何限制,但是唯独一个poll回调函数与众不同,它不能睡眠,这是为何呢?

除了poll以外的回调函数都拥有直接的语义,比如read就是读,write就是写,因此系统调用层可以直接将执行路径交给vfs,比如在sys_read函数中,几乎做了简单的判断之后就马上调用了真正文件系统的file_operationsread函数,但是poll函数比较特殊,它并没有简单的语义,其实它就是轮询,可是它不像readwrite那样系统调用层和vfs层那么统一,pollvfs层的意思就是“看看这个文件是否有动作”,但是在系统调用层的意义就是“看看这些文件中哪个有动作”,这就是不同,为了将系统调用层的语义平滑的过度到vfs层,就必须在系统调用和vfs只见插入机制,这个机制实现了poll,当然还包括select。在poll的实现中,靠的是进程的状态来同步睡眠/唤醒动作的,它并不是在将进程加入睡眠队列后马上睡眠,而是不睡眠等到所有poll的文件描述符均加入队列后再睡眠,其实仅仅是左一个调度罢了,总体框架如下:

for (;;)

set_current_state(TASK_INTERRUPTIBLE)

for each fd to poll

ask driver if I/O can happen

add current process to driver wait queue

if one or more fds are ready

break

schedule_timeout_range(...)

注意,这里是在一开始就将进程的状态设置为TASK_INTERRUPTIBLE但是不睡眠,在中间的for循环中陆续将进程加入到睡眠队列,到了最后才切换进程,等于说就是睡眠了,看看这个糟糕的实现,在设置了进程TASK_INTERRUPTIBLE状态后那么大一会才将进程切换,这很是丑陋,丑陋的本质原因就是vfs实现的pollpoll一个文件描述符,但是系统调用的语义是poll一大堆的文件描述符,在没有必要添加适配机制的情况下,只好用进程状态来实现了,2.6.29内核实在是看不下去这个局面了,于是提出了poll和别的file_operations的回调函数一样,也可以睡眠,并且可以用传统的睡眠唤醒函数来唤醒进程:

+static int pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)

+{

+ struct poll_wqueues *pwq = wait->private;

+ DECLARE_WAITQUEUE(dummy_wait, pwq->polling_task);

+

+ set_mb(pwq->triggered, 1);

+

+ /* perform the default wake up operation */

+ return default_wake_function(&dummy_wait, mode, sync, key);

+}

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)

{

- struct poll_table_entry *entry = poll_get_entry(p);

+ struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);

+ struct poll_table_entry *entry = poll_get_entry(pwq);

if (!entry)

return;

get_file(filp);

entry->filp = filp;

entry->wait_address = wait_address;

- init_waitqueue_entry(&entry->wait, current);

+ init_waitqueue_func_entry(&entry->wait, pollwake);

+ entry->wait.private = pwq;

add_wait_queue(wait_address, &entry->wait);

}

+int poll_schedule_timeout(struct poll_wqueues *pwq, int state,

+ ktime_t *expires, unsigned long slack)

+{

+ int rc = -EINTR;

+

+ set_current_state(state);

+ if (!pwq->triggered)

+ rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);

+ __set_current_state(TASK_RUNNING);

+

+ /* clear triggered for the next iteration */

+ pwq->triggered = 0;

+

+ return rc;

+}

int do_select(int n, fd_set_bits *fds, s

for (;;) {

unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;

- set_current_state(TASK_INTERRUPTIBLE);

inp = fds->in; outp = fds->out; exp = fds->ex;

rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;

@@ -411,10 +436,10 @@ int do_select(int n, fd_set_bits *fds, s

to = &expire;

}

- if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))

+ if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,

+ to, slack))

timed_out = 1;

}

- __set_current_state(TASK_RUNNING);

poll_freewait(&table);

我们可以看到在这个可睡眠的poll的补丁中,去掉了刻意为了适配加入的设置进程状态的语句,加入了统一的linux的睡眠/唤醒机制,poll_schedule_timeout是个新加入的函数,实际上它就是poll中的睡眠函数,和wait_event没有本质区别的,这样的话,加入这些函数,poll的实现和别的回调函数变得统一起来的。

实际上,我发现在2.6.29内核中,代码变得更加统一了,内核逻辑变得更加统一了,和前一篇文章我谈到的credtask_struct中分离一样,poll可睡眠的意义也十分的大,比如将来为了加入新的机制要大改代码,起码file_operations中的回调函数都是统一的,这样就可以将之独立成一个模块而不用和别的模块杂糅。

分享到:
评论

相关推荐

    linux内核select/poll,epoll实现与区别

    - 在内核的`do_select`函数中,会调用`poll_initwait`注册`__pollwait`回调,然后进入循环,不断检查描述符状态并可能调用`schedule_xxx`使进程睡眠,等待事件发生后再唤醒。 2. **poll** - `poll`与`select`非常...

    两个linux按键驱动之一 poll(未去抖动)

    应用程序可以在这个等待队列上睡眠,等待按键事件发生。 ### 关于`poll`操作 `poll`是Linux系统提供的一种异步I/O机制,允许应用程序在一个或多个文件描述符上监控多种类型的I/O事件。在按键驱动中,`poll`操作...

    poll()函数详解.docx

    通过`poll()`,程序可以同时等待多个文件描述符就绪,避免了轮询检查或睡眠等待的低效。 在`poll()`函数中,主要涉及到以下几个关键参数: 1. **事件结构体**:`struct pollfd`,包含以下字段: - `fd`:要监控的...

    Help-to-understand-select-poll-epoll.rar_Help!_epoll_kernel sele

    关于Linux内核的休眠等待机制,`select`、`poll`和`epoll`都会在没有就绪事件时让进程进入睡眠状态,直到有事件发生。在这个过程中,内核会将进程的状态改为不可调度,将其挂起并释放CPU资源。当有事件发生时,内核...

    Test-SleepModes_Poll.rar_嵌入式/单片机/硬件编程_C/C++_

    标题提到的"Test-SleepModes_Poll.rar"是一个针对NXP JN5139 zigbee模块的实例程序,主要关注不同类型的睡眠模式和如何在C/C++中实现它们。JN5139是一款专门用于Zigbee应用的超低功耗无线微控制器。在这个压缩包中,...

    面试题查漏补缺(update).pdf

    epoll的通知机制需要很多函数回调,而select和poll需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。但是epoll在"醒着"的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间...

    Linux 进程管理

    输出结果包含了每个进程的详细信息,如进程ID(PID)、状态(S表示睡眠状态)、用户(这里为root)、优先级(这里为-5)、虚拟内存大小(VM)、以及进程名称等。 2. **`nice`命令**:用于设置进程的优先级。如: ...

    epoll深入浅出源码分析

    epoll通过红黑树(RB树)和链表维护文件描述符集合,相比select和poll,它可以同时处理大量文件描述符而不消耗大量内存。红黑树保证了对数时间复杂度的查找效率,使得epoll可以高效地添加、删除和查找文件描述符。 ...

    2018宇视科技笔试试题

    - **read操作中的睡眠**:一般来说,字符设备的`read`操作是不允许睡眠的,因为睡眠可能会导致当前持有锁的进程无法及时释放锁,从而导致死锁问题。 8. **volatile关键字的用法**: - **volatile变量**:在C语言...

    selenium3源码解析Python篇(十九)-wait源码解析

    本章主讲webdriver超时机制 显示等待,超时则抛出TimeoutException ...poll_frequency:通话之间的睡眠间隔,默认0.5s ignored_exceptions:调用期间忽略的异常类的可迭代结构,默认情况下,它仅包含No

    RustChinaConf2020-14.赖智超-《Rust异步和并发浅谈》.pdf

    当Task需要等待时,它可以调用`thread::park()`进入睡眠状态,而`Thread::unpark()`则用于唤醒线程。Waker通常是通过`thread::current()`获取,并作为参数传递给`poll`方法。 在更复杂的同步操作中,如互斥锁...

    UNIX_Programming_FAQ_中文版.pdf

    - 可以使用`nanosleep`函数来实现少于一秒的睡眠,该函数提供了更高的精度。 - `nanosleep`函数允许指定纳秒级别的睡眠时间,从而能够精确控制等待的时间。 **1.4 如何获得更细分时间单位的`alarm`函数版本:** ...

    802.11传统省电模式与802.11e-U-APSD省电模式对比.pdf

    客户端可以立即确定什么时候他们必须保持清醒,其余的时间可以进入节能的睡眠状态。这样,多轮询节能提供了一个减少竞争的轮询机制,同时还向客户端提供了节能的时机。此外,RIFS 只能在多轮询节能模式下使用来获得...

    unix编程常见问题解答.pdf

    - 在Unix中,通常使用`select()`或`poll()`函数来监视多个文件描述符的状态变化,这些函数可以高效地处理多个并发连接。 **2.2.1 我如何使用select()函数?** - `select()`函数允许监视多个文件描述符的读写状态...

    IEEE802.11DCF.zip

    6. **PS-Poll(电源管理轮询)**: 对于节能操作,设备可以进入睡眠模式,只有在收到PS-Poll帧时才醒来接收数据。 MATLAB仿真是研究DCF工作原理和性能的重要工具。通过仿真,可以模拟不同网络条件下的DCF行为,例如...

    Linux环境编程:从应用到内核

    同时,掌握异步I/O和多线程编程也是提高效率的关键,例如使用`select()`, `poll()`, `epoll`等系统调用实现高效的数据传输。 描述中提到的“内核结构”是Linux系统的核心部分,它管理硬件资源、调度进程、处理中断...

    libevent-2.1.11-stable.rar

    而在socket编程中,libevent可以帮助开发者实现非阻塞的socket操作,通过事件驱动的方式,当socket有数据可读或可写时,libevent会通知相应的事件处理器进行处理,这样可以避免无谓的轮询和睡眠,提高程序执行效率。...

    UWB TOF通信协议

    这些消息用于交换距离测量过程中的必要数据,包括距离编号、睡眠校正和飞行时间(ToF)信息。Poll Message由Tag端发起,而Response Message和Final Message则由基站端发出。 在收发时序中,Tag和基站交替发送消息,...

Global site tag (gtag.js) - Google Analytics