参考资料
- 《Linux内核完全注释》
- linux 0.11源码
- B站视频
- CSDN博客
!!以下图片来自《Linux内核完全注释 修正版V3.0》!!
硬件时钟
'''
一般PC机都会有以下两个时钟:
一:RTC实时时钟(又称CMOS时钟)
二:OS时钟,产生于PC主板上的定时/计数芯片(又称软时钟、系统时钟)————重点要了解的是这个
'''
- 典型的定时/计数芯片:8253、8254,它们可编程。
时钟中断
Linux初始化过程包括了对OS时钟的初始化,使OS时钟成为整个OS工作的心脏。
(8253芯片)OS时钟会每10ms产生一个中断信号,由IRQ接收处理后传递于CPU,触发相应的中断服务。
(也即是10ms为一个时间片,每过一个时间片对进程持有的时间片数进行一次检查调整)
- 时钟中断C函数处理程序do_timer(),位于sched.c文件,在system_call.s中被调用。
- do_timer()主要判断当前进程时间片是否用完,并根据不同情况做出相应的处理。
struct task_struct {//该结构体定义在sched.h中,以下只是部分代码
/* these are hardcoded - don't touch */
long state; //程序运行的状态/* -1 unrunnable, 0 runnable, >0 stopped */
long counter; //时间片
//counter的计算不是单纯的累加,需要下面这个优先级这个参数参与
long priority;//优先级
......
long utime,stime,cutime,cstime,start_time;//运行时间
//utime是用户态运行时间 cutime是内核态运行时间
......
};
void do_timer(long cpl)
{
/*!!!!为了方便说明,我省略了部分代码!!!!*/
/***
*current为进程指针
* %eax is CPL (0 or 3, 0=supervisor)
*cpl为特权级.cpl=0为最高级,表示内核态;cpl>0表示用户态.
***/
if (cpl)
current->utime++;//给用户程序运行时间+1
else
current->stime++;//内核程序运行时间+1
/*定时器处理*/
if (next_timer) {// next_timer 是连接jiffies变量的所有定时器的事件链表
// 可以这样想象,jiffies是一个时间轴,然后这个时间轴上每个绳结上绑了一个事件,运行到该绳结就触发对应的事件
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);//插入一个函数指针
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();//调用处理函数
}
}
/*时间片检测*/
if ((--current->counter)>0) return;//如果进程时间片还没用完,则退出
current->counter=0;//否则,重置进程时间片为0
if (!cpl) return;//如果时间片用完且在内核态下运行,则直接退出
schedule();//如果时间片用完且在用户态下,执行调度函数(分配时间片,执行另一个进程)
}
- 在system_call.s中_timer_interrupt时钟中断处理中,执行完do_timer()后,会跳转到ret_from_sys_call对信号进行处理。
- 在ret_from_sys_call中,首先会检测当前任务,如果是任务0(内核态),则不作信号量方面的处理,直接返回,退出中断;如果是非任务0(用户态),则对任务进行信号量的识别处理。
信号处理程序调用方式
- do_signal()是信号量识别处理的主要操作,定义在signal.c中。
- do_signal()把信号对应的处理函数插入到内核堆栈eip处,并修改用户堆栈,使其中断返回后(用户进程)执行信号处理函数。
do_signal()具体过程
//发送信号的函数,这些参数全部都是寄存器的名字
//进程间通信,无非是A进程传递一些信息给当前的进程
//这个信号传递函数是触发中断,然后接管当前进程的堆栈寄存器等句柄
//再然后可以往里面写入一些东西,其实就是传递了信息给当前运行的进程
void do_signal(long signr,long eax, long ebx, long ecx, long edx,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
{
......
}
- 信号处理函数返回后执行一个sa_restorer,恢复用户堆栈为正常中断退出之后的状态。
- 因为时钟中断、系统调用以及最频繁调用的schedule()里面都会处理信号,所以信号总能及时地得到"触发"。
————————————————————————————————————————————
结束
👍👍👍👍👍👍👍👍
继续加油!! 👍👍👍
👍👍👍👍👍👍👍👍