?? sched.c
字號:
/* * linux/kernel/sched.c * * (C) 1991 Linus Torvalds *//* * 'sched.c'是主要的內(nèi)核文件。其中包括有關(guān)調(diào)度的基本函數(shù)(sleep_on、wakeup、schedule 等)以及 * 一些簡單的系統(tǒng)調(diào)用函數(shù)(比如getpid(),僅從當(dāng)前任務(wù)中獲取一個字段)。 */#include <linux/sched.h> // 調(diào)度程序頭文件。定義了任務(wù)結(jié)構(gòu)task_struct、第1 個初始任務(wù)// 的數(shù)據(jù)。還有一些以宏的形式定義的有關(guān)描述符參數(shù)設(shè)置和獲取的// 嵌入式匯編函數(shù)程序。#include <linux/kernel.h> // 內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。#include <linux/sys.h> // 系統(tǒng)調(diào)用頭文件。含有72 個系統(tǒng)調(diào)用C 函數(shù)處理程序,以'sys_'開頭。#include <linux/fdreg.h> // 軟驅(qū)頭文件。含有軟盤控制器參數(shù)的一些定義。#include <asm/system.h> // 系統(tǒng)頭文件。定義了設(shè)置或修改描述符/中斷門等的嵌入式匯編宏。#include <asm/io.h> // io 頭文件。定義硬件端口輸入/輸出宏匯編語句。#include <asm/segment.h> // 段操作頭文件。定義了有關(guān)段寄存器操作的嵌入式匯編函數(shù)。#include <signal.h> // 信號頭文件。定義信號符號常量,sigaction 結(jié)構(gòu),操作函數(shù)原型。#define _S(nr) (1<<((nr)-1)) // 取信號nr 在信號位圖中對應(yīng)位的二進(jìn)制數(shù)值。信號編號1-32。// 比如信號5 的位圖數(shù)值 = 1<<(5-1) = 16 = 00010000b。#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) // 除了SIGKILL 和SIGSTOP 信號以外其它都是// 可阻塞的(…10111111111011111111b)。// 顯示任務(wù)號nr 的進(jìn)程號、進(jìn)程狀態(tài)和內(nèi)核堆棧空閑字節(jié)數(shù)(大約)。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]) // 檢測指定任務(wù)數(shù)據(jù)結(jié)構(gòu)以后等于0 的字節(jié)數(shù)。 i++; printk ("%d (of %d) chars free in kernel stack\n\r", i, j);}// 顯示所有任務(wù)的任務(wù)號、進(jìn)程號、進(jìn)程狀態(tài)和內(nèi)核堆棧空閑字節(jié)數(shù)(大約)。void show_stat (void){ int i; for (i = 0; i < NR_TASKS; i++)// NR_TASKS 是系統(tǒng)能容納的最大進(jìn)程(任務(wù))數(shù)量(64 個), if (task[i]) // 定義在include/kernel/sched.h 第4 行。 show_task (i, task[i]);}// 定義每個時間片的滴答數(shù)?。#define LATCH (1193180/HZ)extern void mem_use (void); // [??]沒有任何地方定義和引用該函數(shù)。extern int timer_interrupt (void); // 時鐘中斷處理程序(kernel/system_call.s,176)。extern int system_call (void); // 系統(tǒng)調(diào)用中斷處理程序(kernel/system_call.s,80)。union task_union{ // 定義任務(wù)聯(lián)合(任務(wù)結(jié)構(gòu)成員和stack 字符數(shù)組程序成員)。 struct task_struct task; // 因為一個任務(wù)數(shù)據(jù)結(jié)構(gòu)與其堆棧放在同一內(nèi)存頁中,所以 char stack[PAGE_SIZE]; // 從堆棧段寄存器ss 可以獲得其數(shù)據(jù)段選擇符。};static union task_union init_task = { INIT_TASK, }; // 定義初始任務(wù)的數(shù)據(jù)(sched.h 中)。long volatile jiffies; // 從開機(jī)開始算起的滴答數(shù)時間值(10ms/滴答)。// 前面的限定符volatile,英文解釋是易變、不穩(wěn)定的意思。這里是要求gcc 不要對該變量進(jìn)行優(yōu)化// 處理,也不要挪動位置,因為也許別的程序會來修改它的值。long startup_time; // 開機(jī)時間。從1970:0:0:0 開始計時的秒數(shù)。struct task_struct *current = &(init_task.task); // 當(dāng)前任務(wù)指針(初始化為初始任務(wù))。struct task_struct *last_task_used_math = NULL; // 使用過協(xié)處理器任務(wù)的指針。struct task_struct *task[NR_TASKS] = { &(init_task.task), }; // 定義任務(wù)指針數(shù)組。long user_stack[PAGE_SIZE >> 2]; // 定義系統(tǒng)堆棧指針,4K。指針指在最后一項。// 該結(jié)構(gòu)用于設(shè)置堆棧ss:esp(數(shù)據(jù)段選擇符,指針),見head.s,第23 行。struct{ long *a; short b;}stack_start = {&user_stack[PAGE_SIZE >> 2], 0x10};/* * 將當(dāng)前協(xié)處理器內(nèi)容保存到老協(xié)處理器狀態(tài)數(shù)組中,并將當(dāng)前任務(wù)的協(xié)處理器 * 內(nèi)容加載進(jìn)協(xié)處理器。 */// 當(dāng)任務(wù)被調(diào)度交換過以后,該函數(shù)用以保存原任務(wù)的協(xié)處理器狀態(tài)(上下文)并恢復(fù)新調(diào)度進(jìn)來的// 當(dāng)前任務(wù)的協(xié)處理器執(zhí)行狀態(tài)。void math_state_restore (){
struct i387_struct *tmp;
if (last_task_used_math == current) // 如果任務(wù)沒變則返回(上一個任務(wù)就是當(dāng)前任務(wù))。 return; // 這里所指的"上一個任務(wù)"是剛被交換出去的任務(wù)。 // __asm__ ("fwait"); // 在發(fā)送協(xié)處理器命令之前要先發(fā)WAIT 指令。
_asm fwait; if (last_task_used_math) { // 如果上個任務(wù)使用了協(xié)處理器,則保存其狀態(tài)。// __asm__ ("fnsave %0"::"m" (last_task_used_math->tss.i387));
tmp = &last_task_used_math->tss.i387;
_asm mov ebx,tmp
_asm fnsave [ebx]; } last_task_used_math = current; // 現(xiàn)在,last_task_used_math 指向當(dāng)前任務(wù), // 以備當(dāng)前任務(wù)被交換出去時使用。 if (current->used_math) { // 如果當(dāng)前任務(wù)用過協(xié)處理器,則恢復(fù)其狀態(tài)。// __asm__ ("frstor %0"::"m" (current->tss.i387));
tmp = ¤t->tss.i387;
_asm mov ebx,tmp
_asm frstor [ebx]; } else { // 否則的話說明是第一次使用,// __asm__ ("fninit"::); // 于是就向協(xié)處理器發(fā)初始化命令,
_asm fninit; current->used_math = 1; // 并設(shè)置使用了協(xié)處理器標(biāo)志。 }}/* * 'schedule()'是調(diào)度函數(shù)。這是個很好的代碼!沒有任何理由對它進(jìn)行修改,因為它可以在所有的 * 環(huán)境下工作(比如能夠?qū)O-邊界處理很好的響應(yīng)等)。只有一件事值得留意,那就是這里的信號 * 處理代碼。 * 注意!!任務(wù)0 是個閑置('idle')任務(wù),只有當(dāng)沒有其它任務(wù)可以運(yùn)行時才調(diào)用它。它不能被殺 * 死,也不能睡眠。任務(wù)0 中的狀態(tài)信息'state'是從來不用的。 */void schedule (void){ int i, next, c; struct task_struct **p; // 任務(wù)結(jié)構(gòu)指針的指針。/* 檢測alarm(進(jìn)程的報警定時值),喚醒任何已得到信號的可中斷任務(wù) */
// 從任務(wù)數(shù)組中最后一個任務(wù)開始檢測alarm。 for (p = &LAST_TASK; p > &FIRST_TASK; --p) if (*p) {// 如果任務(wù)的alarm 時間已經(jīng)過期(alarm<jiffies),則在信號位圖中置SIGALRM 信號,然后清alarm。// jiffies 是系統(tǒng)從開機(jī)開始算起的滴答數(shù)(10ms/滴答)。定義在sched.h 第139 行。 if ((*p)->alarm && (*p)->alarm < jiffies) { (*p)->signal |= (1 << (SIGALRM - 1)); (*p)->alarm = 0; }// 如果信號位圖中除被阻塞的信號外還有其它信號,并且任務(wù)處于可中斷狀態(tài),則置任務(wù)為就緒狀態(tài)。// 其中'~(_BLOCKABLE & (*p)->blocked)'用于忽略被阻塞的信號,但SIGKILL 和SIGSTOP 不能被阻塞。 if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && (*p)->state == TASK_INTERRUPTIBLE) (*p)->state = TASK_RUNNING; //置為就緒(可執(zhí)行)狀態(tài)。 } /* 這里是調(diào)度程序的主要部分 */ while (1) { c = -1; next = 0; i = NR_TASKS; p = &task[NR_TASKS];// 這段代碼也是從任務(wù)數(shù)組的最后一個任務(wù)開始循環(huán)處理,并跳過不含任務(wù)的數(shù)組槽。比較每個就緒// 狀態(tài)任務(wù)的counter(任務(wù)運(yùn)行時間的遞減滴答計數(shù))值,哪一個值大,運(yùn)行時間還不長,next 就// 指向哪個的任務(wù)號。 while (--i) { if (!*--p) continue; if ((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i; } // 如果比較得出有counter 值大于0 的結(jié)果,則退出124 行開始的循環(huán),執(zhí)行任務(wù)切換(141 行)。 if (c) break; // 否則就根據(jù)每個任務(wù)的優(yōu)先權(quán)值,更新每一個任務(wù)的counter 值,然后回到125 行重新比較。 // counter 值的計算方式為counter = counter /2 + priority。[右邊counter=0??] for (p = &LAST_TASK; p > &FIRST_TASK; --p) if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; }
switch_to (next); // 切換到任務(wù)號為next 的任務(wù),并運(yùn)行之。}//// pause()系統(tǒng)調(diào)用。轉(zhuǎn)換當(dāng)前任務(wù)的狀態(tài)為可中斷的等待狀態(tài),并重新調(diào)度。// 該系統(tǒng)調(diào)用將導(dǎo)致進(jìn)程進(jìn)入睡眠狀態(tài),直到收到一個信號。該信號用于終止進(jìn)程或者使進(jìn)程調(diào)用// 一個信號捕獲函數(shù)。只有當(dāng)捕獲了一個信號,并且信號捕獲處理函數(shù)返回,pause()才會返回。// 此時pause()返回值應(yīng)該是-1,并且errno 被置為EINTR。這里還沒有完全實現(xiàn)(直到0.95 版)。int sys_pause (void){ current->state = TASK_INTERRUPTIBLE; schedule (); return 0;}// 把當(dāng)前任務(wù)置為不可中斷的等待狀態(tài),并讓睡眠隊列頭的指針指向當(dāng)前任務(wù)。// 只有明確地喚醒時才會返回。該函數(shù)提供了進(jìn)程與中斷處理程序之間的同步機(jī)制。// 函數(shù)參數(shù)*p 是放置等待任務(wù)的隊列頭指針。(參見列表后的說明)。void sleep_on (struct task_struct **p){ struct task_struct *tmp; // 若指針無效,則退出。(指針?biāo)傅膶ο罂梢允荖ULL,但指針本身不會為0)。 if (!p) return; if (current == &(init_task.task)) // 如果當(dāng)前任務(wù)是任務(wù)0,則死機(jī)(impossible!)。 panic ("task[0] trying to sleep"); tmp = *p; // 讓tmp 指向已經(jīng)在等待隊列上的任務(wù)(如果有的話)。 *p = current; // 將睡眠隊列頭的等待指針指向當(dāng)前任務(wù)。 current->state = TASK_UNINTERRUPTIBLE; // 將當(dāng)前任務(wù)置為不可中斷的等待狀態(tài)。 schedule (); // 重新調(diào)度。// 只有當(dāng)這個等待任務(wù)被喚醒時,調(diào)度程序才又返回到這里,則表示進(jìn)程已被明確地喚醒。// 既然大家都在等待同樣的資源,那么在資源可用時,就有必要喚醒所有等待該資源的進(jìn)程。該函數(shù)// 嵌套調(diào)用,也會嵌套喚醒所有等待該資源的進(jìn)程。然后系統(tǒng)會根據(jù)這些進(jìn)程的優(yōu)先條件,重新調(diào)度// 應(yīng)該由哪個進(jìn)程首先使用資源。也即讓這些進(jìn)程競爭上崗。 if (tmp) // 若還存在等待的任務(wù),則也將其置為就緒狀態(tài)(喚醒)。 tmp->state = 0;}// 將當(dāng)前任務(wù)置為可中斷的等待狀態(tài),并放入*p 指定的等待隊列中。參見列表后對sleep_on()的說明。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 ();// 如果等待隊列中還有等待任務(wù),并且隊列頭指針?biāo)赶虻娜蝿?wù)不是當(dāng)前任務(wù)時,則將該等待任務(wù)置為// 可運(yùn)行的就緒狀態(tài),并重新執(zhí)行調(diào)度程序。當(dāng)指針*p 所指向的不是當(dāng)前任務(wù)時,表示在當(dāng)前任務(wù)被放// 入隊列后,又有新的任務(wù)被插入等待隊列中,因此,既然本任務(wù)是可中斷的,就應(yīng)該首先執(zhí)行所有// 其它的等待任務(wù)。 if (*p && *p != current) { (**p).state = 0; goto repeat; }// 下面一句代碼有誤,應(yīng)該是*p = tmp,讓隊列頭指針指向其余等待任務(wù),否則在當(dāng)前任務(wù)之前插入// 等待隊列的任務(wù)均被抹掉了。參見圖4.3。 *p = NULL; if (tmp) tmp->state = 0;}// 喚醒指定任務(wù)*p。void wake_up (struct task_struct **p){ if (p && *p) { (**p).state = 0; // 置為就緒(可運(yùn)行)狀態(tài)。 *p = NULL; }}/* * 好了,從這里開始是一些有關(guān)軟盤的子程序,本不應(yīng)該放在內(nèi)核的主要部分中的。將它們放在這里 * 是因為軟驅(qū)需要一個時鐘,而放在這里是最方便的辦法。 */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; // 數(shù)字輸出寄存器(初值:允許DMA 和請求中斷、啟動FDC)。// 指定軟盤到正常運(yùn)轉(zhuǎn)狀態(tài)所需延遲滴答數(shù)(時間)。// nr -- 軟驅(qū)號(0-3),返回值為滴答數(shù)。int ticks_to_floppy_on (unsigned int nr){ extern unsigned char selected; // 當(dāng)前選中的軟盤號(kernel/blk_drv/floppy.c,122)。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -