/* * 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))//取信号nr在信号位图上对应的二进制位图
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))//除了SIGKILL和SIGSTOP两个信号外,都可以阻塞
void show_task(int nr,struct task_struct * p)//显示任务nr的进程号、进程状态、内核堆栈空闲字节数
int i,j = 4096-sizeof(struct task_struct);//总的为4K,
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++)//NR_TASKS=64
if (task[i]) show_task(i,task[i]); }
#define LATCH (1193180/HZ)//PC机的8253定时芯片输入时钟频率为1.193180MHZ,中断的频率为100HZ,也就是10ms发出一次时钟中断。
extern void mem_use(void);
extern int timer_interrupt(void);//时钟中断函数,/kernel/system_call.s
extern int system_call(void);//系统调用函数,/kernel/system_call.s
union task_union { struct task_struct task; char stack[PAGE_SIZE]//PAGE_SIZE=4096
};//任务的内核态椎栈结构
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 ] ;//用户栈,大小为(4K>>2) =1K
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)//#define LAST_TASK task[NR_TASKS-1],#define FIRST_TASK task[0]
if (*p) { if ((*p)->alarm && (*p)->alarm < jiffies) {//P->alarm,报警定时值,滴答数)
(*p)->signal |= (1<<(SIGALRM-1));//对应任务置信号
(*p)->alarm = 0; } if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&//p->blocked,进程号屏蔽
(*p)->state==TASK_INTERRUPTIBLE)//p->state,任务状态
(*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)//p->counter,任务运行时间计数(滴答数,递减的),时间片
c = (*p)->counter, next = i; }//next指向counter值最大,运行时间不长的那个任务
if (c) break;//当c不为0时,说时当前还有任务的时间片没有用完,进行任务切换于141行,C=-1,NEXT=0时,说明没有一个可运行的任务,进行任务切换。
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)//更换每一个任务的counter值
if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; } switch_to(next);//任务切换,当系统中没有其他可运行的任务时,next=0,则去执行任务0,而任务0去执行pause()系统调用,而在pause系统调用中,又调用了schedule()本函数。
}
int sys_pause(void)//转变当前任务的壮态为可中断的等待状态,并重新调度。本函数将使进程睡眼,直到接收到一个信号,执行完进程的操作后,才可本函数返回
{ current->state = TASK_INTERRUPTIBLE; schedule(); return 0; }
//将当前任务置为不可中断的等待状态,并将此放入睡眼队列P中,(是否将当前任务插入睡眼队列头指针后呢???
void sleep_on(struct task_struct **p)//P为等待任务队列头指针
{ struct task_struct *tmp;
if (!p) return; if (current == &(init_task.task))//当前任务为0时,
panic("task[0] trying to sleep"); tmp = *p; *p = current;//将当前任务插入到等待队列P中。
current->state = TASK_UNINTERRUPTIBLE; schedule(); if (tmp)//只有当这个等待任务被唤醒,调度程序才会返回到这里。
tmp->state=0; } //将当前任务置为可中断的等待状态,并将此放入等待队列P中,原理同前
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)//唤醒*p所指向的任务。*p为任务等待队列头指针,由于新等待任务是插入等待队列头指针处的,因此唤醒的是最后进入等待队列的任务。
{ if (p && *p) { (**p).state=0;//置为就绪状态(TASK_RUNNINT)
*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//最多64个定时器
static struct timer_list {//定时器链表结构,循环链表,可以将next_time看作这个链表的头,所指向的就为第一个定时器
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)//时钟中断C函数处理程序,system_call.s line 176 _timer_interupter 调用此函数,参数cpl 为 当前特权级0(发生时正执行内核代码)或3(当前发生时正执行用户代码 )
{ extern int beepcount;//扬声器发声时间滴答数linux-0.11\chr_drv\console.c line 697
extern void sysbeepstop(void);//关闭扬声器linux-0.11\kernel\chr_drv\console.c line 691
if (beepcount) if (!--beepcount) sysbeepstop();
if (cpl) current->utime++;//cpl=3
else current->stime++;//cpl=0
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;//否则,将当前任务运行时间设为0
if (!cpl) return;//如果运行于内核态(cpl=0)则返回,内核态的程序不信赖于counter的人值进行调度。
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)//降低对CPU的优先使用权
{ if (current->priority-increment>0) current->priority -= increment; return 0; }
void sched_init(void)//内核调度程序 的初始化子程序,由main()函数调用
{ 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); }
|