色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核中信號(hào)的傳遞過(guò)程

jf_0tjVfeJz ? 來(lái)源:嵌入式ARM和Linux ? 2024-01-17 09:51 ? 次閱讀

前面我們已經(jīng)介紹了內(nèi)核注意到信號(hào)的到來(lái),調(diào)用相關(guān)函數(shù)更新進(jìn)程描述符以便進(jìn)程接收處理信號(hào)。但是,如果目標(biāo)進(jìn)程此時(shí)沒(méi)有運(yùn)行,內(nèi)核則推遲傳遞信號(hào)。現(xiàn)在,我們看看內(nèi)核如何處理進(jìn)程掛起的信號(hào)。

正如第4章的從中斷和異常返回一節(jié)中提到的,內(nèi)核允許在進(jìn)程返回到用戶態(tài)執(zhí)行之前,檢查進(jìn)程的TIF_SIGPENDING標(biāo)志。因此,內(nèi)核每次完成中斷或異常的處理后,都會(huì)檢查掛起信號(hào)是否存在。為了處理非阻塞的掛起信號(hào),內(nèi)核調(diào)用do_signal()函數(shù),其接受2個(gè)參數(shù)

regs

current當(dāng)前進(jìn)程的用戶態(tài)寄存器內(nèi)容在內(nèi)核棧中保存位置的地址。

oldset

用來(lái)保存阻塞信號(hào)位掩碼數(shù)組的變量地址。

對(duì)do_signal()的描述,主要集中在信號(hào)傳遞的通用機(jī)制;真實(shí)的代碼中涵蓋了許多細(xì)節(jié),比如處理競(jìng)態(tài)條件和其它特殊情況(如凍結(jié)系統(tǒng)、生成核心轉(zhuǎn)儲(chǔ)、停止和殺死整個(gè)線程組等等。我們將忽略這些細(xì)節(jié)。

如前所述,do_signal()函數(shù)通常只在CPU打算返回到用戶態(tài)時(shí)才會(huì)被調(diào)用。所以,如果中斷處理程序里調(diào)用do_signal(),函數(shù)直接返回:

if((regs->xcs&3)!=3)
return1;

如果oldset是NULL,使用current->blocked的地址初始化它:

if(!oldset)
oldset=¤t->blocked;

do_signal()的核心是一段循環(huán)代碼,重復(fù)調(diào)用dequeue_signal()函數(shù),直到私有和共享掛起信號(hào)隊(duì)列沒(méi)有被阻塞的掛起信號(hào)。dequeue_signal()的返回值存儲(chǔ)在局部變量signr中,如果等于0,意味著所有掛起信號(hào)都被處理完,則do_signal()完成;如果返回非零,就有掛起信號(hào)等待被處理。do_signal()處理完這個(gè)信號(hào)后,會(huì)再次調(diào)用dequeue_signal()函數(shù)。

dequeue_signal()首先考慮私有掛起信號(hào)隊(duì)列中的所有信號(hào)(數(shù)字從小到大),然后是共享掛起隊(duì)列中的信號(hào)。它會(huì)更新相應(yīng)的數(shù)據(jù)結(jié)構(gòu),標(biāo)識(shí)該信號(hào)不再掛起并返回信號(hào)值。其中,涉及到清除current->pending.signal或current->signal->shared_pending.signal中的相應(yīng)位,并調(diào)用recalc_sigpending()更新TIF_SIGPENDING的值。

讓我們看一下do_signal()如何處理dequeue_signal()返回的每個(gè)掛起信號(hào)。首先,檢查當(dāng)前接收進(jìn)程是否正在被其它進(jìn)程監(jiān)控;如果是這種情況,do_signal()調(diào)用do_notify_parent_cldstop()和schedule()使監(jiān)控線程意識(shí)到該信號(hào)處理。

然后,do_signal()將待處理信號(hào)的k_sigaction數(shù)據(jù)結(jié)構(gòu)的地址加載到局部變量ka中:

ka=¤t->sig->action[signr-1];

依賴(lài)具體內(nèi)容,可能執(zhí)行三類(lèi)動(dòng)作:忽略信號(hào),執(zhí)行默認(rèn)動(dòng)作或執(zhí)行信號(hào)處理程序。

當(dāng)傳遞的信號(hào)被忽略,do_signal()則繼續(xù)處理其它掛起信號(hào):

if(ka->sa.sa_handler==SIG_IGN)
continue;

接下來(lái)的兩節(jié),我們將描述如何執(zhí)行默認(rèn)動(dòng)作和信號(hào)處理程序。

1 執(zhí)行信號(hào)的默認(rèn)動(dòng)作

如果ka->sa.sa_handler等于SIG_DFL,do_signal()執(zhí)行信號(hào)的默認(rèn)動(dòng)作。唯一的例外是,當(dāng)接收進(jìn)程是init時(shí),這種情況下,信號(hào)會(huì)被拋棄:

if(current->pid==1)
continue;

對(duì)于其它進(jìn)程,忽略信號(hào)的默認(rèn)處理也非常簡(jiǎn)單:

if(signr==SIGCONT||signr==SIGCHLD||
signr==SIGWINCH||signr==SIGURG)
continue;

對(duì)于默認(rèn)動(dòng)作是stop的信號(hào),則會(huì)停止線程組中所有進(jìn)程。為此,do_signal()將它們的狀態(tài)設(shè)置為T(mén)ASK_STOPPED,然后調(diào)用schedule()調(diào)度進(jìn)程:

if(signr==SIGSTOP||signr==SIGTSTP||
signr==SIGTTIN||signr==SIGTTOU){
if(signr!=SIGSTOP&&
is_orphaned_pgrp(current->signal->pgrp))
continue;
do_signal_stop(signr);
}

SIGSTOP和其它信號(hào)有些許不同:SIGSTOP總是停止線程組,而其它信號(hào)只有在線程組處于孤兒進(jìn)程組中時(shí)才會(huì)停止該線程組。POSIX標(biāo)準(zhǔn)指明,只要線程組中某個(gè)進(jìn)程在同一個(gè)會(huì)話的不同進(jìn)程組中有一個(gè)父進(jìn)程,它就不是孤兒進(jìn)程組。因此,如果父進(jìn)程已死,但是,發(fā)起進(jìn)程的用戶仍然處于登錄中,該進(jìn)程組就不是孤兒進(jìn)程組。

do_signal_stop()檢查當(dāng)前進(jìn)程是否是線程組中第一個(gè)被停止的進(jìn)程。如果是,它負(fù)責(zé)停止所有進(jìn)程:本質(zhì)上,該函數(shù)將信號(hào)描述符中的group_stop_count字段設(shè)置為正值,并喚醒線程組中的每個(gè)進(jìn)程。然后,每個(gè)進(jìn)程依次查看此字段以識(shí)別正在進(jìn)行的組停止,將其狀態(tài)更改為T(mén)ASK_STOPPED,并調(diào)用schedule()重新調(diào)度進(jìn)程。do_signal_stop()函數(shù)還向線程組leader的父進(jìn)程發(fā)送SIGCHLD信號(hào),除非父進(jìn)程設(shè)置了SIGCHLD的SA_NOCLDSTOP標(biāo)志。

默認(rèn)動(dòng)作為dump的信號(hào)會(huì)在進(jìn)程的工作目錄中創(chuàng)建核心轉(zhuǎn)儲(chǔ)文件:該文件列出了進(jìn)程地址空間和寄存器的完整內(nèi)容。do_signal()創(chuàng)建核心轉(zhuǎn)儲(chǔ)文件之后,會(huì)殺死線程組。其余18個(gè)信號(hào)的默認(rèn)動(dòng)作是terminate,就是殺死進(jìn)程。為此,調(diào)用do_group_exit(),執(zhí)行一個(gè)優(yōu)雅的group exit處理程序(可以參考第3章的進(jìn)程終止一節(jié))

2 捕獲信號(hào)

如果信號(hào)指定了處理程序,則do_signal()執(zhí)行該程序。通過(guò)調(diào)用invoking handle_signal()

handle_signal(signr,&info,&ka,oldset,regs);
if(ka->sa.sa_flags&SA_ONESHOT)
ka->sa.sa_handler=SIG_DFL;
return1;

如果接收的信號(hào)設(shè)置了SA_ONESHOT標(biāo)志,則必須將其重置為默認(rèn)操作,以便再次出現(xiàn)相同信號(hào)將不會(huì)觸發(fā)信號(hào)處理程序的執(zhí)行。注意do_signal()在處理完單個(gè)信號(hào)后是如何返回的。在下次調(diào)用do_signal()之前,不會(huì)考慮其他掛起的信號(hào)。這種方法確保了實(shí)時(shí)信號(hào)將按適當(dāng)?shù)捻樞蛱幚怼?/p>

