2010-11-17 34 views
15

信號如何在unix中工作?我經歷了W.R.史蒂文斯,但無法理解。請幫幫我。信號如何工作?

+2

http://en.wikipedia.org/wiki/Signal_(computing) – ceejayoz 2010-11-17 18:55:07

回答

8

將信號設施視爲由OS實現的中斷(而不是硬件)。當你的程序高興地遍歷其根源於main()的執行軌跡時,這些中斷可能發生,導致程序被分派到一個向量(處理程序),在那裏運行代碼,然後返回到它的位置被打斷了。

這些中斷(信號)可以來自各種來源,例如,硬件錯誤,如訪問錯誤或未對齊的地址,子進程死亡,使用kill命令生成的用戶信號,或使用kill系統調用的其他進程。消耗信號的方式是指定它們的處理程序,這些處理程序是在信號發生時由操作系統調度的。請注意,這些信號中的一部分無法處理,並導致該過程簡單地死亡。

但是那些可以處理的,可以是非常有用的。您可以將它們用於進程間通信,即一個進程向另一個進程發送信號,該進程處理該進程,並在該進程中執行一些有用的操作。許多守護進程會執行有用的操作,例如,如果向它們發送正確的信號,請重新讀取配置文件。

36

下面的解釋是不完全一樣,和這個作品不同的系統(甚至在不同的硬件相同的操作系統的某些部分)之間有什麼不同,但我認爲它通常是足夠幾個方面因爲你足以滿足你的好奇心使用它們。大多數人開始在編程中使用信號而沒有這種理解,但在我習慣使用它們之前,我想了解它們。

信號輸送

OS內核具有一個數據結構要求,其具有有關該過程數據中的每個進程運行的進程控制塊。這可以通過進程ID(PID)查找,幷包含一個信號動作和未決信號表。

當信號發送到進程時,OS內核將查找進程的進程控制塊並檢查信號動作表以找到正在發送的特定信號的動作。如果信號動作值是SIG_IGN那麼新信號被內核遺忘。如果信號動作值爲SIG_DFL,則內核將在另一個表中查找該信號的默認信號處理操作,並對該操作進行預處理。如果這些值是其他任何值,則假定該信號被髮送到應該被調用的進程內的函數地址。 SIG_IGNSIG_DFL的值是轉換爲函數指針的數值,這些函數指針的值在進程的地址空間(例如0和1,它們都不在映射到進程中)中的值不是有效地址。

如果信號處理函數是由進程註冊的(信號操作值既不是SIG_IGN也不是SIG_DFL),那麼在該信號表中爲該信號創建一個條目,並且該進程被標記爲準備運行(可能會一直在等待一些東西,比如數據可用於呼叫read,等待信號或其他幾件事情)。

現在下一次進程運行時,OS內核將首先向堆棧中添加一些數據並更改該進程的指令指針,以使其看起來幾乎就像進程本身剛纔調用的信號處理程序一樣。這不完全正確,實際上與實際發生的偏差足夠大,我會稍微談一談。

信號處理程序函數可以做它做的任何事情(它是代表它調用的過程的一部分,所以它被寫入關於該程序應該用該信號做什麼的知識)。當信號處理程序返回時,該進程的常規代碼將再次開始執行。 (再次,不準確,但接下來更多)

好吧,上面應該給你一個關於如何將信號傳遞給進程的相當好的想法。我認爲這漂亮想法版本是必要的,然後才能掌握完整的想法,其中包括一些更復雜的東西。

OS內核通常需要知道信號處理程序何時返回。這是因爲信號處理程序需要一個參數(可能需要堆棧空間),您可以在執行信號處理程序期間阻止相同的信號傳送兩次,和/或在傳送信號後重新啓動系統調用。要做到這一點,比堆棧和指令指針改變更多一點。

發生什麼事是內核需要讓進程告訴它它已經完成了執行信號處理函數。這可以通過將一部分RAM映射到進程的地址空間來完成,該地址空間包含用於進行此係統調用的代碼,併爲信號處理函數(此函數開始運行時堆棧上的最高值)作爲地址這段代碼。我認爲這是在Linux中完成的方式(至少是新版本)。另一種方法來實現這一點(我不知道這是否完成,但它可能會)將做是爲了使信號處理函數的返回地址是一個無效的地址(如NULL),這會導致大多數系統中斷,這會再次給OS內核控制。發生這種情況並不重要,但內核必須再次控制修復堆棧並知道信號處理程序已完成。

當觀看另一個問題我學到

Linux內核做了頁面映射到這個過程中,但實際的系統調用登記信號處理(什麼sigaction的調用)需要一個參數sa_restore參數,它是應該用作信號處理程序返回地址的地址,內核只是確保它放在那裏。在這個地址的代碼發出我是完成系統調用(sigreturn),內核知道信號處理程序已完成。

信號生成

我主要假設你知道信號是如何生成的。由於發生的事情,操作系統可以代表進程生成它們,比如定時器到期,子進程死掉,訪問它不應該訪問的內存,或者發出它不應該存在的指令(或者是不存在的指令或有特權的)或其他許多事物。定時器的功能與其他功能稍有不同,因爲它可能在進程沒有運行時發生,所以更像是通過系統調用kill發送的信號。對於代表當前進程發送的非定時器相關信號,這些信號是在發生中斷時生成的,因爲當前進程出錯。這個中斷給出了內核控制(就像系統調用一樣),內核產生信號傳遞給當前進程。

+0

好極了!你從哪裏學到的?信號傳遞,最後一段:「製作信號處理函數的返回值」=你的意思是返回地址,對吧?然後:「Linux內核確實將頁面映射到進程中」=你的意思是「不」,對吧? – 2012-12-23 12:28:33

+3

是的。我通過使用'strace',閱讀代碼,並最終編寫了一個從未發送過的信號中返回的程序,我學到了這一點。 – nategoose 2013-03-13 21:21:17

+0

信號是否僅在操作系統傳遞時,信號將被傳遞到的進程的線程在即將執行的就緒隊列上?這是有道理的,但我只是想確保 – Curious 2015-12-23 03:04:26

5

上述所有語句都沒有涉及的一些問題是多核,在接收信號時在內核空間運行,在接收信號時睡在內核空間,系統調用重啓和信號處理器延遲。

這裏有幾個問題需要考慮:

  • 如果內核知道需要傳遞到處理這是對CPU_X運行X的信號,但是內核得知它CPU_Y運行時(CPU_X!= CPU_Y)。所以內核需要停止運行在不同內核上的進程。
  • 如果進程在接收信號時在內核空間中運行會怎樣?每當進程進行系統調用時,它都會進入內核空間,並在內核空間中進入數據結構和內存分配。這些黑客行爲是否也發生在內核空間?
  • 如果進程正在內核空間中休眠,等待其他事件會怎麼樣? (讀,寫,信號,輪詢,互斥只是一些選擇)。

數目:

  • 如果該進程在另一個CPU內核中運行,經由橫CPU通信,將提供一箇中斷給CPU等和用於它的消息。另一個CPU將通過硬件保存狀態並跳轉到另一個CPU上的內核,然後在另一個CPU上執行信號傳送。這是所有嘗試不在另一個CPU上執行該進程的信號處理程序的一部分,這會破壞緩存局部性。
  • 如果進程在內核空間中運行,則不會中斷。相反,記錄該過程已收到信號。當進程退出內核空間時(在每個系統調用結束時),內核將設置蹦牀來執行信號處理程序。
  • 如果在內核空間中運行的進程在接收到信號後進入睡眠函數,那麼該睡眠函數(對於內核中的所有睡眠函數都是通用的)將檢查進程是否有待處理的信號。如果是這樣的話,它不會讓進程進入休眠狀態,而是在進入內核時取消所有已完成的進程,並在設置蹦牀執行信號處理程序時退出到用戶空間,然後重新啓動系統呼叫。您可以實際控制要中斷系統調用的哪些信號,以及哪些不使用系統調用。當您使用sigaction(2)SA_RESTART標誌註冊信號時,您可以決定是否希望系統調用可以爲某個信號重新啓動。如果系統調用被髮出並被一個信號中斷並且沒有自動重啓,您將得到一個EINTR(中斷)的返回值,並且您必須處理該值。您還可以查看restart_syscall(2)系統調用瞭解更多詳細信息。
  • 如果進程已經在內核空間中休眠/等待(實際上所有休眠/等待總是在內核空間中),它會從睡眠中喚醒,內核代碼自行清理並跳轉到信號處理程序,返回到用戶空間後如果用戶需要,系統調用會自動重新啓動(非常類似於以前對如果進程在內核空間中運行會發生什麼情況的解釋)。

