欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
從kernel原始碼的角度分析signal的錯誤用法和注意事項(zt)
!聲明: 按照Linux的習慣, 我的這篇文件也遵循GPL 協(xié)議: 你能隨意應用并修改本文件,必須發(fā)布你的修改,使其他人能獲得一份Copy,尤其是給我一份Copy! 我的mail :
bob_zhang2004@163.com
|
zhanglinbao@gmail.com
均可。歡迎論壇轉載! 目前有些內容已在
www.linuxforum.net
中進(jìn)行過(guò)討論,能前往:
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=607800&page=0&view=&sb=&o=&fpart=&vc=1
   和  
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=607228&page=1&view=collapsed&sb=5&o=7&fpart
=   歡迎大家繼續討論,以便文件更加完善! 多謝!周末愉快!                                 
--bob
讀這份文件之前,建議先瀏覽一下 《Unix Advanced Programming》里面的signal一章和下面這份出自IBM論壇的文章:進(jìn)程間通信 信號(上)
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
  ,和 進(jìn)程間通信 信號(下)
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html
該作者寫(xiě)了一個(gè)系列的進(jìn)程間通信的文章, 我只是希望對該篇作個(gè)補充!
因為他們都沒(méi)有從原始碼的角度分析,所以我嘗試了一下把上層應用和kernel實(shí)現代碼分析結合起來(lái),這樣使用者才可能真正的理解signal的用法和原理!
目前介紹signal理論和用法書(shū)不少,缺點(diǎn)是只介紹其用法,非常深奧拗口,不容易理解;  而介紹kernel原始碼的書(shū),側重于代碼分析,不講實(shí)際應用!
我就想到如果把兩者結合起來(lái),對上層使用signal函數的用戶(hù)必然能知起所以然了,而且只要順著(zhù)我的代碼注釋大概粗讀一下源碼就能理解 signal的特性和用法及你碰到的種種疑惑和不解了。
如果你對signal的特性和用法有什么疑惑的話(huà), 如果對kernel也感興趣的話(huà), 就能繼續讀源碼 , 把這篇文章加以補充和完善!  前提是遵守上面的聲明!

因為工作的需要,用了2天的時(shí)間周詳的讀了一下 linux kernel 2.4.24 版本的signal方面的原始碼,收獲不小, 因為以前發(fā)現看>的時(shí)候 ,不知道是大師的話(huà)太深奧,還是中文版太爛,有的東西就是理解不了,象吃滿(mǎn)頭囁住了,非常是不爽,總覺(jué)得心里不踏實(shí)??纯丛创a才真正明白什么是信號,及他的kernel流程,所以建議大家對某個(gè)系統調用,函數什么的,如果存在疑惑和不理解的,強烈建議讀讀源碼,粗讀也非常不錯,關(guān)鍵要由參考書(shū)領(lǐng)著(zhù)讀,比如> 就非常不錯。
有的時(shí)候看著(zhù)一個(gè)系統調用成堆的手冊頁(yè),還真不如看看他的實(shí)現來(lái)得更快, 當然兩下對照著(zhù)看就快了。   
另外提醒大家 > 可不是 《Linux Advanced Programming》??!盡信書(shū)不如無(wú)書(shū) ......
在此通過(guò)閱讀源碼,弄清晰了5個(gè)問(wèn)題,每個(gè)問(wèn)題我都給出了結論,當然這些結論肯定是正確的,至少《Unix Advanced Programming》是這樣認為的, 我只是從kernel的角度是驗證他的正確性(簡(jiǎn)單的寫(xiě)了幾個(gè)測試程式,以驗證kernel的做法),而且也歸納了 一些結論,比如怎么避免 Zobie進(jìn)程 等。  相信對大家會(huì )有價(jià)值,也能mail討論!或上相應的論壇!
首先總結一下:在PC linux(RHT 9.0 + kernel-2.4.24) 鍵盤(pán)產(chǎn)生的信號:
Ctrl + c     SIGINT(2) terminate ,以前我總想當然以為是 SIGTERM(15)!
Ctrl + \ SIGQUIT(3) terminate
Ctrl + z SIGTSTP(20) 掛起進(jìn)程
對于一般應用:
掛起一個(gè)進(jìn)程: kill(pid, SIGSTOP)   或 kill(pid,SIGTSTP) , 或 SIGTTIN , SIGTTOU 信號
恢復一個(gè)進(jìn)程  kill(pid,SIGCONT);  
殺死所有的符合某個(gè)名字的進(jìn)程 :比如killall  curl ,發(fā)送的是SIGTERM 信號
強制殺死某個(gè)進(jìn)程 kill ?9 curl  ,發(fā)送的是SIGKILL 信號, 在kernel中,SIGKILL和SIGSTOP是不能被忽略的
....
剩下的大家都清晰了,這里就不羅嗦了。
子進(jìn)程結束時(shí)候發(fā)給父進(jìn)程的信號:   SIGCHLD ,這個(gè)比較特別 , 且看下面3>的論述
Agenda :
1>不可靠的信號
2>Zombie進(jìn)程(僵尸進(jìn)程)和signal
3>特別的SIGCHLD 信號
4>信號和進(jìn)程的關(guān)系 ,進(jìn)程的需求
5>pause() 和 signal
6>關(guān)于信號的技巧
1> 不可靠的信號(linux繼承Unix的結果,考慮兼容性) ,  和可靠的信號(主要就是信號能排隊處理,信號不丟失,linux自己的,但大家似乎用的不多)
什么是不可靠的信號:簡(jiǎn)單的說(shuō),就是當你向一個(gè)進(jìn)程發(fā)送 singal( 1~31,注意這里討論是 1~31 )的時(shí)候 , 當進(jìn)程還沒(méi)有處理該信號(這時(shí)候叫pending,未決信號)或是正在調用信號處理函數的時(shí)候,  進(jìn)程又收到了一個(gè)同樣的信號 , kernel會(huì )把第二個(gè)信號丟棄,或叫和一個(gè)信號合并,這樣的信號就是 不可靠的信號  ,具體正方面的比較權威的解釋請參考
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
,這篇文章對于信號理論介紹的非常周詳清晰明白, 個(gè)人認為比《Unix advanced Programming》要更好!
系統實(shí)現是這樣的:
==>  kernel/signal.c  
int send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
.............................................
/*  
  如果當前進(jìn)程的未決信號集中已包括了這個(gè)信號,就不重新注冊后來(lái)目前的同樣的信號了,  
  據個(gè)例子:  給進(jìn)程發(fā)了 SIGTERM 信號 , 不過(guò)kernel還沒(méi)有來(lái)得及處理(進(jìn)程只有在kernel空間即將返回道用戶(hù)空間的時(shí)候,
  kernel才會(huì )檢測pending信號 ,然后才會(huì )調用do_signal()函數去處理)
  這個(gè)時(shí)候又發(fā)了一個(gè)SIGTERM,那么第二個(gè)SIGTERM 肯定要被cut掉了。
*/
if (sig pending.signal, sig))  //SIGRTMIN 是分水嶺 , 小于他的都是不可靠的信號,否則就是實(shí)時(shí)信號
  goto out;  //跳出了正常執行的范圍
