⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sched.c

📁 一份精简的linux内核源代码
💻 C
字号:
/* *  linux/kernel/sched.c * *  (C) 1991  Linus Torvalds *//* * 'sched.c' is the main kernel file. It contains scheduling primitives * (sleep_on, wakeup, schedule etc) as well as a number of simple system * call functions (type getpid(), which just extracts a field from * current-task */#include <linux/sched.h>#include <linux/kernel.h>#include <linux/sys.h>#include <linux/fdreg.h>#include <asm/system.h>#include <asm/io.h>#include <asm/segment.h>#include <signal.h>#define _S(nr) (1<<((nr)-1))#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))void show_task(int nr,struct task_struct * p)/*显示任务号nr的进程号、进程状态、空闲内核堆栈*/{	int i,j = 4096-sizeof(struct task_struct);	printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);	i=0;	while (i<j && !((char *)(p+1))[i])		i++;	printk("%d (of %d) chars free in kernel stack\n\r",i,j);}void show_stat(void)/*显示所有任务的进程号、进程状态、空闲内核堆栈*/{	int i;	for (i=0;i<NR_TASKS;i++)		if (task[i])			show_task(i,task[i]);}#define LATCH (1193180/HZ)/*将8253设为每10ms发出一次时钟中断*/extern void mem_use(void);extern int timer_interrupt(void);extern int system_call(void);union task_union {/*任务的内核堆栈结构*/	struct task_struct task;	char stack[PAGE_SIZE];};static union task_union init_task = {INIT_TASK,};/*初始化数据(sched.h,113)*/long volatile jiffies=0;/*volatile限定符指定变量必须从内存中取得(一般运行时变量会存入寄存器,此时调用该变量会直接从寄存器中取得)*/long startup_time=0;/*开机时间*/struct task_struct *current = &(init_task.task);/*当前进程指针*/struct task_struct *last_task_used_math = NULL;/*最后一个使用过协处理器进程的指针*/struct task_struct * task[NR_TASKS] = {&(init_task.task), };/*定义任务进程指针数组,并初始化进程0的数据(sched.h)*/long user_stack [ PAGE_SIZE>>2 ] ;/*设置初始化时的零时堆栈(以后将用于进程0的用户态堆栈)*/struct {        /*设置设置初始化时的零时堆栈(SS:ESP),lss指令(将第一个操作数赋予ESP,将第二个操作数赋予SS)*/	long * a;	short b;	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };/*指向user_stack中的最后一项(即堆栈栈顶)*//* *  'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task */void math_state_restore() /*当发生进程调度时,该函数保存原进程的协处理器上下文,并加载新进程的协处理器上下文*/{	if (last_task_used_math == current)		return;	__asm__("fwait");	if (last_task_used_math) {		__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));	}	last_task_used_math=current;	if (current->used_math) {		__asm__("frstor %0"::"m" (current->tss.i387));	} else {		__asm__("fninit"::);		current->used_math=1;	}}/* *  'schedule()' is the scheduler function. This is GOOD CODE! There * probably won't be any reason to change this, as it should work well * in all circumstances (ie gives IO-bound processes good response etc). * The one thing you might take a look at is the signal-handler code here. * *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other * tasks can run. It can not be killed, and it cannot sleep. The 'state' * information in task[0] is never used. */void schedule(void)/*进程调度*/{	int i,next,c;	struct task_struct ** p;/* check alarm, wake up any interruptible tasks that have got a signal */	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)		if (*p) {			if ((*p)->alarm && (*p)->alarm < jiffies) {/*判断当前的系统时间是否超过了进程的alarm,若是则向该进程发送一个SIGALRM信号(该信号默认操作将中止进程)*/					(*p)->signal |= (1<<(SIGALRM-1));					(*p)->alarm = 0;				}			if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&/*若信号位图中除被阻塞信号外还有其它信号,并且进出处于可中断状态,则置进程为就绪状态*/			(*p)->state==TASK_INTERRUPTIBLE)				(*p)->state=TASK_RUNNING;		}/* this is the scheduler proper: */	while (1) {		c = -1;		next = 0;		i = NR_TASKS;		p = &task[NR_TASKS];		while (--i) {			if (!*--p)				continue;			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)/*比较每个就绪的任务的counter(剩余时间片),选择最大的一个,并将其进程号存于next中*/				c = (*p)->counter, next = i;		}		if (c) break;/*如果存在某个就绪进程的剩余时间片大于0,则直接切换进进程(进程号为next,即counter最大的那个)中,					 否则重新设置所有进程的counter值(包括睡眠的进程)如下所示,counter=counter/2+priority,随后重复上述的剩余时间片比较操作*/		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)			if (*p)				(*p)->counter = ((*p)->counter >> 1) +						(*p)->priority;	}	switch_to(next);/*实际的进程切换操作,若此时没有其它可切换的进程则系统将切换进进程0(next=0)进程0会调用pause()将自己设为可中断的睡眠状态并再次调用schedule()*/}int sys_pause(void)/*导致进程进入睡眠状态直到收到一个信号,并调用schedule()*/{	current->state = TASK_INTERRUPTIBLE;	schedule();	return 0;}void sleep_on(struct task_struct **p)  /*把当前进程置为不可中断的等待状态,并让睡眠队列头指针指向当前进程*/{	struct task_struct *tmp;	if (!p)		return;	if (current == &(init_task.task))  /*若当前进程为进程0则死机*/		panic("task[0] trying to sleep");	tmp = *p;	*p = current;	current->state = TASK_UNINTERRUPTIBLE;  /*这就是进程挂起的实质*/	schedule();  /*内核态程序能被中断,但不接受进程调度*/	if (tmp)    /*当进程被唤醒时将从这里开始继续执行*/		tmp->state=0;}void interruptible_sleep_on(struct task_struct **p)/*同上*/{	struct task_struct *tmp;	if (!p)		return;	if (current == &(init_task.task))		panic("task[0] trying to sleep");	tmp=*p;	*p=current;repeat:	current->state = TASK_INTERRUPTIBLE;	schedule();	if (*p && *p != current) {/*因为参数是"**p",所以这里的*p可能不等于current*/		(**p).state=0;		goto repeat;	}	*p=NULL;	if (tmp)		tmp->state=0;}void wake_up(struct task_struct **p){	if (p && *p) {		(**p).state=0;		*p=NULL;	}}/* * OK, here are some floppy things that shouldn't be in the kernel * proper. They are here because the floppy needs a timer, and this * was the easiest way of doing it. */static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};/*看块设备时再看,或不看!(201-262)*/static int  mon_timer[4]={0,0,0,0};static int moff_timer[4]={0,0,0,0};unsigned char current_DOR = 0x0C;int ticks_to_floppy_on(unsigned int nr){	extern unsigned char selected;	unsigned char mask = 0x10 << nr;	if (nr>3)		panic("floppy_on: nr>3");	moff_timer[nr]=10000;		/* 100 s = very big :-) */	cli();				/* use floppy_off to turn it off */	mask |= current_DOR;	if (!selected) {		mask &= 0xFC;		mask |= nr;	}	if (mask != current_DOR) {		outb(mask,FD_DOR);		if ((mask ^ current_DOR) & 0xf0)			mon_timer[nr] = HZ/2;		else if (mon_timer[nr] < 2)			mon_timer[nr] = 2;		current_DOR = mask;	}	sti();	return mon_timer[nr];}void floppy_on(unsigned int nr){	cli();	while (ticks_to_floppy_on(nr))		sleep_on(nr+wait_motor);	sti();}void floppy_off(unsigned int nr){	moff_timer[nr]=3*HZ;}void do_floppy_timer(void){	int i;	unsigned char mask = 0x10;	for (i=0 ; i<4 ; i++,mask <<= 1) {		if (!(mask & current_DOR))			continue;		if (mon_timer[i]) {			if (!--mon_timer[i])				wake_up(i+wait_motor);		} else if (!moff_timer[i]) {			current_DOR &= ~mask;			outb(current_DOR,FD_DOR);		} else			moff_timer[i]--;	}}/*定时器*/#define TIME_REQUESTS 64static struct timer_list {	long jiffies;/*定时器*/	void (*fn)();/*该定时器的处理函数*/	struct timer_list * next;} timer_list[TIME_REQUESTS], * next_timer = NULL;void add_timer(long jiffies, void (*fn)(void))/*添加定时器*/{	struct timer_list * p;	if (!fn)		return;	cli();/*屏蔽中断*/	if (jiffies <= 0)		(fn)();	else {		for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)  /*找空闲项*/			if (!p->fn)				break;		if (p >= timer_list + TIME_REQUESTS)  /*无空闲项则系统崩溃*/			panic("No more time requests free");		p->fn = fn;		p->jiffies = jiffies;		p->next = next_timer;		next_timer = p;		while (p->next && p->next->jiffies < p->jiffies) {			p->jiffies -= p->next->jiffies;			fn = p->fn;			p->fn = p->next->fn;			p->next->fn = fn;			jiffies = p->jiffies;			p->jiffies = p->next->jiffies;			p->next->jiffies = jiffies;			p = p->next;		}	}	sti();/*解除中断屏蔽*/}/*时钟中断程序,每过一个滴答执行发生一次,主要用于更新各时间参数,若当前进程时间片正好用完(且发生中断时该进程正处于用户态)则执行调度程序*/void do_timer(long cpl)/*CPL中存放着当前进程的特权级(用户态或内核态)*/{	extern int beepcount;/*扬声器发声时间(滴答)console.c,697*/	extern void sysbeepstop(void);/*关闭扬声器console.c,691*/	if (beepcount)		if (!--beepcount)			sysbeepstop();	if (cpl)		current->utime++;/*用户态运行时间*/	else		current->stime++;/*内核态运行时间*/	if (next_timer) {		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_DOR & 0xf0)		do_floppy_timer();	if ((--current->counter)>0) return;	current->counter=0;	if (!cpl) return; /*内核态程序不依赖于时间片来进行进程调度(即发生时钟中断的时候若当前进程正处于内核态,则不执行进程调度)*/	schedule();/*进程调度程序*/}int sys_alarm(long seconds)/*设置报警定时值(秒),并返回原报警定时的剩余时间(秒)*/{	int old = current->alarm;	if (old)		old = (old - jiffies) / HZ;	current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;  /* 警报时间=系统已运行时间+SECOND参数,此后当系统的运行时间超过ALARM则系统会向该进程发送一个SIGALARM信号,同时中止该进程或进行相关处理*/	return (old);}int sys_getpid(void){	return current->pid;}int sys_getppid(void){	return current->father;}int sys_getuid(void){	return current->uid;}int sys_geteuid(void){	return current->euid;}int sys_getgid(void){	return current->gid;}int sys_getegid(void){	return current->egid;}int sys_nice(long increment)/*减少进程的剩余时间片*/{	if (current->priority-increment>0)		current->priority -= increment;	return 0;}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));/*设置进程0在gdt中的表项*/	set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));	p = gdt+2+FIRST_TSS_ENTRY;	for(i=1;i<NR_TASKS;i++) {/*清除任务数组和描述符表项,即现在gdt以及任务数组中只有进程0*/		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");/*清除标志寄存器中的位NT(NT用于控制程序的递归调用,若NT置位时,当前中断任务执行iret后将引起任务切换,TN指出tss中的back_link是否有效)*/	ltr(0);/*将进程0的tss描述符加载到tr中*/	lldt(0);/*将进程0的ldt描述符加载到ldtr中*/	outb_p(0x36,0x43);		/* binary, mode 3, LSB/MSB, ch 0 *//*初始化8253定时器*/	outb_p(LATCH & 0xff , 0x40);	/* LSB */	outb(LATCH >> 8 , 0x40);	/* MSB */	set_intr_gate(0x20,&timer_interrupt);/*初始化时钟中断向量*/	outb(inb_p(0x21)&~0x01,0x21);       /*设置8259A开时钟中断*/	set_system_gate(0x80,&system_call);/*初始化系统调用中断*/}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -