wait_event族函数浅析
2017-06-03
周末闲暇无事,聊聊内核中的wait_event*类函数的具体实现,等待事件必定涉及到某个条件,而这些函数的区别主要是等待后唤醒的方式……直奔主题,上源码
wait_event_interruptible
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = ; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
调用该宏首先会先检查条件,如果条件已经满足,则不用等了呀,返回吧……,否则调用__wait_event_interruptible
#define __wait_event_interruptible(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); \
} while ()
首先声明了一个关联当前进程的wait对象,然后进入一个for空循环,开始就调用prepare_to_wait,该函数代码如下,功能就是把wait对象加入到等待队列并设置当前进程的状态,注意此刻仅仅是设置了结构体的状态,并没有触发调度。在真正触发调度之前,需要再次检查条件是否满足,如果满足了,直接break,否则检查当前进程是否有信号存在,因为该宏设置的等待是可以被信号唤醒的,如果有信号则同样break。否则就调用schedule进行调度吧!其他种类的额wait函数都是同样的结构,区别就在于for循环内内部的不同,所以后续主要列举的是for循环的代码。
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags; wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
/*保证只加入一次*/
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
wait_event_interruptible_timeout
该函数和前面类似,但是增加了等待时间,还是简单看下__wait_event_interruptible_timeout的for循环
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
ret = schedule_timeout(ret); \
if (!ret) \
break; \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
}
在没有信号的状态下,是调用了schedule_timeout函数,该函数在调度之前会对当前进程设定一个定时器,并设定process_timeout回调函数用于时间到了就执行该函数,自然该函数就是完成唤醒进程的功能。关于定时器,本文不做介绍。
wait_event_timeout
该函数和上面区别就是没有了对信号的判断,并且设置进程状态为TASK_UNINTERRUPTIBLE,其__wait_event_timeout中for循环如下
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
ret = schedule_timeout(ret); \
if (!ret) \
break; \
}
wait_event
该函数是最简单的,设置状态TASK_UNINTERRUPTIBLE,进行条件判断,如果不符合就调度。没有其他额外的操作。
内核中的sleep函数分析
static inline void sleep(unsigned sec)
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(sec * HZ);
}
这里首先设置当前进程状态为TASK_INTERRUPTIBLE,然后调用了schedule_timeout,参数就是设置的睡眠时间,单位是秒,乘以时钟频率HZ(每秒钟发生时钟中断的次数)
signed long __sched schedule_timeout(signed long timeout)
{
struct timer_list timer;
unsigned long expire; switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable
* in the caller. Nothing more. We could take
* MAX_SCHEDULE_TIMEOUT from one of the negative value
* but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be
* 0 since no piece of kernel is supposed to do a check
* for a negative retval of schedule_timeout() (since it
* should never happens anyway). You just have the printk()
* that will tell you if something is gone wrong and where.
*/
if (timeout < ) {
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx\n", timeout);
dump_stack();
current->state = TASK_RUNNING;
goto out;
}
}
/*定时器到期时间*/
expire = timeout + jiffies;
/*设置定时器*/
setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
/*调度*/
schedule();
/*回来就删除定时器*/
del_singleshot_timer_sync(&timer); /* Remove the timer from the object tracker */
destroy_timer_on_stack(&timer); timeout = expire - jiffies;
/*timeout大于0意味这进程被提前唤醒*/
out:
return timeout < ? : timeout;
}
这里如果设置的睡眠时间足够长,就不设置定时器,直接调度。否则如果设置的时间小于0,就打印下栈信息,然后设置状态会TASK_RUNNING就返回了。正常情况下就设置一个定时器,根据传入的时间设置到期时间,然后再出发调度,这样在这段时间过后如果定时器被有被出发就会由时钟中断触发执行,看到定时器的回调函数是process_timeout,其中调用了wake_up_process唤醒了睡眠的进程。创建定时器之后就调用__mod_timer激活定时器。默认情况下是在当前CPU,但是如果当前CPU是空闲的即IDLE状态,这种情况如果配置了动态时钟会关闭周期时钟,这样会把定时器迁移到一个非空闲的CPU上。因为在动态时钟下,并不是每个jiffies都会发生中断,这样有可能造成延迟。在选定CPU后如果不是当前CPU那么需要对定时器做一些修改,然后调用internal_add_timer把定时器加入到管理向量数组中。从这里看没有考虑高分辨率定时器呀!!!
说明:在schedule函数中,如果当前进程非运行态,在允许抢占的情况下,极有可能会把task从就绪队列移除,而在唤醒进程的时候再重新加入到就绪队列。
以马内利!
参考:linux3.10.1源码
wait_event族函数浅析的更多相关文章
- Linux学习笔记(8)-exec族函数
昨天学习了Linux下的进程创建,创建一个进程的方法极为简单,只需要调用fork函数就可以创建出一个进程,但是-- 介绍fork()函数的时候提到,在创建进程后,子进程与父进程有相同的代码空间,执行的 ...
- [转载]C++虚函数浅析
原文:http://glgjing.github.io/blog/2015/01/03/c-plus-plus-xu-han-shu-qian-xi/ 感谢:单刀土豆 C++虚函数浅析 JAN 3RD ...
- R-- Apply族函数
APPLY族函数: apply(x,a,f) 对矩阵或数据框的某一维度作用函数fx为矩阵或数据框:a为1代表行,a为2代表列:f为作用函数. lapply(x,f) 对x的每一个元组作用函数f,结果以 ...
- exec族函数详解及循环创建子进程
前言:之前也知道exec族函数,但没有完全掌握,昨天又重新学习了一遍,基本完全掌握了,还有一些父子进程和循环创建子进程的问题,还要介绍一下环境变量,今天分享一下. 一.环境变量 先介绍下环境的概念和特 ...
- 【Linux 进程】exec族函数详解
exec族的组成: 在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是: #include <unistd.h> extern char **e ...
- R中的apply族函数和多线程计算
一.apply族函数 1.apply 应用于矩阵和数组 # apply # 1代表行,2代表列 # create a matrix of 10 rows x 2 columns m <- ma ...
- 从0开始自己用C语言写个shell__01_整体的框架以及fork和exec族函数的理解
最近才忙完了一个操作系统的作业,让我们用C语言实现一个Shell.总的来说,其实就是让我们 对系统调用有比较深的了解. 首先 介绍一下我的Shell 所实现的功能.1.运行可执行程序 即输入某个 标志 ...
- Linux-exec族函数
1.为什么需要exec族函数 (1).fork子进程是为了执行新程序(fork创建子进程后,子进程和父进程同时被OS调度执行,因此子程序可以单独的执行一个程序,这样程序宏观上将会和父进程程序同时进行) ...
- Linux exec族函数解析
背景 在提到 vfork 函数时,我们提到了这个概念.为了更好地学习与运用,我们对exec族函数进行展开. exec函数族 介绍 有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中 ...
随机推荐
- C#里面的枚举与位或运算符 一个枚举变量存入多个值
以前我们如果定义一个枚举类型 ,然后定义一个枚举变量,那么这个枚举变量只能为类型中的一个值,现在我们想要一个变量能够代表多个值: 今天看<Pro Net 2.0 Windows Forms An ...
- C语言 · c++_ch02_01(打印元音字母的ASCII码)
算法提高 c++_ch02_01 时间限制:1.0s 内存限制:512.0MB 编写一个程序,利用强制类型转换打印元音字母大小写10种形式的ASCII码. 输出的顺序为:大写的字母A ...
- android 百度地图开发实例(转载)
因为在我的寝室google基站定位返回的数据总是为空,所以换成百度地图,发现百度地图开发起来非常方便,提供了许多有用的工具,地图的加载速度也比google地图快许多. 为了加强记忆,写一点androi ...
- Vim快捷键操作命令大全
Vim是一个超牛的编辑器,命令功能十分强大 .而且这些命令大都可以进行组合 ,比如,9yy命令表示复制9行内容,9表示要复制的行数,同样100dd表示删除100行,当数字和命令合作的时候,就比 ...
- js学习笔记22----BOM属性和方法
BOM基本概念 : Browser Object Model 浏览器对象模型. BOM属性: window.navigator.userAgent : 浏览器信息 判断是否是某个浏览器,可以用 ind ...
- 【转】【iOS测试系列】常用测试小插件的使用
背景介绍 由于iOS系统的限制,在非越狱的自动化测试中无法实现一些常用的功能,比如不同应用之间来回切换.模拟全局的点击事件等等.但是在越狱的环境下,这些限制就不存在了,我们可以利用各种小插件来实现我们 ...
- serv-u ftp服务器搭建
以前在学校的时候,学校的整个宿舍楼都是在一个局域网中,经常有人用个人电脑搭个网站或者FTP啊什么的,主要是进行一些影视资源的传播活动.不乏有些资源充沛的有志青年利用业余时间翻译某岛国影视资源,利用局域 ...
- 快速开发 jQuery 插件的 10 大技巧(转)
1. 把你的代码全部放在闭包里面 这是我用的最多的一条.但是有时候在闭包外面的方法会不能调用.不过你的插件的代码只为你自己的插件服务,所以不存在这个问题,你可以把所有的代码都放在闭包里面.而方法可能应 ...
- Request获取具有相同 name 属性表单元素值
html代码如: <input name="txtName" id="txtFirstName" type="text" /> ...
- 【python】NLTK好文
From:http://m.blog.csdn.net/blog/huyoo/12188573 nltk是一个python工具包, 用来处理和自然语言处理相关的东西. 包括分词(tokenize), ...