Linux作業系統--程序/執行緒(2)
前言
在本系列的上一篇博文裡,我已經介紹了程序/執行緒的基本含義以及一些相關資料結構,現在我們來看看Linux中程序的管理。
程序連結串列
Linux核心定義了一個list_head結構,資料結構定義
struct list_head {
struct list_head *next;
struct list_head *prev;
};
欄位next 和 prev 分別表示通用雙向連結串列向前和向後的指標元素!list_head欄位的指標中存放的是另一個list_head欄位的元素,而不是本身的資料結構地址。如圖
在我們上一篇部落格介紹到的程序描述符(task_struct)也有這個結構體,稱為程序連結串列
這個連結串列是一個迴圈的雙向連結串列,開始的時候只有init_task這一個程序,它是核心的第一個程序,它的初始化是通過靜態分配記憶體,"手動"(其它的程序初始化都是通過動態分配記憶體初始化的)進行的,每新建一個程序,就通過SET_LINKS巨集將該程序的task_struct結構加入到這條雙向連結串列中,不過要注意的是如果一個程序新建一個執行緒(不包括主執行緒),也就是輕量級程序,它是不會加入該連結串列的。通過巨集for_each_process可以從init_task開始遍歷所有的程序。
#define for_each_task(p)
for (p = &init_task ; (p = p->next_task) != &init_task ; )
可執行佇列(runqueue)
當核心尋找一個新程序在CPU上執行時,必須只考慮可執行程序(即處在TASK_RUNNING狀態的程序)。把可執行狀態的程序組成一個雙向迴圈連結串列,也叫可執行佇列(runqueue)。
在task_struct結構中定義了兩個指標。
struct task_struct *next_run, *prev_run;
由正在執行或是可以執行的,其程序狀態均為TASK_RUNNING的程序所組成的一個雙向迴圈連結串列,即run_queue就緒佇列。該連結串列的前後向指標用next_run和prev_run,連結串列的頭和尾都是init_task(即0號
程序)。
但是,為了實現在固定的時間內選出“最佳”的可執行程式,核心將可執行程序的優先順序劃分為0-139,併為此建立了140個可執行程序連結串列,用以組織處於TASK_RUNNING狀態的程序,每個程序優先權對應一個不同的連結串列
linux核心定義了一個prio_array_t型別的結構體來管理這140個連結串列。每個可執行的程序都在這140個連結串列中的一個,通過程序描述符結構中的run_list來實現,它也是一個list_head型別。enqueue_task是把程序描述符插入到某個可執行連結串列中,dequeue_task則從某個可執行連結串列中刪除該程序描述符。TASK_RUNNING狀態的prio_array_t型別的結構體是runqueue結構的arrays[1]成員。
pidhash連結串列
為了通過pid找到程序的描述符,如果直接遍歷程序間互聯的連結串列來查詢程序id為pid的程序描述符顯然是低效的,所以為了更為高效的查詢,linux核心使用了4個hash散列表來加快查詢,之所以使用4個散列表,是為了能根據不同的pid型別來查詢程序描述符,它們分別是程序的pid,執行緒組領頭程序的pid,程序組領頭程序的pid,會話領頭程序的pid。每個型別的散列表中是通過巨集pid_hashfn(x)來進行雜湊值的計算的。每個程序都可能同時處於這是個散列表中,所以在程序描述符中有一個型別為pid結構的pids成員,通過它可以將程序加入散列表中,pid結構中包含解決雜湊衝突的pid_chain成員,它是hlist_node型別的,還有一個是將相同pid鏈起來的pid_list,它是list_head型別。
struct pid_link {
int nr; // pid的數值
struct hlist_node pid_chain;
struct list_head pid_list;
}
struct task_struct {
…
struct pid_link pids[4];
…
}
Linux 程序安全上下文 struct cred
95 *
96 * The parts of the context break down into two categories:
97 *
98 * (1) The objective context of a task. These parts are used when some other
99 * task is attempting to affect this one.
100 *
101 * (2) The subjective context. These details are used when the task is acting
102 * upon another object, be that a file, a task, a key or whatever.
103 *
104 * Note that some members of this structure belong to both categories - the
105 * LSM security pointer for instance.
106 *
107 * A task has two security pointers. task->real_cred points to the objective
108 * context that defines that task's actual details. The objective part of this
109 * context is used whenever that task is acted upon.
110 *
111 * task->cred points to the subjective context that defines the details of how
112 * that task is going to act upon another object. This may be overridden
113 * temporarily to point to another security context, but normally points to the
114 * same context as task->real_cred.
115 */
116 struct cred {
117 atomic_t usage;
118 #ifdef CONFIG_DEBUG_CREDENTIALS
119 atomic_t subscribers; /* number of processes subscribed */
120 void *put_addr;
121 unsigned magic;
122 #define CRED_MAGIC 0x43736564
123 #define CRED_MAGIC_DEAD 0x44656144
124 #endif
125 uid_t uid; /* real UID of the task */
126 gid_t gid; /* real GID of the task */
127 uid_t suid; /* saved UID of the task */
128 gid_t sgid; /* saved GID of the task */
129 uid_t euid; /* effective UID of the task */
130 gid_t egid; /* effective GID of the task */
131 uid_t fsuid; /* UID for VFS ops */
132 gid_t fsgid; /* GID for VFS ops */
133 unsigned securebits; /* SUID-less security management */
134 kernel_cap_t cap_inheritable; /* caps our children can inherit */
135 kernel_cap_t cap_permitted; /* caps we're permitted */
136 kernel_cap_t cap_effective; /* caps we can actually use */
137 kernel_cap_t cap_bset; /* capability bounding set */
138 #ifdef CONFIG_KEYS
139 unsigned char jit_keyring; /* default keyring to attach requested
140 * keys to */
141 struct key *thread_keyring; /* keyring private to this thread */
142 struct key *request_key_auth; /* assumed request_key authority */
143 struct thread_group_cred *tgcred; /* thread-group shared credentials */
144 #endif
145 #ifdef CONFIG_SECURITY
146 void *security; /* subjective LSM security */
147 #endif
148 struct user_struct *user; /* real user ID subscription */
149 struct user_namespace *user_ns; /* cached user->user_ns */
150 struct group_info *group_info; /* supplementary groups for euid/fsgid */
151 struct rcu_head rcu; /* RCU deletion hook */
152 };
正如uid,euid的關係一樣,task_struct也有兩種身份cred
struct task_struct{
...
/* process credentials */
const struct cred __rcu *real_cred; /* objective and real subjective task credentials (COW) */
const struct cred __rcu *cred; /* effective (overridable) subjective task credentials (COW) */
...
}
各種id
uid 和 gid 都是成雙成對的,這裡就拿uid說明其作用:
- uid 是建立程序的使用者的id,不是建立可執行程式的使用者id
- euid 是程序執行過程中實時的ID(或者說動態獲取的ID)
- suid 是儲存的euid切換之前的id,用於euid切換回來