該核心程式主要包含程序排程程式的實現。程序排程採用了基於優先順序的時間片輪轉演算法。
#include
#include
#include
#include
#include
#include
#include
#include
這是乙個巨集,用來取得訊號的二進位制數值。
輸入:訊號編號,1-32
輸出:訊號的二進位制數值
#define _s(nr) (1<<((nr)-1))
被阻塞的訊號掩碼,其中有兩個訊號不能被阻塞sigkll和sigstop。這兩個訊號的
位為0,其他位為1
#define _blockable (~(_s(sigkill) | _s(sigstop)))
顯示制定任務的資訊:包括程序id,任務號,任務當前狀態以及指定任務核心態堆疊空閒的位元組數
void
show_task (int nr, struct task_struct *p)
列印所有任務的資訊
nr_tasks表示系統允許的最大任務數量
void
show_stat (void)
#define latch (1193180/hz)
申明外部定義的函式。
extern void mem_use (void);
extern int timer_interrupt (void);
extern int system_call (void);
乙個聯合體,該聯合體既可以用位元組形式表示,也可以用任務結構表示。
我們知道乙個任務的核心堆疊大小是4kb,即一頁大小。
union task_union
;定義初始化任務陣列
static union task_union init_task = ;
定義變數,從開機起經過的滴答數,10ms為乙個滴答,將變數申明為volatile是因為
防止編譯器優化而導致的變數值不一致的情況。優化後變數值很有可能直接來自暫存器
我們使用此變數申明修飾符,保證每次從記憶體中取變數的值。
long volatile jiffies = 0;
從開機後經歷的秒數。從2023年1月1日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] = ;
定義任務堆疊,大小為1024個4位元組的項組成。
long user_stack[page_size >> 2];
定義核心資料段的乙個結構體,該結構體中包括
乙個核心堆疊指標和乙個段選擇符
0x10:核心資料段段選擇符
struct
stack_start =
;當任務發生切換的時候,用來儲存上乙個任務的協處理器上下文環境,恢復當前任務的
協處理器上下文環境。
void
math_state_restore ()
並置上次換出任務指標為當前任務
last_task_used_math = current;
判斷當前任務是否使用了協處理器,如果是恢復協處理器的上下文。如果是
第一次使用,需要進行協處理器初始化工作。
if (current->used_math)
else
}核心程序排程程式
void
schedule (void)
如果訊號點陣圖中表示有非阻塞訊號被遞送,該任務的狀態是可中斷的,那麼將該任務狀態置為就緒
if (((*p)->signal & ~(_blockable & (*p)->blocked)) &&
(*p)->state == task_interruptible)
(*p)->state = task_running;
}檢查就緒的任務,判斷下乙個執行的任務。
while (1)
如果最大時間片不為0,那麼就切換到該任務去執行
if (c)
break;
如果所有任務的時間片都為0,那麼重新計算各個任務的時間片,計算原則是根據優先順序進行計算
然後從新選擇時間片最大的任務去執行。
for (p = &last_task; p > &first_task; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) + (*p)->priority;
}任務切換
switch_to (next);
}將該任務置為可中斷狀態,然後執行任務排程程式
intsys_pause (void)
在指定任務上睡眠,任務0不能睡眠。如果cpu上沒有任務執行時就執行任務0,該函式是
不可中斷的。
void
sleep_on (struct task_struct **p)
該函式與上面的那個sleep函式的區別在於,如果下次再次排程到
睡眠的程序的時候,判斷如果當前程序是睡眠的程序,需要重新進行
排程。void
interruptible_sleep_on (struct task_struct **p)
*p = null;
if (tmp)
tmp->state = 0;
}將任務陣列中的任務置為就緒喚醒狀態,然後置該任務陣列項為0.
void
wake_up (struct task_struct **p)
}定義了timer list的最大長度
#define time_requests 64
定義timer list結構
static struct timer_list
timer_list[time_requests], *next_timer = null;
加入定時器
void add_timer (long jiffies, void (*fn) (void))
}開啟可遮蔽中斷
sti ();
}時鐘中斷處理函式呼叫此c函式。
void
do_timer (long cpl)
}這部分是對軟碟機的處理,在這裡不講
if (current_dor & 0xf0)
do_floppy_timer ();
if ((--current->counter) > 0)
return;
current->counter = 0;
if (!cpl)
return;
中斷執行完成,重新排程任務
schedule ();
}設定定時器,返回上次設定的定時器剩餘的秒數
將新的秒數轉換成滴答數,設定到當前程序中去
intsys_alarm (long seconds)
intsys_getpid (void)
取程序id
intsys_getppid (void)
取使用者id
intsys_getuid (void)
取有效使用者id
intsys_geteuid (void)
取組id
intsys_getgid (void)
取有效組id
intsys_getegid (void)
設定程序nice值,即降低優先順序
intsys_nice (long increment)
在main.c中呼叫,對schedle進行初始化。
void
sched_init (void)
desc_table[256];
一共256項。
確定描述符標的偏移值。之前設定過tss和第乙個ldt的描述符。
為任務陣列清0,將128個描述符表項清0
p = gdt + 2 + first_tss_entry;
for (i = 1; i < nr_tasks; i++)
清除標誌暫存器中的nt標誌位,巢狀標誌位,如果中斷處理程式呼叫iret指令,就會引起任務切換
__asm__ ("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
將任務0的任務狀態段的選擇符加入到任務暫存器中
ltr (0);
將任務0的區域性表選擇符載入到區域性描述符暫存器中。
lldt (0);
初始化定時器。
outb_p (0x36, 0x43);
outb_p (latch & 0xff, 0x40);
outb (latch >> 8, 0x40);
set_intr_gate (0x20, &timer_interrupt);
outb (inb_p (0x21) & ~0x01, 0x21);
set_system_gate (0x80, &system_call);
}
Mysql 外連線和內連線分析
表1 test user 表2 一 left join 2 select from test user a left join test order b on a.name b.namewhere a.name 張三 3 select from test user a left join test ...
linux核心分析之fork c
include include include include include 申明外部呼叫函式,驗證位址所指向的頁面是否可寫 extern void write verify unsigned long address 全域性變數,用於產生可用的程序id long last pid 0 對指定起始...
linux核心分析之sys c
include include include include include include include 這個檔案中包含了絕大部分系統呼叫函式的實現,如果系統呼叫在該核心版本中沒實現,就直接返回enosys int sys ftime int sys break int sys ptrace ...