From 6e52c164b58c562e0734c24ac40f7a1b65ef89d0 Mon Sep 17 00:00:00 2001 From: gatieme Date: Fri, 8 Jul 2016 23:31:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E8=B0=83=E5=BA=A6-CFS?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E5=99=A8-=E8=BF=9B=E7=A8=8B=E5=94=A4?= =?UTF-8?q?=E9=86=92=E6=8A=A2=E5=8D=A0...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../05-schedule/04-cfs/01-cfs/README.md | 2 + .../05-schedule/04-cfs/07-wakeup/README.md | 104 ++++++++++++++++-- .../04-cfs/08-task_new_fair/README.md | 14 +++ study/kernel/01-process/05-schedule/README.md | 2 + 4 files changed, 110 insertions(+), 12 deletions(-) diff --git a/study/kernel/01-process/05-schedule/04-cfs/01-cfs/README.md b/study/kernel/01-process/05-schedule/04-cfs/01-cfs/README.md index d640e9b..8ab7abb 100644 --- a/study/kernel/01-process/05-schedule/04-cfs/01-cfs/README.md +++ b/study/kernel/01-process/05-schedule/04-cfs/01-cfs/README.md @@ -313,3 +313,5 @@ struct cfs_rq { http://www.360doc.com/content/15/1006/18/18252487_503643269.shtml + +http://linuxperf.com/?p=42 diff --git a/study/kernel/01-process/05-schedule/04-cfs/07-wakeup/README.md b/study/kernel/01-process/05-schedule/04-cfs/07-wakeup/README.md index 20381c8..8e4cb8e 100644 --- a/study/kernel/01-process/05-schedule/04-cfs/07-wakeup/README.md +++ b/study/kernel/01-process/05-schedule/04-cfs/07-wakeup/README.md @@ -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进程: diff --git a/study/kernel/01-process/05-schedule/04-cfs/08-task_new_fair/README.md b/study/kernel/01-process/05-schedule/04-cfs/08-task_new_fair/README.md index 21bbbf7..579da48 100644 --- a/study/kernel/01-process/05-schedule/04-cfs/08-task_new_fair/README.md +++ b/study/kernel/01-process/05-schedule/04-cfs/08-task_new_fair/README.md @@ -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; +``` diff --git a/study/kernel/01-process/05-schedule/README.md b/study/kernel/01-process/05-schedule/README.md index e0c7ef1..eee761c 100644 --- a/study/kernel/01-process/05-schedule/README.md +++ b/study/kernel/01-process/05-schedule/README.md @@ -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