精华内容
下载资源
问答
  • 电脑老是被自动唤醒,使用powercfg -lastwake命令发现是该任务计划在触发,故改之。 正常操作该任务计划会提示无权限,从网上找到这个教程 使用了NSudo这个软件,使用该软件启动计划任务,然后删除重启任务。 N...

    \Microsoft\Windows\UpdateOrchestrator

    电脑老是被自动唤醒,使用powercfg -lastwake命令发现是该任务计划在触发,故改之。

    正常操作该任务计划会提示无权限,从网上找到这个教程

    使用了NSudo这个软件,使用该软件启动计划任务,然后删除重启任务。
    NSudo基于raymai97的超级命令提示符
    请打开【CPU架构】目录,然后双击NSudo.exe。根据提示操作即可。例如,如果你想在你的英特尔或AMD设备上使用64位NSudo,你首先需要打开的是x64目录,然后双击NSudoLG.exe

    \NSudo_8.0_All_Components\NSudo Launcher\x64\NSudoLG.exe"

    下载地址:https://github.com/M2Team/NSudo/releases

    "C:\Windows\System32\taskschd.msc"

     

    参考文章:https://blog.csdn.net/weixin_30668887/article/details/101877866

    展开全文
  • schedule()函数详解

    万次阅读 2019-02-20 16:58:20
    asmlinkage __visible void __sched schedule(void) { struct task_struct *tsk = current; sched_submit_work(tsk); do { __schedule(); } while (need_resched()); } schedul...
     asmlinkage __visible void __sched schedule(void)
     {
         struct task_struct *tsk = current;
     
         sched_submit_work(tsk);
         do {
             __schedule();
         } while (need_resched());
     }
    
    

    schedule主要完成的工作内容如下:
    (1)sched_submit_work用于检测当前进程是否有plugged io需要处理,由于当前进程执行schedule后,有可能会进入休眠,所以在休眠之前需要把plugged io处理掉放置死锁。
    (2)执行__schedule()这个函数是调度的核心处理函数,当前CPU会选择到下一个合适的进程去执行了。
    (3)need_resched()执行到这里时说明当前进程已经被调度器再次执行了,此时要判断是否需要再次执行调度。

     static void __sched __schedule(void)
     {
         struct task_struct *prev, *next;
         unsigned long *switch_count;
         struct rq *rq;
         int cpu;
     
         preempt_disable();
         cpu = smp_processor_id();
         rq = cpu_rq(cpu);
         rcu_note_context_switch();
         prev = rq->curr;
     
         schedule_debug(prev);
     
         if (sched_feat(HRTICK))
             hrtick_clear(rq);
     
         /*
          * Make sure that signal_pending_state()->signal_pending() below
          * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
          * done by the caller to avoid the race with signal_wake_up().
          */
         smp_mb__before_spinlock();
         raw_spin_lock_irq(&rq->lock);
     
         rq->clock_skip_update <<= 1; /* promote REQ to ACT */
     
         switch_count = &prev->nivcsw;
         if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
             if (unlikely(signal_pending_state(prev->state, prev))) {
                 prev->state = TASK_RUNNING;
             } else {
                 deactivate_task(rq, prev, DEQUEUE_SLEEP);
                 prev->on_rq = 0;
     
                 /*
                  * If a worker went to sleep, notify and ask workqueue
                  * whether it wants to wake up a task to maintain
                  * concurrency.
                  */
                 if (prev->flags & PF_WQ_WORKER) {
                     struct task_struct *to_wakeup;
     
                     to_wakeup = wq_worker_sleeping(prev, cpu);
                     if (to_wakeup)
                         try_to_wake_up_local(to_wakeup);
                 }
             }
             switch_count = &prev->nvcsw;
         }
     
         if (task_on_rq_queued(prev))
             update_rq_clock(rq);
     
         next = pick_next_task(rq, prev);
         clear_tsk_need_resched(prev);
         clear_preempt_need_resched();
         rq->clock_skip_update = 0;
     
         if (likely(prev != next)) {
             rq->nr_switches++;
             rq->curr = next;
             ++*switch_count;
     
             rq = context_switch(rq, prev, next); /* unlocks the rq */
             cpu = cpu_of(rq);
         } else
             raw_spin_unlock_irq(&rq->lock);
     
         post_schedule(rq);
     
         sched_preempt_enable_no_resched();
     }
    
    

    (1)内核中定义了一个每CPU变量(percpu)runqueue,这个runqueue中是用来管理当前CPU上的running状态的进程,本函数会先获取当前CPU上的runqueue,然后通过runqueue结构体获取当前正在运行进程的描述符。并把它赋值给prev。
    (2)判断当前进程是否存在挂起的信号,如果有挂起的信号,那么保持当前进程为running状态,避免反复睡眠唤醒的资源消耗,否则当前进程制定dequeue操作进入休眠状态,并且判断当前进程是否为一个worker,如果是则在workqueue中查看是否有需要被唤醒的worker。
    (3)经过前面的逻辑后判断当前进程prev是否依然存在于runqueue队列中,如果存在说明它并没有休眠下去,那么此时需要执行update_rq_clock,为什么休眠的进程就可以跳过此步,那是因为在休眠dequeue的时候会执行此步骤。
    (4)执行pick_next_task选择下一个要执行的进程next,这一步实际上会根据sched_class类型的不同调用不同的回调,而不同的调度器实现的目的只有一个就是根据自己的算法找到下一个需要调度的进程。
    (5)清除prev进程的tif_need_resched标志
    (6)清除prev进程的preempt标志,清除禁止抢占标志
    (7)更新runqueue中跟当前进程相关的数据到next进程
    (8)执行context_switch执行上下文切换
    (9)执行post_schedule后续的一些操作,根据不同sched class调用不同的post回调

     static inline struct rq *
     context_switch(struct rq *rq, struct task_struct *prev,
                struct task_struct *next)
     {
         struct mm_struct *mm, *oldmm;
     
         prepare_task_switch(rq, prev, next);
     
         mm = next->mm;
         oldmm = prev->active_mm;
         /*
          * For paravirt, this is coupled with an exit in switch_to to
          * combine the page table reload and the switch backend into
          * one hypercall.
          */
         arch_start_context_switch(prev);
     
         if (!mm) {
             next->active_mm = oldmm;
             atomic_inc(&oldmm->mm_count);
             enter_lazy_tlb(oldmm, next);
         } else
             switch_mm(oldmm, mm, next);
     
         if (!prev->mm) {
             prev->active_mm = NULL;
             rq->prev_mm = oldmm;
         }
         /*
          * Since the runqueue lock will be released by the next
          * task (which is an invalid locking op but in the case
          * of the scheduler it's an obvious special-case), so we
          * do an early lockdep release here:
          */
         spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
     
         context_tracking_task_switch(prev, next);
         /* Here we just switch the register state and the stack. */
         switch_to(prev, next, prev);
         barrier();
     
         return finish_task_switch(prev);
     }
    
    

    context_switch函数是用于执行进程切换的,它的主要任务有:
    (1)切换两个进程的虚拟地址空间,切换内存页表,通过函数switch_mm实现
    (2)保存prev进程的栈信息和寄存器信息,然后恢复next进程栈信息和寄存器信息,通过switch_to实现
    (3)prepare_task_switch和finish_task_switch成对出现,用于准备和清理进程切换相关的数据结构

    • 内存空间切换
    	if (!mm) {
             next->active_mm = oldmm;
             atomic_inc(&oldmm->mm_count);
             enter_lazy_tlb(oldmm, next);
         } else
             switch_mm(oldmm, mm, next);
     
         if (!prev->mm) {
             prev->active_mm = NULL;
             rq->prev_mm = oldmm;
         }
    

    这部分之所以区分mm是否为空,主要是区分处理内核线程的,内核线程的mm为空,改用active_mm作为内存管理,其中包含了内核页表。对于mm为空的情况,直接把active_mm 设置为prev->active_mm,这就是设置的内核线程的地址空间。而对于用户进程,active_mm就被设置为等于mm,这一步是在fork的时候做的。
    针对内核线程不用执行switch_mm,因为内核页表都应该是一样的,从而减少了开销。

    • CPU寄存器和栈切换
    #define switch_to(prev, next, last)                 \
        do {                                \
            ((last) = __switch_to((prev), (next)));         \
        } while (0)
    
    

    __switch_to是平台相关的实现,主要实现思想如下:
    (1)先把当前进程的所有寄存器信息和栈信息(sp寄存器)存入当前进程的stack中
    (2)从next进程中去除所有的寄存器信息以及栈信息(sp寄存器)

    current进程的更新

    register unsigned long current_stack_pointer asm ("sp");
     
    static inline struct thread_info *current_thread_info(void)
    {
        return (struct thread_info *)
            (current_stack_pointer & ~(THREAD_SIZE - 1));
    }
    
    
    #define get_current() (current_thread_info()->task)
    #define current get_current()
    

    每个进程都会有两个堆栈,这里我们只关注内核栈,内核预留了一部分空间作为每个进程的内核栈,

    union thread_union {
        struct thread_info thread_info;
        unsigned long stack[THREAD_SIZE/sizeof(long)];
    };
    
    

    thread_info存放在预留空间stack的低地址位置。而stack高地址位置是内核栈底,sp寄存器是栈顶,内核栈是递减向下栈。

    展开全文
  • linux0.11学习之schedule

    2018-05-26 00:10:05
    在一个操作系统中,最重要的部分就是任务调度,linux的...下面我们贴出来schedule函数的代码。 1 2 /* 3 * 'schedule()' is the scheduler function. This is GOOD CODE! There 4 * probably won't be any reas...

    在一个操作系统中,最重要的部分就是任务调度,linux的0.11版本的任务调度函数比较简单,所以学习任务调度的原理非常适用。

    下面我们贴出来schedule函数的代码。

      1
      2 /*
      3  *  'schedule()' is the scheduler function. This is GOOD CODE! There
      4  * probably won't be any reason to change this, as it should work well
      5  * in all circumstances (ie gives IO-bound processes good response etc).
      6  * The one thing you might take a look at is the signal-handler code here.
      7  *
      8  *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
      9  * tasks can run. It can not be killed, and it cannot sleep. The 'state'
     10  * information in task[0] is never used.
     11  */
     12 void schedule(void)
     13 {
     14     int i,next,c;
     15     struct task_struct ** p;
     16
     17 /* check alarm, wake up any interruptible tasks that have got a signal */
     18
     19     for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
     20         if (*p) {
     21             if ((*p)->alarm && (*p)->alarm < jiffies) {
     22                     (*p)->signal |= (1<<(SIGALRM-1));
     23                     (*p)->alarm = 0;
     24                 }
     25             if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
     26             (*p)->state==TASK_INTERRUPTIBLE)
     27                 (*p)->state=TASK_RUNNING;
     28         }
     29
     30 /* this is the scheduler proper: */
     31
     32     while (1) {
     33         c = -1;
     34         next = 0;
     35         i = NR_TASKS;
     36         p = &task[NR_TASKS];
     37         while (--i) {
     38             if (!*--p)
     39                 continue;
     40             if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
     41                 c = (*p)->counter, next = i;
     42         }
     43         if (c) break;
     44         for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
     45             if (*p)
     46                 (*p)->counter = ((*p)->counter >> 1) +
     47                         (*p)->priority;
     48     }
     49     switch_to(next);
     50 }
    

    从代码注释中,我们可以看到linus本人对这段代码是比较推崇的。这段代码可以运行在任何边界,没有理由修改这段代码。有一点需要注意的是信号处理的代码。任务是一个空闲任务,只有在其他任务没有运行时该空闲任务才会被运行。空闲任务不能 被杀死,也不能睡眠。它的tcb即task[0].state,任务状态信息从来不用的。

    接下来我们来分析代码。

    14,15行定义了一些变量,要说明的是p 这是一个双重指针,双重针指向的类型是任务数据块.数据块里面存放着一个任务的所有信息。

    19-28行的代码作用是进行的报警定时值,唤醒任何已经得到信号的可中断任务。



    19行开始for循环从最后一个任务开始向前开始遍历所有的任务。

    20行判断该任务是否存在,不存在则跳过。

    21行判断如果设置过该任务的定时值,且该定时值已经过期则,在信号位图中置位SIGALRM,并清alarm.

    要说明的一点是jiffies变量是系统开机开始算起的计时数  10ms/滴答

    25.26行如果信号位图中除了被阻塞的信号外还有其它信号,并且任务处于可中断状态,则任务置位就绪态。 ~(_BLOCKABLE & (*p)->blocked)为忽略阻塞信号。

    32到48行是调度程序中主要的部分。

    先对几个变量进行说明。

    (*p)->counter 表示 任务状态的剩余滴答计数。

    i表示任务标号,p表示任务数组的地址。

    c和next用来保存(*p)->counter和i

    这里需要对i和p进行说明。初始

    i = NR_TASKS;

    p =&task[NR_TASKS]

    这两个都是无效值,因为NR_TASKS,用宏定义为64,也就是最多运行64个任务。但因为有任务0,所以任务数只能从0-63.


    所以在37,38行判断前对i和p都进行了前减减操作。

    39行也是为了判断任务存在,不存在的任务跳过。

    40和41行是为了找到当前所有任务中即处于就绪态,且剩余的滴答计时最多的任务。

    43行如果找到当前所有任务中即处于就绪态,且剩余的滴答计时最多的任务。跳出循环到49行进行任务切换。

    如果当前就绪的所有任务的剩余的滴答数都为0,则重新为所有任务都分配滴答数。

    45行为跳过不存在的任务。

    46.47行为任务重新分配滴答数。

    (*p)->counter = ((*p)->counter >> 1) +  (*p)->priority;

    算法为counter = counter/2 + priority

    44到48行是为所有的任务都重新分配滴答数。因为之前40行判断的时候可能处于非就绪状态的任务滴答数可能不为0,所以本次分配要对那些在40行判断非就绪状态的任务的滴答数加上之前剩余的一半 再加上自己的优先级。

    此时系统中就绪态的任务的滴答数已经重新分配,所以循环回32行,可以重新找到就绪态的最大滴答数的任务。

    然后在43行通过break退出循环,执行任务切换。


    展开全文
  • linux schedule 理解

    千次阅读 2018-01-08 14:41:49
    Linux内核进程调度schedule深入理解   一.说明 本文以linux-2.4.10 为例主要分析Linux 进程调度模块中的schedule 函数及其相关的函数。另外相关的前提知识也会说明。默认系统平台是自己的i386 架构的...

    Linux内核进程调度schedule深入理解

     

    一.说明

    本文以linux-2.4.10 为例主要分析Linux 进程调度模块中的schedule 函数及其相关的函数。另外相关的前提知识也会说明。默认系统平台是自己的i386 架构的pc

    二.前提知识

    在进行schedule 分析之前有必要简单说明一下系统启动过程,内存分配使用等。这样才能自然过渡到schedule 模块。

    首先是Linux各个功能模块之间的依赖关系:

     

     

     

    可见进程调度是整个内核的核心。但这部分,我想说明的是,我的pc是怎样把操作系统从硬盘装载到内存中,并启动进程调度模块的。然后才是后面对schedule的具体分析。

    首先,启动操作系统部分,涉及到到三个文件:/arch/i386/boot/bootsect.s/arch/i386/boot/setup.s/arch/i386/boot/compressed/head.s。编译安装好一个Linux系统后,bootsect.s模块被放置在可启动设备的第一个扇区(磁盘引导扇区,512字节)。那么下面开始启动过程,三个文件在内存中的分布与位置的移动如下图。

     

     

     

    在经过上图这一系列过程后,程序跳转到system模块中的初始化程序init中执行,即/init/main.c文件。该程序执行一系列的初始化工作,如寄存器初始化、内存初始化、中断设置等。之后内存的分配如下图:

     

     

     

    此后,CPU有序地从内存中读取程序并执行。前面的main从内核态移动到用户态后,操作系统即建立了任务0,即进程调度程序。之后再由schedule模块进行整个Linux操作系统中进程的创建(fork),调度(schedule),销毁(exit)及各种资源的分配与管理等操作了。值得一说的是schedule将创建的第一个进程是initpid=1),请注意它不是前面的/init/main.c程序段。如果是在GNU/Debian系统下,init 进程将依次读取rcS.drcN.drc0.d~rc6.d),rc.local三个run command脚本等,之后系统的初始化就完成了,一系列系统服务被启动了,系统进入单用户或者多用户状态。然后init 读取/etc/inittab,启动终端设备((exec)getty)供用户登陆,如debian中会启动6tty,你可以用组合键ctrl+alt+FnF1~F6)来切换。

    到这里就知道了Linux怎样启动进程调度模块了,也知道了进程调度模块启动的第一个进程init及之后的系统初始化和登陆流程。下面就回过头来分析schedule代码及其相关函数调用。

    三.进程调度涉及的数据结构

    文件:/linux/include/linux/sched.h

    下面只简单介绍数据结构task_struct中的两个字段。

    Linux中,进程(Linux中用轻量级的进程来模拟线程)使用的核心数据结构。一个进程在核心中使用一个task_struct结构来表示,包含了大量描述该进程的信息,其中与调度器相关的信息主要包括以下几个:

    1. state

    volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

    Linux的进程状态主要分为三类:可运行的(TASK_RUNNING,相当于运行态和就绪态);被挂起的(TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLETASK_STOPPED);不可运行的(TASK_ZOMBIE),调度器主要处理的是可运行和被挂起两种状态下的进程,其中TASK_STOPPED又专门用于SIGSTPIPC信号的响应,而TASK_ZOMBIE指的是已退出而暂时没有被父进程收回资源的"僵死"进程。

    2. counter

    long counter;

    该属性记录的是当前时间片内该进程还允许运行的时间。

    四. 就绪进程选择算法(即进程调度算法)

    文件:/kernel/sched.c

    1.上下文切换

    从一个进程的上下文切换到另一个进程的上下文,因为其发生频率很高,所以通常都是调度器效率高低的关键。schedule()函数中调用了switch_to宏,这个宏实现了进程之间的真正切换,其代码存放于include/i386/system.hswitch_to宏是用嵌入式汇编写成的,较难理解。

    switch_to()实现,而它的代码段在schedule()过程中调用,以一个宏实现。

    switch_to()函数正常返回,栈上的返回地址是新进程的task_struct::thread::eip,即新进程上一次被挂起时设置的继续运行的位置(上一次执行switch_to()时的标号"1:"位置)。至此转入新进程的上下文中运行。

    这其中涉及到wakeupsleepon等函数来对进程进行睡眠与唤醒操作。

    2.选择算法

    Linux schedule()函数将遍历就绪队列中的所有进程,调用goodness()函数计算每一个进程的权值weight,从中选择权值最大的进程投入运行。

    Linux的调度器主要实现在schedule()函数中。

    调度步骤:

    Schedule函数工作流程如下:

    1)清理当前运行中的进程

    2)选择下一个要运行的进程(pick_next_task

    3)设置新进程的运行环境

    4 进程上下文切换

     

    . Linux 调度器将进程分为三类

    进程调度是操作系统的核心功能。调度器只是调度过程中的一部分,进程调度是非常复杂的过程,需要多个系统协同工作完成。本文所关注的仅为调度器,它的主要工作是在所有 RUNNING 进程中选择最合适的一个。作为一个通用操作系统,Linux 调度器将进程分为三类:

    1. 交互式进程

    此类进程有大量的人机交互,因此进程不断地处于睡眠状态,等待用户输入。典型的应用比如编辑器 vi。此类进程对系统响应时间要求比较高,否则用户会感觉系统反应迟缓。

     

    2. 批处理进程

    此类进程不需要人机交互,在后台运行,需要占用大量的系统资源。但是能够忍受响应延迟。比如编译器。

     

    3. 实时进程

    实时对调度延迟的要求最高,这些进程往往执行非常重要的操作,要求立即响应并执行。比如视频播放软件或飞机飞行控制系统,很明显这类程序不能容忍长时间的调度延迟,轻则影响电影放映效果,重则机毁人亡。

     

    根据进程的不同分类 Linux 采用不同的调度策略。对于实时进程,采用 FIFO 或者 Round Robin 的调度策略。对于普通进程,则需要区分交互式和批处理式的不同。传统 Linux 调度器提高交互式应用的优先级,使得它们能更快地被调度。而 CFS  RSDL 等新的调度器的核心思想是“完全公平”。这个设计理念不仅大大简化了调度器的代码复杂度,还对各种调度需求的提供了更完美的支持。

     

    调度时机:调度什么时候发生?即:schedule()函数什么时候被调用?

     

    调度的发生主要有两种方式:

    1:主动式调度(自愿调度)

    在内核中主动直接调用进程调度函数schedule(),当进程需要等待资源而暂时停止运行时,会把状态置于挂起(睡眠),并主动请求调度,让出cpu

    2:被动式调度(抢占式调度、强制调度)

    用户抢占(2.4  2.6

    内核抢占(2.6

     

    (1)用户抢占发生在:从系统调用返回用户空间;

                                  从中断处理程序返回用户空间。

    内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时就会发生用户抢占。

    主动式调度是用户程序自己调度schedule,也许有人会觉得自己的代码中能引用schedule吗?也许不行吧,但大家知道wait4我们是可以调用的,前面我们没有给出wait4的代码,但我们知道在执行了wait4效果是父进程被挂起,所谓的挂起就是不运行了,放弃了CPU,这里发生了进程调度是显而易见的,其实在代码中有如下几行:

    current->state = TASK_INTERRUPIBLE;schedule();

    还有exit也有

    current->state = TASK_ZOMBIE; schedule();

    2种发生了进程调度,从代码上也可以看出(状态被改成了睡眠和僵死,然后去调度可运行进程,当前进程自然不会再占有CPU运行了),从效果中也能看出。这说明用户程序自己可以执行进程调度。

    (2)内核抢占:在不支持内核抢占的系统中,进程/线程一旦运行于内核空间,就可以一直执行,直到它主动放弃或时间片耗尽为止。这样一些非常紧急的进程或线程将长时间得不到运行。

    在支持内核抢占的系统中,更高优先级的进程/线程可以抢占正在内核空间运行的低优先级的进程/线程。

    关于抢占式调度(强制调度),需要知道的是,CPU在执行了当前指令之后,在执行下一条指令之前,CPU要判断在当前指令执行之后是否发生了中断或异常,如果发生了,CPU将比较到来的中断优先级和当前进程的优先级(有硬件参与实现,如中断控制器8259A芯片;通过比较寄存器的值来判断优先级;中断服务程序的入口地址形成有硬件参与实现,等等,具体实现请见相关资料和书籍),如果新来任务的优先级更高,则执行中断服务程序,在返回中断时,将执行进程调度函数schedule

     

    关于抢占式调度,系统代码中,除了前面我们说到的wait4exit等外(这两个系统函数是自愿或主动调度),还有一个地方会出现了schedule,就是中断返回代码里面出现了,这里出现了还加了限制条件,我们可以看看这个代码(所谓的中断返回代码,就是恢复中断现场的代码,每一个发生中断都会执行到的代码,无论是什么中断),这段代码是:

    277 testl $(VM_MASK | 3),%eax  # return to VM86 mode or non-supervisor?

    278 jne ret_with_reschedule

    279 jmp restore_all

    我们看到jne ret_with_reschedule在此之前还有一次条件判断,代码就不过多解释了,意思是:当中断发生在用户控件时候才会执行ret_with_reschedule,那么我们就看到,在中断返回到用户空间的前夕也是可能会发生进程调度的。

     简单的说进程调度发生的两种情况:中断返回用户空间前夕,和用户程序自愿放弃CPU,这2种情况会发生进程调度。

     

    在支持内核抢占的系统中,某些特例下是不允许内核被抢占的:

    a)内核正在运行中断处理程序,进程调度函数schedule()会对此作出判断,如果是在中断中调用,会打印出错误信息。

    b 内核正在进行中断上下文的bottom half(中断的底半部)处理,硬件中断返回前会执行软中断,此时仍然处于中断上下文。

    c 进程正持有spinlock自旋锁,writelock/readlock读写锁等,当持有这些锁时,不应该被抢占,否则由于抢占将导致其他cpu长时间不能获得锁而死锁。

    d 内核正在执行调度程序scheduler

     

    为了保证linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_count,称为内核抢占计数。这一变量被设置在进程的thread_info结构体中,每当内核要进入以上几种状态时,变量preempt_count就加1,指示内核不允许抢占,反之减1 

     

    内核抢占可能发生在:

    1:中断处理程序完成,返回内核空间之前

    2:当内核代码再一次具有可抢占性的时候,如解锁及使能软中断等。 

     

    调度标志——Tif_NEED_RESCHED

    作用:内核提供了一个need_resched标志来表明是否需要重新执行一次调度。

    设置:当某个进程耗尽它的时间片,会设置这个标志

            当一个优先级更高的进程进入可执行状态的时候,也会设置这个标志位

     

    进程并发不能靠进程自觉调度,只有靠中断(时钟中断)

     

    内核调度和内核的理解

    1. 内核调度也算是一个任务吗??

    答:不,内核调度只能说是一种任务调度的算法,它不一直在运行,只是在任务结束/时间片结束的时候才执行,选择下一个要运行的任务。

    2. 任务和内核的关系?

    答:任务是运行在内核的管理之下的,也可以说任务是运行在内核的这个环境里的。

    内核调度只是内核功能的一部份。内核本身不存在调度,它可以说一直在运行,主要是运行在任务之内和之间,它负责任务所需的资源处理。

    3. 它和正在运行的那个最高优先级的任务是一种什么样的关联呢??

    答:不管优先级多高,它都是运行在内核环境下的,内核是一直在运行的,只不过它是把CPU和其它资源分配给任务,让它运行而已。

    4. 什么是内核?

    答:其实内核不是一个进程,也不是一个现程。

    内核通过他提供的api,融合进了应用程序。也就是说内核只是一种抽象的说法,他本身并不存在,而是在一些特定的时间和特定的条件才运行,才给我们的应用程序提供各种服务。

    展开全文
  • 关于schedule_timeout

    2019-06-22 09:44:00
    schedule_timeout这个函数除了对当前进程调用schedule之外,还有一个功能,如同其名字中暗示的,在指定的时间到期后(timeout了)将进程唤醒。我们知道,进程一旦进入睡眠状态,就会从cpu的run queue中移走,直觉是...
  • schedule函数浅析

    千次阅读 2015-10-28 18:41:05
    如果prev进程在工作对列中(申请了I/O资源被插入工作对列,那么意味着prev进程要进入睡眠期等待资源来唤醒),prev进程开始沉睡,然后在工作队列唤醒一个新进程并插入就绪队列(如果没有进程需要被唤醒则返回NULL)...
  • schedule_timeout

    2018-09-26 14:32:57
    fastcall signed long __sched schedule_timeout(signed long timeout) {  struct timer_list timer;  unsigned long expire;  switch (timeout)  {  case MAX_SCHEDULE_TIMEOUT:  schedule(...
  • 文章源自于:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3641752 int schedule_work(struct work_struct *work) {  return queue_work(keventd_wq, work); } /*  * schedule_work_on - put wo
  • JAVA schedule pool 理解

    千次阅读 2017-01-17 11:38:07
    java schedule implement 如下例: public static void main(String[] args) { ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1); //task1 pool.schedule(()->{ Syste
  • schedule()函数

    2015-03-26 00:24:53
    好了,前面的准备工作都做完了,我们就进入进程调度的主体程序——schedule()函数。 函数schedule()实现调度程序。它的任务是从运行队列的链表rq中找到一个进程,并随后将CPU分配给这个进程。schedule()可以由几...
  • schedule调用相关整理

    2013-10-08 21:18:05
    1 schedule的调用时机 ×××正常情况下进程上下文中的直接调用schedule(),就会尝试着进行上下文切换。比如: int down_interruptible(struct semaphore *sem)copy_from_user(to, from, n)--------只能在用户...
  • 1 Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为TASK_RUNNING。一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个...
  • Linux内核进程调度schedule深入理解

    千次阅读 2013-09-09 22:49:18
    进程调度模块中的schedule 函数及其相关的函数。另外相关的前提知识也会说明。默认系统平台是自己的i386 架构的pc。 二.前提知识 在进行schedule 分析之前有必要简单说明一下系统启动过程,内存分配使用等。这样...
  • Linux进程睡眠和唤醒以及无效唤醒

    千次阅读 2013-08-22 10:51:09
    1 Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状态标志位为TASK_RUNNING。一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个...
  • schedule_timeout()

    千次阅读 2012-05-14 14:08:35
    schedule_timeout()进入延时唤醒状态. 如果在延迟过程中. 在通常的驱动程序中,可以以下列两种方式重新获得执行:在等待队列上调用一个 wake_up,或者 timout 超时。在这个特定实现中,没人会调用 wake_up...
  • Linux schedule 3、负载计算

    千次阅读 2017-12-16 02:24:44
    3、负载计算schedule里面这个负载(load average)的概念常常被理解成cpu占用率,这个有比较大的偏差。schedule不使用cpu占用率来评估负载,而是使用平均时间runnable的数量来评估负载。shedule也分了几个层级来计算...
  • 函数schedule()实现进程的调度。它的任务是从运行队列rq中找到一个进程,并随后将CPU分配给这个进程。schedule()可以采取主动调用或被动调用(可延迟的)的方式。 1 直接调用 如果current进程因缺乏资源而要立刻被...
  • 内核在两种情况下会设置该标志,一个是在时钟中断进行周期性的检查时,另一个是在被唤醒进程的优先级比正在运行的进程的优先级高时。 周期性地更新当前任务的状态时: 定时中断处理函数中会调用schedule_tick()用于...
  • schedule_timeout()进入延时唤醒状态. 如果在延迟过程中. 在通常的驱动程序中,可以以下列两种方式重新获得执行:在等待队列上调用一个 wake_up,或者 timout 超时。在这个特定实现中,没人会调用 wake_up...
  • schedule_timeout这个函数除了对当前进程调用schedule之外,还有一个功能,如同其名字中暗示的,在指定的时间到期后(timeout了)将进程唤醒。我们知道,进程一旦进入睡眠状态,就会从cpu的run queue中移走,直觉是...
  • schedule-调度器

    2017-04-13 17:46:42
    由于schedule的时间复杂为O(1),而且关中断和禁用抢占,schedule的执行时间可以确定。若实时进程的优先级为最高,实时进程就会被调度执行。这时,抢占延时计时结束。 也是说,在do_IRQ函数中,由于执行完中断处理...
  • Linux在众多进程中是怎么进行调度的,这个牵涉到Linux进程调度时机的概念,由Linux内核中Schedule()的函数来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等等。 Linux进程调度时机主要有: 1、进程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,680
精华内容 7,872
关键字:

schedule唤醒