linux系统的进程运转方式

Posted Linux bsping

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux系统的进程运转方式相关的知识,希望对你有一定的参考价值。

一、系统时间:(jiffies 系统滴答)

CPU内部有一个RTC,会在上电的时候调用mktime函数计算出1970年1.1日0时开始到当前开机点所过的秒数,给mktime函数传来的时间结构体的赋值是由初始化时从RTC中读出的参数,转化为时间存入全局变量中,并且会为jiffies所用。

jiffies是一个系统的时钟滴答,一个系统滴答是10ms。

每10ms发生一个定时器的中断,中断首先会将jiffies自加一,之后获取任务的特权级,将其作为参数调用do_timer函数。

### int32 - (int 0x20)时钟中断处理程序。中断频率被设置为100Hz。
# 定时芯片8253/8254是在kernel/sched.c中初始化的。因此这里jiffies每10 ms加1.
# 这段代码将jiffies增1,发送结束中断指令给8259控制器,然后用当前特权级作为
# 参数调用C函数do_timer(long CPL).当调用返回时转去检测并处理信号。
.align 2
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
# 由于初始化中断控制芯片时没有采用自动EOI,所以这里需要发指令结束该硬件中断。
	movb $0x20,%al		# EOI to interrupt controller #1
	outb %al,$0x20      # 操作命令字OCW2送0x20端口
# 下面从堆栈镇南关取出执行系统调用代码的选择符(CS段寄存器值)中的当前特权级别(0或3)
# 并压入堆栈,作为do_timer的参数。do_timer函数执行任务切换、计时等工作。
	movl CS(%esp),%eax
	andl $3,%eax		# %eax is CPL (0 or 3, 0=supervisor)
	pushl %eax
	call do_timer		# 'do_timer(long CPL)' does everything from
	addl $4,%esp		# task switching to accounting ...
	jmp ret_from_sys_call
/// 时钟中断C函数处理程序,在system_call.s中timer_interrupt被调用。
// 参数cpl是当前特权级0或3,是时钟中断发生时正在被执行的代码选择符中的特权级。
// cpl=0时表示中断发生时正在执行内核代码;cpl=3表示中断发生时正在执行用户代码。
// 对于一个进程由于执行时间片用完时,则进城任务切换。并执行一个计时更新工作。
void do_timer(long cpl)


    // 如果当前特权级(cpl)为0,则将内核代码运行时间stime递增;
	if (cpl)
		current->utime++;
	else
		current->stime++;

    // 如果有定时器存在,则将链表第1个定时器的值减1.如果已等于0,则调用相应的
    // 处理程序,并将该处理程序指针置空。然后去掉该项定时器。next_timer是定时器
    // 链表的头指针。
	if (next_timer) 
		next_timer->jiffies--;
		while (next_timer && next_timer->jiffies <= 0) 
			void (*fn)(void);       // 这里插入了一个函数指针定义!!!! o(︶︿︶)o 
			
			fn = next_timer->fn;
			next_timer->fn = NULL;
			next_timer = next_timer->next;
			(fn)();                 // 调用处理函数
		
	
    // 如果进程运行时间还没完,则退出。否则置当前任务计数值为0.并且若发生时钟中断
    // 正在内核代码中运行则返回,否则调用执行调度函数。
	if ((--current->counter)>0) return;
	current->counter=0;
	if (!cpl) return;                       // 内核态程序不依赖counter值进行调度
	schedule();

cpl变量是内核中用来表示被时钟中断中断的程序的特权级,0为内核态、1为用户态。根据这个来给内核态或者用户态的时间自加一。next_timer是jiffies变量的所有定时器的事件链表的头指针,

current->counter是指当前任务剩余的时间片。后面代码的意思是如果存在定时器链表,则将首个定时器jiffies自减一,当减为0之后将指针函数替换为下一个定时器的指针函数,并将链表后移,时触发fn指针函数。大体意思就是到时间了触发fn函数。后面判断当前任务的时间片是否用完,不为零就返回继续执行,为零的话,判断特权级,内核程序不依赖时间片调度,若为用户进程,则进行一次调度。

#task_struct代表一个进程,task_struct[]是进程向量表。

进程的调度就是task_struct[]进程链表的检索,找到时间片最大的那个进程对象(task_struct),然后进行调用,直到时间片为零退出并调用新的任务,全部为零之后在新一轮的调用中重新赋值。

总结:因为有了系统滴答,10ms进一次中断,引起jiffies自加一,从而引发do_timer函数,里面可以定时的触发一些函数,并且里面提出了时间片,进而引发了进程调度,这就是进程的工作方式。

以上是关于linux系统的进程运转方式的主要内容,如果未能解决你的问题,请参考以下文章

Out of memory: Kill process 解决

Linux系统的进程调度

操作系统基础知识

Linux后台进程

编译内核

linux系统的进程间通信有哪几种方式