#ifndef _SCHED_H
#define _SCHED_H
#define NR_TASKS 64 // 系統中同時最多任務(進程)數。
#define HZ 100 // 定義系統時鍾滴答頻率(1 百赫茲,每個滴答10ms)
#define FIRST_TASK task[0] // 任務0 比較特殊,所以特意給它單獨定義一個符號。
#define LAST_TASK task[NR_TASKS-1] // 任務數組中的最后一項任務。
#include <linux/head.h> // head 頭文件,定義了段描述符的簡單結構,和幾個選擇符常量。
#include <linux/fs.h> // 文件系統頭文件。定義文件表結構(file,buffer_head,m_inode 等)。
#include <linux/mm.h> // 內存管理頭文件。含有頁面大小定義和一些頁面釋放函數原型。
#include <signal.h> // 信號頭文件。定義信號符號常量,信號結構以及信號操作函數原型。
#if (NR_OPEN > 32)
#error "Currently the close-on-exec-flags are in one word, max 32 files/proc"
#endif
// 這里定義了進程運行可能處的狀態。
#define TASK_RUNNING 0 // 進程正在運行或已准備就緒。
#define TASK_INTERRUPTIBLE 1 // 進程處於可中斷等待狀態。
#define TASK_UNINTERRUPTIBLE 2 // 進程處於不可中斷等待狀態,主要用於I/O 操作等待。
#define TASK_ZOMBIE 3 // 進程處於僵死狀態,已經停止運行,但父進程還沒發信號。
#define TASK_STOPPED 4 // 進程已停止。
#ifndef NULL
#define NULL ((void *) 0) // 定義NULL 為空指針。
#endif
// 復制進程的頁目錄頁表。Linus 認為這是內核中最復雜的函數之一。( mm/memory.c, 105 )
extern int copy_page_tables (unsigned long from, unsigned long to, long size);
// 釋放頁表所指定的內存塊及頁表本身。( mm/memory.c, 150 )
extern int free_page_tables (unsigned long from, unsigned long size);
// 調度程序的初始化函數。( kernel/sched.c, 385 )
extern void sched_init (void);
// 進程調度函數。( kernel/sched.c, 104 )
extern void schedule (void);
// 異常(陷阱)中斷處理初始化函數,設置中斷調用門並允許中斷請求信號。( kernel/traps.c, 181 )
extern void trap_init (void);
// 顯示內核出錯信息,然后進入死循環。( kernel/panic.c, 16 )。
extern void panic (const char *str);
// 往tty 上寫指定長度的字符串。( kernel/chr_drv/tty_io.c, 290 )。
extern int tty_write (unsigned minor, char *buf, int count);
typedef int (*fn_ptr) (); // 定義函數指針類型。
// 下面是數學協處理器使用的結構,主要用於保存進程切換時i387 的執行狀態信息。
struct i387_struct
{
long cwd; // 控制字(Control word)。
long swd; // 狀態字(Status word)。
long twd; // 標記字(Tag word)。
long fip; // 協處理器代碼指針。
long fcs; // 協處理器代碼段寄存器。
long foo;
long fos;
long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
};
// 任務狀態段數據結構(參見列表后的信息)。
struct tss_struct
{
long back_link; /* 16 high bits zero */
long esp0;
long ss0; /* 16 high bits zero */
long esp1;
long ss1; /* 16 high bits zero */
long esp2;
long ss2; /* 16 high bits zero */
long cr3;
long eip;
long eflags;
long eax, ecx, edx, ebx;
long esp;
long ebp;
long esi;
long edi;
long es; /* 16 high bits zero */
long cs; /* 16 high bits zero */
long ss; /* 16 high bits zero */
long ds; /* 16 high bits zero */
long fs; /* 16 high bits zero */
long gs; /* 16 high bits zero */
long ldt; /* 16 high bits zero */
long trace_bitmap; /* bits: trace 0, bitmap 16-31 */
struct i387_struct i387;
};
// 這里是任務(進程)數據結構,或稱為進程描述符。
// ==========================
// long state 任務的運行狀態(-1 不可運行,0 可運行(就緒),>0 已停止)。
// long counter 任務運行時間計數(遞減)(滴答數),運行時間片。
// long priority 運行優先數。任務開始運行時counter = priority,越大運行越長。
// long signal 信號。是位圖,每個比特位代表一種信號,信號值=位偏移值+1。
// struct sigaction sigaction[32] 信號執行屬性結構,對應信號將要執行的操作和標志信息。
// long blocked 進程信號屏蔽碼(對應信號位圖)。
// --------------------------
// int exit_code 任務執行停止的退出碼,其父進程會取。
// unsigned long start_code 代碼段地址。
// unsigned long end_code 代碼長度(字節數)。
// unsigned long end_data 代碼長度 + 數據長度(字節數)。
// unsigned long brk 總長度(字節數)。
// unsigned long start_stack 堆棧段地址。
// long pid 進程標識號(進程號)。
// long father 父進程號。
// long pgrp 父進程組號。
// long session 會話號。
// long leader 會話首領。
// unsigned short uid 用戶標識號(用戶id)。
// unsigned short euid 有效用戶id。
// unsigned short suid 保存的用戶id。
// unsigned short gid 組標識號(組id)。
// unsigned short egid 有效組id。
// unsigned short sgid 保存的組id。
// long alarm 報警定時值(滴答數)。
// long utime 用戶態運行時間(滴答數)。
// long stime 系統態運行時間(滴答數)。
// long cutime 子進程用戶態運行時間。
// long cstime 子進程系統態運行時間。
// long start_time 進程開始運行時刻。
// unsigned short used_math 標志:是否使用了協處理器。
// --------------------------
// int tty 進程使用tty 的子設備號。-1 表示沒有使用。
// unsigned short umask 文件創建屬性屏蔽位。
// struct m_inode * pwd 當前工作目錄i 節點結構。
// struct m_inode * root 根目錄i 節點結構。
// struct m_inode * executable 執行文件i 節點結構。
// unsigned long close_on_exec 執行時關閉文件句柄位圖標志。(參見include/fcntl.h)
// struct file * filp[NR_OPEN] 進程使用的文件表結構。
// --------------------------
// struct desc_struct ldt[3] 本任務的局部表描述符。0-空,1-代碼段cs,2-數據和堆棧段ds&ss。
// --------------------------
// struct tss_struct tss 本進程的任務狀態段信息結構。
// ==========================
struct task_struct
{
/* these are hardcoded - don't touch */
long state; /* -1 unrunnable, 0 runnable, >0 stopped */
long counter;
long priority;
long signal;
struct sigaction sigaction[32];
long blocked; /* bitmap of masked signals */
/* various fields */
int exit_code;
unsigned long start_code, end_code, end_data, brk, start_stack;
long pid, father, pgrp, session, leader;
unsigned short uid, euid, suid;
unsigned short gid, egid, sgid;
long alarm;
long utime, stime, cutime, cstime, start_time;
unsigned short used_math;
/* file system info */
int tty; /* -1 if no tty, so it must be signed */
unsigned short umask;
struct m_inode *pwd;
struct m_inode *root;
struct m_inode *executable;
unsigned long close_on_exec;
struct file *filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
struct desc_struct ldt[3];
/* tss for this task */
struct tss_struct tss;
};
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x9ffff (=640kB)
*/
/*
* INIT_TASK 用於設置第1 個任務表,若想修改,責任自負?!
* 基址Base = 0,段長limit = 0x9ffff(=640kB)。
*/
// 對應上面任務結構的第1 個任務的信息。
#define INIT_TASK \
/* state etc */ { 0,15,15, \ // state, counter, priority
/* signals */ 0,
{
{
}
,}
, 0, \ // signal, sigaction[32], blocked
/* ec,brk... */ 0, 0, 0, 0, 0, 0, \
// exit_code,start_code,end_code,end_data,brk,start_stack
/* pid etc.. */ 0, -1, 0, 0, 0, \
// pid, father, pgrp, session, leader
/* uid etc */ 0, 0, 0, 0, 0, 0, \
// uid, euid, suid, gid, egid, sgid
/* alarm */ 0, 0, 0, 0, 0, 0, \
// alarm, utime, stime, cutime, cstime, start_time
/* math */ 0, \
// used_math
/* fs info */ -1, 0022, NULL, NULL, NULL, 0, \
// tty,umask,pwd,root,executable,close_on_exec
/* filp */
{
NULL,}
, \ // filp[20]
{
\ // ldt[3]
{
0, 0}
,
/* ldt */
{
0x9f, 0xc0fa00}
, \ // 代碼長640K,基址0x0,G=1,D=1,DPL=3,P=1 TYPE=0x0a
{
0x9f, 0xc0f200}
, \ // 數據長640K,基址0x0,G=1,D=1,DPL=3,P=1 TYPE=0x02
}
,
/*tss*/
{
0, PAGE_SIZE + (long) &init_task, 0x10, 0, 0, 0, 0, (long) &pg_dir, \ // tss
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, _LDT (0), 0x80000000,
{
}
}
,}
extern struct task_struct *task[NR_TASKS]; // 任務數組。
extern struct task_struct *last_task_used_math; // 上一個使用過協處理器的進程。
extern struct task_struct *current; // 當前進程結構指針變量。
extern long volatile jiffies; // 從開機開始算起的滴答數(10ms/滴答)。
extern long startup_time; // 開機時間。從1970:0:0:0 開始計時的秒數。
#define CURRENT_TIME (startup_time+jiffies/HZ) // 當前時間(秒數)。
// 添加定時器函數(定時時間jiffies 滴答數,定時到時調用函數*fn())。( kernel/sched.c,272)
extern void add_timer (long jiffies, void (*fn) (void));
// 不可中斷的等待睡眠。( kernel/sched.c, 151 )
extern void sleep_on (struct task_struct **p);
// 可中斷的等待睡眠。( kernel/sched.c, 167 )
extern void interruptible_sleep_on (struct task_struct **p);
// 明確喚醒睡眠的進程。( kernel/sched.c, 188 )
extern void wake_up (struct task_struct **p);
/*
* Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
* 4-TSS0, 5-LDT0, 6-TSS1 etc ...
*/
/*
* 尋找第1 個TSS 在全局表中的入口。0-沒有用nul,1-代碼段cs,2-數據段ds,3-系統段syscall
* 4-任務狀態段TSS0,5-局部表LTD0,6-任務狀態段TSS1,等。
*/
// 全局表中第1 個任務狀態段(TSS)描述符的選擇符索引號。
#define FIRST_TSS_ENTRY 4
// 全局表中第1 個局部描述符表(LDT)描述符的選擇符索引號。
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
// 宏定義,計算在全局表中第n 個任務的TSS 描述符的索引號(選擇符)。
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
// 宏定義,計算在全局表中第n 個任務的LDT 描述符的索引號。
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
// 宏定義,加載第n 個任務的任務寄存器tr。
#define ltr(n) __asm__( "ltr %%ax":: "a" (_TSS(n)))
// 宏定義,加載第n 個任務的局部描述符表寄存器ldtr。
#define lldt(n) __asm__( "lldt %%ax":: "a" (_LDT(n)))
// 取當前運行任務的任務號(是任務數組中的索引值,與進程號pid 不同)。
// 返回:n - 當前任務號。用於( kernel/traps.c, 79)。
#define str(n) \
__asm__( "str %%ax\n\t" \ // 將任務寄存器中TSS 段的有效地址??ax
"subl %2,%%eax\n\t" \ // (eax - FIRST_TSS_ENTRY*8)??eax
"shrl $4,%%eax" \ // (eax/16)??eax = 當前任務號。
: "=a" (n):"a" (0), "i" (FIRST_TSS_ENTRY << 3))
/*
* switch_to(n) should switch tasks to task nr n, first
* checking that n isn't the current task, in which case it does nothing.
* This also clears the TS-flag if the task we switched to has used
* tha math co-processor latest.
*/
/*
* switch_to(n)將切換當前任務到任務nr,即n。首先檢測任務n 不是當前任務,
* 如果是則什么也不做退出。如果我們切換到的任務最近(上次運行)使用過數學
* 協處理器的話,則還需復位控制寄存器cr0 中的TS 標志。
*/
// 輸入:%0 - 新TSS 的偏移地址(*&__tmp.a); %1 - 存放新TSS 的選擇符值(*&__tmp.b);
// dx - 新任務n 的選擇符;ecx - 新任務指針task[n]。
// 其中臨時數據結構__tmp 中,a 的值是32 位偏移值,b 為新TSS 的選擇符。在任務切換時,a 值
// 沒有用(忽略)。在判斷新任務上次執行是否使用過協處理器時,是通過將新任務狀態段的地址與
// 保存在last_task_used_math 變量中的使用過協處理器的任務狀態段的地址進行比較而作出的。
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__( "cmpl %%ecx,_current\n\t" \ // 任務n 是當前任務嗎?(current ==task[n]?)
"je 1f\n\t" \ // 是,則什么都不做,退出。
"movw %%dx,%1\n\t" \ // 將新任務的選擇符??*&__tmp.b。
"xchgl %%ecx,_current\n\t" \ // current = task[n];ecx = 被切換出的任務。
"ljmp %0\n\t" \ // 執行長跳轉至*&__tmp,造成任務切換。
// 在任務切換回來后才會繼續執行下面的語句。
"cmpl %%ecx,_last_task_used_math\n\t" \ // 新任務上次使用過協處理器嗎?
"jne 1f\n\t" \ // 沒有則跳轉,退出。
"clts\n" \ // 新任務上次使用過協處理器,則清cr0 的TS 標志。
"1:"::"m" (*&__tmp.a), "m" (*&__tmp.b),
"d" (_TSS (n)), "c" ((long) task[n]));
}
// 頁面地址對准。(在內核代碼中沒有任何地方引用!!)
#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)
// 設置位於地址addr 處描述符中的各基地址字段(基地址是base),參見列表后說明。
// %0 - 地址addr 偏移2;%1 - 地址addr 偏移4;%2 - 地址addr 偏移7;edx - 基地址base。
#define _set_base(addr,base) \
__asm__( "movw %%dx,%0\n\t" \ // 基址base 低16 位(位15-0)??[addr+2]。
"rorl $16,%%edx\n\t" \ // edx 中基址高16 位(位31-16)??dx。
"movb %%dl,%1\n\t" \ // 基址高16 位中的低8 位(位23-16)??[addr+4]。
"movb %%dh,%2" \ // 基址高16 位中的高8 位(位31-24)??[addr+7]。
::"m" (*((addr) + 2)), "m" (*((addr) + 4)), "m" (*((addr) + 7)), "d" (base):"dx")
// 設置位於地址addr 處描述符中的段限長字段(段長是limit)。
// %0 - 地址addr;%1 - 地址addr 偏移6 處;edx - 段長值limit。
#define _set_limit(addr,limit) \
__asm__( "movw %%dx,%0\n\t" \ // 段長limit 低16 位(位15-0)??[addr]。
"rorl $16,%%edx\n\t" \ // edx 中的段長高4 位(位19-16)??dl。
"movb %1,%%dh\n\t" \ // 取原[addr+6]字節??dh,其中高4 位是些標志。
"andb $0xf0,%%dh\n\t" \ // 清dh 的低4 位(將存放段長的位19-16)。
"orb %%dh,%%dl\n\t" \ // 將原高4 位標志和段長的高4 位(位19-16)合成1 字節,
"movb %%dl,%1" \ // 並放會[addr+6]處。
::"m" (*(addr)), "m" (*((addr) + 6)), "d" (limit):"dx")
// 設置局部描述符表中ldt 描述符的基地址字段。
#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
// 設置局部描述符表中ldt 描述符的段長字段。
#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )
// 從地址addr 處描述符中取段基地址。功能與_set_base()正好相反。
// edx - 存放基地址(__base);%1 - 地址addr 偏移2;%2 - 地址addr 偏移4;%3 - addr 偏移7。
#define _get_base(addr) ({\
unsigned long __base; \
__asm__( "movb %3,%%dh\n\t" \ // 取[addr+7]處基址高16 位的高8 位(位31-24)??dh。
"movb %2,%%dl\n\t" \ // 取[addr+4]處基址高16 位的低8 位(位23-16)??dl。
"shll $16,%%edx\n\t" \ // 基地址高16 位移到edx 中高16 位處。
"movw %1,%%dx" \ // 取[addr+2]處基址低16 位(位15-0)??dx。
:"=d" (__base) \ // 從而edx 中含有32 位的段基地址。
:"m" (*((addr) + 2)), "m" (*((addr) + 4)), "m" (*((addr) + 7)));
__base;
}
)
// 取局部描述符表中ldt 所指段描述符中的基地址。
#define get_base(ldt) _get_base( ((char *)&(ldt)) )
// 取段選擇符segment 的段長值。
// %0 - 存放段長值(字節數);%1 - 段選擇符segment。
#define get_limit(segment) ({ \
unsigned long __limit; \
__asm__( "lsll %1,%0\n\tincl %0": "=r" (__limit): "r" (segment)); \
__limit;})