....................................................
}
!正確的: 1~31都是不可靠的信號! SIGRTMIN ~SIGRTMAX都是可靠的信號!
以前大家有個(gè)誤區:
!誤區1>
以為不可靠的信號,是指 給進(jìn)程發(fā)了一個(gè)信號(之前沒(méi)有發(fā)過(guò)),那么這個(gè)信號可能丟失,也就是進(jìn)程收不到
這樣的理解是錯誤的, 根據上面的定義 , 應該是”一個(gè)信號發(fā)了多遍,后來(lái)的信號丟失了, 而不是第一個(gè)丟了“。
具體的原因能參照上面的代碼分析,就一目了然,還能看 《unix advanced programming 》,不過(guò)我覺(jué)得他講的都是老的Unix ,對Linux只能是參考而已!
!誤區2>
signal() 發(fā)送的是不可靠的信號 ,而 sigaction()發(fā)送的是可靠的信號


只要是1-31的信號,他就是不可靠的信號。 無(wú)論在注冊信號處理函數的時(shí)候用的是sigaction() ,還是signal() ,只要你發(fā)送的信號 是  1-31,那么就是不可靠的信號。中國有句俗語(yǔ)叫”爛泥扶不上墻“,我看放在這里挺合適!
signal()和 sigaction()的差別到底在哪里呢?   通過(guò)對比一看便知:
   對于signal() ,他的kernel實(shí)現函數,也叫系統調用服務(wù)歷程sys_signal()
