📄 sched.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 + -