爲什麼這一切如此複雜的幾個注意事項:

  • 因爲內核開發人員分配內存,做事的數據結構,更不能只停留在內核空間運行的進程。如果您只是將控制權移開,您將損壞內核狀態並導致機器掛起。內核代碼必須以受控的方式通知它必須停止運行,返回到用戶空間並允許用戶空間來處理信號。這是通過內核中所有(幾乎全部)睡眠函數的返回值完成的。預計內核程序員將尊重這些返回值並相應採取行動。
  • 信號是異步的。這意味着他們應該儘快交付。設想一個只有一個線程的進程,休息一小時,併發送一個信號。睡眠在內核中。所以,除了內核代碼喚醒之後,它自己清理之後,返回到用戶空間並執行信號處理程序,可能在信號處理程序完成後重新啓動系統調用。你當然不希望這個過程只在一小時後執行信號處理程序。那麼你期望睡眠恢復。用戶空間和內核人員爲此付出了巨大的麻煩。
  • 所有信號都像中斷處理程序,但用戶空間。這是一個很好的比喻,但不完美。雖然中斷處理程序是由硬件生成的,但一些信號處理程序來自硬件,但大多數只是軟件(有關子進程死亡的信號,來自使用系統調用的其他進程的信號等)。

那麼什麼是信號處理的延遲?

  • 如果當你得到一個信號,一些其他的進程正在運行,然後它到內核調度,以決定是否讓其他進程完成它的時間片,然後才傳遞信號或沒有。如果你在一個普通的Linux/Unix系統上,這意味着你在得到信號之前可能會延遲一個或多個時間片(這意味着等於永恆的毫秒)。
  • 當你得到一個信號時,如果你的進程是高優先級的,或者其他進程已經獲得了他們的時間片,你將得到相當快的信號。如果您在用戶空間運行,你會得到它「立即」,如果你正在運行在內核空間,你將很快達到一個休眠功能或在這種情況下,當您返回到用戶空間的信號處理程序將調用內核返回。這通常是很短的時間,因爲在內核中沒有花費大量時間。
  • 如果你是睡在內核中,並沒有別的就是你優先級高於或需要運行的內核線程處理系統調用被喚醒後,它確實在一路下跌到內核中的所有東西清理,回到用戶空間並執行你的信號。這不需要太長時間(這裏說的是微秒)。
  • 如果您運行的是Linux的實時版本和過程具有最高的實時優先級,那麼你很快就會被觸發後,得到信號。是說50微秒甚至更好(取決於其他因素,我不能進入)。
1

信號不過是執行過程中的中斷。一個進程可以自己發出信號,或者它甚至可以向另一個進程發出信號。可能是家長可以給其子終止或過程它的自我可能會導致一個信號,停止自身或殺人等的信號,

查看以下鏈接,這將有助於你理解。

https://unix.stackexchange.com/questions/80044/how-signals-work-internally

http://www.linuxjournal.com/article/3985

http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show

+0

謝謝,我這樣做。@ shivam – 2015-12-24 06:43:13