==>kernel/signal.c
asmlinkage unsigned long
sys_signal(int sig, __sighandler_t handler)
{
struct k_sigaction new_sa, old_sa;
int ret;
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
    //SA_ONESHOT:當執行一次信號處理程式后, 馬上恢復為SIG_DFL ,
    //SA_NOMASK : 表示在信號處理函數執行期間,不屏蔽的當前正在處理的那個(gè)信號
ret = do_sigaction(sig, &new_sa, &old_sa);   //sys_sigaction 也調用這個(gè)函數
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
而sigaction()函數的kernel實(shí)現是: sys_sigaction()
==>arch/i386/kernel/signal.c
asmlinkage int
sys_sigaction(int sig, const struct old_sigaction *act,struct old_sigaction *oact)
{
struct k_sigaction new_ka, old_ka;
int ret;
if (act) {
  old_sigset_t mask;
  if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
      __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
      __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
   return -EFAULT;
  __get_user(new_ka.sa.sa_flags, &act->sa_flags);
  __get_user(mask, &act->sa_mask);
  siginitset(&new_ka.sa.sa_mask, mask);
}
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);//都調的這個(gè)函數
if (!ret && oact) {
  if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
      __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
      __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
   return -EFAULT;
  __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
  __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
}
signal()和sigaction() 都是用do_signaction()來(lái)包裝的, 都是用 struct sigaction()這個(gè)結構體的,差別在下面標出來(lái)了
struct sigaction {
__sighandler_t sa_handler;  //2// typedef void (*__sighandler_t)(int);  signal()和sigaction()函數都需求要戶(hù)提供信號處理函數
unsigned long sa_flags; //signal()函數默認就用 SA_ONESHOT | SA_NOMASK;  //sigaction()要由用戶(hù)自己指定!
void (*sa_restorer)(void); //沒(méi)用了
sigset_t sa_mask;    //執行信號處理函數的時(shí)候要阻塞的信號,signal()使用默認的,就屏蔽正處理的信號,其他的不屏蔽,sigaction() 需求用戶(hù)自己指定!
};
討論時(shí)間: 讀到這里我有個(gè)疑問(wèn):sys_signal()函數明明把 sa_flags = SA_ONESHOT | SA_NOMASK; 而且在kernel執行信號處理函數之前,他會(huì )檢查SA_ONESHOT標志 ,如果有這個(gè)標志,  就把sa_handler = SIG_DFL ,如果是這樣的話(huà), 我們需要反復注冊某個(gè)信號的處理函數才行啊, 不過(guò)事實(shí)上,我們并沒(méi)有這樣作,而且程式運行的非常好!
Kernel的signal()函數實(shí)現代碼如下:

==>arch/i386/kernel/signal.c
static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
       siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
{
...........................................................
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
  setup_rt_frame(sig, ka, info, oldset, regs);
else
  setup_frame(sig, ka, oldset, regs);
//here , 我加了debug信息, 確實(shí)執行到這里了,
if (ka->sa.sa_flags & SA_ONESHOT){  //sys_signal()函數明明設置了這個(gè)標志
  //通過(guò)debug ,知道居然沒(méi)有到這里,就說(shuō)明, sa_flags 根本就沒(méi)有SA_ONESHOT標志了 ,可是sys_signal() 卻又明明設置了這個(gè)標志, 而且我搜索過(guò), 根本沒(méi)有地方,取消了 SA_ONESHOT 標志
  printk(" the signal (%d) handler will reset to SIG_DFL\n",sig);
  ka->sa.sa_handler = SIG_DFL;  //這難道還不明確嗎?
if (!(ka->sa.sa_flags & SA_NODEFER)) {
  spin_lock_irq(&current->sigmask_lock);
  sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
  sigaddset(&current->blocked,sig);
  recalc_sigpending(current);
  spin_unlock_irq(&current->sigmask_lock);
}
}
既然這樣的話(huà)  ,如果我們調用signal()就應該在信號處理函數中反復注冊自己的信號處理函數才對 , 否則無(wú)法處理下一個(gè)同樣的信號了。
比如 void signal_catch(int signo)
{
//信號處理函數細節
//最后一行
signal(signo, signal_catch); //再注冊一遍, 否則就變成  SIG_DFL 了 。
}
對于這個(gè)問(wèn)題 《Unix Advanced Programming》 也提到過(guò),說(shuō)早期的Unix 也存在這個(gè)問(wèn)題, 是信號不可靠的一個(gè)原因 (見(jiàn) P206)
不過(guò)實(shí)際上我們在用signal()函數的時(shí)候 , 我們似乎并不必這么作  ,比如一個(gè)簡(jiǎn)單的測試程式。
為了測試, 我寫(xiě)了一個(gè)最簡(jiǎn)單的例子:
void sigterm_handler(int signo)
{
          printf("Have caught sig N.O. %d\n",signo);
          //按照kernel代碼,應該還要有signal(signo,sigterm_handler);   才對呀 ,但事實(shí)上,我們大家都知道沒(méi)有必要這樣用 ,為什么呢? 請前往論壇討論:
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=607961&page=0&view=collapsed&sb=5&o=7&fpart=&vc=1&PHPSESSID
=
}
int main(void)
{
       printf("-------------111111111111111-------------\n");
        signal(SIGTERM,sigterm_handler);
     pause();
       printf("----------222222222222222----------------\n");
      
        pause();//如果按照kernel代碼里面寫(xiě)的, 當再發(fā)一個(gè)SIGTERM信號的時(shí)候 , sa_handler 就編程SIG_DFL 了,那默認就是 //terminate ,所以不會(huì )打出來(lái) 333333333333333333  了,  
       printf("-------------3333333333333333----------\n");
      
        return 0;
}
不過(guò)執行結果確實(shí):  
333333333333333333333333 也打出來(lái)了, 這就又說(shuō)明signal函數 ,不必反復注冊信號處理函數 ,  這不就矛盾嗎?  
所以目前問(wèn)題就是
if (ka->sa.sa_flags & SA_ONESHOT){  
  ka->sa.sa_handler = SIG_DFL;
是在什么情況下 改動(dòng)了 sigaction->sa_flags (去掉了 SA_ONESHOT 標志呢?)我在代碼里面搜索不到??!
如果感興趣的朋友能前往論壇討論:
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=607949&page=0&view=collapsed&sb=5&o=7&fpart=&vc=1

2> 僵尸進(jìn)程:也叫Zombie進(jìn)程:  
僵尸進(jìn)程定義:進(jìn)程結束后,該進(jìn)程的父進(jìn)程沒(méi)有調用wait或waitpid()對子進(jìn)程進(jìn)行回收 , 子進(jìn)程一直是Zombie狀態(tài)。
關(guān)于kernel怎么殺死Zombie 請看 kernel/exit.c ==>sys_wait4() 函數 , waitpid 就是sys_wait4()實(shí)現的。

首先看看正確的編程方法:
當一個(gè)進(jìn)程fork()出一個(gè)子進(jìn)程的時(shí)候 ,正確的情況下,父進(jìn)程應該回收進(jìn)程的資源:通過(guò)下面兩個(gè)辦法中的一個(gè)即可避免Zombie(僵尸進(jìn)程):
 父進(jìn)程顯式的忽略SIGCHLD 信號
只要在fork一個(gè)子進(jìn)程之前加上這么 一行:   signal(SIGCHLD, SIG_IGN);  //這樣肯定不會(huì )出現僵尸進(jìn)程,
為什么呢?  看kernel的代碼吧:
==>asm/i386/signal.c  ==>do_signal()
  ka = &current->sig->action[signr-1];//&current->sig : signal_struct
  if (ka->sa.sa_handler == SIG_IGN) {
   if (signr != SIGCHLD)
    continue;  //對于信號處理方式是 SIG_IGN , 非SIGCHLD的信號 ,kernel什么也不作! SIGCHLD 比較特別??!
   /* Check for SIGCHLD: it’s special.  
    類(lèi)似調用waitpid()來(lái)回收child process的進(jìn)程表項
   */
  //SIG_CHLD 信號的行為設置為SIG_IGN  , 由內核來(lái)處理僵死進(jìn)程。
  //如果你的程式中沒(méi)有特別的需求需要處理SIGCHLD , 為了避免僵尸進(jìn)程(Zombie進(jìn)程),你能顯式的忽略他,kernel會(huì )調用sys_wait4()來(lái)處理僵尸進(jìn)程的),他執行一個(gè)while() loop , 來(lái)處理系統中所有的僵尸進(jìn)程,老黃牛精神??!   
   while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)   // 看看是不是和waitpid的用法相同??!  
    /* nothing */;
   continue;
  }
如果 SIGCHLD 是默認的  SIG_DFL 的話(huà):kernel就不管了,所以肯定會(huì )有僵尸進(jìn)程的!
==>asm/i386/signal.c  ==>do_signal()
  if (ka->sa.sa_handler == SIG_DFL) {
   int exit_code = signr;
   /* Init gets no signals it doesn’t want.  */
   if (current->pid == 1)  //誰(shuí)都不能給init(1) 進(jìn)程發(fā)信號, 這樣說(shuō)比較準確: 發(fā)了也白發(fā),kernel不認可
    continue;
   switch (signr) {
   case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
    continue;  //對于SIGCHLD 信號,kernel對他默認是忽略的, (請不要和SIG_IGN 混淆了)
      //所以非常明顯, kernel并沒(méi)有調用sys_wait4() 來(lái)處理僵尸進(jìn)程 ,你要自己處理了,^_^
  ..............
  }
 父進(jìn)程給SIGCHLD信號注冊handler(里面調用waitpid()回收child Zombie process)
比如:這樣寫(xiě):
while(waitpid(-1,NULL,WNOHANG) > 0)  {   //自動(dòng)處理所有的僵尸進(jìn)程,當然你能不用while,只調用一次,看需要 : 比如父進(jìn)程是個(gè)http server,就會(huì )fork()出非常多子進(jìn)程 , 所以while()是有必要的。
//WNOHANG 非常關(guān)鍵,如果沒(méi)有僵死進(jìn)程,就馬上返回 ,這樣while()才能結束啊 , 可是wait()就沒(méi)有這個(gè)參數,  所以wait就阻塞了。 所以一般情況下,我們用waitpid還是最佳的了!
;//什么也不必作了, 能打印看看到底回收了哪些進(jìn)程pid
}
!如果你沒(méi)有用上面所有一個(gè)辦法,就會(huì )出現僵尸進(jìn)程。  
ps ax 命令可能會(huì )顯示:  
22149 tty8  S  0:00   test_pro
22150  ?    Z  0:00    [test_pro ]   //這就是僵尸進(jìn)程  Z 就是Zombie的意思 , 你用kill -9 也無(wú)法殺掉他 。
怎么殺掉Zombie進(jìn)程呢?  你能kill他的父進(jìn)程就能殺掉Zombie進(jìn)程。
kill -SIGTERM 22149 ,  你在ps ax 看看  ,兩個(gè)進(jìn)程都沒(méi)有了。

避免僵尸進(jìn)程的第三種辦法
個(gè)人不推薦! 因為上面兩種方法已夠用了, 除非你更有其他的需求,比如 使子進(jìn)程無(wú)法獲得控制終端,這種情況下, 就必須fork()兩次了 。 否則一般情況下,我們需要父子進(jìn)程同步和通信的, 父親和兒子交流尚且比較方便(用pipe最佳,配合使用select()) , 你讓爺爺和孫子通信不是比較困難嗎?  兩代人的代溝呢。。。。
當       你也能fork()兩次,  父親(比如http server,循環(huán)處理) ->  兒子進(jìn)程(exit) -> 孫子進(jìn)程 (處理每次的任務(wù),正常結束,就不會(huì )成為Zombie)  

下面是事例代碼:   
pid_t pid = 0;
pid = fork();
if(pid  0)
//這里可能是個(gè)Server一類(lèi)的, 父親進(jìn)程永遠不會(huì )結束的,是while() 循環(huán)
else {
//目前兒子 process 了,
if(pid = fork()  0) //兒進(jìn)程也結束了
  exit(0);//即時(shí)殺死兒子進(jìn)程 ,這樣孫子就成孤兒了,孫子進(jìn)程會(huì )被init(1)領(lǐng)養的。
else { //到孫子進(jìn)程了。
  /*  some code …………..
  */
  exit(0);
   }
}
對于 原理其實(shí)非常簡(jiǎn)單: 兒子死了, 只有孫子了, 孫子是孤兒了, 那么init(1)進(jìn)程就會(huì )領(lǐng)養這個(gè) 孤兒,  同時(shí)孤兒就認為init(1)就是他的父進(jìn)程,由init進(jìn)程負責收尸!   
3> 特別的 SIGCHLD 信號
SIGCHLD 特別在哪里呢?? 一般情況下, 子進(jìn)程結束后 ,都會(huì )給父進(jìn)程發(fā)送 SIGCHLD 信號 ,不過(guò)這不是絕對的 。
• 當一個(gè)父進(jìn)程fork()一個(gè)子進(jìn)程后,  當父進(jìn)程沒(méi)有為SIGCHLD 注冊新的處理函數,處理方式為SIG_DFL  ,那么當子進(jìn)程結束的時(shí)候, 就不會(huì )給父進(jìn)程發(fā)送SIGCHLD 信號 。
   從代碼的角度: 執行到send_sig_info(),會(huì )在isgnore_signal() 函數里面做是否要發(fā)信號的判斷,結果 SIGCHLD被忽略了!

• 就是普通的進(jìn)程,在某個(gè)地方pause(),也不是隨便發(fā)一個(gè)信號就能喚醒他, 比如 發(fā) SIGCONT 信號(在kernel中當SIGCONT 的處理方式為SIG_DFL的時(shí)候, 他要被ignore的) ,就不能!
例子:  int main(void)
{
  pause();
  printf(“I am waken up\n”);
  return 0;
}
如果你在外部隨便發(fā)下列信號:SIGCONT , SIGWINCH , SIGCHLD SIGURG ,肯定是要被進(jìn)程忽略的,并不能喚醒該進(jìn)程!  
如果你發(fā)SIGTERM,SIGQUIT,SIGINT等信號,沒(méi)有注冊handler , 那么默認是中止他;如果注冊了handler , 則能喚醒該進(jìn)程。
且看下面的代碼分析:
/*
* Determine whether a signal should be posted or not.
*
* Signals with SIG_IGN can be ignored, except for the
* special case of a SIGCHLD.
*
* Some signals with SIG_DFL default to a non-action.
*/
//定義了那些信號要被忽略!

static int ignored_signal(int sig, struct task_struct *t)
{
/* Don’t ignore traced or blocked signals */
if ((t->ptrace & PT_PTRACED) || sigismember(&t->blocked, sig))
  return 0;
return signal_type(sig, t->sig) == 0;  
}
/*
* Signal type:
*     0 : wake up.
*/
//
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */
//
static signal_type(int sig, struct signal_struct *signals)
{
unsigned long handler;
//-----------------------------空信號 ignore  -----------------------------
if (!signals)
  return 0;   //

handler = (unsigned long) signals->action[sig-1].sa.sa_handler;
if (handler > 1)   //該信號有特定的信號處理函數不能ignore ,必須wake_up ()
  return 1; //can’t ignore
// -----父進(jìn)程設置SIGCHLD 的處理方式為 SIG_IGN : 子進(jìn)程結束的時(shí)候不會(huì )給父進(jìn)程發(fā)信號,也就無(wú)法喚醒了。
/* "Ignore" handler.. Illogical, but that has an implicit handler for SIGCHLD */
if (handler == 1)   
  return sig == SIGCHLD;//當信號是 SIGCHLD的時(shí)候,信號不能被忽略,其他的要被活略
// --------------------------當把信號設置為SIG_DFL 時(shí)的情況---------------------
/* Default handler. Normally lethal, but.. */
switch (sig) {
/* Ignored */
case SIGCONT: case SIGWINCH:
case SIGCHLD: case SIGURG:
  return 0; //這些信號忽略干脆就忽略了 ,那你可能奇怪了?那SIGCONT 信號怎么喚醒 TASK_STOPPED狀態(tài)的進(jìn)程呢?  如果你有這個(gè)疑問(wèn) ,請看 5>的討論!
/* Implicit behaviour */    //can’t ignore
case SIGTSTP: case SIGTTIN: case SIGTTOU: //這些信號就時(shí)要暫停進(jìn)程的
  return 1; //這些信號會(huì )喚醒該進(jìn)程的, 程式會(huì )接著(zhù)望下跑的,  最后 把進(jìn)程的狀態(tài)置為 TASK_STOPPED 的。
/* Implicit actions (kill or do special stuff) */
default:  //對于象SIGKILL , SIGTERM ,SIGQUIT 這樣的信號直接就默認操作, 一般就是terminate 該進(jìn)程
  return -1;
}
?怎么在應用程式驗證上述kernel的代碼呢?
既然提到了”喚醒“ ,肯定要用上  pause(2)函數了, 且看pause(2)的manunal :
DESCRIPTION
       The  pause  library function causes the invoking process (or thread) to
       sleep until a signal is received that either terminates it or causes it
       to call a signal-catching function.  (也就是發(fā)的信號有對應的信號處理函數,或是強行中止的哪些信號)
RETURN VALUE
       The  pause  function only returns when a signal was caught and the sig-
       nal-catching function returned. In this  case  pause  returns  -1,  and
       errno is set to EINTR.
上面的手冊說(shuō)得非常清晰了, 對于pause過(guò)的進(jìn)程,  只有發(fā)送類(lèi)似 SIGKILL , SIGQUIT, SIGTERM 的信號或是 注冊了新的處理函數,才能喚醒他!
另外再看一下:pause()的系統實(shí)現:
asmlinkage int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;  //設置成INTERRUPTABLE 狀態(tài),就不在CPU調度的隊列里面了
schedule();//重新調度,使當前進(jìn)程即時(shí)讓出CPU,  kernel從TASK_RUNNING隊列中選擇合適的進(jìn)程重新運行
return -ERESTARTNOHAND;
}
下面是測試的例子:  
#include
#include
#include
#include
#include
#include
#include
void sig_handler(int signo)
{
        printf("signo = %d\n",signo);
        if(signo == SIGCHLD) {
                pid_t child_pid = 0;
                int status = 0;
                printf("into singal handler\n");
                //wait() fault : it will blocked if no defunced  process ,so I will use waitpid(.. WNOHANG) ,it will return immidietly
              while( (child_pid = waitpid(-1,&status,WNOHANG))> 0) //循環(huán)回收所有的Zombie進(jìn)程
                      printf("child_pid = %d  ,  status = %d\n",child_pid,status);
        }
}
int main(void)
{
        pid_t pid = 0;
/* 感興趣的讀者能試試!你能試著(zhù)注釋掉下面的兩個(gè)signal()函數, 用這個(gè),試著(zhù)回答下面的兩個(gè)問(wèn)題
        struct sigaction sa,old_sa;
        sigaction(SIGCHLD,&sa,&old_sa);
        sa = old_sa;
        sa.sa_flags |= SA_NOCLDSTOP; //當子進(jìn)程結束的時(shí)候,阻止子進(jìn)程向其父進(jìn)程發(fā)SIGCHLD
        sa.sa_handler = SIG_IGN;
        sigaction(SIGCHLD,&sa,NULL);
*/

  signal(SIGCHLD,sig_handler);  //避免僵尸進(jìn)程
  //signal(SIGCHLD,SIG_IGN);   //注釋上面那行,用這行 , 再試著(zhù)重新回答下面的兩個(gè)問(wèn)題
  pid = fork();
        if(pid
                printf("child ................... \n");
                exit(0);
        }
        else {
                printf("parent pause() ..............\n");
                pause();   //父進(jìn)程會(huì )被喚醒嗎???????????????????
                fflush(stdout);
                printf("parent process has been waken up \n");
                return 0;
        }
}
大家能思考一下?
(1)  子進(jìn)程會(huì )成為孤兒進(jìn)程嗎?     
(2)  父進(jìn)程會(huì )被喚醒嗎?
如果你看明白了上面的kernel代碼 ,你就非??烀靼琢舜鸢噶耍海ㄉ厦鏇](méi)有被注釋的代碼的運行結果 ,注釋的部分,讀者自己能驗證試著(zhù)讀原始碼解釋程式行為!
(1)答:不會(huì ),正常結束 ,因為有聲明 :signal(SIGCHLD,SIG_IGN);   //避免僵尸進(jìn)程
(2)答:(至少在linux-2.4.24上會(huì ),我在linux-2.4.20-8上試了一下,就不會(huì )喚醒,代碼肯定不同了), 因為子進(jìn)程結束后, 會(huì )給parent進(jìn)程發(fā)送一個(gè)SIGCHLD 信號, 此信號會(huì )喚醒 parent 進(jìn)程! 有關(guān)這方面的討論能訪(fǎng)問(wèn)論壇頁(yè):
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=607949&page=0&view=collapsed&sb=5&o=7&fpart=&vc=1

  
4> 在給一個(gè)進(jìn)程發(fā)送信號的過(guò)程中, 只要目標進(jìn)程(遲早要成為當前running的進(jìn)程)沒(méi)有block該信號, kernel都會(huì )調用 wake_up_process() 函數來(lái)喚醒他 , 為什么呢?   因為 只有當前活動(dòng)進(jìn)程才會(huì )handle signal ,過(guò)程是這樣的:  當一個(gè)進(jìn)程被喚醒后, 他肯定處于kernel空間 , 在他即將返回道用戶(hù)空間的時(shí)候, 開(kāi)始檢測 task_struct->sigpending ,如果為1   就說(shuō)明該進(jìn)程收到了信號(目前這個(gè)信號叫pending信號,只要有pending 信號, sigpending 就是等于1 ) ,開(kāi)始調用do_signal() 函數來(lái)處理 , 也就是重要的一點(diǎn) , 只有當前活動(dòng)進(jìn)程才能處理信號(類(lèi)似中斷 , 當一個(gè)進(jìn)程收到一個(gè)信號后, 就active了, 至于該信號怎么處理,是 "kernel處理信號的任務(wù)“ 。
具體說(shuō)明如下:
關(guān)于這個(gè)函數我覺(jué)得也也值得注意!
1> 當向一個(gè)進(jìn)程發(fā)送 SIGCONT信號時(shí)候,
如果進(jìn)程本身更有一些類(lèi)似SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等會(huì )使進(jìn)程停止的信號,
就要把他們刪掉
2>如果想要停止某個(gè)進(jìn)程的話(huà),
就一定要刪除SIGCONT信號(這個(gè)肯定,否則kernel在處理的時(shí)候,
進(jìn)程優(yōu)先處理SIGCONT信號,然后再處理這4個(gè) ,那就多余了,沒(méi)有必要。既然要停止,
就直接停止,忽略之前的SIGCONT操作)
static void handle_stop_signal(int sig, struct task_struct *t)
{
switch (sig) {
case SIGKILL: case SIGCONT:
  /* Wake up the process if stopped.  */
  if (t->state == TASK_STOPPED)
   wake_up_process(t);
  t->exit_code = 0;
  rm_sig_from_queue(SIGSTOP, t); //刪除這些未決信號
  rm_sig_from_queue(SIGTSTP, t);
  rm_sig_from_queue(SIGTTOU, t);
  rm_sig_from_queue(SIGTTIN, t);
  break;
case SIGSTOP: case SIGTSTP://因為這些信號排在SIGCONT信號的后面,如果不刪除隊列中的SIGCONT信號, 在do_singal()會(huì )先執行SIGCONT的操作的,這樣就多次一舉了。Note:我看代碼里面是 kernel在檢測信號的時(shí)候, 先處理sigset_t類(lèi)型值中前面的bit對應的信號
case SIGTTIN: case SIGTTOU:
  /* If we’re stopping again, cancel SIGCONT */
  rm_sig_from_queue(SIGCONT, t);
  break;
}
}
  
5> ? 對于3>提到的特別的SIGCHLD 信號, 我們提到了到底哪些信號要被忽略。那么對于善于思考你, 不知道你是否有此疑問(wèn):
  (1)對于一個(gè)普通的進(jìn)程發(fā)SIGCONT 信號肯定是要被kernel忽略的;
  (2)不過(guò)一般的上層熟悉signal用法的R&D都知道 SIGCONT信號是SIGSTOP/SIGTSTP/SIGTTIN/SIGTTOU的后繼信號, 是專(zhuān)門(mén)用來(lái)把掛起的進(jìn)程恢復running的,根據上面的結論SIGCONT不是也要被忽略的嗎???? 那進(jìn)程又怎么可能恢復執行呢?
  
答案:要回答這個(gè)問(wèn)題就要弄清晰kernel在發(fā)一個(gè)signal的流程:  
1. 判斷信號是否是bad的,  參考 kernel/signal.c ==>bad_signal()
2. 處理狀態(tài)是TASK_STOPPED的進(jìn)程, 如果是 , 就調用wake_up_process() , 參考 kernel/signal.c==>handle_stop_signal() 函數
3. 判斷哪些信號該忽略,參考 kernel/signal.c==>ignore_signal()
4. 最后 調用 deliver_signal() 正是發(fā)送信號(其實(shí)發(fā)送信號,說(shuō)白了,就是修改task_struct 相應的數據成員),發(fā)送完信號成功后,如果進(jìn)程是處于TASK_INTERRUPTABLE 狀態(tài)的(且信號沒(méi)有被阻塞), 就喚醒他。
到此為止,信號就算正式發(fā)送完畢了。
所以,你目前你就知道答案了:
雖然SIGCONT 信號要在 3.被忽略,可是2. 卻能被執行, 進(jìn)程被變?yōu)門(mén)ASK_RUNNING 狀態(tài)了(!TASK_RUNNING狀態(tài)的進(jìn)程有非常多,不過(guò)同一個(gè)時(shí)間,占用CPU的就只有一個(gè))
那為什么要必須把 TASK_STOPPED 狀態(tài)的進(jìn)程 變?yōu)?TASK_RUNNING 呢?  也許你能從 4>中得到答案!
6> !關(guān)于阻塞信號注意事項
1. 阻塞信號非常有用, 比如你運行某個(gè)程式, 比如upgrade 程式, 再比如更新BIOS , 都是不允許忽然斷電和忽然停止的,否則會(huì )發(fā)生災難性的后果!至少在代碼某個(gè)部分,是不能停止的,這個(gè)時(shí)候 必須阻塞一些信號(比如SIGTERM,SIGQUIT,SIGINT等等)。阻塞信號后, 在適當的時(shí)機要對該信號解除阻塞!以處理被阻塞信號!
利用信號阻塞能實(shí)現上面的功能,你能在關(guān)鍵的不可停止的代碼上面加 sigprocmask(&block_set)函數阻塞一個(gè)信號集,這樣當這部分關(guān)鍵代碼執行的時(shí)候,阻塞block_set集合里面的信號, 這些信號只有被解除阻塞的時(shí)候,才能被處理! 當關(guān)鍵代碼執行完畢后, 你再 調用suspend(&zero_set) 來(lái)允許所有的信號, 為的就是處理剛才被阻塞的信號,比如如果剛才發(fā)了SIGQUIT信號,目前就能處理SIGQUIT信號了,最后(如果你的程式還沒(méi)有中止,要繼續執行其他操作的話(huà))調用sigprocmask(SIG_SETMASK,&old_set,NULL); 來(lái)恢復原來(lái)的信號掩碼集合,使你的程式繼續running下去!
下面是例子程式:例子程式我寫(xiě)了周詳的操作說(shuō)明,讀者能清晰了解sigsuspend()的用法
程式說(shuō)明: 在執行critical code的過(guò)程中,不允許SIGQUIT信號,你能隨意copy使用!
#include
#include
#include
void sig_int(int signo)
{ printf("int:signo %d\n",signo);
}
void sig_quit(int signo)
{ printf("quit:signo %d\n",signo);
}
int main(void)
{
sigset_t newmask,oldmask,zeromask;
signal(SIGINT,sig_int);  //only test
signal(SIGQUIT,sig_quit);//only test
sigemptyset(&zeromask);

sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);
printf("just sigprocmask , blocked newmask \n");
sigprocmask(SIG_BLOCK,&newmask,&oldmask );

/*critical code begin */
printf("only testing ,please send SIGQUIT signal to test,it will be blocked \n");
pause();//在這里停住僅僅用于測試, 否則程式馬上掠過(guò)這里, 你將沒(méi)有機會(huì )測試 SIGQUIT信號了!
//實(shí)際使用的時(shí)候,你不能用pause()
/*critical code over */
//deal these blocked signal
printf("allow all signals, and deal all blocked signals \n");
sigsuspend(&zeromask);
printf("haha , SIGQUIT has been deal \n");
printf("will restore the signal mask \n");
sigprocmask(SIG_SETMASK,&oldmask,NULL);
printf("has restored ,please testing with SIGQUIT \n");//這個(gè)時(shí)候,沒(méi)有阻塞SIGQUIT信號!
pause();   //這里也僅僅用于測試 ,你能發(fā)SIGQUIT信號試試!
printf("haha , testing over ,this is suspend () usage \n");
exit(0);
}
上面演示了sigsuspend()和sigprocmask的用法, 通常情況下,這兩個(gè)函數都是配合使用的,其他的例子能參考《Unix Advanced Programming 》P229
2.?如果我阻塞了一個(gè)信號, 不過(guò)當我恢復信號阻塞掩碼的時(shí)候, 我并不想處理該信號我該怎么辦呢?比如SIGQUIT信號,如果我不另加處理, 他會(huì )中止程式的,天哪?那怎么行?
這個(gè)時(shí)候需要用到sigpending()這個(gè)函數了, 當你的關(guān)鍵的code 段,結束后, 你能利用sigpending()來(lái)檢查某個(gè)信號是否在pending中,然后設置這個(gè)信號的處理方式, 比如,如果你想刪除這個(gè)信號, 直接忽略他 。 等恢復了原來(lái)的信號阻塞掩碼后,再恢復該信號的處理方式!
還是據個(gè)例子吧:
#include
#include
#include
int main(void)
{
sigset_t block_set,save_set;
sigset_t pending_set;

sigemptyset(&pending_set);

sigemptyset(&block_set);
sigaddset(&block_set,SIGQUIT);
sigprocmask(SIG_BLOCK,&block_set,&save_set);

/* your key code segment ,can’t be interrupt by SIGQUIT
   code
  */
  printf("Please send SIGQUIT signal , by  Ctrl+ \\ \n");
  sleep(6);  //僅僅是為了測試,這個(gè)時(shí)候 ,你能發(fā)SIGQUIT信號,實(shí)際應用中,這個(gè)肯定不要的!
printf("key code has been end !\n");
printf("you can unblock those signals\n");
  //如果你發(fā)了SIGQUIT 信號, 這時(shí)候, 肯定在pending里面了
  
  sigpending(&pending_set);
  if(sigismember(&pending_set,SIGQUIT)) //測試SIGQUIT是否在pending隊列中
  { printf("yes , SIGQUIT is pending signal\n");
   signal(SIGQUIT,SIG_IGN);  //暫時(shí)改動(dòng)一下SIGQUIT的行為,稍后再改回去!
  }
// sigsuspend(&zero_set);  //來(lái)處理其他的剛才被阻塞的信號,不過(guò)不處理SIGQUIT信號
  sigprocmask(SIG_SETMASK,&save_set,NULL);
  signal(SIGQUIT,SIG_DFL);  //復原SIGQUIT的信號部署方式
  printf("yet pause() , please ctrl+\\ to test SIGQUIT’s handler \n");
  pause(); //僅僅測試,SIGQUIT 是否還被阻塞
  
  return 0;
  
}

3.另外兩個(gè)比較有用的函數:sigwaitinfo(), sigtimedwait(),   專(zhuān)門(mén)等待某種信號的到來(lái),sigtimedwait()能在有限的時(shí)間內等待某個(gè)信號集!而且sigwaitinfo() | sigtimedwait()也是經(jīng)常和sigprocmask配合使用, 當然只用sigwaitinfo()系列也能!而且功能也相同!為什么這樣?請man 2 sigwaitinfo,你一定能找到答案!,不過(guò)如果配合sigprocmask功能會(huì )更多一些!比如你想要哪些信號到來(lái)(sigwaitinfo()),但又不想要哪些信號(sigpromask)
具體的復雜的可應用的例子能參考8>中的父子進(jìn)程通信的例子。
使用注意事項:
sigwaitinfo(wait_set)等待set信號集中的信號的到來(lái), 如果在沒(méi)有等到信號集中的信號,或收到了一個(gè)不在set集合中的信號,該函數就會(huì )返回-1,在返回-1之前會(huì )處理這個(gè)不速之客(或默認處理,或調用信號處理函數)。
這里舉個(gè)簡(jiǎn)單的例子;也加進(jìn)了sigsuspend()函數,來(lái)處理增經(jīng)阻塞過(guò)的信號
請注意block_set和wait_set的差別
#include
#include
#include
/*
* 該程式主要是說(shuō)明并演示 sigwaitinfo()和sigsuspend()和sigprocmask()的用法
* 用法和說(shuō)明
  sigwaitinfo僅僅等待set里面的信號, 當收到這個(gè)信號后, 僅僅返回signal值和信號的信息siginfo_t, 不執行信號處理函數(如果注冊了信號處理函數的話(huà))
  sigsuspend()相當于 先sigprocmask() ,然后再pause() , 不過(guò)sigsupend()是個(gè)原子操作
• 程式功能:
程式運行過(guò)程中wait SIGINT信號, 在調用sigwaitinfo之前阻塞了 SIGINT 和SIGQUIT信號,
如果sigwaitinfo()阻塞的時(shí)候, 發(fā)SIGQUIT信號,必然要被阻塞, 程式依舊睡眠, 當發(fā)送SIGINT信號的時(shí)候, 也要被阻塞, 不過(guò)sigwaitinfo一旦檢測到pending 信號中有SIGINT,就即時(shí)返回 。
程式繼續執行, 不過(guò)我們要處理剛才發(fā)送的被阻塞的SIGQUIT信號, 所以調用suspend(&zero_set)來(lái)處理所有被阻塞的信號 , 所以調用SIGQUIT的信號處理函數, 然后從suspend()返回, 程式繼續執行!調用sigprocmask()來(lái)恢復原來(lái)的信號掩碼集。
           --bob
*/
void sig_quit(int signo)
{
printf("======================SIGQUIT=======================\n");
}
int main(void)
{
sigset_t wait_set,block_set,old_set,zero_set;
siginfo_t info;
int recv_signo = 0;
signal(SIGQUIT,sig_quit); //你能捕捉SIGQUIT信號,隨你的便,如果你不想被SIGQUIT退出的話(huà)。
sigemptyset(&zero_set);
sigemptyset(&wait_set);
sigaddset(&wait_set,SIGINT);


block_set = wait_set;
sigaddset(&block_set,SIGQUIT);  //阻塞了block_set集合里面的信號, 比wait_set多阻塞了一個(gè)SIGQUIT信號!,讀者能在這里加上你想阻塞的信號,這些信號可能會(huì )對正常的流程不利!

sigprocmask(SIG_BLOCK,&block_set,&old_set);
recv_signo = sigwaitinfo(&wait_set,&info);
printf("--debug recv signo = %d\n",info.si_signo);
printf(“recv signo = %d\n”,recv_signo);  //確實(shí)收到了想要的信號時(shí),結果和上面相同, 收到了 wait_set意外的信號時(shí), 就返回 -1  ,所以用這個(gè)判斷是否出錯,比較好!
/*----this is error ,為什么錯,please see 《unix advanced programming》P229-230 */
//sigprocmask(SIG_SETMASK,&old_set,NULL);//restore the signal mask
//pause();
/* ----------------------------------------------------------------------------*/
sigsuspend(&zero_set);//臨時(shí)允許所有的信號,為的處理剛才有可能被阻塞的信號!

/* ---------------目前信號掩碼集合已被恢復設置了, 你能再試試 SIGQUIT 信號了。這個(gè)時(shí)候就能處理 SIGQUIT信號了。
* ---------------雖然下面兩行看起來(lái)和上面注釋的認為的錯誤行相同,可是他的用途確實(shí)孑然不同 ,你看出來(lái)了嗎?
*/
sigprocmask(SIG_SETMASK,&old_set,NULL);//restore the signal mask
pause();

return 0;
}
?對于上面的程式讀者不知道發(fā)現一個(gè)bug沒(méi)有?  就是如果這個(gè)時(shí)候在等SIGINT的時(shí)候, 發(fā)生了別的信號比如SIGQUIT,SIGTERM什么的,程式該怎么運行呢?  答案: 不妙, 本來(lái)時(shí)要等待SIGINT 信號的,不過(guò)沒(méi)有等到,由于收到其他的信號可能會(huì )異常退出, 這怎么能行呢? 所以當收到了其他的信號的時(shí)候, 一定要判斷errno == EINTR
改進(jìn)如下:
……………………………………………………………..
again:
sigprocmask(SIG_BLOCK,&block_set,&old_set);
recv_signo = sigwaitinfo(&wait_set,&info);

