You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							439 lines
						
					
					
						
							8.5 KiB
						
					
					
				
			
		
		
	
	
							439 lines
						
					
					
						
							8.5 KiB
						
					
					
				| /*
 | |
|  *  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>
 | |
| #include <linux/tty.h>
 | |
| #define _S(nr) (1<<((nr)-1))
 | |
| #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
 | |
| 
 | |
| void show_task(int nr,struct task_struct * p)
 | |
| {
 | |
| 	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)
 | |
| 
 | |
| 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,};
 | |
| 
 | |
| long volatile jiffies=0;
 | |
| 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), };
 | |
| 
 | |
| long user_stack [ PAGE_SIZE>>2 ] ;
 | |
| 
 | |
| struct {
 | |
| 	long * a;
 | |
| 	short b;
 | |
| 	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
 | |
| /*
 | |
|  *  '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) {
 | |
| 					(*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)
 | |
| 				c = (*p)->counter, next = i;
 | |
| 		}
 | |
| 		if (c) break;
 | |
| 		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
 | |
| 			if (*p)
 | |
| 				(*p)->counter = ((*p)->counter >> 1) +
 | |
| 						(*p)->priority;
 | |
| 	}
 | |
| 	switch_to(next);
 | |
| }
 | |
| 
 | |
| int sys_pause(void)
 | |
| {
 | |
| 	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))
 | |
| 		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).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};
 | |
| 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 64
 | |
| 
 | |
| static 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)
 | |
| {
 | |
| 	extern int beepcount;
 | |
| 	extern void sysbeepstop(void);
 | |
| 	user_timer *t=timer_head,*pre_t=NULL,*t1;
 | |
| 	while(t){
 | |
| 		t->jiffies--;
 | |
| 		if(t->jiffies==0){
 | |
| 			message *msg=(message*)malloc(sizeof(message));
 | |
| 			msg->pid=-1;
 | |
| 			msg->mid=MSG_TIME;
 | |
| 			msg->next=NULL;
 | |
| 			post_message(msg);
 | |
| 			if(t->type==0){
 | |
| 				t->jiffies=t->init_jiffies;
 | |
| 				pre_t=t;
 | |
| 				t=t->next;
 | |
| 			}
 | |
| 			else{
 | |
| 				t1=t;
 | |
| 				t=t->next;
 | |
| 				if(pre_t) pre_t->next=t;
 | |
| 				else timer_head=t;
 | |
| 				free(t1);
 | |
| 			}
 | |
| 		}
 | |
| 		else{
 | |
| 			pre_t=t;
 | |
| 			t=t->next;
 | |
| 		}
 | |
| 	}
 | |
| 	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;
 | |
| 	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));
 | |
| 	set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
 | |
| 	p = gdt+2+FIRST_TSS_ENTRY;
 | |
| 	for(i=1;i<NR_TASKS;i++) {
 | |
| 		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");
 | |
| 	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);
 | |
| }
 |