iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 17
1
自我挑戰組

跨界的追尋:trace 30個基本Linux系統呼叫系列 第 17

trace 30個基本Linux系統呼叫第十七日:pause

  • 分享至 

  • xImage
  •  

前情提要

終於來到了訊號處理系列的尾聲。程序對訊號的處理,端看這個訊號是否被阻攔(sigprocmask相關設定),以及訊號處理程序的設置(sigaction的註冊)。程序與程序之間也可以用kill指令傳訊號。本日為鐵人賽中期,來探索pause這個系統呼叫,作為中間喘口氣的同時也結束訊號處理篇章。


介紹

NAME
       pause - wait for signal

SYNOPSIS
       #include <unistd.h>

       int pause(void);

這是pause(2)手冊的開頭內容。一個程序若是呼叫了這個系統呼叫,則會停止直到接受到訊號訊號處理程序回傳,才會繼續下去。一個簡單的範例如下:

  1 #include<stdio.h>                                                                                                     
  2 #include<unistd.h>
  3 #include<signal.h>
  4  
  5 int state = 0;
  6  
  7 void siguser1(int signal){
  8         state = 1;
  9         return;
 10 }
 11  
 12 int main(){
 13  
 14         struct sigaction sa;
 15         sa.sa_handler = &siguser1;
 16         sigemptyset (&sa.sa_mask);
 17         sa.sa_flags = 0;
 18  
 19         sigaction(SIGUSR1, &sa, NULL);
 20  
 21         printf("before the pause: %d\n", state);
 22         pause();
 23         printf("after the pause: %d\n", state);
 24 }

pause會無限期卡在那裡,直到一個來自外部的SIGUSR1 signal,可以成功喚起siguser1的訊號處理,然後將執行流程還給這個程序,並且可以觀察到這個訊號對於整個程序的狀態的改變。

至於pause的程式碼,同樣在kernel/signal.c之中:

3498 SYSCALL_DEFINE0(pause)
3499 {                
3500         while (!signal_pending(current)) {
3501                 __set_current_state(TASK_INTERRUPTIBLE);                                                                              
3502                 schedule();
3503         }        
3504         return -ERESTARTNOHAND;
3505 }

其中,signal_pending(current)檢查該程序的TIF_SIGPENDING旗標,若是沒有的話就將當前程序設為可中斷的,然後透過schedule呼叫將執行權還給系統,反之則離開迴圈,並回傳-ERESTARTNOHAND。但是根據手冊,這個呼叫將回傳-1,這又是怎麼回事呢?

觀察上述使用者程式的系統呼叫,則可以看到:

...
write(1, "before the pause: 0\n", 20before the pause: 0
)   = 20
pause()                                 = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=4487, si_uid=1000} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
write(1, "after the pause: 1\n", 19after the pause: 1
)    = 19
...

這是因為訊號處理的時候,最後return的是rt_sigreturn的緣故。從手冊上可以看到,這個呼叫是為了處理從核心空間返回時,因為訊號的關係而必須加入的中間層,用來整理程序的context用。


結論

雖說到了訊號處理篇章的尾聲,但是這個部份因為牽涉到許多非同步事件,只從系統呼叫下手所能獲得的知識相當有限。筆者自己感到興趣卻尚未得到滿足的的部份有二:

  • Ctrl+C的處理,究竟是怎麼讓核心知道要砍掉哪個程序?目前的猜測是終端機的讀取功能取得這個控制字元之後,經過類似session ID的審查得知當前程序,然後決定將之關閉。

  • SIGSEGV的產生。一個違法的記憶體存取如何導這這個訊號產生?

接下來若是有機會的話,或許還可以回來探討這些謎團。接下來的數日筆者打算安排一些難以分類(卻絕不輕鬆!)的系統呼叫,先是alarm這種與時間和訊號同時相關的(畢竟應該沒有時間探討時間了),然後是ptrace這個我們其實一直都很依賴的系統呼叫,再來是execve這個真正產生程序生態的呼叫。然後筆者預計接下來探討一些記憶體相關的部份,再緊接著使用者相關的功能和網路的功能。期望能夠圓滿完賽的同時,也祝福所有鐵人一起挑戰自己的極限並推動社群正向成長。我們明日再會!


上一篇
trace 30個基本Linux系統呼叫第十六日:sigprocmask
下一篇
trace 30個基本Linux系統呼叫第十八日:alarm
系列文
跨界的追尋:trace 30個基本Linux系統呼叫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言