執(zhí)行信號(hào)處理程序是一項(xiàng)相當(dāng)復(fù)雜的任務(wù),因?yàn)樵谟脩魬B(tài)和內(nèi)核態(tài)之間切換時(shí)需要小心地切換堆棧。我們?cè)谶@里詳細(xì)解釋一下:

信號(hào)處理程序是由用戶進(jìn)程定義的函數(shù),包含在用戶代碼段中。handle_signal()函數(shù)在內(nèi)核態(tài)運(yùn)行,而信號(hào)處理程序在用戶態(tài)運(yùn)行;這意味著當(dāng)前進(jìn)程必須首先在用戶態(tài)執(zhí)行信號(hào)處理程序,然后才能被允許恢復(fù)其“正常”執(zhí)行。此外,當(dāng)內(nèi)核試圖恢復(fù)進(jìn)程的正常執(zhí)行時(shí),內(nèi)核堆棧不再包含被中斷程序的硬件上下文,因?yàn)閮?nèi)核堆棧在每次從用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài)時(shí)都會(huì)被清空。

下圖11-2說(shuō)明了捕獲信號(hào)的函數(shù)執(zhí)行流程。假設(shè)非阻塞信號(hào)被發(fā)送給進(jìn)程。中斷或異常發(fā)生時(shí),進(jìn)程切換到內(nèi)核態(tài)。在即將返回到用戶態(tài)之前,內(nèi)核調(diào)用do_signal()函數(shù),依次處理信號(hào)(handle_signal())并配置用戶態(tài)棧(setup_frame()或setup_rt_frame())。進(jìn)程切換到用戶態(tài)后,開(kāi)始執(zhí)行信號(hào)處理程序,因?yàn)樵撎幚沓绦虻牡刂繁粡?qiáng)制加載到了PC程序計(jì)數(shù)器中。當(dāng)信號(hào)程序終止后,調(diào)用setup_frame()或setup_rt_frame()將返回代碼加載到用戶態(tài)棧中。這段返回代碼會(huì)調(diào)用sigreturn()和rt_sigreturn()系統(tǒng)調(diào)用;相應(yīng)的服務(wù)例程會(huì)將正常程序的硬件上下文內(nèi)容拷貝到內(nèi)核態(tài)棧并將用戶態(tài)棧恢復(fù)到其原始狀態(tài)(restore_sigcontext())。當(dāng)系統(tǒng)調(diào)用終止時(shí),正常程序繼續(xù)其執(zhí)行。

880c0774-b45f-11ee-8b88-92fbcf53809c.png

圖11-2 捕獲一個(gè)信號(hào)

現(xiàn)在,讓我們看一下其執(zhí)行細(xì)節(jié):

2.1 Setting up the frame

為了正確設(shè)置進(jìn)程的用戶態(tài)棧,handle_signal()函數(shù)既可以調(diào)用setup_frame()(對(duì)于那些不需要siginfo_t的信號(hào)),也可以調(diào)用setup_rt_frame()(對(duì)于那些確定需要siginfo_t的信號(hào))。具體調(diào)用哪個(gè)函數(shù),依賴(lài)于信號(hào)的sigaction表中sa_flags字段的SA_SIGINFO標(biāo)志。

接下來(lái),我們看一下setup_frame()函數(shù)的具體實(shí)現(xiàn):(Linux內(nèi)核版本是v2.6.11,文件位置:arch/x86_64/kernel/signal.c)

/*這些符號(hào)的定義在vsyscall內(nèi)存頁(yè)中,查看vsyscall-sigreturn.S文件*/
externvoid__user__kernel_sigreturn;
externvoid__user__kernel_rt_sigreturn;