If(recv_signo == -1) {  //那就出錯了,不過(guò) -1 ,包含了兩種情況 EINTR 和 EAGAIN
  If(errno == EINTR)  //表示被別的信號給中斷了
  {
   goto again;  //重新調用sigwaitinfo() 繼續等待
  else if(errno == EAGAIN);   //如果你用的時(shí)sigtimedwait()函數,這個(gè)也許有用,表示在規定的timeout時(shí)間無(wú)法獲得資源,  errno== EAGAIN
   ;  //do nothing ,直接退出即可(當你用sigtimedwait()的時(shí)候)
}
else {  //收到我們想要的信號
  printf("--debug recv signo = %d\n",info.si_signo);
  printf(“recv signo = %d\n”,recv_signo);  //確實(shí)收到了想要的信號時(shí),結果和上面相同, 收到了 wait_set意外的信號時(shí), 就返回 -1  ,所以用這個(gè)判斷是否出錯,比較好!
}
!我們通過(guò)一個(gè)循環(huán)來(lái)判斷該sigwaitinfo()系統調用是否是被信號中斷的, 如果是就繼續循環(huán)重新調用sigwaitinfo()來(lái)阻塞并等待期待的信號的到來(lái)!

7>!不可再入函數
比如malloc(), printf(), 這些都是不可再入的函數!,使用不可再入的函數使危險的,不要用他們, 具體哪些是不可再入的,哪些使可再入的, 要查看《unix advanced programming》P209
這里指出最常見(jiàn)的錯誤:
在信號處理函數中:
• 不能調用malloc()函數來(lái)動(dòng)態(tài)分配內存,因為主程式在被信號中斷的時(shí)候, 有可能正在調用malloc()函數,也就是主程式調用malloc的時(shí)候被中斷了。
• 不能調用printf()來(lái)打印信息!當然偶爾調試也問(wèn)題不大, 不過(guò)調試成功后, 要去掉printf()函數!
8> 關(guān)于信號的技巧:
 Q :怎么判斷一個(gè)進(jìn)程是否還活著(zhù) ?
A: 發(fā)一個(gè)空信號就好了, 什么?什么是空信號?  就是  0
 Q:怎么暫時(shí)掛起某個(gè)進(jìn)程?
  A: 如果你想暫時(shí)掛起某個(gè)進(jìn)程的運行,以后要恢復,請用SIGSTOP信號 , 想重新執行該進(jìn)程的時(shí)候,就發(fā)SIGCONT信號。 記住當你對一個(gè)進(jìn)程發(fā)SIGSTOP信號的時(shí)候,  子進(jìn)程會(huì )給父進(jìn)程發(fā)SIGCHLD 信號,這樣父進(jìn)程會(huì )被喚醒。具體為什么請看 3> ,kernel 代碼里面已非常清晰了。
 Q:我想父子進(jìn)程間通信而且同步,比如: 父進(jìn)程需要等待子進(jìn)程執行的結果,然后最會(huì )退出, 當然不能無(wú)限期等待,比如等 5秒鐘 , 用信號行嗎?  
A:你以為信號是萬(wàn)能的? 不過(guò)這個(gè)功能用信號確實(shí)能實(shí)現,而且還非常簡(jiǎn)單。
具體的我們來(lái)分析一下:
1. 同步:這是信號的最基本的功能了, 無(wú)論你用signal()/kill() 系列還是  sigaction()/sigqueue() 系列肯定能滿(mǎn)足你的需求 。
2. 通信:如果你用kill(2)函數發(fā)信號, 通信肯定不可能了, 傳遞不了信息啊,接收進(jìn)程只知道收到收到這個(gè)信號,不過(guò)到底發(fā)生了什么事情一概不知!這也是kill(2)的局限了!
   你能用sigqueue()函數 ,里面有項參數就是用來(lái)傳遞數據的 ,內核里面有個(gè)結構叫 siginfo_t ,就是干這個(gè)用的。具體請看
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html
(進(jìn)程間通信 信號(下),里面舉了個(gè)例子)
3. 父進(jìn)程有限時(shí)間等待子進(jìn)程,用sigtimedwait(const sigset_t, siginfo_t *info, const struct timespec timeout) ,不過(guò)僅限于等信號,可不是等別的什么!
舉個(gè)非常有實(shí)用價(jià)值的例子:比如有個(gè)downloader (libcurl),我想在主程式中調用curl執行下載, 然后主程式等待curl的下載結果,能只等5秒鐘, 如果5秒鐘還是沒(méi)有收到信號, 說(shuō)明curl一直在執行!
主進(jìn)程fork()出一個(gè)子進(jìn)程 ,子進(jìn)程去作真正的事情。
為了方便理解,用子進(jìn)程運行curl下載文件 ,根據文件的大小和網(wǎng)速,下載時(shí)間會(huì )有非常大不同。
下面分三種情況:
 file比較小, 子進(jìn)程(curl進(jìn)程)非??旆祷?,下載完畢
 file比較大, 子進(jìn)程(curl進(jìn)程)過(guò)20分鐘返回,下載才完畢。父進(jìn)程剛開(kāi)始timeout時(shí)間就結束了。
 URL出錯, 子進(jìn)程馬上返回一個(gè)出錯碼!
父進(jìn)程能根據這三種情況update某個(gè) database,來(lái)記錄這次的下載狀態(tài)!并及時(shí)的反饋給前端的UI
下面是我寫(xiě)的程式, 非常高興被你隨意copy!
思路1: 用signal來(lái)實(shí)現, 非常簡(jiǎn)單和直觀(guān)?。▽Ρ认旅娴膒ipe做法)
父進(jìn)程需要在有限的時(shí)間內等待子進(jìn)程發(fā)送SIGUSR2信號, 而子進(jìn)程結束時(shí)候,也要報告自己的exit code ,不過(guò)這個(gè)時(shí)候父進(jìn)程已結束了,子進(jìn)程被init(1)領(lǐng)養 ,所以就不用報告狀態(tài)了,直接被init(1)回收!
下面是代碼:
#include
#include
#include
void sig_usr2(int signo,siginfo_t *info,void *myact)
{
printf("signo = %d\n",signo);
}
int main(void)
{
sigset_t wait_set;
int sig_no;
const struct timespec tv = {5,0}; //timeout
siginfo_t sig_info ;//傳遞的信息結構
pid_t pid = 0;
sigemptyset(&wait_set);
sigaddset(&wait_set,SIGUSR2);

signal(SIGCHLD,SIG_IGN); //父進(jìn)程顯式的忽略子進(jìn)程發(fā)來(lái)的SIGCHLD信號 ,防止出現Zombie 進(jìn)程,如果忘記了,復習一下上面!
pid = fork();
if(pid
http://www.kernel.org/pub/linux/kernel/v2.4/linux-2.4.24.tar.gz
&>/dev/null");
  printf("rc = %d\n",rc);
  rc_val.sival_int = rc/255;  //傳遞整型值
  if(getppid() > 1) { //因為如果過(guò)了timeout , 父進(jìn)程就退出了, 該子進(jìn)程被init(1)領(lǐng)養, 所以千萬(wàn)不要向init(1)發(fā)信號!否則整個(gè)系統都要reboot 了!
   sigqueue(getppid(),SIGUSR2,rc_val);//給父進(jìn)程發(fā)信號!,getppid()能獲得父進(jìn)程的pid
  }  
  exit(0);
}
  
//parent process
sigprocmask(&wait_set);
sig_no = sigtimedwait(&wait_set,&sig_info,&tv); //父進(jìn)程會(huì )一直阻塞tv時(shí)間,然后就返回
if(sig_no == -1) {   //說(shuō)明超時(shí)
  printf("child process timeout \n");
  printf(“curl 一直沒(méi)有返回,這樣下載看起來(lái)沒(méi)有問(wèn)題, 正在下載\n”);
}
else {
  printf("child process return value = %d\n",sig_info.si_int);
  if(sig_info.si_int == 0)   //信號傳遞的信息! 我們在這里傳遞integer值!
   printf(“下載完畢\n”);
  else
   printf(“下載出錯,錯誤代碼 %d\n”, sig_info.si_int);
}
return 0;
}

