Linux系统的进程管理

122 阅读3分钟

系统的进程的运转方式

  • 系统时间:(jiffies 系统滴答)
  • CPU内部有一个RTC,会在上电的时候调用mktime函数,算出从1970年1月1日0时开始到当前开机点所过的秒数
  • 给mktime函数传过来的时间结构体的赋值是由初始化时从RTC(coms)中读出的参数 转换为时间存入全局变量中,并且会为jiffies所用
  • jiffies是一个系统时钟滴答,一个滴答是10ms,这个滴答就像操作系统的脉搏一样。
  • 10m一个滴答,每隔10ms会引发一个定时器中断(中断服务函数timer_interrup: t-->call do_timer()中,首先进行了jiffies自加)

进程管理.png

源码链接: elixir.bootlin.com/linux/0.10/…

_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 %al,$0x20
	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

内核态 不可抢占

用户态 可以抢占

void do_timer(long cpl)
{
	if (cpl)          // CPL变量是内核中用来指示被中断程序的特权, 0表示内核进程  1表示用户进程
		current->utime++;   // utime用户程序的运行时间
	else
		current->stime++;   // stime内核程序的运行时间
	if (next_timer) {   // next_timer 是嫁接于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)();
		}
	}

        // current->counter 进程的时间片
	if ((--current->counter)>0) return;   // 当前进程的时间片还没到则返回
	current->counter=0;
	if (!cpl) return;                 // 时间片到了,但是当前进程是内核进程也返回
	schedule();                       // 当前进程是用户进程,时间片到了,进行下一轮的进程调度
}

进程结构体

struct task_struct {
/* these are hardcoded - don't touch */
	long state;	/* 进程状态:-1 unrunnable, 0 runnable, >0 stopped */
	long counter;   /* 时间片 */
	long priority;  /* 优先级 */
	long signal;    /* 信号 */
	struct sigaction sigaction[32]; /* 信号位图 */
	long blocked;	/* bitmap of masked signals */
/* various fields */
	int exit_code;
	unsigned long end_code,end_data,brk,start_stack;
	long pid,father,pgrp,session,leader;
	unsigned short uid,euid,suid;
	unsigned short gid,egid,sgid;
	long alarm;
	long utime,stime,cutime,cstime,start_time;
	unsigned short used_math;
/* file system info */
	int tty;		/* -1 if no tty, so it must be signed */
	unsigned short umask;
	struct m_inode * pwd;
	struct m_inode * root;
	unsigned long close_on_exec;
	struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
	struct desc_struct ldt[3];  /* 局部描述符 */
/* tss for this task */
	struct tss_struct tss;      /* 进程状态描述符 */
};

进程调度

void main(void)		/* This really IS void, no error here. */
{			/* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 	ROOT_DEV = ORIG_ROOT_DEV;
 	drive_info = DRIVE_INFO;
	memory_end = (1<<20) + (EXT_MEM_K<<10);
	memory_end &= 0xfffff000;
	if (memory_end > 16*1024*1024)
		memory_end = 16*1024*1024;
	if (memory_end > 6*1024*1024)
		buffer_memory_end = 2*1024*1024;
	else
		buffer_memory_end = 1*1024*1024;
	mem_init(buffer_memory_end,memory_end);
	trap_init();
	blk_dev_init();
	chr_dev_init();
	tty_init();
	time_init();  // 设置开机启动时间。
	sched_init(); // 调度程序初始化(加载任务0的tr,ldtr)
	buffer_init(buffer_memory_end);
	hd_init();
	floppy_init();
	sti();
	move_to_user_mode(); // 移到用户模式下执行
	if (!fork()) {		/* we count on this going ok */
		init();      // 在新建的子进程(任务1即init进程)中执行。
	}
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
	for(;;) pause();
}
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));  //把进程的tss copy到gdt
	set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));  //把进程的ldt copy到gdt
	p = gdt+2+FIRST_TSS_ENTRY;
	for(i=1;i<NR_TASKS;i++) {  // 清任务数组和描述符表项(注意i=1开始,所以初始任务的描述符还在)
		task[i] = NULL;
		p->a=p->b=0;
		p++;
		p->a=p->b=0;
		p++;
	}
	ltr(0);
	lldt(0);
	outb_p(0x36,0x43);		/* binary, mode 3, LSB/MSB, ch 0 */
	outb_p(LATCH & 0xff , 0x40);	/* LSB */
	outb(LATCH >> 8 , 0x40);	/* MSB */
	set_intr_gate(0x20,&timer_interrupt); // 设置时钟中断处理程序句柄(设置时钟中断门)。
	outb(inb_p(0x21)&~0x01,0x21);
	set_system_gate(0x80,&system_call);  // 然后设置系统调用中断门。
}