1. 程式人生 > 其它 >kernel原始碼(十二)sched.c

kernel原始碼(十二)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) { 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); }
View Code

列印任務資訊

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;
}