staticvoidsetup_frame(intsig,structk_sigaction*ka,
sigset_t*set,structpt_regs*regs)
{
void__user*restorer;
structsigframe__user*frame;
interr=0;
intusig;

frame=get_sigframe(ka,regs,sizeof(*frame));

if(!access_ok(VERIFY_WRITE,frame,sizeof(*frame)))
gotogive_sigsegv;

usig=current_thread_info()->exec_domain
&¤t_thread_info()->exec_domain->signal_invmap
&&sigexec_domain->signal_invmap[sig]
:sig;

err=__put_user(usig,&frame->sig);
if(err)
gotogive_sigsegv;

err=setup_sigcontext(&frame->sc,&frame->fpstate,regs,set->sig[0]);
if(err)
gotogive_sigsegv;

if(_NSIG_WORDS>1){
err=__copy_to_user(&frame->extramask,&set->sig[1],
sizeof(frame->extramask));
if(err)
gotogive_sigsegv;
}

restorer=&__kernel_sigreturn;
if(ka->sa.sa_flags&SA_RESTORER)
restorer=ka->sa.sa_restorer;

/*Setuptoreturnfromuserspace.*/
err|=__put_user(restorer,&frame->pretcode);

/*
*Thisispopl%eax;movl$,%eax;int$0x80
*
*WEDONOTUSEITANYMORE!It'sonlylefthereforhistorical
*reasonsandbecausegdbusesitasasignaturetonotice
*signalhandlerstackframes.
*/
err|=__put_user(0xb858,(short__user*)(frame->retcode+0));
err|=__put_user(__NR_sigreturn,(int__user*)(frame->retcode+2));
err|=__put_user(0x80cd,(short__user*)(frame->retcode+6));

if(err)
gotogive_sigsegv;

/*為信號(hào)處理程序配置寄存器*/
regs->esp=(unsignedlong)frame;
regs->eip=(unsignedlong)ka->sa.sa_handler;
regs->eax=(unsignedlong)sig;
regs->edx=(unsignedlong)0;
regs->ecx=(unsignedlong)0;

/*恢復(fù)用戶態(tài)的段寄存器*/
set_fs(USER_DS);
regs->xds=__USER_DS;
regs->xes=__USER_DS;
regs->xss=__USER_DS;
regs->xcs=__USER_CS;

/*在進(jìn)入信號(hào)處理程序時(shí)清除TF標(biāo)志,但通知正在單步跟蹤的跟蹤器,
*跟蹤器也可能希望在信號(hào)處理程序內(nèi)部進(jìn)行單步執(zhí)行
*/
regs->eflags&=~TF_MASK;
if(test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP);
//...省略,打印調(diào)試信息,然后返回。

give_sigsegv:
force_sigsegv(sig,current);
}

setup_frame()接收4個(gè)參數(shù),如下所示:

sig

信號(hào)

ka

信號(hào)的k_sigaction表地址

oldset

阻塞信號(hào)的位掩碼組地址

regs

用戶態(tài)寄存器內(nèi)容在內(nèi)核棧的保存位置

setup_frame()將一個(gè)稱(chēng)為frame的數(shù)據(jù)結(jié)構(gòu)壓倒用戶態(tài)棧中,該數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)著處理信號(hào)和能夠正確返回到sys_sigreturn()函數(shù)的所需要信息。frame是一個(gè)sigframe表,包含以下字段(參見(jiàn)圖11-3):

pretcode

信號(hào)處理程序的返回地址。其實(shí)就是__kernel_sigreturn標(biāo)簽處的匯編代碼。

sig

信號(hào),信號(hào)處理程序需要的一個(gè)參數(shù)。

sc

包含用戶態(tài)進(jìn)程即將切換到內(nèi)核態(tài)之前的進(jìn)程上下文內(nèi)容,其數(shù)據(jù)類(lèi)型為sigcontext(這些信息是從current的內(nèi)核態(tài)棧中拷貝而來(lái))。另外,它還包含一個(gè)進(jìn)程阻塞信號(hào)的位數(shù)組。

fpstate

用來(lái)保存用戶態(tài)進(jìn)程的浮點(diǎn)寄存器信息,數(shù)據(jù)結(jié)構(gòu)類(lèi)型為_(kāi)fpstate。(參見(jiàn)第3章的保存和加載FPU、MMX和XMM寄存器)。

extramask

指定阻塞實(shí)時(shí)信號(hào)的位數(shù)組。

retcode

發(fā)起sigreturn()系統(tǒng)調(diào)用的8字節(jié)代碼。在Linux早期版本中,這段代碼用來(lái)從信號(hào)處理程序返回;但Linux 2.6版本以后,僅用作符號(hào)簽名,以便調(diào)試器可以識(shí)別信號(hào)的棧幀。

88212c12-b45f-11ee-8b88-92fbcf53809c.png

圖11-3 用戶態(tài)棧上的frame

setup_frame()函數(shù)調(diào)用get_sigframe()計(jì)算frame第一個(gè)內(nèi)存位置,因?yàn)樵搩?nèi)存位置位于用戶態(tài)棧上,所以函數(shù)返回的值為(regs->esp - sizeof(struct sigframe)) & 0xfffffff8。

Linux允許進(jìn)程調(diào)用signaltstack()系統(tǒng)調(diào)用為它們的信號(hào)處理程序指定一個(gè)替換棧;這個(gè)特性也是X/Open標(biāo)準(zhǔn)要求的。如果使用的是替換棧,get_sigframe()函數(shù)返回的是替換棧中的一個(gè)地址。對(duì)于此特性,我們不過(guò)多討論,從概念上講,其與常規(guī)信號(hào)處理非常類(lèi)似。

因?yàn)樵趚86架構(gòu)上,棧是向下增長(zhǎng)的,所以,frame的首地址等于當(dāng)前棧頂位置的地址減去frame的大小,結(jié)果按照8字節(jié)對(duì)齊。

返回地址使用access_ok進(jìn)行驗(yàn)證:如果合法,函數(shù)重復(fù)調(diào)用__put_user(),以便填充frame的所有字段。pretcode字段初始化為&__kernel_sigreturn,這是vsyscall內(nèi)存頁(yè)上的一段匯編代碼地址。(參見(jiàn)第10章的通過(guò)sysenter指令發(fā)起系統(tǒng)調(diào)用的一節(jié))

接下來(lái),修改內(nèi)核態(tài)棧的regs內(nèi)容,保證當(dāng)current切換到用戶態(tài)時(shí),CPU控制權(quán)能夠傳遞給信號(hào)處理程序:

