9,linux進程切換
進程切換:
基本概念:
進程上下文:當一個進程在執行時,CPU的所有寄存器中的值、進程的狀態以及堆棧中的內容被稱為該進程的上下文。當內核需要切換到另一個進程時,它需要保存當前進程的所有狀態,即保存當前進程的上下文,運行於進程上下文的進程是可以被搶占的。
硬件上下文:進程恢復執行前必須載入寄存器的一組數據稱為硬件上下文
linux內核在進程切換的時候是並不區分進程和線程的~因為切換是針對task,
進程切換的時機:一般是在系統調用或者中斷的時候,發生在內核態。
進程切換過程:
進程切換統一發生在schedule()函數中,核心函數是context_switch()函數,代碼位置:kernel/kernel/sched/Core.c文件,以ARM為基礎講解linux進程的切換:linux進程切換過程,schedule()函數代碼分析:schedule()代碼分析 schedule代碼分析二
10,idle進程
idle進程的概念:
簡單的說idle是一個進程,其pid號為 0。其前身是系統創建的第一個進程,也是唯一一個沒有通過fork()產生的進程。在smp系統中,每個處理器單元有獨立的一個運行隊列,而每個運行隊列上又有一個idle進程,即有多少處理器單元,就有多少idle進程。系統的空閑時間,其實就是指idle進程的"運行時間"。idle進程pid==o,也就是init_task.
idle進程的創建:
vmlinux的入口是arch/arm/kernel/head.S,(關注ENTRY(stext)的位置)入口地址可以從kernel/arch/arm/kernel/vmlinux.lds.S得知文件,在該匯編文件中為pid為0的原始進程設置了執行環境,包括先禁止MMU,內核剛開始是用物理地址的,禁止I-Cache和D-Cache,然后讀取RO,R1,R2,然后原是進程開始執行start_kernel()完成Linux內核的初始化工作。包括初始化頁表,初始化中斷向量表,初始化系統時間等。繼而調用 fork(),創建第一個用戶進程: 創建init進程(pid==1)的函數如下:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
pid為1的init進程,它會繼續完成剩下的初始化工作,然后execve(/sbin/init), 成為系統中的其他所有進程的祖先,pid為0的idle進程在創建了init進程后,pid=0的進程調用 cpu_idle()演變成了idle進程。current_thread_info()->status |= TS_POLLING;init在演變成/sbin/init之前,會執行一部分初始化工作,其中一個就是 smp_prepare_cpus(),初始化SMP處理器,在這過程中會在處理每個從處理器時調用 task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, NULL, 0); init_idle(task, cpu);
idle進程的運行時機:
idle進程優先級為MAX_PRIO,即最低優先級。早先版本中,idle是參與調度的,所以將其優先級設為最低,當沒有其他進程可以運行時,才會調度執行idle,而目前的版本中idle並不在運行隊列中參與調度,而是在運行隊列結構中含idle指針,指向idle進程,在調度器發現運行隊列為空的時候運行,調入運行
idle進程到底做了什么:
idle進程最終調用了cpu_idle()函數
- process.c
- void cpu_idle(void)
- {
- ......
- /* endless idle loop with no priority at all */
- while (1) {
- ......
- while (!need_resched()) {
- if (cpu_is_offline(smp_processor_id())) {
- tick_set_cpu_plugoff_flag(1);
- cpu_die(); /* plugoff CPU */
- }
- ......
- if (cpuidle_idle_call())
- pm_idle(); /* 進入低電 */
- }
- ......
- schedule_preempt_disabled(); /* 調用schedule() */
- }
- }
補充:linux kernel的啟動流程
當嵌入式設備從uboot跳轉到kernel的時候,首先運行的是kernel的自解壓程序,linux內核在編譯后是以壓縮文件形式存在,路徑是kernel/out/arch/arm/boot/zImage
zImage里的代碼和數據是如何存放的,答:根據out/arch/arm/boot/compressed/vmlinux.lds文件,如果要修改代碼段和數據段位置也需要修改該鏈接腳本。
kernel init的基本流程如下:
.stext
start_kernel()
rest_init()
kernel_init()