為了比較, 我原來(lái)寫(xiě)過(guò)一個(gè)用 無(wú)名管道/select 來(lái)實(shí)現 上述功能的, 不過(guò)你需要了解pipe和select的用法,需要的知識點(diǎn)比較多。
你能隨意copy我的代碼,
思路2:用pipe實(shí)現父子進(jìn)程通信,再配合select()在timeout時(shí)間內監視管道讀管道)
父進(jìn)程創(chuàng )建了一個(gè)無(wú)名管道, 子進(jìn)程在管道寫(xiě)端寫(xiě)入value , 父進(jìn)程通過(guò)select()函數檢測管道的讀端,如果5秒鐘內讀端無(wú)反應,說(shuō)明超時(shí),否則就能讀value! 這樣就是簡(jiǎn)單的實(shí)現父子進(jìn)程同步,通信,且有限時(shí)間等待的需求!
pid_t pid = 0;
int fd[2];    //pipe operation :
unsigned char share_buffer[3]; //share info between parent process and child process
  
fd_set read_fds;
int fd_max ;     /* for select */
struct timeval tv;
int select_rc = 0;
if(pipe(fd)
  setsid();
  /* generate a daemon process
    * setsid()是創(chuàng )建daemon的關(guān)鍵函數,(1)成為session的leader process ,
    *(2)成為進(jìn)程組的leader process ,(3)沒(méi)有終端
    */
   umask(0);
   /* 當創(chuàng )建文件的時(shí)候和目錄的時(shí)候 默認是 umask(022) ,
    * umask()函數能改便創(chuàng )建文件時(shí)候的默認許可權位 , 據個(gè)例子,當你用root權限
    * 創(chuàng )建一個(gè)文件 , > bob.txt   ,你會(huì )發(fā)現: ll bob.txt ,  顯示 -rw-r--r-- ,
    * 這就是umask(022)的作用 , 022 對應的二進(jìn)制: 000     010 010 ,表示 對于
    * 組內用戶(hù)和其他的用戶(hù) 不可有w的權限。   w位置1 就表示不能w !以此類(lèi)推!
   close(0);   //關(guān)閉標準輸入
  close(1); //關(guān)閉標準輸出
  close(2); //關(guān)閉標準錯誤輸出
  chdir(“/”);
  close(fd[0]); //把管道的讀 一端 關(guān)閉 ,只留寫(xiě) 一端 即可
//執行你的程式 ,你的code
。。。
//在exit(0)之前,通知你的父進(jìn)程你的執行結果, rc就是執行結果
buffer[0] = rc;
buffer[1] = ’\0’;
buffer[2] = ’\0’;
write(fd[1],buffer,sizeof(buffer));
close(fd[1]); //end of write to the "write pipe" ,must close it
exit(0);
}  //子進(jìn)程結束!
//父進(jìn)程內
close(fd[1]); //關(guān)閉寫(xiě)端 ,只要留著(zhù)讀端即可!
  
FD_ZERO(&read_fds); //clear the read_fds
        FD_SET(fd[0], &read_fds);
        
        tv.tv_sec = 5  //假設父進(jìn)程就等待5s
        fd_max = fd[0]+1;
               
        //select 不熟悉select()的朋友能到google搜索他的用法,一定要掌控!
        select_rc = select(fd_max,&read_fds,NULL,NULL,&tv);

if(!select_rc) //wait超時(shí) ,
  //你的處理code , 也許這正是你期待的呢!
else {
  read(fd[0],share_buffer,sizeof(share_buffer));
  jprintf("read successfully\n");
  jprintf("in father :buffer[0] = %d\n",share_buffer[0]);
  jprintf("in father :buffer[1] = %d\n",share_buffer[1]);
  jprintf("in father :buffer[2] = %d\n",share_buffer[2]);
  close(fd[0]); //close read pipe , 讀完畢后記著(zhù)關(guān)閉他!
  //你能根據buffer讀出的內容作進(jìn)一步的處理
  //你的code
}
………………………………..
附錄:
參考資料:
1. 《linux內核原始碼情景分析》上冊 ,對kernel代碼分析的周詳完全,不回避難點(diǎn),非常多kernel代碼分析的書(shū)沒(méi)有講到的知識,他基本上都講到了,而且周詳透徹,值得一讀!
2. 《深入理解linux 內核》,雖然沒(méi)有《情景分析》那么詳盡細節, 不過(guò)總是有畫(huà)龍點(diǎn)睛之筆,讓人驚嘆作者的功力之深看問(wèn)題的銳利!建議先看他,看代碼的時(shí)候,再看情景分析!
3. 《Unix環(huán)境高級編程》,是每個(gè)linux編程者必備的手冊。
4. 進(jìn)程間通信 信號(上)
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
絕對的好文章
5. 進(jìn)程間通信 信號(下)
http://www-128.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html
同一個(gè)人寫(xiě)的
6. signal(),signaction(),sigwaitinfo(),sigtimedwait()等其他函數的manual
文件完
作者:英文名bob  ,職業(yè):Embeded Linux程式員,研發(fā)方向NAS和linux 客人呢了 , birth : 1980/3/28
mail :
bob_zhang2004@163.com
    或
zhanglinbao@gmail.com
  均可
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
signal函數
linux信號 linux signal
linux下基于C語(yǔ)言的信號編程實(shí)例
Linux 信號處理
Linux環(huán)境進(jìn)程間通信(二): 信號(下)
linux信號函數
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久