信號和線程

萬事皆有因

隨着公司的業務不斷擴大,咱們在2013年末開始逐步的進入Java體系的階段,不過誰都沒有Java的經驗,咱們就決定本身動手豐衣足食的策略,學習,請教和顧問。通過2014年的一年的努力,成功的組建了一個Java團隊,並嘗試作了一些新業務和基礎性組件。雖然比較順利,但仍是在我心中留下了許多疑問,例如說JVM的安全點和安全區,正好上週出差回來獲得了一段時間的放鬆,又開始閱讀了下JVM中的代碼。安全

 

那麼收穫有什麼呢?多線程

糾正了本身之前一個錯誤的認識,VMThread的是JVM用來完成JVM內的事情的線程,並不是執行OpCode的線程,而這正執行OpCode的線程是JavaThread。函數

發現了JVM進入安全區的一些原理,該原理和Linux的信號以及線程是有很是大的關係的。學習

 

Linux的線程

在Linux的上古時代,Linux的線程技術和POSIX的標準是不一樣的,它使用本身的LinuxThread庫。這會爲咱們帶來什麼影響呢?spa

首先,咱們說下POSIX是如何定義多線程的,POSIX下一個多線程的進程只有一個PID。從這個定義中,你們可能已經猜出LinuxThread的實現有什麼不一樣了,對,就是LinuxThread下每一個線程都有一個PID,每一個線程在系統中的表現就如同進程通常。操作系統

其次,現代的Linux的線程已經徹底符合POSIX的標準了。線程

總結就是,咱們能夠忽略這件事情(不要丟雞蛋)。code

 

Linux的信號

隨着Linux的內核版本不斷提高,Linux的信號如今已經能夠按照線程級別的觸發了,換句話說就是,每一個線程能夠關注本身的信號了,而且能夠區別性對待了。那咱們須要注意什麼呢?隊列

  1. 在多線程應用中,咱們應當使用sigaction來代替singal函數,由於按POSIX的說法singal函數並無明肯定義本身在多線程應用中的行爲。進程

  2. 可使用pthread_sigmask來爲每一個線程設置獨立的信號掩碼。同時在多線程應用中應當避免使用sigprocmask這個函數,緣由也是POSIX中該函數病沒有明肯定義本身在多線程應用中的行爲。

這個時候,有人會產生疑問了,那麼多線程下kill發出的進程級別的信號A怎麼辦?Linux是這樣解決的,它會把這個信號交付給任意一個沒有屏蔽信號A的線程。若是這信號沒有被任何線程設置handler進行處理,就會觸發POSIX規定的默認動做。

接着有人就會問,我怎麼向某個線程發消息呢,POSIX爲咱們準備了pthread_kill函數,咱們能夠直接向特定的線程發送消息。那麼若是一個線程收到信號A,可是本身沒有安裝handler會發生什麼?其實和進程級別的信號處理方法同樣,直接觸發默認動做,一樣會結束整個進程。

說到這裏了,好像已經沒什麼可說的了,可是我在看JVM中的時候,發現了一個很是重要的信號SIGSEGV。

 

信號SIGSEGV

這個信號,也許是你們最不想見到,爲何呢?咱們看這個信號的定義:

噹噹前程序對內存的引用無效時,就會產生當前信號,也就是咱們常說的「段違例」。

如下幾種狀況會產生該信號:
1.進程引用的內存頁面不存在(例如,該頁面位於堆和棧之間的映射的區域)
2.進程試圖更新只讀內存頁(例如,程序文本段或已經被標記爲只讀的內存映射區域)
3.進程試圖在用戶態去訪問內核部分的內存

OK,咱們都知道這個信號引起的結果就是進程退出。不過咱們都忽視了一個問題,在現代的Linux上,按照POSIX的定義,

這個信號是系統產生的線程級別的信號。換句話說,若是某個線程A出現了內存引用無效,那麼產生的信號,會投遞到線程A的信號隊列中,而不是像進程級別的信號沒法肯定接受者是誰。

 

JVM的安全區域

若是咱們想讓全部Java線程停下來的時候,在JVM的JavaThread執行到你們所知道的test 特定頁面的指令時,就會由於更新不可讀頁面而觸發SIGSEGV信號。那麼對於那些正在執行native代碼的JavaThread該怎麼辦,JVM中的註釋寫的很是清楚,native返回JVM時會檢查是否能返回的。

好了再多說一句,JVM是若是將特定內存保護起來的呢?這個須要看操做系統的API了,在Linux中是mprotect。

 

總結下

多讀讀POSIX標準和Intel的CPU體系結構,會讓本身在開發變的容易些。

轉自TTalkIM,深刻閱讀點擊 https://ttalk.im/articles/19

相關文章
相關標籤/搜索