linux 信号_Linux中的信号处理机制 [四]
信號與線程
Unix的信號機制在誕生之初,生活在只有進程(process)的相對單純的環境中。自從Unix世界有了線程(thread)的概念,信號就被賦予了發往進程中某個特定線程的能力,當然,這也增加了整個信號機制實現的復雜度。本系列的前面三篇文章都是基于進程進行的信號實現機制的討論,本文將著重介紹Linux中信號和線程之間的交互。
發送信號給線程無論是kill()還是sigqueue(),都只能向進程發送信號,在Linux中,要向進程內的線程發送信號,需要使用tkill()或者tgkill():
int兩個函數中,"tid"都是代表目標線程的PID,但tgkill()比tkill()多了一個"tgid"的參數。"tgid"是目標線程所在進程的PID,它可以用來防止向錯誤的線程發送信號。
發送方給目標線程發送信號時,可能目標線程已經因為某種原因退出了,按照Linux中PID的分配規則,退出線程/進程的PID可被分配給其他的線程/進程使用。這種情況下,如果使用tkill(),就可能出現將信號發送到不相干的線程上。引入"tgid"可以幫助進行目標線程所在進程的校驗,這樣出現錯發的可能性就被大大地降低了。
線程對信號的接收根據POSIX標準的定義,進程內的所有線程共享進程的信號處理函數,當進程內的一個線程為某個信號注冊了處理函數,另一個線程可以更改這個處理函數。在Linux的實現中,線程作為獨立的調度實體也有自己的task_struct,同一進程的不同線程的task_struct的"sighand"將指向同一個包含信號處理函數列表的sighand_struct。
但是,每個線程可以有單獨的pending位圖/隊列和block位圖。如果一個信號是發送給線程的,那么內核在遞送該信號時,會將它放入線程私有的pending位圖/隊列中,之后根據目標線程的block位圖的設置,直接由目標線程處理就可以了。
如果信號是發送給一個進程的,那么該信號在遞送時將被內核放入進程的pending位圖/隊列中,由進程內的所有線程共享。接下來,內核會從進程的各個線程中,挑選一個block位圖中沒有屏蔽該信號的線程,來執行對應的信號處理函數,其中,進程的主線程將被內核優先選擇。
但是有一些信號是需要進程內的全體線程都做出響應的,比如前面提到的令進程聞風喪膽的SIGKILL,它一旦到來,就不會留下一個活口。
當一個線程即將被內核調度執行,而該線程私有的penging位圖/隊列和所在進程共享的penging位圖/隊列上都有待處理的信號時,內核將優先向線程遞送私有的penging位圖/隊列上的信號:
int作為Unix時代的產物,前面講到的sigprocmask()函數最初是用來設置進程的block位圖的,但是同pending位圖/隊列不同的是,并沒有一個所謂的線程共享的block位圖的概念,所以在多線程環境下,sigprocmask()的語義也就變成了設置線程的block位圖。當然,為了語義更加明顯,你可以使用POSIX線程庫提供的pthread_procmask()函數,兩者的參數和行為都是一樣的。
參考:
- 《Linux環境編程:從應用到內核》第6章
- Understanding the Linux Kernel 第3版第11章
- UNIX Internals: The New Frontiers 第4章
- http://kernel.meizu.com/linux-signal.html
- https://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html?ca=drs-
原創文章,轉載請注明出處。
總結
以上是生活随笔為你收集整理的linux 信号_Linux中的信号处理机制 [四]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国住房公积金制度到了该废除的时候了?
- 下一篇: 我国粮食总库存又多少,14亿人够吃多久?