regs->esp=(unsignedlong)frame;
regs->eip=(unsignedlong)ka->sa.sa_handler;
regs->eax=(unsignedlong)sig;
regs->edx=regs->ecx=0;
regs->xds=regs->xes=regs->xss=__USER_DS;
regs->xcs=__USER_CS;

最后,setup_frame()函數(shù)將保存在內(nèi)核態(tài)棧上的段寄存器復(fù)位成用戶態(tài)默認(rèn)值而終止。現(xiàn)在,信號(hào)處理程序所需的信息都在用戶態(tài)棧頂位置了。

setup_rt_frame()函數(shù)與setup_frame()類(lèi)似,但是它把一個(gè)擴(kuò)展幀(數(shù)據(jù)結(jié)構(gòu)為rt_sigframe)存放到了用戶態(tài)棧中,該擴(kuò)展幀還包括與信號(hào)有關(guān)的siginfo_t表的內(nèi)容。此外,該函數(shù)還將pretcode字段指向vsyscall內(nèi)存頁(yè)中的__kernel_rt_sigreturn代碼段。

2.2 計(jì)算信號(hào)標(biāo)志

配置完用戶態(tài)棧后,handle_signal()函數(shù)檢查與該信號(hào)相關(guān)的標(biāo)志。如果該信號(hào)沒(méi)有設(shè)置SA_NODEFER標(biāo)志,則在信號(hào)處理程序執(zhí)行期間,sigaction表中的sa_mask字段中的所有信號(hào)必須被阻塞,以便該信號(hào)快速處理完成:

if(!(ka->sa.sa_flags&SA_NODEFER)){
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
sigaddset(¤t->blocked,sig);
recalc_sigpending(current);
spin_unlock_irq(¤t->sighand->siglock);
}

正如先前描述的,recalc_sigpending()函數(shù)檢查該進(jìn)程是否有非阻塞的掛起信號(hào),并設(shè)置其相應(yīng)的TIF_SIGPENDING標(biāo)志。

完成之后,返回do_signal(),隨即也返回。

2.3 啟動(dòng)信號(hào)處理程序

當(dāng)從do_signal()返回后,當(dāng)前進(jìn)程切換到用戶態(tài)執(zhí)行。因?yàn)閟etup_frame()的準(zhǔn)備工作,eip寄存器指向了信號(hào)處理程序的第一條指令,而esp指向了壓入用戶態(tài)棧頂?shù)膄rame的第一個(gè)內(nèi)存位置。于是,開(kāi)始執(zhí)行信號(hào)處理程序。

2.4 終止信號(hào)處理程序

當(dāng)信號(hào)處理程序執(zhí)行完成時(shí),其棧頂?shù)姆祷氐刂分赶騰syscall內(nèi)存頁(yè)(frame中的pretcode字段)

__kernel_sigreturn:
popl%eax
movl$__NR_sigreturn,%eax
int$0x80

因此,信號(hào)(也就是frame中的sig字段)被從棧中丟棄;然后,調(diào)用sigreturn()系統(tǒng)調(diào)用。

sys_sigreturn()函數(shù)計(jì)算regs(類(lèi)型為pt_regs)的地址,其中包含用戶進(jìn)程的硬件上下文內(nèi)容,以便完成內(nèi)核態(tài)切換到用戶態(tài)執(zhí)行。因?yàn)槲覀冊(cè)趶膬?nèi)核態(tài)切換到用戶態(tài)執(zhí)行信號(hào)處理程序的過(guò)程中,內(nèi)核態(tài)棧已經(jīng)被破壞,所以需要重新建立一個(gè)臨時(shí)內(nèi)核態(tài)棧,數(shù)據(jù)來(lái)源就是用戶態(tài)棧中配置的frame數(shù)據(jù)結(jié)構(gòu)。

asmlinkageintsys_sigreturn(unsignedlong__unused)
{
/*建立進(jìn)程在內(nèi)核態(tài)的臨時(shí)棧*/
structpt_regs*regs=(structpt_regs*)&__unused;
//內(nèi)核態(tài)棧中用戶存儲(chǔ)
structsigframe__user*frame=(structsigframe__user*)(regs->esp-8);
sigset_tset;
inteax;

/*驗(yàn)證`frame`數(shù)據(jù)結(jié)構(gòu)是否正確*/
if(verify_area(VERIFY_READ,frame,sizeof(*frame)))
gotobadframe;
/*處理實(shí)時(shí)信號(hào)*/
if(__get_user(set.sig[0],&frame->sc.oldmask)
||(_NSIG_WORDS>1
&&__copy_from_user(&set.sig[1],&frame->extramask,
sizeof(frame->extramask))))
gotobadframe;

/*將在信號(hào)處理程序期間阻塞的信號(hào)恢復(fù)掛起狀態(tài)*/
sigdelsetmask(&set,~_BLOCKABLE);
spin_lock_irq(¤t->sighand->siglock);
current->blocked=set;
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);

/*將用戶態(tài)棧中的frame中保存的用戶進(jìn)程硬件上下文拷貝到內(nèi)核態(tài)棧,并移除frame*/
if(restore_sigcontext(regs,&frame->sc,&eax))
gotobadframe;
returneax;

/*錯(cuò)誤數(shù)據(jù)處理*/
badframe:
force_sig(SIGSEGV,current);
return0;
}

