mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-09-22 20:53:34 +08:00
进程调度-CFS调度器-进程唤醒抢占...
This commit is contained in:
@@ -313,3 +313,5 @@ struct cfs_rq {
|
||||
|
||||
|
||||
http://www.360doc.com/content/15/1006/18/18252487_503643269.shtml
|
||||
|
||||
http://linuxperf.com/?p=42
|
||||
|
||||
@@ -35,21 +35,22 @@ CFS负责处理普通非实时进程, 这类进程是我们linux中最普遍的
|
||||
|
||||
周期调度的工作形式上sched_class调度器类的task_tick函数完成, CFS则对应task_tick_fair函数, 但实际上工作交给entity_tick完成.
|
||||
|
||||
|
||||
##1.4 唤醒抢占
|
||||
-------
|
||||
|
||||
|
||||
当在try_to_wake_up和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当前进程可以抢占当前运行的进程. 请注意该过程不涉及核心调度器.
|
||||
当在try_to_wake_up/wake_up_process和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当前进程可以抢占当前运行的进程. 请注意该过程不涉及核心调度器.
|
||||
|
||||
|
||||
每个调度器类都因应该实现一个check_preempt_curr函数, 在全局check_preempt_curr中会调用进程其所属调度器类check_preempt_curr进行抢占检查, 对于完全公平调度器CFS处理的进程, 则对应由check_preempt_wakeup函数执行该策略.
|
||||
|
||||
|
||||
新唤醒的进程不必一定由完全公平调度器处理, 如果新进程是一个实时进程, 则会立即请求调度, 因为实时进程优先极高, 实时进程总会抢占CFS进程.
|
||||
|
||||
|
||||
|
||||
#2 Linux进程的睡眠和唤醒
|
||||
-------
|
||||
#2 Linux进程的睡眠
|
||||
|
||||
在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为TASK_RUNNING. 一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个进程对CPU的控制权, 并且从运行队列中选择一个合适的进程投入运行.
|
||||
|
||||
@@ -77,20 +78,99 @@ func1();
|
||||
/* Rest of the code ... */
|
||||
```
|
||||
|
||||
在第一个语句中, 程序存储了一份进程结构指针sleeping_task, current 是一个宏,它指向正在执行
|
||||
的进程结构。set_current_state()将该进程的状态从执行状态TASK_RUNNING 变成睡眠状态
|
||||
TASK_INTERRUPTIBLE。 如果schedule()是被一个状态为TASK_RUNNING 的进程调度,那么schedule()将调度另外一个进程占用CPU;如果schedule()是被一个状态为TASK_INTERRUPTIBLE 或TASK_UNINTERRUPTIBLE 的进程调度,那么还有一个附加的步骤将被执行:当前执行的进程在另外一个进程被调度之前会被从运行队列中移出,这将导致正在运行的那个进程进入睡眠,因为 它已经不在运行队列中了。
|
||||
我们可以使用下面的这个函数将刚才那个进入睡眠的进程唤醒。
|
||||
wake_up_process(sleeping_task);
|
||||
在调用了wake_up_process()以后,这个睡眠进程的状态会被设置为TASK_RUNNING,而且调度器
|
||||
会把它加入到运行队列中去。当然,这个进程只有在下次被调度器调度到的时候才能真正地投入运行。
|
||||
在第一个语句中, 程序存储了一份进程结构指针sleeping_task, current 是一个宏,它指向正在执行的进程结构。set_current_state()将该进程的状态从执行状态TASK_RUNNING变成睡眠状态TASK_INTERRUPTIBLE.
|
||||
|
||||
* 如果schedule是被一个状态为TASK_RUNNING的进程调度,那么schedule将调度另外一个进程占用CPU;
|
||||
|
||||
* 如果schedule是被一个状态为TASK_INTERRUPTIBLE 或TASK_UNINTERRUPTIBLE 的进程调度,那么还有一个附加的步骤将被执行:当前执行的进程在另外一个进程被调度之前会被从运行队列中移出,这将导致正在运行的那个进程进入睡眠,因为它已经不在运行队列中了.
|
||||
|
||||
|
||||
#3 linux进程的唤醒
|
||||
-------
|
||||
|
||||
当在try_to_wake_up/wake_up_process和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当前进程可以抢占当前运行的进程. 请注意该过程不涉及核心调度器.
|
||||
|
||||
|
||||
## wake_up_process
|
||||
|
||||
|
||||
我们可以使用wake_up_process将刚才那个进入睡眠的进程唤醒, 该函数定义在[kernel/sched/core.c, line 2043](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L2043).
|
||||
|
||||
|
||||
```c
|
||||
int wake_up_process(struct task_struct *p)
|
||||
{
|
||||
return try_to_wake_up(p, TASK_NORMAL, 0);
|
||||
}
|
||||
```
|
||||
|
||||
在调用了wake_up_process以后, 这个睡眠进程的状态会被设置为TASK_RUNNING,而且调度器会把它加入到运行队列中去. 当然, 这个进程只有在下次被调度器调度到的时候才能真正地投入运行.
|
||||
|
||||
|
||||
## try_to_wake_up
|
||||
-------
|
||||
|
||||
try_to_wake_up函数通过把进程状态设置为TASK_RUNNING, 并把该进程插入本地CPU运行队列rq来达到唤醒睡眠和停止的进程的目的.
|
||||
|
||||
例如: 调用该函数唤醒等待队列中的进程, 或恢复执行等待信号的进程.
|
||||
|
||||
|
||||
```c
|
||||
static int
|
||||
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
|
||||
```
|
||||
|
||||
|
||||
|
||||
该函数接受的参数有: 被唤醒进程的描述符指针(p), 可以被唤醒的进程状态掩码(state), 一个标志wake_flags,用来禁止被唤醒的进程抢占本地CPU上正在运行的进程.
|
||||
|
||||
try_to_wake_up函数定义在[kernel/sched/core.c, line 1906](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L1906)
|
||||
|
||||
|
||||
## wake_up_new_task
|
||||
-------
|
||||
|
||||
```c
|
||||
void wake_up_new_task(struct task_struct *p)
|
||||
```
|
||||
|
||||
该函数定义在[kernel/sched/core.c, line 2421
|
||||
](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L2421
|
||||
)
|
||||
|
||||
|
||||
之前进入睡眠状态的可以通过try_to_wake_up和wake_up_process完成唤醒, 而我们fork新创建的进程在完成自己的创建工作后, 可以通过wake_up_new_task完成唤醒工作, 参见[Linux下进程的创建过程分析(_do_fork/do_fork详解)--Linux进程的管理与调度(八)](http://blog.csdn.net/gatieme/article/details/51569932)
|
||||
|
||||
使用fork创建进程的时候, 内核会调用_do_fork(早期内核对应do_fork)函数完成内核的创建, 其中在进程的信息创建完毕后, 就可以使用wake_up_new_task将进程唤醒并添加到就绪队列中等待调度. 代码参见[kernel/fork.c, line 1755](http://lxr.free-electrons.com/source/kernel/fork.c?v=4.6#L1755)
|
||||
|
||||
|
||||
## check_preempt_curr
|
||||
-------
|
||||
|
||||
当在try_to_wake_up和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当前进程可以抢占当前运行的进程.
|
||||
|
||||
函数定义在[kernel/sched/core.c, line 905](http://lxr.free-electrons.com/source/kernel/sched/core.c?v=4.6#L905)
|
||||
|
||||
```c
|
||||
check_preempt_curr(rq, p, WF_FORK);
|
||||
#ifdef CONFIG_SMP
|
||||
if (p->sched_class->task_woken) {
|
||||
/*
|
||||
* Nothing relies on rq->lock after this, so its fine to
|
||||
* drop it.
|
||||
*/
|
||||
lockdep_unpin_lock(&rq->lock);
|
||||
p->sched_class->task_woken(rq, p);
|
||||
lockdep_pin_lock(&rq->lock);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
# 无效唤醒
|
||||
-------
|
||||
|
||||
|
||||
几乎在所有的情况下,进程都会在检查了某些条件之后,发现条件不满足才进入睡眠。可是有的时候
|
||||
进程却会在 判定条件为真后开始睡眠,如果这样的话进程就会无限期地休眠下去,这就是所谓的无效唤醒问题。在操作系统中,当多个进程都企图对共享数据进行某种处理,而 最后的结果又取决于进程运行的顺序时,就会发生竞争条件,这是操作系统中一个典型的问题,无效唤醒恰恰就是由于竞争条件导致的。
|
||||
几乎在所有的情况下, 进程都会在检查了某些条件之后, 发现条件不满足才进入睡眠. 可是有的时候进程却会在判定条件为真后开始睡眠, 如果这样的话进程就会无限期地休眠下去, 这就是所谓的无效唤醒问题。在操作系统中,当多个进程都企图对共享数据进行某种处理,而 最后的结果又取决于进程运行的顺序时,就会发生竞争条件,这是操作系统中一个典型的问题,无效唤醒恰恰就是由于竞争条件导致的。
|
||||
设想有两个进程A 和B,A 进程正在处理一个链表,它需要检查这个链表是否为空,如果不空就对链
|
||||
表里面的数据进行一些操作,同时B进程也在往这个链表添加节点。当这个链表是空的时候,由于无数据可操作,这时A进程就进入睡眠,当B进程向链表里面添加了节点之后它就唤醒A 进程,其代码如下:
|
||||
A进程:
|
||||
|
||||
@@ -169,6 +169,7 @@ dequeue_entity():
|
||||
se->vruntime -= cfs_rq->min_vruntime;
|
||||
|
||||
task_fork_fair():
|
||||
|
||||
se->vruntime -= cfs_rq->min_vruntime;
|
||||
|
||||
switched_from_fair():
|
||||
@@ -185,3 +186,16 @@ switched_from_fair():
|
||||
|
||||
|
||||
加上min_vruntime的情形
|
||||
```c
|
||||
enqueue_entity:
|
||||
// http://lxr.free-electrons.com/source/kernel/sched/fair.c?v=4.6#L3196
|
||||
|
||||
if (!(flags & ENQUEUE_WAKEUP) || (flags & ENQUEUE_WAKING))
|
||||
se->vruntime += cfs_rq->min_vruntime;
|
||||
|
||||
attach_task_cfs_rq:
|
||||
// http://lxr.free-electrons.com/source/kernel/sched/fair.c?v=4.6#L8267
|
||||
|
||||
if (!vruntime_normalized(p))
|
||||
se->vruntime += cfs_rq->min_vruntime;
|
||||
```
|
||||
|
||||
@@ -31,3 +31,5 @@ http://blog.csdn.net/b02042236/article/details/6076473
|
||||
http://www.tuicool.com/articles/MjyANr
|
||||
http://iamzhongyong.iteye.com/blog/1895728
|
||||
http://blog.csdn.net/xiaofei0859/article/details/8113211
|
||||
|
||||
http://blog.csdn.net/u201017971/article/details/50511511
|
||||
|
||||
Reference in New Issue
Block a user