?? sched.c
字號:
// 指定軟盤到正常運轉狀態所需延遲滴答數(時間)。// nr -- 軟驅號(0-3),返回值為滴答數。intticks_to_floppy_on (unsigned int nr){ extern unsigned char selected; // 當前選中的軟盤號(kernel/blk_drv/floppy.c,122)。 unsigned char mask = 0x10 << nr; // 所選軟驅對應數字輸出寄存器中啟動馬達比特位。 if (nr > 3) panic ("floppy_on: nr>3"); // 最多4 個軟驅。 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; } // 如果數字輸出寄存器的當前值與要求的值不同,則向FDC 數字輸出端口輸出新值(mask)。并且如果 // 要求啟動的馬達還沒有啟動,則置相應軟驅的馬達啟動定時器值(HZ/2 = 0.5 秒或50 個滴答)。 // 此后更新當前數字輸出寄存器值current_DOR。 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];}// 等待指定軟驅馬達啟動所需時間。voidfloppy_on (unsigned int nr){ cli (); // 關中斷。 while (ticks_to_floppy_on (nr)) // 如果馬達啟動定時還沒到,就一直把當前進程置 sleep_on (nr + wait_motor); // 為不可中斷睡眠狀態并放入等待馬達運行的隊列中。 sti (); // 開中斷。}// 置關閉相應軟驅馬達停轉定時器(3 秒)。voidfloppy_off (unsigned int nr){ moff_timer[nr] = 3 * HZ;}// 軟盤定時處理子程序。更新馬達啟動定時值和馬達關閉停轉計時值。該子程序是在時鐘定時// 中斷中被調用,因此每一個滴答(10ms)被調用一次,更新馬達開啟或停轉定時器的值。如果某// 一個馬達停轉定時到,則將數字輸出寄存器馬達啟動位復位。voiddo_floppy_timer (void){ int i; unsigned char mask = 0x10; for (i = 0; i < 4; i++, mask <<= 1) { if (!(mask & current_DOR)) // 如果不是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 個定時器鏈表(64 個任務)。// 定時器鏈表結構和定時器數組。static struct timer_list{ long jiffies; // 定時滴答數。 void (*fn) (); // 定時處理程序。 struct timer_list *next; // 下一個定時器。}timer_list[TIME_REQUESTS], *next_timer = NULL;// 添加定時器。輸入參數為指定的定時值(滴答數)和相應的處理程序指針。// jiffies – 以10 毫秒計的滴答數;*fn()- 定時時間到時執行的函數。voidadd_timer (long jiffies, void (*fn) (void)){ struct timer_list *p; // 如果定時處理程序指針為空,則退出。 if (!fn) return; cli (); // 如果定時值<=0,則立刻調用其處理程序。并且該定時器不加入鏈表中。 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; // 鏈表項按定時值從小到大排序。在排序時減去排在前面需要的滴答數,這樣在處理定時器時只要 // 查看鏈表頭的第一項的定時是否到期即可。[[?? 這段程序好象沒有考慮周全。如果新插入的定時 // 器值 < 原來頭一個定時器值時,也應該將所有后面的定時值均減去新的第1 個的定時值。]] 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 ();}//// 時鐘中斷C 函數處理程序,在kernel/system_call.s 中的_timer_interrupt(176 行)被調用。// 參數cpl 是當前特權級0 或3,0 表示內核代碼在執行。// 對于一個進程由于執行時間片用完時,則進行任務切換。并執行一個計時更新工作。voiddo_timer (long cpl){ extern int beepcount; // 揚聲器發聲時間滴答數(kernel/chr_drv/console.c,697) extern void sysbeepstop (void); // 關閉揚聲器(kernel/chr_drv/console.c,691) // 如果發聲計數次數到,則關閉發聲。(向0x61 口發送命令,復位位0 和1。位0 控制8253 // 計數器2 的工作,位1 控制揚聲器)。 if (beepcount) if (!--beepcount) sysbeepstop (); // 如果當前特權級(cpl)為0(最高,表示是內核程序在工作),則將超級用戶運行時間stime 遞增; // 如果cpl > 0,則表示是一般用戶程序在工作,增加utime。 if (cpl) current->utime++; else current->stime++; // 如果有用戶的定時器存在,則將鏈表第1 個定時器的值減1。如果已等于0,則調用相應的處理 // 程序,并將該處理程序指針置為空。然后去掉該項定時器。 if (next_timer) { // next_timer 是定時器鏈表的頭指針(見270 行)。 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) (); // 調用處理函數。 } } // 如果當前軟盤控制器FDC 的數字輸出寄存器中馬達啟動位有置位的,則執行軟盤定時程序(245 行)。 if (current_DOR & 0xf0) do_floppy_timer (); if ((--current->counter) > 0) return; // 如果進程運行時間還沒完,則退出。 current->counter = 0; if (!cpl) return; // 對于超級用戶程序,不依賴counter 值進行調度。 schedule ();}// 系統調用功能 - 設置報警定時時間值(秒)。// 如果已經設置過alarm 值,則返回舊值,否則返回0。intsys_alarm (long seconds){ int old = current->alarm; if (old) old = (old - jiffies) / HZ; current->alarm = (seconds > 0) ? (jiffies + HZ * seconds) : 0; return (old);}// 取當前進程號pid。intsys_getpid (void){ return current->pid;}// 取父進程號ppid。intsys_getppid (void){ return current->father;}// 取用戶號uid。intsys_getuid (void){ return current->uid;}// 取euid。intsys_geteuid (void){ return current->euid;}// 取組號gid。intsys_getgid (void){ return current->gid;}// 取egid。intsys_getegid (void){ return current->egid;}// 系統調用功能 -- 降低對CPU 的使用優先權(有人會用嗎??)。// 應該限制increment 大于0,否則的話,可使優先權增大??!intsys_nice (long increment){ if (current->priority - increment > 0) current->priority -= increment; return 0;}// 調度程序的初始化子程序。voidsched_init (void){ int i; struct desc_struct *p; // 描述符表結構指針。 if (sizeof (struct sigaction) != 16) // sigaction 是存放有關信號狀態的結構。 panic ("Struct sigaction MUST be 16 bytes"); // 設置初始任務(任務0)的任務狀態段描述符和局部數據表描述符(include/asm/system.h,65)。 set_tss_desc (gdt + FIRST_TSS_ENTRY, &(init_task.task.tss)); set_ldt_desc (gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt)); // 清任務數組和描述符表項(注意i=1 開始,所以初始任務的描述符還在)。 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 */ /* 清除標志寄存器中的位NT,這樣以后就不會有麻煩 */ // NT 標志用于控制程序的遞歸調用(Nested Task)。當NT 置位時,那么當前中斷任務執行 // iret 指令時就會引起任務切換。NT 指出TSS 中的back_link 字段是否有效。 __asm__ ("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); // 復位NT 標志。 ltr (0); // 將任務0 的TSS 加載到任務寄存器tr。 lldt (0); // 將局部描述符表加載到局部描述符表寄存器。 // 注意?。∈菍DT 中相應LDT 描述符的選擇符加載到ldtr。只明確加載這一次,以后新任務 // LDT 的加載,是CPU 根據TSS 中的LDT 項自動加載。 // 下面代碼用于初始化8253 定時器。 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);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -