linux內(nèi)核啟動(dòng)過程的后期,在kernel_init()函數(shù)代表的init線程中,會(huì)嘗試執(zhí)行用戶空間的init進(jìn)程:

從上述代碼可見,會(huì)嘗試執(zhí)行/sbin/、/etc、/bin三個(gè)目錄中的init。從《busybox源碼分析筆記(一)》一文可以知道,在busybox編譯構(gòu)建完成并安裝后,會(huì)生成對(duì)應(yīng)的目錄(注:/etc目錄不存在)。在/sbin目錄中,則會(huì)存在一個(gè)init鏈接:

查看其屬性,其本質(zhì)則是鏈接到了../bin/busybox:

綜上所述,證明linux內(nèi)核啟動(dòng)后期,運(yùn)行的第一個(gè)用戶空間程序是init,在busybox源碼中,init程序則由位于/init目錄中的init.c編譯構(gòu)建而成,程序入口是:init_main(),小生在該函數(shù)中添加一行標(biāo)識(shí)代碼:
linux內(nèi)核運(yùn)行后期的結(jié)果如下:

可見,linux內(nèi)核后期加載的就是busybox下的init程序。
init_main分析
貼上該函數(shù)的完整代碼,下文將分段描述:
intinit_main(intargcUNUSED_PARAM,char**argv) { structsigactionsa; INIT_G(); /*SomeuserssendpoweroffsignalstoinitVERYearly. *Tohandlethis,masksignalsearly. */ /*sigemptyset(&G.delayed_sigset);-donebyINIT_G()*/ sigaddset(&G.delayed_sigset,SIGINT);/*Ctrl-Alt-Del*/ sigaddset(&G.delayed_sigset,SIGQUIT);/*re-execanotherinit*/ #ifdefSIGPWR sigaddset(&G.delayed_sigset,SIGPWR);/*halt*/ #endif sigaddset(&G.delayed_sigset,SIGUSR1);/*halt*/ sigaddset(&G.delayed_sigset,SIGTERM);/*reboot*/ sigaddset(&G.delayed_sigset,SIGUSR2);/*poweroff*/ #ifENABLE_FEATURE_USE_INITTAB sigaddset(&G.delayed_sigset,SIGHUP);/*reread/etc/inittab*/ #endif sigaddset(&G.delayed_sigset,SIGCHLD);/*makesigtimedwait()exitonSIGCHLD*/ sigprocmask(SIG_BLOCK,&G.delayed_sigset,NULL); #ifDEBUG_SEGV_HANDLER memset(&sa,0,sizeof(sa)); sa.sa_sigaction=handle_sigsegv; sa.sa_flags=SA_SIGINFO; sigaction_set(SIGSEGV,&sa); sigaction_set(SIGILL,&sa); sigaction_set(SIGFPE,&sa); sigaction_set(SIGBUS,&sa); #endif if(argv[1]&&strcmp(argv[1],"-q")==0){ returnkill(1,SIGHUP); } #if!DEBUG_INIT /*ExpecttobeinvokedasinitwithPID=1orbeinvokedaslinuxrc*/ if(getpid()!=1 &&(!ENABLE_LINUXRC||applet_name[0]!='l')/*notlinuxrc?*/ ){ bb_simple_error_msg_and_die("mustberunasPID1"); } #ifdefRB_DISABLE_CAD /*TurnoffrebootingviaCTL-ALT-DEL-wegeta *SIGINTonCADsowecanshutthingsdowngracefully...*/ reboot(RB_DISABLE_CAD);/*misnomer*/ #endif #endif /*If,say,xmallocwouldeverdie,wedon'twanttooopskernel *byexiting. *NB:wesetdie_func*after*PID1checkandbb_show_usage. *Otherwise,forexample,"initu"("pleaserexecyourself" *commandforsysvinit)willshowhelptext(whichisn'ttoobad), **andsleepforever*(whichisbad!) */ die_func=sleep_much; /*Figureoutwherethedefaultconsoleshouldbe*/ console_init(); set_sane_term(); xchdir("/"); setsid(); /*Makesureenvironsissettosomethingsane*/ putenv((char*)"HOME=/"); putenv((char*)bb_PATH_root_path); putenv((char*)"SHELL=/bin/sh"); putenv((char*)"USER=root");/*needed?why?*/ if(argv[1]) xsetenv("RUNLEVEL",argv[1]); #if!ENABLE_FEATURE_INIT_QUIET /*Helloworld*/ message(L_CONSOLE|L_LOG,"initstarted:%s",bb_banner); #endif /*Checkifwearesupposedtobeinsingleusermode*/ if(argv[1] &&(strcmp(argv[1],"single")==0||strcmp(argv[1],"-s")==0||LONE_CHAR(argv[1],'1')) ){ /*???shouldn'twesetRUNLEVEL="b"here?*/ /*Startashellonconsole*/ new_init_action(RESPAWN,bb_default_login_shell,""); }else{ /*Notinsingleusermode-seewhatinittabsays*/ /*NOTEthatifCONFIG_FEATURE_USE_INITTABisNOTdefined, *thenparse_inittab()simplyaddsinsomedefault *actions(i.e.,INIT_SCRIPTandapair *of"askfirst"shells)*/ parse_inittab(); } #ifENABLE_SELINUX if(getenv("SELINUX_INIT")==NULL){ intenforce=0; putenv((char*)"SELINUX_INIT=YES"); if(selinux_init_load_policy(&enforce)==0){ BB_EXECVP(argv[0],argv); }elseif(enforce>0){ /*SELinuxinenforcingmodebutload_policyfailed*/ message(L_CONSOLE,"can'tloadSELinuxPolicy." "Machineisinenforcingmode.Haltingnow."); returnEXIT_FAILURE; } } #endif #ifENABLE_FEATURE_INIT_MODIFY_CMDLINE /*Makethecommandlinejustsay"init"-that'sall,nothingelse*/ strncpy(argv[0],"init",strlen(argv[0])); /*Wipeargv[1]-argv[N]sotheydon'tclutterthepslisting*/ while(*++argv) nuke_str(*argv); #endif /*SetupSTOPsignalhandlers*/ /*StophandlermustallowonlySIGCONTinsideitself*/ memset(&sa,0,sizeof(sa)); sigfillset(&sa.sa_mask); sigdelset(&sa.sa_mask,SIGCONT); sa.sa_handler=stop_handler; sa.sa_flags=SA_RESTART; sigaction_set(SIGTSTP,&sa);/*pause*/ /*Doesnotworkasintended,atleastin2.6.20. *SIGSTOPissimplyignoredbyinit *(NB:behaviormightdifferunderstrace): */ sigaction_set(SIGSTOP,&sa);/*pause*/ /*Nowruneverythingthatneedstoberun*/ /*Firstrunthesysinitcommand*/ run_actions(SYSINIT); check_delayed_sigs(&G.zero_ts); /*Nextrunanythingthatwantstoblock*/ run_actions(WAIT); check_delayed_sigs(&G.zero_ts); /*Nextrunanythingtoberunonlyonce*/ run_actions(ONCE); /*Nowruntheloopingstufffortherestofforever*/ while(1){ /*(Re)runtherespawn/askfirststuff*/ run_actions(RESPAWN|ASKFIRST); /*Waitforanysignal(typicallyit'sSIGCHLD)*/ check_delayed_sigs(NULL);/*NULLtimespecmakesitwait*/ /*Waitforanychildprocess(es)toexit*/ while(1){ pid_twpid; structinit_action*a; wpid=waitpid(-1,NULL,WNOHANG); if(wpid<=?0) ????break; ???a?=?mark_terminated(wpid); ???if?(a)?{ ????message(L_LOG,?"process?'%s'?(pid?%u)?exited.?" ??????"Scheduling?for?restart.", ??????a->command,(unsigned)wpid); } } /*Don'tconsumeallCPUtime-sleepabit*/ sleep1(); }/*while(1)*/ }
跳過條件宏定義下的編譯分支,主要分析其通用的代碼部分:
(1)首先使用sigaddset()將信號(hào)添加到信號(hào)集合,添加的信號(hào)有:Ctrl-Alt-Del、SIGQUIT、SIGPWR、SIGUSR1、SIGTERM、SIGUSR2、SIGUSR2。
(2)然后找出系統(tǒng)默認(rèn)的控制臺(tái),并將路徑切換到/,接著重新創(chuàng)建一個(gè)新的會(huì)話:
console_init();
set_sane_term();
xchdir("/");
setsid();
(3)設(shè)置默認(rèn)的環(huán)境變量:
putenv((char*)"HOME=/"); putenv((char*)bb_PATH_root_path); putenv((char*)"SHELL=/bin/sh"); putenv((char*)"USER=root");/*needed?why?*/
(4)如果是單用戶模式,則會(huì)調(diào)用new_init_action(RESPAWN, bb_default_login_shell, "");在控制臺(tái)啟動(dòng)一個(gè)shell;否則,則會(huì)調(diào)用parse_inittab()函數(shù)。
parse_inittab()函數(shù)定義如下:
staticvoidparse_inittab(void)
{
#ifENABLE_FEATURE_USE_INITTAB
char*token[4];
parser_t*parser=config_open2("/etc/inittab",fopen_for_read);
if(parser==NULL)
#endif
{
/*Noinittabfile-setupsomedefaultbehavior*/
/*Sysinit*/
new_init_action(SYSINIT,INIT_SCRIPT,"");
/*Askfirstshellontty1-4*/
new_init_action(ASKFIRST,bb_default_login_shell,"");
//TODO:VC_1insteadof""?""isconsole->cttyproblems->angryusers
new_init_action(ASKFIRST,bb_default_login_shell,VC_2);
new_init_action(ASKFIRST,bb_default_login_shell,VC_3);
new_init_action(ASKFIRST,bb_default_login_shell,VC_4);
/*RebootonCtrl-Alt-Del*/
new_init_action(CTRLALTDEL,"reboot","");
/*Umountallfilesystemsonhalt/reboot*/
new_init_action(SHUTDOWN,"umount-a-r","");
/*Swapoffonhalt/reboot*/
new_init_action(SHUTDOWN,"swapoff-a","");
/*RestartinitwhenaQUITisreceived*/
new_init_action(RESTART,"init","");
return;
}
#ifENABLE_FEATURE_USE_INITTAB
/*optional_ttyaction:command
*Delimsarenottobecollapsedandneedexactly4tokens
*/
while(config_read(parser,token,4,0,"#:",
PARSE_NORMAL&~(PARSE_TRIM|PARSE_COLLAPSE))){
/*ordermustcorrespondtoSYSINIT..RESTARTconstants*/
staticconstcharactions[]ALIGN1=
"sysinit?""wait?""once?""respawn?""askfirst?"
"ctrlaltdel?""shutdown?""restart?";
intaction;
char*tty=token[0];
if(!token[3])/*lessthan4tokens*/
gotobad_entry;
action=index_in_strings(actions,token[2]);
if(action0?||?!token[3][0])?/*?token[3]:?command?*/
???goto?bad_entry;
??/*?turn?.*TTY?->/dev/TTY*/
if(tty[0]){
tty=concat_path_file("/dev/",skip_dev_pfx(tty));
}
new_init_action(1<lineno);
}
config_close(parser);
#endif
}
如果定義了CONFIG_FEATURE_USE_INITTAB,則會(huì)使用/etc/inittab文件中的action;如果CONFIG_FEATURE_USE_INITTAB沒有定義,parse_inittab()則會(huì)簡(jiǎn)單添加一些默認(rèn)actions(例如,運(yùn)行INIT_SCRIPT,然后啟動(dòng)一個(gè)“askfirst”shell)。如果ENABLE_FEATURE_USE_INITTAB已定義,但是/etc/inittab文件缺失也會(huì)使用相同的默認(rèn)行為集合。
(5)設(shè)置STOP信號(hào)處理程序:
memset(&sa,0,sizeof(sa)); sigfillset(&sa.sa_mask); sigdelset(&sa.sa_mask,SIGCONT); sa.sa_handler=stop_handler; sa.sa_flags=SA_RESTART; sigaction_set(SIGTSTP,&sa);/*pause*/ sigaction_set(SIGSTOP,&sa);/*pause*/
(6)接下來運(yùn)行想要運(yùn)行的命令,依次運(yùn)行:SYSINIT、WAIT、ONCE:
run_actions(SYSINIT); check_delayed_sigs(&G.zero_ts); run_actions(WAIT); check_delayed_sigs(&G.zero_ts); run_actions(ONCE);
(7)在最后,則是一個(gè)while(1)的死循環(huán),用于處理我們?cè)诿钚邢螺斎氲拿睿?/p>
while(1){
/*重新運(yùn)行respawn/askfisrt*/
run_actions(RESPAWN|ASKFIRST);
/*等待信號(hào)*/
check_delayed_sigs(NULL);/*NULLtimespecmakesitwait*/
/*等待所有的子進(jìn)程退出*/
while(1){
pid_twpid;
structinit_action*a;
wpid=waitpid(-1,NULL,WNOHANG);
if(wpid<=?0)
????break;
???a?=?mark_terminated(wpid);
???if?(a)?{
????message(L_LOG,?"process?'%s'?(pid?%u)?exited.?"
??????"Scheduling?for?restart.",
??????a->command,(unsigned)wpid);
}
}
/*短暫讓出CPU,不要消耗所有的CPU時(shí)間*/
sleep1();
}/*while(1)*/
補(bǔ)充
BusyBox的init不支持運(yùn)行級(jí)別。runlevels字段在BusyBox init中將會(huì)被完全忽略。
所以如果想要使用運(yùn)行級(jí)別的系統(tǒng),需使用sysvinit作為啟動(dòng)進(jìn)程。
一、7個(gè)運(yùn)行級(jí)別
(1)運(yùn)行級(jí)別0:系統(tǒng)停機(jī)狀態(tài),系統(tǒng)默認(rèn)運(yùn)行級(jí)別不能設(shè)為0,否則不能正常啟動(dòng)。其實(shí)就是關(guān)機(jī)。
(2)運(yùn)行級(jí)別1:?jiǎn)斡脩艄ぷ鳡顟B(tài),root權(quán)限,用于系統(tǒng)維護(hù),禁止遠(yuǎn)程登陸。在忘記root密碼時(shí)一般用這個(gè)運(yùn)行級(jí)別,進(jìn)去修改root密碼。
(3)運(yùn)行級(jí)別2:多用戶狀態(tài)(沒有NFS),沒有網(wǎng)絡(luò)連接。
(4)運(yùn)行級(jí)別3:完全的多用戶狀態(tài)(有NFS),登陸后進(jìn)入控制臺(tái)命令行模式。linux很常見的運(yùn)行級(jí)別
(5)運(yùn)行級(jí)別4:系統(tǒng)未使用,保留。
(6)運(yùn)行級(jí)別5:X11控制臺(tái),登陸后進(jìn)入圖形GUI模式。就是圖形模式。
(7)運(yùn)行級(jí)別6:系統(tǒng)正常關(guān)閉并重啟,默認(rèn)運(yùn)行級(jí)別不能設(shè)為6,否則不能正常啟動(dòng)。
二、查看運(yùn)行級(jí)別
1、runlevel命令:打印系統(tǒng)的上一個(gè)和當(dāng)前運(yùn)行級(jí)別:

N:“N”表示自系統(tǒng)啟動(dòng)后運(yùn)行級(jí)別尚未更改。從上圖可見,小生的Ubuntu系統(tǒng)的運(yùn)行級(jí)別為5。
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4413瀏覽量
67261 -
代碼
+關(guān)注
關(guān)注
30文章
4959瀏覽量
73584 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
321瀏覽量
23137
發(fā)布評(píng)論請(qǐng)先 登錄
Linux編譯驅(qū)動(dòng)、內(nèi)核及應(yīng)用程序分析
Linux內(nèi)核啟動(dòng)過程和Bootloader(總述)
【OK210試用體驗(yàn)】bootloader啟動(dòng)linux內(nèi)核
Linux內(nèi)核編譯和啟動(dòng)的相關(guān)資料分享
Android的Linux內(nèi)核與驅(qū)動(dòng)程序開發(fā)教程
linux內(nèi)核啟動(dòng)內(nèi)核解壓過程分析
linux內(nèi)核啟動(dòng)流程
linux內(nèi)核無法啟動(dòng)
嵌入式Linux內(nèi)核的驅(qū)動(dòng)程序開發(fā)是怎樣的
最硬核的Linux內(nèi)核文章
快速理解什么是Linux內(nèi)核以及Linux內(nèi)核的內(nèi)容
如何使用Linux內(nèi)核實(shí)現(xiàn)USB驅(qū)動(dòng)程序框架
基于linux內(nèi)核啟動(dòng)程序
評(píng)論