sys_sigreturn()函數(shù)計(jì)算出regs(類(lèi)型為pt_regs)的地址,它包含用戶進(jìn)程的硬件上下文內(nèi)容(參考第10章的參數(shù)傳遞一節(jié)。根據(jù)regs中的esp字段,就能推斷出用戶棧中的frame地址。

然后,從frame的sc字段中拷貝在調(diào)用信號(hào)處理程序之前被阻塞的信號(hào)(位數(shù)組)到當(dāng)前進(jìn)程current的blocked字段中。也就是將這些被阻塞的信號(hào)解除阻塞。調(diào)用recalc_sigpending()將這些信號(hào)重新加入到掛起信號(hào)隊(duì)列中。

接下來(lái),sys_sigreturn()函數(shù)需要將frame的sc字段中的進(jìn)程硬件上下文拷貝到內(nèi)核態(tài)棧,并從用戶態(tài)棧中移除frame數(shù)據(jù)。這兩步的完成都是restore_sigcontext()函數(shù)實(shí)現(xiàn)的。

如果信號(hào)是由系統(tǒng)調(diào)用發(fā)送的(比如,rt_sigqueueinfo()),要求信號(hào)相關(guān)的siginfo_t表數(shù)據(jù),其機(jī)制與上面類(lèi)似。擴(kuò)展幀中的pretcode字段指向__kernel_rt_sigreturn標(biāo)簽處的匯編代碼(位于vsyscall內(nèi)存頁(yè)),這段代碼會(huì)調(diào)用rt_sigreturn()系統(tǒng)調(diào)用。相應(yīng)的系統(tǒng)服務(wù)例程sys_rt_sigreturn()將擴(kuò)展幀中的進(jìn)程硬件上下文拷貝到內(nèi)核態(tài)棧,并且將擴(kuò)展幀從用戶態(tài)棧中移除,以便恢復(fù)原始的用戶態(tài)棧。

3 系統(tǒng)調(diào)用的重新執(zhí)行

對(duì)于系統(tǒng)調(diào)用請(qǐng)求,內(nèi)核有時(shí)不能立即滿足。這時(shí)候,發(fā)起系統(tǒng)調(diào)用的進(jìn)程會(huì)被置成TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE狀態(tài)。

如果進(jìn)程處于TASK_INTERRUPTIBLE狀態(tài)且其它進(jìn)程發(fā)送信號(hào)給它,內(nèi)核在沒(méi)有完成系統(tǒng)調(diào)用的情況下將進(jìn)程置為T(mén)ASK_RUNNING(參考第4章的從中斷和異常返回一節(jié))。當(dāng)進(jìn)程想要切換回用戶態(tài),同時(shí)信號(hào)傳遞過(guò)來(lái)時(shí),系統(tǒng)調(diào)用服務(wù)例程還沒(méi)有完成其工作,所以會(huì)返回錯(cuò)誤碼EINTR、ERESTARTNOHAND、ERESTART_RESTARTBLOCK、ERESTARTSYS、ERESTARTNOINTR。

事實(shí)上,在這種場(chǎng)景下,用戶態(tài)進(jìn)程能得到的錯(cuò)誤碼只能是EINTR,這意味著系統(tǒng)調(diào)用還沒(méi)有完成。應(yīng)用編程者可以檢查這個(gè)錯(cuò)誤碼并決定是否重新發(fā)起系統(tǒng)調(diào)用。其余的錯(cuò)誤碼由內(nèi)核內(nèi)部使用,指定是否在信號(hào)處理程序結(jié)束之后自動(dòng)重新執(zhí)行系統(tǒng)調(diào)用。

表11-11 列出了未完成系統(tǒng)調(diào)用相關(guān)的錯(cuò)誤碼,以及它們?nèi)N信號(hào)默認(rèn)行為的影響。表中的術(shù)語(yǔ)說(shuō)明如下:

Terminate

系統(tǒng)調(diào)用將不會(huì)自動(dòng)重新執(zhí)行;進(jìn)程將切換到用戶態(tài)下int $0x80或sysenter之后的指令處繼續(xù)執(zhí)行,同時(shí),通過(guò)寄存器eax返回-EINTR值。

Reexecute

內(nèi)核強(qiáng)制用戶進(jìn)程重新加載系統(tǒng)調(diào)用號(hào)(eax),然后重新調(diào)用int $0x80或sysenter;而進(jìn)程不會(huì)意識(shí)到重新執(zhí)行,也不會(huì)傳遞錯(cuò)誤碼給它。

Depends

只有被傳遞的信號(hào)設(shè)置了SA_RESTART標(biāo)志,系統(tǒng)調(diào)用才會(huì)被重新執(zhí)行;否則,系統(tǒng)調(diào)用將終止并返回錯(cuò)誤碼-EINTR。

表11-11 系統(tǒng)調(diào)用的重新執(zhí)行

信號(hào)行為 EINTR ERESTARTSYS ERESTARTNOHAND
ERESTART_RESTARTBLOCK*
ERESTARTNOINTR
Default Terminate Reexecute Reexecute Reexecute
Ignore Terminate Reexecute Reexecute Reexecute
Catch Terminate Depends Terminate Reexecute

ERESTARTNOHAND和ERESTART_RESTARTBLOCK重啟系統(tǒng)調(diào)用的機(jī)制不同。

在傳遞信號(hào)時(shí),內(nèi)核必須在重新執(zhí)行它之前確保進(jìn)程發(fā)起了系統(tǒng)調(diào)用。這就是regs寄存器上下文的orig_eax字段發(fā)揮關(guān)鍵作用的地方。讓我們回憶一下,當(dāng)中斷或異常處理程序啟動(dòng)時(shí),這個(gè)字段是如何初始化的:

中斷

該字段為中斷IRQ減去256(因?yàn)橹袛嗵?hào)數(shù)量小于224,減去256表示內(nèi)核使用負(fù)數(shù)表示IRQ)(參考第4章的為中斷處理程序保存寄存器)。

0x80異常(包括sysenter)

該字段包含系統(tǒng)調(diào)用號(hào)(第10章的進(jìn)入和推出系統(tǒng)調(diào)用一節(jié))。

其它異常

該字段為–1(參考第4章的為異常處理程序保存寄存器)。

因此,orig_eax中的非負(fù)值意味著信號(hào)喚醒了一個(gè)在系統(tǒng)調(diào)用中休眠的可中斷進(jìn)程(TASK_INTERRUPTIBLE)。服務(wù)例程意識(shí)到了系統(tǒng)調(diào)用被中斷,因此返回一個(gè)前面提到的錯(cuò)誤碼。

3.1 重新啟動(dòng)non-caught信號(hào)中斷的系統(tǒng)調(diào)用

對(duì)于被忽略或執(zhí)行默認(rèn)動(dòng)作的信號(hào),do_signal()分析系統(tǒng)調(diào)用的錯(cuò)誤碼,判斷系統(tǒng)調(diào)用是否自動(dòng)重新執(zhí)行,如表11-1所示。如果系統(tǒng)調(diào)用必須重啟,則修改regs上下文內(nèi)容:eip-2表示將eip指向int $0x80或sysenter,eax包含系統(tǒng)調(diào)用號(hào):

if(regs->orig_eax>=0){
if(regs->eax==-ERESTARTNOHAND||regs->eax==-ERESTARTSYS||
regs->eax==-ERESTARTNOINTR){
regs->eax=regs->orig_eax;
regs->eip-=2;
}
if(regs->eax==-ERESTART_RESTARTBLOCK){
regs->eax=__NR_restart_syscall;
regs->eip-=2;
}
}

regs->eax包含著系統(tǒng)調(diào)用服務(wù)例程的返回碼(參加第10章的進(jìn)入和退出系統(tǒng)調(diào)用一節(jié))。因?yàn)閕nt $0x80和sysreturn指令都是2個(gè)字節(jié)長(zhǎng)度,所以eip-2指向了int $0x80或sysenter,可以再次觸發(fā)系統(tǒng)調(diào)用。

錯(cuò)誤碼ERESTART_RESTARTBLOCK是特殊的,因?yàn)閑ax被設(shè)置為了restart_syscall()系統(tǒng)調(diào)用號(hào);因此,用戶不會(huì)重新啟動(dòng)被信號(hào)中斷的同一個(gè)系統(tǒng)調(diào)用。此錯(cuò)誤碼只有與時(shí)間有關(guān)的系統(tǒng)調(diào)用使用,這些系統(tǒng)調(diào)用重新啟動(dòng)時(shí),應(yīng)該調(diào)整其用戶態(tài)參數(shù)。典型的例子是nanosleep()系統(tǒng)調(diào)用(參考第6章的動(dòng)態(tài)定時(shí)器的應(yīng)用:nanosleep()系統(tǒng)調(diào)用):假設(shè)進(jìn)程調(diào)用它來(lái)暫停執(zhí)行20毫秒,隨后過(guò)了10毫秒之后發(fā)生了一個(gè)信號(hào)。如果系統(tǒng)調(diào)用還和平常一樣重啟,那么,總的延時(shí)時(shí)間會(huì)超過(guò)30毫秒。

相反,如果nanosleep()系統(tǒng)調(diào)用服務(wù)例程被中斷,則使用特殊服務(wù)例程的地址填充current進(jìn)程的thread_info結(jié)構(gòu)體的restart_block字段,并返回ERESTART_RESTARTBLOCK錯(cuò)誤碼。sys_restart_syscall()服務(wù)例程只執(zhí)行前面特殊的服務(wù)里程,計(jì)算首次調(diào)用和重新啟動(dòng)之間經(jīng)過(guò)的時(shí)間,從而調(diào)整延時(shí)。

3.2 重新啟動(dòng)caught信號(hào)中斷的系統(tǒng)調(diào)用

如果信號(hào)需要捕獲處理,handle_signal()分析錯(cuò)誤碼,根據(jù)sigaction中的SA_RESTART標(biāo)志,判斷是否需要重啟:

if(regs->orig_eax>=0){
switch(regs->eax){
case-ERESTART_RESTARTBLOCK:
case-ERESTARTNOHAND:
regs->eax=-EINTR;
break;
case-ERESTARTSYS:
if(!(ka->sa.sa_flags&SA_RESTART)){
regs->eax=-EINTR;
break;
}
/*fallthrough*/
case-ERESTARTNOINTR:
regs->eax=regs->orig_eax;
regs->eip-=2;
}
}

如果必須重新啟動(dòng)系統(tǒng)調(diào)用,handle_signal()的處理方式與do_signal()完全相同;否則,它會(huì)向用戶進(jìn)程返回一個(gè)-EINTR錯(cuò)誤碼。

4 x86_64架構(gòu)-do_signal()

Linux內(nèi)核版本是v2.6.11,文件位置:arch/x86_64/kernel/signal.c:

/*
*注意init是一個(gè)特殊進(jìn)程:它不會(huì)收到不想處理的信號(hào)。所以,即使錯(cuò)誤地發(fā)送
*`SIGKILL`信號(hào)給它,也不會(huì)殺死它。
*/
intdo_signal(structpt_regs*regs,sigset_t*oldset)
{
structk_sigactionka;
siginfo_tinfo;
intsignr;

/*如果不是返回到用戶態(tài),則直接返回。*/
if((regs->cs&3)!=3){
return1;
}

//...省略

if(!oldset)
oldset=¤t->blocked;

signr=get_signal_to_deliver(&info,&ka,regs,NULL);
if(signr>0){
/*
*在將信號(hào)傳遞到用戶空間之前重新啟動(dòng)多有觀察點(diǎn)。
*如果觀察點(diǎn)在內(nèi)核內(nèi)部觸發(fā),寄存器將被清除。
*/
if(current->thread.debugreg7)
asmvolatile("movq%0,%%db7"::"r"(current->thread.debugreg7));

/*傳遞信號(hào)*/
handle_signal(signr,&info,&ka,oldset,regs);
return1;
}

no_signal:
/*是否是系統(tǒng)調(diào)用*/
//...省略(見(jiàn)前面第3.1節(jié)的處理)
return0;
}

x86-64架構(gòu)的handle_signal()函數(shù)(文件位置:arch/x86_64/kernel/signal.c):

staticvoid
handle_signal(unsignedlongsig,siginfo_t*info,structk_sigaction*ka,
sigset_t*oldset,structpt_regs*regs)
{
//...省略(調(diào)試信號(hào)信息)

//省略(被中斷的系統(tǒng)調(diào)用的相關(guān)處理)

//省略(IA32_EMULATION配置)

setup_rt_frame(sig,ka,info,oldset,regs);

if(!(ka->sa.sa_flags&SA_NODEFER)){
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
}
}

i386架構(gòu)的handle_signal()函數(shù)(文件位置:arch/i386/kernel/signal.c):

