《深入理解Linux内核》--第七章:进程调度:读书笔记

零、感想
      进程调度涉及的 priority、进程的状态(TASK_RUNNING、TASK_INTERRUPT、TASK_UNINTERRUPT、TASK_STOPPED)。Linux系统有个PID为0的进程——swapper进程(只有在CPU不能执行其他进程才执行)。
       在多处理器系统中涉及 调度域,比较值得注意的是Intel的超线程技术(包括几个内部寄存器的拷贝,当前线程在访问内存的间隙时,处理器可以使用它的机器周期去执行另一个线程),因为超线程技术,引入了逻辑CPu和物理CPU的概念,在一个物理CPU中可以有两个逻辑CPU(超线程所致);超线程中又带来一个注意点就是:当调度程序检查其他CPU空闲与否时,调度程序需要判断物理CPU是否有空,如果仅仅是超线程CPU的一个逻辑CPU空闲而另一个忙碌则不能将其它CPU上待运行的进程调度该物理CPU上。
      在多处理器系统中,如果被迁移的进程已经在远程CPU的 Cache中(高速缓存命中),那么不应该迁移该进程到其他CPU上去执行,因为已经在远程CPU上可能Cache中的数据和RAM中的数据不一致,需要等待期写回。
      比较重要的数据结构:runqueue(运行队列)
      比较重要的函数:schedule()进程调度函数 。

一、单核调度策略及数据结构和函数
     进程一种分类:I/O受限(I/O-bound)、CPU受限(CPU-bound)
     进程另一种分类:交互式(Interactive)经常与用户进行交互、
                             批处理(batch)不必与用户交互,经常在后台运行、
                             实时(real-time)要很短的相应时间,而且响应时间的变化应该小
                         交互式进程相对有较高的优先级,不管时间片多长,都会很快抢占批处理进程。
      如果进程进入TASK_RUNNING状态,内核检查它的动态优先级是否大于当前运行进程的优先级。
     1)调度算法
      调度类型:a)SCHED_FIFO,如果没有比当前优先级高(不包括同级)的进程则一直使用CPU直到
                          放弃 。 CPU调度室,将进程描述符放在运行队列链表的当前位置
                     b)SCHED_RR,时间片轮转,CPU调度时,将进程描述符放在运行队列链表的末尾。
                     c)SCHED_NORMAL,普通分时
      基本时间片(ms)= a】 (140-静态优先级)*20, 静态优先级<120
                                   b】 (140-静态优先级)*5,   静态优先级>=120
      动态优先级=max{100,min【静态优先级-bonus+5,139】}
                        bonus:0~10,<5表示降低动态优先级以表示惩罚,>5表示升高优先级表奖赏。
                       bonus-5>=静态优先级/4-28
      活动和过期进程:a】活动进程,还没有用完他们的时间片,运行它们运行
                              b】过期进程,这些可运行(TASK_RUNNING)进程用完了它们的时间片,被禁止
                                   运行,直到所有活动进程都过期,才恢复运行。
                              注意:一般复杂些,用完其时间片的交互式进程通常仍然是活动进程。实时进程总是
                                      被当做活动进程。
       2)数据结构
       runqueue运行队列(存放在runqueues的Per-CPU变量中):
                     系统中每个可运行进程属于且只属于一个运行队列。
                     array字段:包含两个prio_array_t结构数组。
                      每个prio_array_t数据结构都表示一个可运行进程集合:      包括140个双向链表头(每
                      个链表对应一个可能的进程优先级0~139),一个优先级位图,  一个可运行进程集合的
                      进程数量的计数器。

       进程描述符
             thread_info->cpu:可运行进程所在队列的逻辑CPU号(注意是threa,所以是线程级的
                                           CPU逻辑号)。
             time_slice:进程时间片中海剩余的时钟节拍数。
                              p->time_slice=(current->time_slice+1)>>1;
                              curent->time_slice>>=1;(如果当前为1,则子进程为1,自己为0,父爱伟大)
                              父进程创建子进程是,time_slice划分为两等分,一份给父进程,一份给子进程。
                              (避免无限获得CPU时间:父进程创建一个运行相同代码的子进程,并随后杀死自
                              己,通过适当调节创建速度,子进程总是在父进程过期之前获得新的时间片)
          3)函数
            scheduler_tick()维持当前最新的time_slice计数器
            try_to_wake_up()唤醒随眠进程
                                        将进程状态设置为TAK_RUNNING,然后把进程插入到本地CPU的运顶队列
            recalc_task_prio()更新动态优先级 和平均睡眠时间
            schedule()选择要被执行的新进程
                             在运行队列的链表中找到一个进程,将CPU分配给这个进程.
                             如果进程current设置了TIF_NEED_RESCHED为1,则需要调用schedule()
            load_balance()维持多处理器系统中运行队列平衡

二、多处理系统中运行队列平衡
      多处理涉及体系结构:a】标准的多处理器体系结构:共有的RAM被所有CPU共享
                                    b】超线程:包括几个内部寄存器的拷贝,当前线程在访问内存的间隙时,处理
                                                    器可以使用它的机器周期去执行另一个线程.
                                    c】NUMA:CPU和RAm以本地“节点”为单位分组。内存仲裁器是多处理器系
                                                    统的性能瓶颈。本地CPU访问远程RAM比较慢。
                                                   (这样一个保持可运行状态的进程通常限制在一个固定CPU上)
       调度域:一个CPU集合。最上层的调度域包括多个子调度域,每个子调度域包括一个CPU子集。
《深入理解Linux内核》--第七章:进程调度:读书笔记_第1张图片
       函数:
           rebalance_tick():
           load_balance()
           move_task()          
      

三、调度使用的函数:
      nice()系统调用:允许进程改变它们基本优先级。
      getpriority(),setpriority()
      sched_getaffinity(), sched_setaffinity()

英文含义:
Schedule Policy调度策略
time  sharing 时分
NUMA (Non-Uniform Memory Access)非一致内存访问
RR(Round-robin),轮转
Schedule domain 调度域

你可能感兴趣的:(《深入理解Linux内核》--第七章:进程调度:读书笔记)