開啟linux系統的命令列,敲下「ps -a」命令,就能看到系統中的所有任務:其中有乙個pid號為0任務格外的顯眼。
我們知道linux中的task都是通過fork族函式由父程序創造出來,而這個0號程序卻是個例外,它由人類捏造出來,沒有parent。
linux中所有task都由乙個task_struct結構來代表,一旦task有了task_struct,我們就能夠看得見摸得著;乙個新task的建立過程首先就是為其分配task_struct結構,使它的靈魂有所寄託。
而這個0號程序(在linux中一般採用「任務」來指代乙個程序,這裡「程序」的說法和「任務」乙個意思)的task_struct結構的物件通過靜態全域性變數的方式建立,這個物件例項叫init_task。它隨核心**一起編到核心映象之中,核心映象一旦編譯完成0號程序的task_struct例項init_task也隨之生成,即在核心中已經有存在。這個例項init_task的定義在init/init_task.c目錄下:
struct task_struct init_task = init_task(init_task);
這個init_task就是0號程序task_struct結構物件例項,init_task中的各個成員通過init_task(init_task)這個巨集來初始化,我們列舉出一些我們關注的初始化成員值,沒有列出的用省略號代替:
#define init_task(tsk) \
可以看出init_task的成員就是通過上面的巨集展開後被一一賦予了給定的值:
1. 核心堆疊
乙個任務核心堆疊指標由task_struct.stack來指定,init_task的stack指標也是靜態分配的:
.stack = &init_thread_info
2. 任務標誌
init_task是乙個核心任務,其標誌也初始設定為pf_kthread。
.flags = pf_kthread
3. 排程策略
init_task雖然是核心中的第乙個任務,但它並不高特殊,不論是排程策略(sched_normal)還是優先順序(120),都是最普通的:
.prio = max_prio-20, \
.static_prio = max_prio-20, \
.normal_prio = max_prio-20, \
.policy = sched_normal, \
4. init_task的父輩
init_task是系統中的第乙個任務,所以它就是系統中的始祖:
.real_parent = &tsk, \
.parent = &tsk, \
.comm = init_task_comm
一旦核心編譯完成生成映象,這個init_task就已經在其中占有自己的一席之地,核心已啟動它便存在!
從上面我們已經看到0號任務的堆疊,準確的叫核心堆疊,已經初始化為init_thread_info:
.stack = &init_thread_info
#define init_thread_info (init_thread_union.thread_info)
看到沒?這個init_thread_info只是乙個幌子,實際上stack指向的是 .stack = &init_thread_union.thread_info;而這個init_thread_union是乙個與架構無關的全域性union,定義在init/init_task.c中:
union thread_union init_thread_union __init_task_data =
;
這個init_thread_union又是乙個巨集,我們再將他展開看看.....等等,思路似乎有些亂。我們先縷一縷:
1) 前面已經確定了0號任務的核心堆疊指標init_task.stack指向的是&init_thread_union.thread_info;
2) 乙個task的核心堆疊指標指向一片連續的記憶體區域(即核心堆疊使用區域),這片堆疊區域的大小隨架構的不同其size也有所差異,一般通過巨集thread_size來表示。
3) 綜合1)和2)可以知道0號程序核心堆疊指標與&init_thread_union.thread_info相等,即這片核心堆疊區域的底端實際放的就是init_thread_union.thread_info。而實際在一般情況下乙個task的核心堆疊底部確實放著乙個thread_info結構!
4) 核心堆疊的作用就是用來儲存資料用的,而0號任務的這塊堆疊區域卻是靜態分配的;那這塊區域是在**分配的呢??
好,我們就再來深挖一下0號任務堆疊以及init_thread_union.thread_info的真面目。
既然0號task的堆疊指標指向init_thread_union.thread_info的位址,那就撥開雲霧見天日,研究一下我們深挖的重點:0號任務init_task.stack指標指向的大小thread_size的區域究竟在**分配。
首先,init_task.stack是與init_thread_union這個變數關聯,我們先來關注它。
union thread_union init_thread_union __init_task_data =
;
這個thred_union聯合體定義如下:
union thread_union ;
即這個靜態全域性變數 init_thread_union實際上已經靜態分配了thread_size的大小,由於它是乙個聯合體,所以我們引用init_thread_union.thread_info就可以夠著這個thread_size的記憶體區域,我們的init_task.stack也就在這裡著落。
一般情況下全域性變數都放在核心映象的資料段,但是這裡可以的init_thread_union通過__init_task_data屬性使其init_thread_union最終鏈結到映象中的__init_task_data這個段中,即init_task的核心堆疊thread_size大小的區域最終放在這個__init_task_data屬性段中。
既然我們知道0號任務核心堆疊是在__init_task_data屬性段中,那我們就來研究一下:
#define __init_task_data __attribute__((__section__(".data..init_task")))
即init_thread_union最終是鏈結到.data..init_task這個資料段中,即init_task的堆疊指向.data..init_task這個位置。
而.data..init_task這個段的位置是在vmlinux.lds.s檔案中決定的,不同的架構其位置不一樣:
在arm架構中如下所示:
.data : at(__data_loc) /* r3指向__mmap_switched_data + 4*sizeof(long) */
cmp r4, r5 @ copy data segment if needed
......
/* 將 init_thread_union+thread_start_sp 裝入sp暫存器 */
arm( ldmia r3, )
........
b start_kernel
.....................
__mmap_switched_data:
.long __data_loc @ r4
.long _sdata @ r5
.long __bss_start @ r6
.long _end @ r7
/* 執行ldmia r3!, 後r3指向這裡 */
.long processor_id @ r4
.long __machine_arch_type @ r5
.long __atags_pointer @ r6
#ifdef config_cpu_cp15
.long cr_alignment @ r7
#else
.long 0 @ r7
#endif
/* 這裡存放值 init_thread_union+thread_start_sp */
.long init_thread_union + thread_start_sp @ sp
Linux中的0號程序和1號程序
系統允許乙個程序建立新程序,新程序即為子程序,子程序還可以建立新的子程序,形成程序樹結構模型。整個linux系統的所有程序也是乙個樹形結 構。樹根是系統自動構造的,即在核心態下執行的0號程序,它是所有程序的祖先。由0號程序建立1號程序 核心態 1號負責執行核心的部分初始化工作及進 行系統配置,並建立...
Linux中的延時任務以及定時任務
root localhost at 23 37 設定任務執行時間 at rm fr mnt 任務執行動作 at ctrl d 用ctrl d發起任務 root localhost at now 1min 延時1分鐘 at rm fr mnt at 命令 注釋at l 檢視任務列表 at c 檢視任務...
Linux中建立定時任務
首先來看一下 crontab的常用命令 crontab e user 使用預設的文字編輯器開啟指定使用者的任務列表檔案,user是要開啟的使用者的檔案 crontab l user 列出當前執行的任務,相當於檢視任務列表檔案的內容 我們一般的用法就是 第一步 crontab e 打卡任務列表 第二步...