staticvoid
handle_signal(unsignedlongsig,siginfo_t*info,structk_sigaction*ka,
sigset_t*oldset,structpt_regs*regs)
{
//省略(被中斷的系統(tǒng)調(diào)用的相關(guān)處理)

/*Setupthestackframe*/
if(ka->sa.sa_flags&SA_SIGINFO)
setup_rt_frame(sig,ka,info,oldset,regs);
else
setup_frame(sig,ka,oldset,regs);

if(!(ka->sa.sa_flags&SA_NODEFER)){
spin_lock_irq(¤t->sighand->siglock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
sigaddset(¤t->blocked,sig);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
}
}
staticvoidsetup_rt_frame(intsig,structk_sigaction*ka,siginfo_t*info,
sigset_t*set,structpt_regs*regs)
{
structrt_sigframe__user*frame;
struct_fpstate__user*fp=NULL;
interr=0;
structtask_struct*me=current;

if(used_math()){
fp=get_stack(ka,regs,sizeof(struct_fpstate));
frame=(void__user*)round_down((unsignedlong)fp-sizeof(structrt_sigframe),16)-8;

if(!access_ok(VERIFY_WRITE,fp,sizeof(struct_fpstate))){
gotogive_sigsegv;
}

if(save_i387(fp)sa.sa_flags&SA_SIGINFO){
err|=copy_siginfo_to_user(&frame->info,info);
if(err){
gotogive_sigsegv;
}
}

/*Createtheucontext.*/
err|=__put_user(0,&frame->uc.uc_flags);
err|=__put_user(0,&frame->uc.uc_link);
err|=__put_user(me->sas_ss_sp,&frame->uc.uc_stack.ss_sp);
err|=__put_user(sas_ss_flags(regs->rsp),
&frame->uc.uc_stack.ss_flags);
err|=__put_user(me->sas_ss_size,&frame->uc.uc_stack.ss_size);
err|=setup_sigcontext(&frame->uc.uc_mcontext,regs,set->sig[0],me);
err|=__put_user(fp,&frame->uc.uc_mcontext.fpstate);
if(sizeof(*set)==16){
__put_user(set->sig[0],&frame->uc.uc_sigmask.sig[0]);
__put_user(set->sig[1],&frame->uc.uc_sigmask.sig[1]);
}else{
err|=__copy_to_user(&frame->uc.uc_sigmask,set,sizeof(*set));
}

/*Setuptoreturnfromuserspace.Ifprovided,useastub
alreadyinuserspace.*/
/*x86-64shouldalwaysuseSA_RESTORER.*/
if(ka->sa.sa_flags&SA_RESTORER){
err|=__put_user(ka->sa.sa_restorer,&frame->pretcode);
}else{
/*coulduseavstubhere*/
gotogive_sigsegv;
}

if(err){
gotogive_sigsegv;
}

#ifdefDEBUG_SIG
printk("%doldrip%lxoldrsp%lxoldrax%lx
",current->pid,regs->rip,regs->rsp,regs->rax);
#endif

/*Setupregistersforsignalhandler*/
{
structexec_domain*ed=current_thread_info()->exec_domain;
if(unlikely(ed&&ed->signal_invmap&&sigsignal_invmap[sig];
}
regs->rdi=sig;
/*Incasethesignalhandlerwasdeclaredwithoutprototypes*/
regs->rax=0;

/*ThisalsoworksfornonSA_SIGINFOhandlersbecausetheyexpectthe
nextargumentafterthesignalnumberonthestack.*/
regs->rsi=(unsignedlong)&frame->info;
regs->rdx=(unsignedlong)&frame->uc;
regs->rip=(unsignedlong)ka->sa.sa_handler;

regs->rsp=(unsignedlong)frame;

set_fs(USER_DS);
if(regs->eflags&TF_MASK){
if((current->ptrace&(PT_PTRACED|PT_DTRACE))==(PT_PTRACED|PT_DTRACE)){
ptrace_notify(SIGTRAP);
}else{
regs->eflags&=~TF_MASK;
}
}

#ifdefDEBUG_SIG
printk("SIGdeliver(%s:%d):sp=%ppc=%pra=%p
",
current->comm,current->pid,frame,regs->rip,frame->pretcode);
#endif

return;

give_sigsegv:
force_sigsegv(sig,current);
}

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1372

    瀏覽量

    40277
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11292

    瀏覽量

    209328
  • 信號(hào)
    +關(guān)注

    關(guān)注

    11

    文章

    2789

    瀏覽量

    76730
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4327

    瀏覽量

    62571

原文標(biāo)題:Linux內(nèi)核-信號(hào)的傳遞過(guò)程

文章出處:【微信號(hào):嵌入式ARM和Linux,微信公眾號(hào):嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    linux內(nèi)核信號(hào)是如何處理的?看完全懂了……

    本文簡(jiǎn)單介紹下Linux信號(hào)處理機(jī)制,為介紹二進(jìn)制翻譯下信號(hào)處理機(jī)制做一個(gè)鋪墊。 本文主要參考書(shū)目《Linux內(nèi)核源代碼情景分析》《獨(dú)辟蹊徑
    的頭像 發(fā)表于 11-16 05:11 ?1.4w次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>信號(hào)</b>是如何處理的?看完全懂了……

    Linux內(nèi)核的編譯主要過(guò)程

    Linux內(nèi)核的編譯主要過(guò)程: 配置、編譯、安裝 。
    發(fā)表于 08-08 16:02 ?726次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的編譯主要<b class='flag-5'>過(guò)程</b>

    Linux內(nèi)核中信號(hào)詳解

    信號(hào)和多線程程序 4 與信號(hào)相關(guān)的數(shù)據(jù)結(jié)構(gòu) 4.2.1 x86/Linux2.6.11的定義 4.2.2 x86-64/Linux2.6.11的定義 4.2.3 x86-64/
    的頭像 發(fā)表于 01-13 09:40 ?1371次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>中信號(hào)</b>詳解

    Linux內(nèi)核地址映射模型與Linux內(nèi)核高端內(nèi)存詳解

    Linux 操作系統(tǒng)和驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間,應(yīng)用程序運(yùn)行在用戶空間,兩者不能簡(jiǎn)單地使用指針傳遞數(shù)據(jù),因?yàn)?b class='flag-5'>Linux使用的虛擬內(nèi)存機(jī)制,用戶空間的數(shù)據(jù)可能被換出,當(dāng)
    發(fā)表于 05-08 10:33 ?3453次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>地址映射模型與<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>高端內(nèi)存詳解

    Linux內(nèi)核啟動(dòng)過(guò)程和Bootloader(總述)

    供給 Linux 內(nèi)核Linux 內(nèi)核在啟動(dòng)過(guò)程中會(huì)根據(jù)該處理器類(lèi)型調(diào)用相應(yīng)的初始化程序(4)設(shè)置 L
    發(fā)表于 08-18 17:35

    Linux內(nèi)核自解壓過(guò)程

    Linux內(nèi)核的啟動(dòng)流程。有興趣的用戶可以參考其他書(shū)籍或資料進(jìn)行深入了解。  嵌入式linux內(nèi)核的啟動(dòng)全過(guò)程主要分為三個(gè)階段。第一階段為
    發(fā)表于 12-29 07:35

    linux內(nèi)核啟動(dòng)內(nèi)核解壓過(guò)程分析

    linux啟動(dòng)時(shí)內(nèi)核解壓過(guò)程分析,一份不錯(cuò)的文檔,深入了解內(nèi)核必備
    發(fā)表于 03-09 13:39 ?1次下載

    Linux內(nèi)核源碼分析--內(nèi)核啟動(dòng)命令行的傳遞過(guò)程

    內(nèi)核的啟動(dòng)參數(shù)其實(shí)不僅僅包含在了cmdline中,cmdline不過(guò)是bootloader傳遞內(nèi)核的信息中的一部分。bootloader和內(nèi)核的通信方式根據(jù)構(gòu)架的不同而異。
    發(fā)表于 05-05 15:28 ?1694次閱讀

    你了解u-boot與linux內(nèi)核間的參數(shù)傳遞過(guò)程

    U-boot會(huì)給Linux Kernel傳遞很多參數(shù),如:串口,RAM,videofb、MAC地址等。而Linux kernel也會(huì)讀取和處理這些參數(shù)。兩者之間通過(guò)struct tag來(lái)傳遞
    發(fā)表于 05-13 10:00 ?1767次閱讀
    你了解u-boot與<b class='flag-5'>linux</b><b class='flag-5'>內(nèi)核</b>間的參數(shù)<b class='flag-5'>傳遞</b><b class='flag-5'>過(guò)程</b>?

    BootLoader與Linux內(nèi)核的參數(shù)傳遞

    Linux 內(nèi)核是怎樣接收BootLoader傳遞過(guò)來(lái)的內(nèi)核參數(shù),zImage 啟動(dòng)過(guò)程如下圖所示。(圖有時(shí)間再畫(huà))在文件 arch/arm
    發(fā)表于 04-02 14:31 ?360次閱讀

    LINUX內(nèi)核信號(hào)量設(shè)計(jì)與實(shí)現(xiàn)

    控制路徑可以睡眠。我們從 LINUX內(nèi)核信號(hào)量最直觀的設(shè)計(jì)/實(shí)現(xiàn)出發(fā),通過(guò)一步步改進(jìn),揭示在x86平臺(tái)上完整的信號(hào)量設(shè)計(jì)/實(shí)現(xiàn),然后探討在不同平臺(tái)上通用的
    發(fā)表于 01-14 16:55 ?18次下載

    LINUX內(nèi)核信號(hào)量設(shè)計(jì)與實(shí)現(xiàn)

    控制路徑可以睡眠。我們從 LINUX內(nèi)核信號(hào)量最直觀的設(shè)計(jì)/實(shí)現(xiàn)出發(fā),通過(guò)一步步改進(jìn),揭示在x86平臺(tái)上完整的信號(hào)量設(shè)計(jì)/實(shí)現(xiàn),然后探討在不同平臺(tái)上通用的
    發(fā)表于 01-14 16:55 ?5次下載

    BootLoader與Linux內(nèi)核的參數(shù)傳遞詳細(xì)資料說(shuō)明

    不同的體系結(jié)構(gòu),如 ARM, Powerpc,X86,MIPS等。本文著重介紹 Bootloader與內(nèi)核之間參數(shù)傳遞這一基本功能。本文的硬件平臺(tái)是基于AT91RM9200處理器系統(tǒng),軟件平臺(tái)是 Linux-2.6.19,2
    發(fā)表于 03-16 10:39 ?13次下載
    BootLoader與<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>的參數(shù)<b class='flag-5'>傳遞</b>詳細(xì)資料說(shuō)明

    從軟件角度分析linux內(nèi)核USB子系統(tǒng)的熱插拔過(guò)程

    本文從軟件角度分析linux內(nèi)核USB子系統(tǒng)的熱插拔過(guò)程,以實(shí)際分析思路和過(guò)程行文,基于linux內(nèi)核
    的頭像 發(fā)表于 01-15 09:28 ?5620次閱讀

    Linux內(nèi)核模塊參數(shù)傳遞與sysfs文件系統(tǒng)

    Linux應(yīng)用開(kāi)發(fā)中,為使應(yīng)用程序更加靈活地執(zhí)行用戶的預(yù)期功能,我們有時(shí)候會(huì)通過(guò)命令行傳遞一些參數(shù)到main函數(shù)中,使得代碼邏輯可以依據(jù)參數(shù)執(zhí)行不同的任務(wù)。同樣,Linux內(nèi)核也提供了
    發(fā)表于 06-07 16:23 ?2120次閱讀
    主站蜘蛛池模板: 在线伦理电影网| 污文乖不疼的| 国产欧美亚洲综合第一页| 亚洲免费网站观看视频| 美丽的姑娘BD在线观看| 成人女人A级毛片免费软件| 亚洲 欧美 视频 手机在线| 久久视频这有精品63在线国产 | 99国产精品欲AV蜜桃臀麻豆| 日韩毛片在线视频| 国产永久视频| 99re久久超碰视频精品| 台湾佬休闲中性娱乐网| 久久aa毛片免费播放嗯啊| MELODY在线播放无删减| 香蕉59tv视频| 免费亚洲视频| 国产伦子沙发午休系列资源曝光| 中文无码不卡的岛国片国产片| 日本黄色官网| 九色91精品国产网站| WWW婷婷AV久久久影片| 亚洲狠狠网站色噜噜| 暖暖视频免费高清在线观看 视频| 国产精品亚洲第一区二区三区 | 国产成人精品免费青青草原app | 狼人无码伊人AV啪啪| 抽插内射高潮呻吟V杜V| 亚洲色欲色欲WWW在线丝| 欧洲-级毛片内射八十老太婆| 国产呦精品一区二区三区下载 | 日韩男明星| 久久高清一本无码| 国产1广场舞丰满老女偷| 一本道亚洲区免费观看| 人妻美妇疯狂迎合| 久久99精品国产麻豆婷婷| 大胸美女脱内衣黄网站| 中文字幕日本久久2019| 午夜国产精品视频| 免费国产在线观看|