255 lines
5.3 KiB
C
255 lines
5.3 KiB
C
/*
|
|
* '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 <signal.h>
|
|
#include <linux/sys.h>
|
|
#include <asm/system.h>
|
|
#include <asm/io.h>
|
|
#include <asm/segment.h>
|
|
|
|
#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), *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)
|
|
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
|
|
if (current->used_math)
|
|
__asm__("frstor %0"::"m" (current->tss.i387));
|
|
else {
|
|
__asm__("fninit"::);
|
|
current->used_math=1;
|
|
}
|
|
last_task_used_math=current;
|
|
}
|
|
|
|
/*
|
|
* '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 && (*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;
|
|
}
|
|
}
|
|
|
|
void do_timer(long cpl)
|
|
{
|
|
if (cpl)
|
|
current->utime++;
|
|
else
|
|
current->stime++;
|
|
if ((--current->counter)>0) return;
|
|
current->counter=0;
|
|
if (!cpl) return;
|
|
schedule();
|
|
}
|
|
|
|
int sys_alarm(long seconds)
|
|
{
|
|
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
|
|
return seconds;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int sys_signal(long signal,long addr,long restorer)
|
|
{
|
|
long i;
|
|
|
|
switch (signal) {
|
|
case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL:
|
|
case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1:
|
|
case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM:
|
|
case SIGCHLD:
|
|
i=(long) current->sig_fn[signal-1];
|
|
current->sig_fn[signal-1] = (fn_ptr) addr;
|
|
current->sig_restorer = (fn_ptr) restorer;
|
|
return i;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
void sched_init(void)
|
|
{
|
|
int i;
|
|
struct desc_struct * p;
|
|
|
|
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++;
|
|
}
|
|
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);
|
|
}
|