Linux内核分析-8/进程的调度

进程的调度

  • 之前我们在mykernel中体验了一次进程的调度,调度分为两个过程
  • 1/保存当前进程
  • 2/装载之前进程
和之前的fork exec 有什么区别呢?
fork 和exec 只是创造和修改了一个task_struct 结构体,exec后的进程是要调度模块调度才能运行的.
而进程的调度就是保存一个结构体,并装载一个结构体.

linux-0.11中的调度

//init/main.c
init
//kernel/sched.c
//这是调度的初始化模块
void sched_init(void)
{
    int i;
    struct desc_struct * p;

    if (sizeof(struct sigaction) != 16)
        panic("Struct sigaction MUST be 16 bytes");
    set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
    set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
    p = gdt+2+FIRST_TSS_ENTRY;
    for(i=1;i<NR_TASKS;i++) {
        task[i] = NULL;
        p->a=p->b=0;
        p++;
        p->a=p->b=0;
        p++;
    }
/* Clear NT, so that we won't have troubles with that later on */
    __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
    ltr(0);
    lldt(0);
    outb_p(0x36,0x43);      /* binary, mode 3, LSB/MSB, ch 0 *///设置时钟1
    outb_p(LATCH & 0xff , 0x40);    /* LSB *///设置时钟2
    outb(LATCH >> 8 , 0x40);    /* MSB *///设置时钟3
    set_intr_gate(0x20,&timer_interrupt);//设置时钟中断处理函数
    outb(inb_p(0x21)&~0x01,0x21);
    set_system_gate(0x80,&system_call);
}

//1/调度函数(kernel/sched.c)
//在mykernel中是内核上层的函数在想切换的时候,调用了 my_schedule 函数进行调度
//所以我猜这里也是在想要切换进程的时候调用了内核中的函数切换,这里切换函数是 schedule
//2/调度时机
//但是在什么时候切换呢?在mykernel中,是时钟中断达到了10^7次,才调用了调度函数
//在linux-0.11中只有8个地方调用了 schedule 函数
//schedule

1/修改任务数组中每个任务的 signal alarm state
2/找出next
  2.1/在任务数组中从后往前找,按照counter 的大小, 越大,优先级越高
  2.2/找不到,按照 priority 更新counter,回到 2.1
3/switch_to(next)
/*
* switch_to(n)将切换当前任务到任务nr,即n。首先检测任务n 是不是当前任务,
* 如果是则什么也不做退出。如果我们切换到的任务最近(上次运行)使用过数学
* 协处理器的话,则还需复位控制寄存器cr0 中的TS 标志。
*/
// 输入:%0 - 新TSS 的偏移地址(*&__tmp.a); %1 - 存放新TSS 的选择符值(*&__tmp.b);
// dx - 新任务n 的选择符;ecx - 新任务指针task[n]。
// 其中临时数据结构__tmp 中,a 的值是32 位偏移值,b 为新TSS 的选择符。在任务切换时,a 值
// 没有用(忽略)。在判断新任务上次执行是否使用过协处理器时,是通过将新任务状态段的地址与
// 保存在last_task_used_math 变量中的使用过协处理器的任务状态段的地址进行比较而作出的。
/*
 *  switch_to(n) should switch tasks to task nr n, first
 * checking that n isn't the current task, in which case it does nothing.
 * This also clears the TS-flag if the task we switched to has used
 * tha math co-processor latest.
 */
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \
    "je 1f\n\t" \
    "movw %%dx,%1\n\t" \
    "xchgl %%ecx,_current\n\t" \
    "ljmp %0\n\t" \
    "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])); \
}
//这里说调度时机
1/中断处理过程 do_timer(timer_interrupt->do_timer) 
2/主动调用 release do_exit sys_waitpid sys_pause sleep_on interruptible_sleep_on tty_write
例子:
tty_write(sys_write->rw_char->crw_table->rw_tty->rw_ttyx->tty_write- 写阻塞->schedule)
//所以说调度时机分为两种
//1/中断处理进程
//2/主动调用
//其实上面两种都是用 set_intr_gate set_trap_gate set_system_gate 来将中断号与中断处理函数绑定
//主动调用是从上面的分支 set_system_gate 来的
//中断是从上面的分支 set_intr_gate 来的

从中断来说,包括时钟中断、I/O中断、系统调用和异常(linux0.11中只有时钟中断调用了 schedule )
从系统调用来说,有open close mount sync pause 等等,这些系统调用通过 int 80 进入内核,然后产生内核线程调用了 schedule

//中断
set_intr_gate
    set_intr_gate (0x2E, &hd_interrupt);//在kernel/system_call.s中
    set_intr_gate (0x20, &timer_interrupt);//在kernel/system_call.s中
    set_intr_gate (0x24, rs1_interrupt);//在kernel/chr_drv/rs_io.s 中
    set_intr_gate (0x23, rs2_interrupt);//在kernel/chr_drv/rs_io.s中
set_trap_gate
    set_trap_gate (0x21, &keyboard_interrupt);
    set_trap_gate (0x26, &floppy_interrupt);
    void trap_init(void)
    {
        int i;

        set_trap_gate(0,&divide_error);// 设置除操作出错的中断向量值。以下雷同。
        set_trap_gate(1,&debug);
        set_trap_gate(2,&nmi);
        set_system_gate(3,&int3);   /* int3-5 can be called from all *///
        set_system_gate(4,&overflow);//系统调用
        set_system_gate(5,&bounds);//
        set_trap_gate(6,&invalid_op);
        set_trap_gate(7,&device_not_available);
        set_trap_gate(8,&double_fault);
        set_trap_gate(9,&coprocessor_segment_overrun);
        set_trap_gate(10,&invalid_TSS);
        set_trap_gate(11,&segment_not_present);
        set_trap_gate(12,&stack_segment);
        set_trap_gate(13,&general_protection);
        set_trap_gate(14,&page_fault);
        set_trap_gate(15,&reserved);
        set_trap_gate(16,&coprocessor_error);
    // 下面将int17-48 的陷阱门先均设置为reserved,以后每个硬件初始化时会重新设置自己的陷阱门。
        for (i=17;i<48;i++)
            set_trap_gate(i,&reserved);
        set_trap_gate(45,&irq13);// 设置协处理器的陷阱门。
        outb_p(inb_p(0x21)&0xfb,0x21);// 允许主8259A 芯片的IRQ2 中断请求。
        outb(inb_p(0xA1)&0xdf,0xA1);// 允许从8259A 芯片的IRQ13 中断请求。
        set_trap_gate(39,&parallel_interrupt);// 设置并行口的陷阱门。
    }
set_system_gate
    set_system_gate (0x80, &system_call);
    set_system_gate(3,&int3);   /* int3-5 can be called from all */
    set_system_gate(4,&overflow);
    set_system_gate(5,&bounds);

下面的说的是glibc-2.25和linux-3.10中的调用路径


asmlinkage void __sched schedule(void)
{
    struct task_struct *tsk = current;

    sched_submit_work(tsk);
    __schedule();
}
//先不看是怎么调度的
//下面看是谁调用了这个函数
//
1/驱动里面
2/系统调用
3/中断处理函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值