linux0.11訊號機制

來源:互聯網
上載者:User

linux0.11訊號機制

本文簡單描述linux0.11訊號機制的實現
  www.2cto.com  
一:有關訊號
當進程收到一個訊號後,進程根據相關設定調用訊號處理函數。

有三類訊號處理方式:預設處理方式、忽略訊號方式、執行使用者設定的訊號處理函數。

發送訊號的方式:按下相應的鍵(如CTRL+C)、使用kill命令或函數向指定進程發送訊號。

typedef void sig_func(int);
sig_func *signal(int signr, sig_func *handler);

當在進程中調用signal(signo, handler)之後,
如果進程收到訊號signo,則進程會執行handler指向的函數。

假設有進程A和進程B,當進程A給進程B發送了一個訊號後,那進程B的訊號處理函數什麼時候執行?
若要執行進程B的訊號處理函數,則進程B必須處於執行狀態。也就是說只有當發送器調度到了進程B後,
才可能執行進程B的訊號處理函數。否則進程B不處於可執行狀態,收到了訊號也沒用。

若進程B自己調用kill函數,給自己發送了一個訊號,我們會假定這個訊號處理函數會立即執行。
因此在執行系統函數kill之後會立即處理進程B的訊號。


從這兩點可以意識到,核心需要在時鐘中斷和系統調用後對當前進程的訊號進行處理。
需要在時鐘中斷時是因為時鐘中斷會調用schedule函數,因為這是分時系統,
如果進程A給B發了訊號,而且現在調度到了B,那理所當然要執行B的訊號處理函數。


二:linux0.11的訊號機制
以 kill 函數為例來簡單說明大致流程, 下面再來詳細描述核心中的do_signal函數。

當以kill函數給當前進程發送一個訊號之後。
因為這是個系統函數,因此會執行int 0x80進入system_call的進入點
_system_call:
 cmpl $nr_system_calls-1, %eax # %eax儲存kill函數的調用號
 ja bad_sys_call # 無效的系統調用
 push %ds
 push %es
 push %fs
 push %edx
 push %ecx
 push %ebx # 相關資料入棧
.....................
 call _sys_call_table(,%eax,4)   # 執行系統調用, 這裡就是 sys_kill 函數了
 pushl %eax # 系統調用的傳回值入棧,也即是 sys_kill 的傳回值
......................
 
ret_from_sys_call:
 .....
 pushl %ecx  # %ecx中儲存了訊號的訊號值。
 call _do_signal  # 對訊號進行處理
 popl %eax #  將訊號值出棧  
 popl %eax  #  將系統調用傳回值出棧,  也就是sys_kill的傳回值存入%eax寄存器
 popl %ebx
 popl %ecx
 popl %edx
 pop %fs
 pop %es
 pop %ds
 iret


可見每次系統調用之後,可能會執行ret_from_sys_call,進而對訊號進行處理。

除了在_system_call裡會這樣, 在一些中斷下也會調用ret_from_sys_call,時鐘中斷就是其中之一。
現在已經知道核心是“何時”來處理進程的訊號了。


三:do_signal函數。
do_signal的功能主要是設定了核心的堆棧和應用的使用者堆棧,設定好堆棧後,
當執行ret_from_sys_call最下面的iret指令的時候,去自動執行進程的訊號處理函數。當訊號處理函數執行完成後,又會接著進程的下一條指令去執行。

是《Linux0.11核心完全注釋》一書裡的,很好的顯示調用do_signal前後的堆棧變化。

左邊的為核心態堆棧,就是在執行call _do_signal之前的堆棧內容。

do_signal執行如下操作
1:將堆棧中的eip值,儲存到old_eip中,old_eip就指向了使用者程式中即將執行代碼
2:將eip執行訊號處理函數。這樣當執行ret_from_sys_call中的iret時,會執行cs:eip指向的代碼,也就是訊號處理函數。
3:將使用者態堆棧的esp的值,向下移7或8個長字(32位)
4:然後將sa_resotrer, signr等值放入堆棧, 見圖右邊的使用者堆棧。

完成上述操作後,do_signal執行完畢,返回到ret_from_sys_call中,
ret_from_sys_call執行一些pop操作後執行iret指令, 這時會跳轉到訊號處理函數去執行。
當訊號處理函數執行完後,會執行ret操作(函數的返回使用ret,中斷的返回使用iret),這時會將sa_restorer存入eip,
因此接下來就會執行sa_restorer

sa_restorer會恢複使用者堆棧
__sig_restore:
 addl $4, %esp
 popl %eax  # 將系統調用的傳回值存入eax
 popl %ecx
 popl %edx
 popfl
 ret 
當執行完popfl之後,明顯使用者堆棧裡面只剩下old_eip了, 因此執行ret,程式就會跳轉到cs:old_eip去執行,也就是系統調用的下一條使用者指令了。
至此訊號處理函數已經執行,系統調用也已返回,使用者程式無憂無慮的繼續執行。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.