加入收藏 | 设为首页 | 会员中心 | 我要投稿 徐州站长网 (https://www.0516zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux 2.4调度系统分析--转

发布时间:2021-01-25 11:26:02 所属栏目:Linux 来源:网络整理
导读:副标题#e# 简介:?本文详尽地分析了Linux 2.4内核中调度系统的工作原理,特别是i386体系结构下SMP系统的调度表现。通过对2.4调度系统实现原理及其细节的分析,文章在文末指出了2.4调度系统在功能上、实时性上以及多处理机系统表现上存在的不足,为后继的2.6

系统最初的引导进程(init_task)在引导结束后即成为cpu 0上的idle进程。在每个cpu上都有一个idle进程,正如上文所言,这些进程登记在init_tasks[]数组中,并可用idle_task()宏访问(见上"相关数据结构")。idle进程不进入就绪队列,系统稳定后,仅当就绪队列为空的时候idle进程才会被调度到。

init_task的task_struct是静态配置的,定义在[include/linux/sched.h]中的INIT_TASK()宏中,其中与调度相关的几个属性分别是:

  • state:TASK_RUNNING;
  • counter:10*HZ/100;i386上大约100ms
  • nice:0;缺省的优先级
  • policy:SCHED_OTHER;非实时进程
  • cpus_runnable:-1;全1,未在任何cpu上运行
  • cpus_allowed:-1;全1,可在任何cpu上运行

在smp_init()中(实际上是在[arch/i386/kernel/smpboot.c]中的smp_boot_cpus()中),init_task的processor属性被设为0,对应的schedule_data也设置好相应的值。在创建了一个核心线程用于执行init()函数之后([/init/main.c]rest_init()),init_task设置自己的need_resched等于1,然后调用cpu_idle()进入IDLE循环。

在cpu_idle()中,init_task的nice值被设为20(最低优先级),counter为-100(无意义的足够小),然后cpu_idle()进入无限循环:

need_resched) idle(); schedule(); check_pgt_cache(); } 初始化过程中第一次执行cpu_idle(),因need_resched为1,所以直接启动schedule()进行第一次调度。如上文所述,schedule()会清掉need_resched位,因此,之后本循环都将执行idle()函数,直至need_resched再被设置为非0(比如在reschedule_idle()中,见上"调度器工作时机")。

idle()函数有三种实现可能:

  • default_idle(),执行hlt指令;
  • poll_idle(),如果核心参数上定义了"idle=poll",则pm_idle会指向poll_idle(),它将need_resched设置为特殊的-1,然后反复循环直到need_resched不等于-1。因为poll_idle()采用更高效的指令,所以运行效率比default_idle()要高;
  • 电源管理相关的idle过程,例如APM和ACPI模块中定义的idle过程。

因为仅当就绪队列为空的时候才会调度到idle进程,所以,只有在系统完全空闲时才会执行check_pgt_cache()操作,清理页表缓存。

系统中除了init_task是手工创建的以外,其他进程,包括其他CPU上的idle进程都是通过do_fork()创建的,所不同的是,创建idle进程时使用了CLONE_PID标志位。

在do_fork()中,新进程的属性设置为:

  • state:TASK_UNINTERRUPTIBLE
  • pid:如果设置了CLONE_PID则与父进程相同(仅可能为0),否则为下一个合理的pid
  • cpus_runnable:全1;未在任何cpu上运行
  • processor:与父进程的processor相同;子进程在哪里创建就优先在哪里运行
  • counter:父进程counter值加1的一半;同时父进程自己的counter也减半,保证进程不能通过多次fork来偷取更多的运行时间(同样,在子进程结束运行时,它的剩余时间片也将归还给父进程,以免父进程因创建子进程而遭受时间片的损失)
  • 其他值与父进程相同

子进程通过SET_LINKS()链入进程列表,然后调用wake_up_process()唤醒(见上"调度器工作时机")。

init_task在完成关键数据结构初始化之后,在进行硬件的初始化之前,会调用smp_init()对SMP系统进行初始化。smp_init()调用smp_boot_cpus(),smp_boot_cpus()对每一个CPU都调用一次do_boot_cpu(),完成SMP其他CPU的初始化工作。

processor = cpu; idle->cpus_runnable = 1 << cpu; /* 在指定CPU上运行 */ map_cpu_to_boot_apicid(cpu,apicid); idle->thread.eip = (unsigned long) start_secondary; /* 被调度到后的启动地址 */ del_from_runqueue(idle); /* idle进程不通过就绪队列调度 */ unhash_process(idle); init_tasks[cpu] = idle; /* 所有idle进程都可通过init_tasks[]数组访问 */ 该进程被调度到时即执行start_secondary(),最终将调用cpu_idle(),成为IDLE进程。

2.4内核中进程缺省时间片是根据以下公式计算的:

> 2) #elif HZ < 400 #define TICK_SCALE(x) ((x) >> 1) #elif HZ < 800 #define TICK_SCALE(x) (x) #elif HZ < 1600 #define TICK_SCALE(x) ((x) << 1) #else #define TICK_SCALE(x) ((x) << 2) #endif #define NICE_TO_TICKS(nice) (TICK_SCALE(20-(nice))+1) …… schedule() { …… p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice); …… } (编辑:徐州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!