linux 0.11 源码学习(八)

 

shed.c

sched实现内核的调度工作,最主要的是以下几个函数的实现:

  • 初始化 void sched_init(void),完成工作如下:
    • 设置TSS和LDT的描述符:
    set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); //初始化一个TSS
    set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); //初始化一个LDT,linux中每个任务定义了三个LDT,分别是0,代码段和数据段
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \ //段界限为104??
    "movw %%ax,%2\n\t" \ //addr的低16字节赋值至描述符的BYTE2/3
    "rorl $16,%%eax\n\t" \//addr右移16个bit
    "movb %%al,%3\n\t" \//将al,相当于把原始addr的第三个BYTE赋值到描述符的BYTE4
    "movb $" type ",%4\n\t" \ //将0x89(可用的TSS)和0x82(LDT)赋值给描述符的第五个字节
    "movb $0x00,%5\n\t" \//描述符属性定义的高字节为0
    "movb %%ah,%6\n\t" \ //eax高8位,相当于把原始addr的第四个BYTE赋值到描述符的BYTE7
    "rorl $16,%%eax" \ //ead清零
    ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ //addr是描述符定义的起始地址,赋值给eax
     "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
    )

#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x82")
    • 清任务数据(struct task_struct * task[NR_TASKS] = {&(init_task.task), };)和描述符表项(desc_struct *p);
    • 加载任务0的TSS和LDT到相应寄存器,设置时钟中断、系统调用中断;
  • 内核调度void schedule(void),主要完成工作如下:

备注:linux中的进程或者说任务,定义的状态有五种:

 

#define TASK_RUNNING            0 //运行状态,随时被调度
#define TASK_INTERRUPTIBLE      1 //可中断睡眠状态,当收到相应信号时可已被唤醒,进入TASK_RUNNING状态
#define TASK_UNINTERRUPTIBLE    2 //不可中断睡眠状态,只能被wake_up函数唤醒
#define TASK_ZOMBIE             3 //进程已停止运行
#define TASK_STOPPED            4 //暂停状态,收到SIGSTOP等信号        
    • 首先判断全部TASK_INTERRUPTIBLE状态的进程,是否满足被唤醒条件,有则设置状态为RUNNING;
    • 遍历全部任务数组,查找属于RUNNING状态,且counter最大的任务。counter随任务运行时间递减,counter大则证明运行时间不长;
    • 若无法通过counter获取当前待运行任务,则重新计算counter值,算法为:(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;重新至上一步骤;
    • 当得到当前待运行任务时,调用switch_to(next),进行任务切换;代码如下:
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,current\n\t" \ //待切换任务,不是当前任务
    "je 1f\n\t" \ //是当前任务跳转至1:,即不做任何工作
    "movw %%dx,%1\n\t" \ //描述符复制给_tmp.b
    "xchgl %%ecx,current\n\t" \ //将新任务的地址复制给current
    "ljmp *%0\n\t" \ //长跳转到TSS选择描述符,CPU根据给出的TSS完成切换及上下文保存
    "cmpl %%ecx,last_task_used_math\n\t" \
    "jne 1f\n\t" \
    "clts\n" \
    "1:" \
    ::"m" (*&__tmp.a),"m" (*&__tmp.b), \
    "d" (_TSS(n)),"c" ((long) task[n])); \ //edx为TSS的选择描述符,ecx为新任务变量的起始地址
}

 

posted @ 2013-05-04 19:07  Fredric_2013  阅读(491)  评论(0编辑  收藏  举报