kernel原始碼(十二)sched.c
阿新 • • 發佈:2022-03-20
原始碼
/* * 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 */ #includeView Code<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))) voidshow_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); 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); }
列印任務資訊
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); }
列印所有任務的資訊。NR_TASKS在標頭檔案中定義為64,也就是最多有64個任務
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]; };
定義init_task,其型別為上面定義的task_union,且第二項不給初始值
static union task_union init_task = {INIT_TASK,};
INIT_TASK在標頭檔案中定義
#define INIT_TASK \ /* state etc */ { 0,15,15, \ /* signals */ 0,{{},},0, \ /* ec,brk... */ 0,0,0,0,0,0, \ /* pid etc.. */ 0,-1,0,0,0, \ /* uid etc */ 0,0,0,0,0,0, \ /* alarm */ 0,0,0,0,0,0, \ /* math */ 0, \ /* fs info */ -1,0022,NULL,NULL,NULL,0, \ /* filp */ {NULL,}, \ { \ {0,0}, \ /* ldt */ {0x9f,0xc0fa00}, \ {0x9f,0xc0f200}, \ }, \ /*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ 0,0,0,0,0,0,0,0, \ 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ _LDT(0),0x80000000, \ {} \ }, \ }
long volatile jiffies=0; long startup_time=0; //開機時間 struct task_struct *current = &(init_task.task); //當前task struct task_struct *last_task_used_math = NULL; // struct task_struct * task[NR_TASKS] = {&(init_task.task), }; //task陣列,只初始化了第一個元素,其他63個未初始化 long user_stack [ PAGE_SIZE>>2 ] ; //使用者堆疊陣列,長度為PAGE_size右移2位,也就是1kb
下面定義了一個結構,並初始化。用作描述堆疊段,ss=0x10,sp=上面定義的user_stack的最後一個元素(因為堆疊是向下生長的)
struct { long * a; short b; } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
下面是程序排程的核心程式碼
void schedule(void) { int i,next,c; struct task_struct ** p; /* check alarm, wake up any interruptible tasks that have got a signal 檢查alarm,喚醒所有擁有signal的task*/ for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) //迴圈遍歷task_struct if (*p) { if ((*p)->alarm && (*p)->alarm < jiffies) { //如果該task設定過定時alarm並且alarm定時已經到達,jiffies是系統開機以來的嘀嗒數,每個嘀嗒10ms (*p)->signal |= (1<<(SIGALRM-1)); //給task設定一個signal訊號 (*p)->alarm = 0; //清除alarm } if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && //經過一系列判斷訊號點陣圖signal的狀態,如果條件滿足,則設定task狀態為可執行狀態。 (*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迴圈從task陣列中獲取到一個task,並執行switch_to(是一個巨集定義)開始執行這個task。 while (--i) { //從任務陣列的最後一個任務開始處理,陣列大小為64 if (!*--p) //陣列大小為64,如果系統中沒有那麼多工,比如剛開始核心啟動的時候只有一個task0,這跳過 continue; // if ((*p)->state == TASK_RUNNING && (*p)->counter > c) //如果任務狀態為就緒態,且counter>c,則把counter賦給c c = (*p)->counter, next = i; } if (c) break;//執行完上面的while迴圈之後,有3種結果:1、核心剛啟動階段沒有任務,c=-1;2、我們得到counter值最大的一個任務;3、所有task的counter都是0;對於第一種情況,則會執行我們熟悉的idel任務,對於第2種情況,則會執行該task;對於第3種情況,則繼續下面的程式碼 for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) //所有任務重新計算counter值,然後繼續while迴圈,直到得到一個任務去執行。 if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; } switch_to(next); }
總結,上面的schedule函式是任務排程的核心,在這裡會從任務列表中挑選出一個任務進行執行(得到時間片,cpu開始執行這個任務)。如果沒有任務可以執行,則執行任務0,。
對於任務0,則會執行下面的程式碼,在這裡面再次呼叫schedule函式。
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)) //如果當前任務是是任務0,則宕機。因為把任務0置為睡眠狀態是毫無意義的。 panic("task[0] trying to sleep"); tmp = *p; *p = current; current->state = TASK_UNINTERRUPTIBLE; //當前任務設定為不可中斷狀態 schedule(); //重新呼叫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; }