亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

linux內核線程 [創建]

2019-11-14 09:57:57
字體:
來源:轉載
供稿:網友

本文以ARM架構為例,講解linux的內核線程是如何創建的。

Linux內核在完成初始之后,會把控制權交給應用程序。只有當硬件中斷、軟中斷、異常等發生時,CPU才會從用戶空間切換到內核空間來執行相應的處理,完成后又回來用戶空間。

如果內核需要周期性地做一些事情(比如頁面的換入換出,磁盤高速緩存的刷新等),又該怎么辦呢?內核線程(內核進程)可以解決這個問題。

內核線程(kernel thread)是由內核自己創建的線程,也叫做守護線程(deamon)。在終端上用命令”ps -Al”列出的所有進程中,名字以k開關以d結尾的往往都是內核線程,比如kthreadd、kswapd。

內核線程與用戶線程的相同點是:

都由do_fork()創建,每個線程都有獨立的task_struct和內核棧;都參與調度,內核線程也有優先級,會被調度器平等地換入換出。

不同之處在于:

內核線程只工作在內核態中;而用戶線程則既可以運行在內核態,也可以運行在用戶態;內核線程沒有用戶空間,所以對于一個內核線程來說,它的0~3G的內存空間是空白的,它的current->mm是空的,與內核使用同一張頁表;而用戶線程則可以看到完整的0~4G內存空間。

在Linux內核啟動的最后階段,系統會創建兩個內核線程,一個是init,一個是kthreadd。其中init線程的作用是運行文件系統上的一系列”init”腳本,并啟動shell進程,所以init線程稱得上是系統中所有用戶進程的祖先,它的pid是1。kthreadd線程是內核的守護線程,在內核正常工作時,它永遠不退出,是一個死循環,它的pid是2。

內核初始化工作的最后一部分是在函數rest_init()中完成的。在這個函數中,主要做了4件事情,分別是:創建init線程,創建kthreadd線程,執行schedule()開始調度,執行cpu_idle()讓CPU進入idle狀態。經過簡化的代碼如下:

12345678staticnoinline void__init_refok rest_init(void)__releases(kernel_lock){kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);schedule();cpu_idle();}

內核線程的創建過程比較曲折,讓我們一步一步來看。

創建內核線程的入口函數是kernel_thread,定義如下:

123456789101112131415pid_t kernel_thread(int(*fn)(void *), void *arg, unsignedlong flags){structpt_regs regs;memset(&regs, 0,sizeof(regs));regs.ARM_r4 = (unsignedlong)arg;regs.ARM_r5 = (unsignedlong)fn;regs.ARM_r6 = (unsignedlong)kernel_thread_exit;regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;regs.ARM_pc = (unsignedlong)kernel_thread_helper;regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;returndo_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);}

它的第一個參數是線程所要執行的函數的指針,第二個參數是線程的參數,第三個是線程屬性。

在kernel_thread()函數中先是準備一些寄存器的值,并保存起來。然后執行了do_fork()來復制task_struct內容,并建立起自己的內核棧。在kernel_thread() > do_fork() > copy_PRocess() > copy_thread()函數調用中,有一個很重要的操作需要留意一下:

123456789101112intcopy_thread(unsignedlong clone_flags, unsignedlong stack_start,unsignedlong stk_sz, struct task_struct *p,struct pt_regs *regs){structthread_info *thread= task_thread_info(p);......memset(&thread->cpu_context, 0,sizeof(structcpu_context_save));thread->cpu_context.sp = (unsignedlong)childregs;thread->cpu_context.pc = (unsignedlong)ret_from_fork;......return0;}

注意這里把cpu_context中保存的pc寄存器值設為ret_from_fork函數的地址,這在后面調度的時候會用到。

注:前面的這兩段代碼中都有設置pc寄存器,但是所設的內容是不同的:在kernel_thread()中設置的regs.ARM_*值最后會被壓入內核棧,是在context_switch完成之后待要運行的目標代碼;而在copy_thread()中設置的sp和pc則是thread_info結構中cpu_context的值,是在context_switch()過程中要用的。

rest_init()中兩次調用過kernel_thread()之后,就分別創建好了init和kthreadd內核線程的運行上下文,并已經加入了運行隊列,隨時可以運行了。

接下來在schedule()里面最終會運行到switch_to()做上下文切換,這個函數的實現細節在此前的文章中已經講過,不再贅述,這里只說我們的場景。在switch_to()完成之后,新線程的sp寄存器已經切換到線程自己的棧上,新線程的pc則成了ret_from_fork。

接下來新線程就跳轉到ret_from_fork()函數繼續執行。ret_from_fork()是用匯編代碼來寫的,用于fork系統調用(軟中斷)完成后的收尾工作。中斷的收尾工作最后都會要完成一件事情,就是恢復原先運行的“用戶”程序狀態,即彈出設置內核棧上所保存的各寄存器值。而我們此前保存在這里的pc寄存器指向的是函數kernel_thread_helper()的地址,這個函數是用匯編寫的:

1234567891011externvoid kernel_thread_helper(void);asm( ".pushsection .text/n"" .align/n"" .type kernel_thread_helper, #function/n""kernel_thread_helper:/n"" msr cpsr_c, r7/n"" mov r0, r4/n"" mov lr, r6/n"" mov pc, r5/n"" .size kernel_thread_helper, . - kernel_thread_helper/n"" .popsection");

這段代碼把pc值設為r5,在kernel_thread()中我們已經把r5設為線程的目標函數的值,而返回地址寄存器lr被設為r6,即此前設置的kernel_thread_exit()函數地址。

所以,接下來內核線程將會被正式啟動,如果線程退出(即線程函數運行結束)的話,kernel_thread_exit()會做掃尾工作。

到這里,我們已經講完了內核線程啟動的整個過程。

 

最后我們看一下剛剛啟動起來的兩個內核線程都做了哪些事情:

init線程:

12345678910111213141516staticint __init kernel_init(void* unused){......init_post();}staticnoinline intinit_post(void) __releases(kernel_lock){......run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");panic("No init found. Try passing init= option to kernel. ""See Linux Documentation/init.txt for guidance.");}

在init線程中,將運行完”/sbin/init”、”/etc/init”和”/bin/init”三個腳本,并啟動shell。run_init_process(“/bin/sh”)并不會返回,init線程就停在這里,以后所有的應用程序進程都將從/bin/sh克隆,而sh來自init內核線程,所以init線程最終成為所有用戶進程的祖先。

kthreadd線程:

1234567891011int kthreadd(void *unused){for(;;) {if(list_empty(&kthread_create_list))schedule();while(!list_empty(&kthread_create_list)) {create_kthread(create);}}return0;}

可見,在每一次循環里kthreadd只做兩件事:如果有其它的內核線程需要創建,就調用create_kthread()來逐個創建;如果沒有就調用schedule()把自己換出CPU,讓別的線程進來運行。

在內核線程創建過程中還有兩個有趣的細節值得說一下:

雖然init線程是在kthreadd之前創建的,pid也比較小,但是在schedule()的時候,最先被選中先運行的是kthreadd。這不會有任何影響,因為kthreadd總會讓出CPU,init線程一定能啟動。進程號PID的分配是從0開始的,但是在”ps”命令中看不到0號進程。這是因為0號pid被分給了“啟動”內核進程,就是完成了系統引導工作的那個進程。在函數rest_init()中,0號進程在創建完成了init和kthreadd兩個內核線程之后,調用schedule()使得pid=1和2的兩個線程得以啟動,但是pid=0的線程并不參與調度,所以這個進程就再也得不到運行了。如下所示,在我們前面已經看到過的這段代碼中,schedule()不會返回,最后一行的cpu_idle()其實是不會被運行到的:
12345678staticnoinline void__init_refok rest_init(void)__releases(kernel_lock){kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);schedule();<del>cpu_idle();</del>}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
夜夜嗨av一区二区三区免费区| 亚洲欧洲中文天堂| 久久九九有精品国产23| 超碰日本道色综合久久综合| 亚洲综合国产精品| 97久久精品国产| 亚洲欧美在线一区二区| 国产精品丝袜久久久久久高清| 国产欧美日韩免费看aⅴ视频| 国产精品va在线播放我和闺蜜| 亚洲国产精品一区二区三区| 亚洲精品免费在线视频| 成人乱人伦精品视频在线观看| 欧美性猛交xxxx免费看漫画| 欧美精品一本久久男人的天堂| 中文字幕亚洲第一| 日韩在线观看免费全集电视剧网站| 国产精品一二三在线| 亚洲在线观看视频网站| 在线观看成人黄色| 亚洲欧美日韩中文视频| 久久中文精品视频| 色婷婷亚洲mv天堂mv在影片| 久久精品视频在线观看| 91精品国产亚洲| 最好看的2019的中文字幕视频| 国产成人久久精品| 一本色道久久88精品综合| 国产精品久久久久久久久免费| 国产精品久久综合av爱欲tv| 91chinesevideo永久地址| 欧美成人剧情片在线观看| 日韩精品视频观看| 国产区亚洲区欧美区| 国产精品99久久久久久久久久久久| 久久久久久久久国产| 揄拍成人国产精品视频| 欧美在线亚洲一区| 亚洲精品久久久一区二区三区| 欧美国产视频日韩| 久久99国产综合精品女同| 在线电影中文日韩| 国产成人免费av| 日韩在线观看电影| 国产一区二区在线免费视频| 日韩av电影在线网| 久久久久久久999精品视频| 日韩av免费一区| 欧美激情按摩在线| 中文亚洲视频在线| 久久精品99久久久久久久久| 日韩在线视频免费观看| 国语自产精品视频在线看一大j8| 国内精品久久久久影院 日本资源| 国产精品久久久久久久久久99| 亚洲最新在线视频| 欧美三级欧美成人高清www| 久久av红桃一区二区小说| 久久夜色精品亚洲噜噜国产mv| 国内精品久久久| 久久91精品国产91久久跳| 国产精品色视频| 成人淫片在线看| 欧美成人精品三级在线观看| 亚洲专区国产精品| 姬川优奈aav一区二区| 中文字幕亚洲无线码a| 亚洲一区二区少妇| 亚洲激情自拍图| 国产精品久久77777| 精品久久久久久久久久| 久久精品国产96久久久香蕉| 中文字幕不卡在线视频极品| 久久久久久久久久久网站| 精品一区二区亚洲| 97超碰蝌蚪网人人做人人爽| 亚洲日本成人女熟在线观看| 久久精品99久久香蕉国产色戒| 欧美性xxxx极品高清hd直播| 欧美午夜片在线免费观看| 中文字幕日韩欧美精品在线观看| 日本久久91av| 中文字幕久久精品| 国产午夜精品理论片a级探花| 国产午夜精品视频免费不卡69堂| 青青久久av北条麻妃海外网| 欧美中文在线免费| 色伦专区97中文字幕| 国产一区二区三区免费视频| 亚洲精品国产拍免费91在线| 欧美日韩成人免费| 欧美高清自拍一区| 久久影视电视剧免费网站清宫辞电视| 欧美成人精品在线观看| 欧美成人免费全部| 97视频在线播放| 国产在线视频欧美| 国产精品久久久久久久久久久不卡| 成人精品视频99在线观看免费| 91成人性视频| 狠狠操狠狠色综合网| 日韩有码在线电影| 亚洲色图国产精品| 欧美性理论片在线观看片免费| 成人国产在线激情| 日韩电影免费观看在线| 国产精品女视频| 久久精品男人天堂| 亚洲白虎美女被爆操| 狠狠色狠狠色综合日日小说| 亚洲福利视频专区| 亚洲视屏在线播放| 国产亚洲精品久久久久久777| 免费91麻豆精品国产自产在线观看| 国产欧美韩国高清| 精品国产区一区二区三区在线观看| 中文字幕v亚洲ⅴv天堂| 久久在线视频在线| 日韩电影免费在线观看| 日韩av在线免播放器| 中文字幕国产精品久久| 亚洲图片欧美日产| 国产成+人+综合+亚洲欧美丁香花| 欧美精品激情视频| 日韩精品在线免费观看| 91国偷自产一区二区三区的观看方式| 国产精品视频在线播放| 欧美性猛交xxxx免费看| 久久99久久亚洲国产| 欧美在线影院在线视频| 亚洲影视九九影院在线观看| 日韩电视剧在线观看免费网站| 亚洲国产日韩欧美在线动漫| 久久中文久久字幕| 欧美日韩另类字幕中文| 欧美激情xxxx性bbbb| 久久99久久久久久久噜噜| 最好看的2019的中文字幕视频| 欧美激情乱人伦一区| 国产日韩在线一区| 国产香蕉精品视频一区二区三区| 国产精品视频免费观看www| 国产成人在线亚洲欧美| 午夜精品在线观看| 亚洲精品wwww| 高清欧美性猛交| 91在线视频精品| 另类图片亚洲另类| 欧美日韩一区二区精品| 久久在线免费观看视频| 国产98色在线| 国产精品一区av| 精品亚洲一区二区三区四区五区| 欧美黑人巨大xxx极品| 亚洲午夜未满十八勿入免费观看全集| 亚洲free性xxxx护士hd| 久久亚洲一区二区三区四区五区高| 国产a∨精品一区二区三区不卡| 一区二区av在线| 欧美日韩成人精品| 亚洲成人中文字幕| 亚洲精品白浆高清久久久久久| 91精品久久久久久久| 国产69久久精品成人|