Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)【转】...
前言
Linux下有3個特殊的進(jìn)程,idle進(jìn)程(PID = 0), init進(jìn)程(PID = 1)和kthreadd(PID = 2)
- idle進(jìn)程由系統(tǒng)自動創(chuàng)建, 運(yùn)行在內(nèi)核態(tài)
idle進(jìn)程其pid=0,其前身是系統(tǒng)創(chuàng)建的第一個進(jìn)程,也是唯一一個沒有通過fork或者kernel_thread產(chǎn)生的進(jìn)程。完成加載系統(tǒng)后,演變?yōu)檫M(jìn)程調(diào)度、交換
- init進(jìn)程由idle通過kernel_thread創(chuàng)建,在內(nèi)核空間完成初始化后, 加載init程序, 并最終用戶空間
由0進(jìn)程創(chuàng)建,完成系統(tǒng)的初始化. 是系統(tǒng)中所有其它用戶進(jìn)程的祖先進(jìn)程
Linux中的所有進(jìn)程都是有init進(jìn)程創(chuàng)建并運(yùn)行的。首先Linux內(nèi)核啟動,然后在用戶空間中啟動init進(jìn)程,再啟動其他系統(tǒng)進(jìn)程。在系統(tǒng)啟動完成完成后,init將變?yōu)槭刈o(hù)進(jìn)程監(jiān)視系統(tǒng)其他進(jìn)程。
- kthreadd進(jìn)程由idle通過kernel_thread創(chuàng)建,并始終運(yùn)行在內(nèi)核空間, 負(fù)責(zé)所有內(nèi)核線程的調(diào)度和管理
它的任務(wù)就是管理和調(diào)度其他內(nèi)核線程kernel_thread, 會循環(huán)執(zhí)行一個kthread的函數(shù),該函數(shù)的作用就是運(yùn)行kthread_create_list全局鏈表中維護(hù)的kthread, 當(dāng)我們調(diào)用kernel_thread創(chuàng)建的內(nèi)核線程會被加入到此鏈表中,因此所有的內(nèi)核線程都是直接或者間接的以kthreadd為父進(jìn)程
我們下面就詳解分析0號進(jìn)程的前世(init_task)今生(idle)
idle的創(chuàng)建
在smp系統(tǒng)中,每個處理器單元有獨(dú)立的一個運(yùn)行隊列,而每個運(yùn)行隊列上又有一個idle進(jìn)程,即有多少處理器單元,就有多少idle進(jìn)程。
idle進(jìn)程其pid=0,其前身是系統(tǒng)創(chuàng)建的第一個進(jìn)程,也是唯一一個沒有通過fork()產(chǎn)生的進(jìn)程。在smp系統(tǒng)中,每個處理器單元有獨(dú)立的一個運(yùn)行隊列,而每個運(yùn)行隊列上又有一個idle進(jìn)程,即有多少處理器單元,就有多少idle進(jìn)程。系統(tǒng)的空閑時間,其實(shí)就是指idle進(jìn)程的”運(yùn)行時間”。既然是idle是進(jìn)程,那我們來看看idle是如何被創(chuàng)建,又具體做了哪些事情?
我們知道系統(tǒng)是從BIOS加電自檢,載入MBR中的引導(dǎo)程序(LILO/GRUB),再加載linux內(nèi)核開始運(yùn)行的,一直到指定shell開始運(yùn)行告一段落,這時用戶開始操作Linux。
0號進(jìn)程上下文信息–init_task描述符
init_task是內(nèi)核中所有進(jìn)程、線程的task_struct雛形,在內(nèi)核初始化過程中,通過靜態(tài)定義構(gòu)造出了一個task_struct接口,取名為init_task,然后在內(nèi)核初始化的后期,通過rest_init()函數(shù)新建了內(nèi)核init線程,kthreadd內(nèi)核線程
- 內(nèi)核init線程,最終執(zhí)行/sbin/init進(jìn)程,變?yōu)樗杏脩魬B(tài)程序的根進(jìn)程(pstree命令顯示),即用戶空間的init進(jìn)程
開始的init是有kthread_thread創(chuàng)建的內(nèi)核線程, 他在完成初始化工作后, 轉(zhuǎn)向用戶空間, 并且生成所有用戶進(jìn)程的祖先
- 內(nèi)核kthreadd內(nèi)核線程,變?yōu)樗袃?nèi)核態(tài)其他守護(hù)線程的父線程。
它的任務(wù)就是管理和調(diào)度其他內(nèi)核線程kernel_thread, 會循環(huán)執(zhí)行一個kthread的函數(shù),該函數(shù)的作用就是運(yùn)行kthread_create_list全局鏈表中維護(hù)的kthread, 當(dāng)我們調(diào)用kernel_thread創(chuàng)建的內(nèi)核線程會被加入到此鏈表中,因此所有的內(nèi)核線程都是直接或者間接的以kthreadd為父進(jìn)程
所以init_task決定了系統(tǒng)所有進(jìn)程、線程的基因, 它完成初始化后, 最終演變?yōu)?號進(jìn)程idle, 并且運(yùn)行在內(nèi)核態(tài)
內(nèi)核在初始化過程中,當(dāng)創(chuàng)建完init和kthreadd內(nèi)核線程后,內(nèi)核會發(fā)生調(diào)度執(zhí)行,此時內(nèi)核將使用該init_task作為其task_struct結(jié)構(gòu)體描述符,當(dāng)系統(tǒng)無事可做時,會調(diào)度其執(zhí)行, 此時該內(nèi)核會變?yōu)閕dle進(jìn)程,讓出CPU,自己進(jìn)入睡眠,不停的循環(huán),查看init_task結(jié)構(gòu)體,其comm字段為swapper,作為idle進(jìn)程的描述符。
idle的運(yùn)行時機(jī)
idle 進(jìn)程優(yōu)先級為MAX_PRIO-20。早先版本中,idle是參與調(diào)度的,所以將其優(yōu)先級設(shè)低點(diǎn),當(dāng)沒有其他進(jìn)程可以運(yùn)行時,才會調(diào)度執(zhí)行 idle。而目前的版本中idle并不在運(yùn)行隊列中參與調(diào)度,而是在運(yùn)行隊列結(jié)構(gòu)中含idle指針,指向idle進(jìn)程,在調(diào)度器發(fā)現(xiàn)運(yùn)行隊列為空的時候運(yùn)行,調(diào)入運(yùn)行
簡言之, 內(nèi)核中init_task變量就是是進(jìn)程0使用的進(jìn)程描述符,也是Linux系統(tǒng)中第一個進(jìn)程描述符,init_task并不是系統(tǒng)通過kernel_thread的方式(當(dāng)然更不可能是fork)創(chuàng)建的, 而是由內(nèi)核黑客靜態(tài)創(chuàng)建的.
該進(jìn)程的描述符在init/init_task中定義,代碼片段如下
/* Initial task structure */ struct task_struct init_task = INIT_TASK(init_task); EXPORT_SYMBOL(init_task);init_task描述符使用宏INIT_TASK對init_task的進(jìn)程描述符進(jìn)行初始化,宏INIT_TASK在include/linux/init_task.h文件中
init_task是Linux內(nèi)核中的第一個線程,它貫穿于整個Linux系統(tǒng)的初始化過程中,該進(jìn)程也是Linux系統(tǒng)中唯一一個沒有用kernel_thread()函數(shù)創(chuàng)建的內(nèi)核態(tài)進(jìn)程(內(nèi)核線程)
在init_task進(jìn)程執(zhí)行后期,它會調(diào)用kernel_thread()函數(shù)創(chuàng)建第一個核心進(jìn)程kernel_init,同時init_task進(jìn)程繼續(xù)對Linux系統(tǒng)初始化。在完成初始化后,init_task會退化為cpu_idle進(jìn)程,當(dāng)Core 0的就緒隊列中沒有其它進(jìn)程時,該進(jìn)程將會獲得CPU運(yùn)行。新創(chuàng)建的1號進(jìn)程kernel_init將會逐個啟動次CPU,并最終創(chuàng)建用戶進(jìn)程!
備注:core0上的idle進(jìn)程由init_task進(jìn)程退化而來,而AP的idle進(jìn)程則是BSP在后面調(diào)用fork()函數(shù)逐個創(chuàng)建的
進(jìn)程堆棧init_thread_union
init_task進(jìn)程使用init_thread_union數(shù)據(jù)結(jié)構(gòu)描述的內(nèi)存區(qū)域作為該進(jìn)程的堆??臻g,并且和自身的thread_info參數(shù)公用這一內(nèi)存空間空間
.stack = &init_thread_info,而init_thread_info則是一段體系結(jié)構(gòu)相關(guān)的定義,被定義在[/arch/對應(yīng)體系/include/asm/thread_info.h]中,但是他們大多數(shù)為如下定義
#define init_thread_info (init_thread_union.thread_info) #define init_stack (init_thread_union.stack)其中init_thread_union被定義在init/init_task.c, 緊跟著前面init_task的定義
/** Initial thread structure. Alignment of this is handled by a special* linker map entry.*/ union thread_union init_thread_union __init_task_data ={ INIT_THREAD_INFO(init_task) };我們可以發(fā)現(xiàn)init_task是用INIT_THREAD_INFO宏進(jìn)行初始化的, 這個才是我們真正體系結(jié)構(gòu)相關(guān)的部分, 他與init_thread_info定義在一起,被定義在/arch/對應(yīng)體系/include/asm/thread_info.h中,以下為x86架構(gòu)的定義
參見
http://lxr.free-electrons.com/source/arch/x86/include/asm/thread_info.h?v=4.5#L65
其他體系結(jié)構(gòu)的定義請參見
/arch/對應(yīng)體系/include/asm/thread_info.h中
| x86 | arch/x86/include/asm/thread_info.h |
| arm64 | arch/arm64/include/asm/thread_info.h |
init_thread_info定義中的__init_task_data表明該內(nèi)核棧所在的區(qū)域位于內(nèi)核映像的init data區(qū),我們可以通過編譯完內(nèi)核后所產(chǎn)生的System.map來看到該變量及其對應(yīng)的邏輯地址
cat System.map-3.1.6 | grep init_thread_union進(jìn)程內(nèi)存空間
init_task的虛擬地址空間,也采用同樣的方法被定義
由于init_task是一個運(yùn)行在內(nèi)核空間的內(nèi)核線程, 因此其虛地址段mm為NULL, 但是必要時他還是需要使用虛擬地址的,因此avtive_mm被設(shè)置為init_mm
參見
http://lxr.free-electrons.com/source/include/linux/init_task.h?v=4.5#L202
其中init_mm被定義為init-mm.c中,參見 http://lxr.free-electrons.com/source/mm/init-mm.c?v=4.5#L16
struct mm_struct init_mm = {.mm_rb = RB_ROOT,.pgd = swapper_pg_dir,.mm_users = ATOMIC_INIT(2),.mm_count = ATOMIC_INIT(1),.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),.page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),.mmlist = LIST_HEAD_INIT(init_mm.mmlist),INIT_MM_CONTEXT(init_mm) };0號進(jìn)程演化
rest_init創(chuàng)建init進(jìn)程(PID =1)和kthread進(jìn)程(PID=2)
Linux在無進(jìn)程概念的情況下將一直從初始化部分的代碼執(zhí)行到start_kernel,然后再到其最后一個函數(shù)調(diào)用rest_init
大致是在vmlinux的入口startup_32(head.S)中為pid號為0的原始進(jìn)程設(shè)置了執(zhí)行環(huán)境,然后原是進(jìn)程開始執(zhí)行start_kernel()完成Linux內(nèi)核的初始化工作。包括初始化頁表,初始化中斷向量表,初始化系統(tǒng)時間等。
從rest_init開始,Linux開始產(chǎn)生進(jìn)程,因?yàn)閕nit_task是靜態(tài)制造出來的,pid=0,它試圖將從最早的匯編代碼一直到start_kernel的執(zhí)行都納入到init_task進(jìn)程上下文中。
這個函數(shù)其實(shí)是由0號進(jìn)程執(zhí)行的, 他就是在這個函數(shù)中, 創(chuàng)建了init進(jìn)程和kthreadd進(jìn)程
這部分代碼如下:
static noinline void __init_refok rest_init(void) {int pid;rcu_scheduler_starting();smpboot_thread_init();/** We need to spawn init first so that it obtains pid 1, however* the init task will end up wanting to create kthreads, which, if* we schedule it before we create kthreadd, will OOPS.*/kernel_thread(kernel_init, NULL, CLONE_FS);numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();complete(&kthreadd_done);/** The boot idle thread must execute schedule()* at least once to get things moving:*/init_idle_bootup_task(current);schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE); }創(chuàng)建kernel_init
在rest_init函數(shù)中,內(nèi)核將通過下面的代碼產(chǎn)生第一個真正的進(jìn)程(pid=1):
kernel_thread(kernel_init, NULL, CLONE_FS);這個進(jìn)程就是著名的pid為1的init進(jìn)程,它會繼續(xù)完成剩下的初始化工作,然后execve(/sbin/init), 成為系統(tǒng)中的其他所有進(jìn)程的祖先。
但是這里我們發(fā)現(xiàn)一個問題, init進(jìn)程應(yīng)該是一個用戶空間的進(jìn)程, 但是這里卻是通過kernel_thread的方式創(chuàng)建的, 哪豈不是式一個永遠(yuǎn)運(yùn)行在內(nèi)核態(tài)的內(nèi)核線程么, 它是怎么演變?yōu)檎嬲饬x上用戶空間的init進(jìn)程的?
1號kernel_init進(jìn)程完成linux的各項(xiàng)配置(包括啟動AP)后,就會在/sbin,/etc,/bin尋找init程序來運(yùn)行。該init程序會替換kernel_init進(jìn)程(注意:并不是創(chuàng)建一個新的進(jìn)程來運(yùn)行init程序,而是一次變身,使用sys_execve函數(shù)改變核心進(jìn)程的正文段,將核心進(jìn)程kernel_init轉(zhuǎn)換成用戶進(jìn)程init),此時處于內(nèi)核態(tài)的1號kernel_init進(jìn)程將會轉(zhuǎn)換為用戶空間內(nèi)的1號進(jìn)程init。戶進(jìn)程init將根據(jù)/etc/inittab中提供的信息完成應(yīng)用程序的初始化調(diào)用。然后init進(jìn)程會執(zhí)行/bin/sh產(chǎn)生shell界面提供給用戶來與Linux系統(tǒng)進(jìn)行交互。
調(diào)用init_post()創(chuàng)建用戶模式1號進(jìn)程。
關(guān)于init其他的信息我們這次先不研究,因?yàn)槲覀冞@篇旨在探究0號進(jìn)程的詳細(xì)過程,
創(chuàng)建kthreadd
在rest_init函數(shù)中,內(nèi)核將通過下面的代碼產(chǎn)生第一個kthreadd(pid=2)
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);它的任務(wù)就是管理和調(diào)度其他內(nèi)核線程kernel_thread, 會循環(huán)執(zhí)行一個kthread的函數(shù),該函數(shù)的作用就是運(yùn)行kthread_create_list全局鏈表中維護(hù)的kthread, 當(dāng)我們調(diào)用kernel_thread創(chuàng)建的內(nèi)核線程會被加入到此鏈表中,因此所有的內(nèi)核線程都是直接或者間接的以kthreadd為父進(jìn)程
0號進(jìn)程演變?yōu)閕dle
/** The boot idle thread must execute schedule()* at least once to get things moving:*/init_idle_bootup_task(current);schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);因此我們回過頭來看pid=0的進(jìn)程,在創(chuàng)建了init進(jìn)程后,pid=0的進(jìn)程調(diào)用 cpu_idle()演變成了idle進(jìn)程。
0號進(jìn)程首先執(zhí)行init_idle_bootup_task, 讓init_task進(jìn)程隸屬到idle調(diào)度類中。即選擇idle的調(diào)度相關(guān)函數(shù)。
這個函數(shù)被定義在kernel/sched/core.c中,如下
void init_idle_bootup_task(struct task_struct *idle) {idle->sched_class = &idle_sched_class; }接著通過schedule_preempt_disabled來執(zhí)行調(diào)用schedule()函數(shù)切換當(dāng)前進(jìn)程,在調(diào)用該函數(shù)之前,Linux系統(tǒng)中只有兩個進(jìn)程,即0號進(jìn)程init_task和1號進(jìn)程kernel_init,其中kernel_init進(jìn)程也是剛剛被創(chuàng)建的。調(diào)用該函數(shù)后,1號進(jìn)程kernel_init將會運(yùn)行
這個函數(shù)被定義在kernel/sched/core.c中,如下
/** * schedule_preempt_disabled - called with preemption disabled * * Returns with preemption disabled. Note: preempt_count must be 1 */ void __sched schedule_preempt_disabled(void) {sched_preempt_enable_no_resched();schedule();preempt_disable(); }最后cpu_startup_entry調(diào)用cpu_idle_loop(),0號線程進(jìn)入idle函數(shù)的循環(huán),在該循環(huán)中會周期性地檢查
cpu_startup_entry定義在kernel/sched/idle.c
void cpu_startup_entry(enum cpuhp_state state) {/** This #ifdef needs to die, but it's too late in the cycle to* make this generic (arm and sh have never invoked the canary* init for the non boot cpus!). Will be fixed in 3.11*/ #ifdef CONFIG_X86/** If we're the non-boot CPU, nothing set the stack canary up* for us. The boot CPU already has it initialized but no harm* in doing it again. This is a good place for updating it, as* we wont ever return from this function (so the invalid* canaries already on the stack wont ever trigger).*/boot_init_stack_canary(); #endifarch_cpu_idle_prepare();cpu_idle_loop(); }其中cpu_idle_loop就是idle進(jìn)程的事件循環(huán),定義在kernel/sched/idle.c
整個過程簡單的說就是,原始進(jìn)程(pid=0)創(chuàng)建init進(jìn)程(pid=1),然后演化成idle進(jìn)程(pid=0)。init進(jìn)程為每個從處理器(運(yùn)行隊列)創(chuàng)建出一個idle進(jìn)程(pid=0),然后演化成/sbin/init。
idle的運(yùn)行與調(diào)度
idle的workload–cpu_idle_loop
從上面的分析我們知道,idle在系統(tǒng)沒有其他就緒的進(jìn)程可執(zhí)行的時候才會被調(diào)度。不管是主處理器,還是從處理器,最后都是執(zhí)行的cpu_idle_loop()函數(shù)
其中cpu_idle_loop就是idle進(jìn)程的事件循環(huán),定義在kernel/sched/idle.c,早期的版本中提供的是cpu_idle,但是這個函數(shù)是完全依賴于體系結(jié)構(gòu)的,不利用架構(gòu)的分層,因此在新的內(nèi)核中更新為更加通用的cpu_idle_loop,由他來調(diào)用體系結(jié)構(gòu)相關(guān)的代碼
所以我們來看看cpu_idle_loop做了什么事情。
因?yàn)閕dle進(jìn)程中并不執(zhí)行什么有意義的任務(wù),所以通常考慮的是兩點(diǎn)
其代碼如下
/** Generic idle loop implementation** Called with polling cleared.*/ static void cpu_idle_loop(void) {while (1) {/** If the arch has a polling bit, we maintain an invariant:** Our polling bit is clear if we're not scheduled (i.e. if* rq->curr != rq->idle). This means that, if rq->idle has* the polling bit set, then setting need_resched is* guaranteed to cause the cpu to reschedule.*/__current_set_polling();quiet_vmstat();tick_nohz_idle_enter();while (!need_resched()) {check_pgt_cache();rmb();if (cpu_is_offline(smp_processor_id())) {rcu_cpu_notify(NULL, CPU_DYING_IDLE,(void *)(long)smp_processor_id());smp_mb(); /* all activity before dead. */this_cpu_write(cpu_dead_idle, true);arch_cpu_idle_dead();}local_irq_disable();arch_cpu_idle_enter();/** In poll mode we reenable interrupts and spin.** Also if we detected in the wakeup from idle* path that the tick broadcast device expired* for us, we don't want to go deep idle as we* know that the IPI is going to arrive right* away*/if (cpu_idle_force_poll || tick_check_broadcast_expired())cpu_idle_poll();elsecpuidle_idle_call();arch_cpu_idle_exit();}/** Since we fell out of the loop above, we know* TIF_NEED_RESCHED must be set, propagate it into* PREEMPT_NEED_RESCHED.** This is required because for polling idle loops we will* not have had an IPI to fold the state for us.*/preempt_set_need_resched();tick_nohz_idle_exit();__current_clr_polling();/** We promise to call sched_ttwu_pending and reschedule* if need_resched is set while polling is set. That* means that clearing polling needs to be visible* before doing these things.*/smp_mb__after_atomic();sched_ttwu_pending();schedule_preempt_disabled();} }循環(huán)判斷need_resched以降低退出延遲,用idle()來節(jié)能。
默認(rèn)的idle實(shí)現(xiàn)是hlt指令,hlt指令使CPU處于暫停狀態(tài),等待硬件中斷發(fā)生的時候恢復(fù),從而達(dá)到節(jié)能的目的。即從處理器C0態(tài)變到 C1態(tài)(見 ACPI標(biāo)準(zhǔn))。這也是早些年windows平臺上各種”處理器降溫”工具的主要手段。當(dāng)然idle也可以是在別的ACPI或者APM模塊中定義的,甚至是自定義的一個idle(比如說nop)。
希望通過本文你能全面了解linux內(nèi)核中idle知識。
idle的調(diào)度和運(yùn)行時機(jī)
我們知道, linux進(jìn)程的調(diào)度順序是按照 rt實(shí)時進(jìn)程(rt調(diào)度器), normal普通進(jìn)程(cfs調(diào)度器),和idel的順序來調(diào)度的
那么可以試想如果rt和cfs都沒有可以運(yùn)行的任務(wù),那么idle才可以被調(diào)度,那么他是通過怎樣的方式實(shí)現(xiàn)的呢?
由于我們還沒有講解調(diào)度器的知識, 所有我們只是簡單講解一下
在normal的調(diào)度類,cfs公平調(diào)度器sched_fair.c中, 我們可以看到
static const struct sched_class fair_sched_class = { .next = &idle_sched_class,也就是說,如果系統(tǒng)中沒有普通進(jìn)程,那么會選擇下個調(diào)度類優(yōu)先級的進(jìn)程,即使用idle_sched_class調(diào)度類進(jìn)行調(diào)度的進(jìn)程
當(dāng)系統(tǒng)空閑的時候,最后就是調(diào)用idle的pick_next_task函數(shù),被定義在/kernel/sched/idle_task.c中
static struct task_struct *pick_next_task_idle(struct rq *rq) {schedstat_inc(rq, sched_goidle);calc_load_account_idle(rq);return rq->idle; //可以看到就是返回rq中idle進(jìn)程。 }這idle進(jìn)程在啟動start_kernel函數(shù)的時候調(diào)用init_idle函數(shù)的時候,把當(dāng)前進(jìn)程(0號進(jìn)程)置為每個rq運(yùn)行隊列的的idle上。
rq->curr = rq->idle = idle;這里idle就是調(diào)用start_kernel函數(shù)的進(jìn)程,就是0號進(jìn)程。
idle進(jìn)程總結(jié)
系統(tǒng)允許一個進(jìn)程創(chuàng)建新進(jìn)程,新進(jìn)程即為子進(jìn)程,子進(jìn)程還可以創(chuàng)建新的子進(jìn)程,形成進(jìn)程樹結(jié)構(gòu)模型。整個linux系統(tǒng)的所有進(jìn)程也是一個樹形結(jié)構(gòu)。樹根是系統(tǒng)自動構(gòu)造的(或者說是由內(nèi)核黑客手動創(chuàng)建的),即在內(nèi)核態(tài)下執(zhí)行的0號進(jìn)程,它是所有進(jìn)程的遠(yuǎn)古先祖。
在smp系統(tǒng)中,每個處理器單元有獨(dú)立的一個運(yùn)行隊列,而每個運(yùn)行隊列上又有一個idle進(jìn)程,即有多少處理器單元,就有多少idle進(jìn)程。
轉(zhuǎn)載于:https://www.cnblogs.com/linhaostudy/p/9640955.html
總結(jié)
以上是生活随笔為你收集整理的Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)【转】...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NumPy来自现有数据的数组
- 下一篇: python 进程理论基础