前言
上一篇文章,通过系统调用引出了中断以及分段机制和保护模式相关内容,并进行了简单说明。本篇将在中断基础上,从时间片轮转和任务切换的角度切入,进一步地探究linux内核。
时间片轮转
时间片轮转是操作系统实现并发的方式,简单地说,操作系统为每个进程分配一定的时间片,当前进程的时间片用尽,则切换到下一个就绪进程进行执行。实现时间片轮转的方式很容易想到,根据上一篇举例的时钟中断,我们可以设计实现如下
- 设置定时芯片隔N毫秒发出时钟中断
- 设计时钟中断的执行程序完成以下步骤
- 获取当前任务结构
- 将当前任务时间片-1
- 判断当前任务时间片是否剩余
- 若时间片有剩余,则中断返回,继续执行当前任务
- 若时间片用尽,则根据CPL权限标志判断当前任务为内核任务,还是用户任务
- 若为内核任务,直接中断返回。(因为内核任务不允许被抢占)
- 若为用户任务,则执行任务调度,完成任务切换
- 将设计好的中断程序与时钟中断向量号绑定。即修改idt
实际上,linux内核也是这么干的。根据源码,可以进行印证。
设置定时芯片隔N毫秒发出时钟中断
kernel\sched.c
// 下面代码用于初始化 8253 定时器。通道 0,选择工作方式 3,二进制计数方式。通道 0 的
// 输出引脚接在中断控制主芯片的 IRQ0 上,它每 10 毫秒发出一个 IRQ0 请求。LATCH 是初始
// 定时计数值。
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */ // 定时值低字节。
outb(LATCH >> 8 , 0x40); /* MSB */ // 定时值高字节。
将设计好的中断程序与时钟中断向量号绑定。即修改idt
kernel\sched.c
set_intr_gate(0x20,&timer_interrupt);
设计时钟中断的执行程序
kernel\system_call.s
_timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
incl _jiffies //自加自身
movb $0x20,%al # EOI to interrupt controller #1
outb