2008年1月26日 星期六

Linux Process - 2

Linux Kernel 將所有的 Task 藉由 struct list_head 串成串列
依據指標鏈結方式的不同,會得到不同的串列結果

• 由 tasks 構成的行程的環狀雙向鏈結串列
串列首為 init_task,即 swapper(0),最後一個行程的 tasks 指標會指回 swapper(0)

假設系統中只存在 3個行程,分別為 swapper、init 及 Process-X,則 tasks 串列如下:
(以下的圖形都只畫出 next 方向的指標)

欲走訪 tasks 串列的所有節點,可使用以下方式:

struct task_struct *task = NULL;

for_each_process( task )
{
  //列出所有行程的名稱與 PID
  printk("%s(%d)\n", task->comm, task->pid);
}


• 由 children & sibliing 構成的父程序與子程序串列
每一個行程都可能會衍生出子行程,以 children 指標表示其第一個子行程
而子行程彼此之間以 sibling 指標鏈結,最後一個子行程的 sibling 指標則指向父行程
至於沒有子行程的行程,其 children 指標會指向自己

假設 Process-A 衍生出 Process-B,而 Process-B 衍生出 Process-C
children & sibling 串列如下:

假設 Process-D 衍生出 Process-E 與 Process-F,則 children & sibling 串列如下:

欲搜尋目前行程的所有子行程,可使用以下方式:

struct task_struct *task = NULL;
struct list_head *list = NULL;

list_for_each(list, &(current->children))
{
  task = list_entry(list, struct task_struct, sibling);
  //現在 task 已指向 current 的某個子行程
}

2008年1月25日 星期五

Linux Process - 1

Linux 將 Process 與 Thread 都視為 Task,所以兩者使用同樣的資料結構
定義於 include/linux/sched.h
事實上,struct task_struct 是很大的結構,又稱為 Process Descriptor,這裏只列出幾個欄位
其中 taskschildrensibling 為 struct list_head 的型態,用來建立所有 Task 彼此間的串列關係
pid 是用來識別各個 Task 的唯一編號

struct list_head
{
  struct list_head *next;
  struct list_head *prev;
};

定義於 include/linux/list.h
這是一個雙向鏈結串列,在 Task 結構中嵌入 struct list_head 的欄位,便可以建立串列
若要經由 list_head 的指標取回 Task 的結構,則使用以下的巨集:

list_entry(ptr, type, member)

此外,在 Kernel 中使用 current 這個巨集,可以直接取得目前正在執行的 Task 的指標
定義於 include/asm-i386/current.h


Linux 第一個執行的 Task 是 swapper(0),以靜態變數 init_task 表示
第二個 Task 是 init(1),是由 swapper 衍生出來的,然後 init 會再衍生出其他 Task
而這兩個最初的 Task 可以畫出以下的串列關係:

2008年1月8日 星期二

Linux Memory Paging - 3

Linux Kernel 有自己專屬的 Page Directory 及 Page Table
在系統初始化時,會先建立 2個 Page Table -- 包含 2048個 Page,共 8MB 的記憶體空間
這 8MB 是 Linux 開機最少需要的記憶體大小,而且保留給 Kernel 使用

Kernel Page Global Directory 是以變數 swapper_pg_dir 表示
其資料結構可視為含有 1024 個元素的 pgd_t 型態的陣列
實體記憶體位址為 0x00101000

Kernel Page Table (第 0及第 1個 Table) 是以變數 pg0pg1 表示
其資料結構可視為含有 1024個元素的 pte_t 型態的陣列
實體記憶體位址分別為 0x001020000x00103000

Linux Page 初始化的動作定義於 arch/i386/kernel/head.S
因為開機時僅需要 8MB,所以只要初始化 2個 Page Table 便可,即 pg0 及 pg1
其餘的 Page Table 均填入 0 的值

又為了讓這 2個 Page Table 可以被 Real Mode 及 Protect Mode 所存取
Kernel Page Global Directory 中的第 0、第 768個 Entry以及第 1、第 769個 Entry
分別會設為相同的 Page Table 的實體記憶體位址,如圖所示


初始化完成後,得到以下的結果:
swapper_pg_dir[0] = swapper_pg_dir[768] = 0x00102007
swapper_pg_dir[1] = swapper_pg_dir[769] = 0x00103007
pg0 加上 pg1 定址到實體記憶體 0x00000000 - 0x007FFFFF,共 8MB 的分頁

根據 pgd_t 及 pte_t 的欄位格式可得知:
1) bit 0-11 為 0x007,表示 Enable 旗號 Present、Read/Write、User/Supervisor
2) bit 12-31 為 Base Address