核心執行緒中獲取接收到的訊號

2021-12-30 00:11:58 字數 2838 閱讀 2633

在測試開發的核心模組時,發現了乙個bug:在模組沒有解除安裝時使用reboot命令重啟系統的話,系統重啟不了,檢視日誌發現在建立的核心執行緒中陷入了死迴圈,導致系統無法重啟。檢查了**,發現產生問題的原因是當系統呼叫返回-eintr(也就是被訊號中斷),核心執行緒中的迴圈沒有退出,而是繼續迴圈操作,這個邏輯跟業務是相符合的並沒有錯誤。問題就在於沒有檢查接收到的是什麼訊號,如果是在系統重啟時傳送的訊號或者執行關機時傳送的訊號,應該退出迴圈。剩下的就是找到在核心執行緒中獲取接收的訊號的方法。

在使用者態獲取阻塞的訊號,呼叫的就是sigpending(),因此首先嘗試呼叫sys_sigpending()來獲取。sys_sigpending()作為系統呼叫是沒有匯出的,因此不能直接呼叫,但是可以通過/proc/kallsyms檔案來獲取sys_sigpending()的位址來呼叫這個函式。在我的測試機上,sys_sigpending()的位址為0xffffffff810802e0。測試**如下所示:  

[cpp] view plaincopy

/*  * fcluster.c 

*/  

#include  

#include  

#include  

#include  

#include  

#include  

static int remove_mod = 0;  

static int my_sigpending(sigset_t *set)  

static int thread_process(void *arg)  

}  return 0;  

}    

static int __init fcluster_init(void)  

static void __exit fcluster_exit(void)  

module_license("gpl");  

module_init(fcluster_init);  

module_exit(fcluster_exit);  

核心執行緒如果想接收使用者終端傳送的訊號,必須在處理函式中呼叫allow_signal()來指定允許接收哪些訊號。my_sigpending()是對sys_sigpending()的簡單封裝,用來獲取當前核心執行緒阻塞的訊號。

將上面的**編譯成核心模組,插入到系統中,開啟系統日誌,檢視建立的核心執行緒的id(我的是3278,如下圖所示),然後在另乙個終端中使用kill命令給建立的核心執行緒傳送sigterm命令。測試結果要通過系統日誌檔案(/var/log/messages)來檢視,如下圖所示:  

檢視系統日誌發現獲取到的訊號位圖竟然是0!不可能啊,因為從上面的**中可以看出只有在signal_pending()函式返回true的情況下(也就是接收到訊號時),才能輸出上圖中的日誌資訊。**很簡單,關鍵的函式就是my_sigpending(),該函式只是對sys_sigpending()進行了簡單的封裝,

因此還是要從sys_sigpending()的實現中查詢原因。

檢視sys_sigpending()的原始碼,只是對do_sigpending()函式的簡單封裝,繼續從do_sigpending()中找原因。do_sigpending()原始碼如下:  

[cpp] view plaincopy

long do_sigpending(void __user *set, unsigned long sigsetsize)  

do_sigpending()首先呼叫sigorsets()將當前程序的訊號資訊(current->pending.signal和current->signal->shared_pending.signal)進行或操作(也就是將兩個地方的訊號掩碼合併起來),儲存在臨時變數pending中。獲取當前程序的訊號掩碼後,在傳遞到上層時,還要呼叫sigandsets()將pending和當前程序的訊號掩碼進行與操作後的結果就再傳遞到上層。

現在的問題就是要確定在呼叫sigandsets()之前pending的值和current->blocked的值。根據do_sigpending()來修改thread_process()函式,列印輸出當前程序的訊號點陣圖和訊號掩碼,修改後的thread_process()函式如下所示:  

[cpp] view plaincopy

static int thread_process(void *arg)  

}  return 0;  

}  測試結果如下所示:  

從藍色的部分可以看出,current->blocked為0,也就是說當前核心執行緒的訊號掩碼為0,所以在do_sigpending()中呼叫sigandsets()來將當前程序的的訊號點陣圖和掩碼執行與操作的結果總是0。

從測試結果來看呼叫sys_sigpending()來獲取核心執行緒的方法不行,獲取核心執行緒接收的訊號可以通過將current->pending.signal和current->signal->shared_pending.signal中的訊號合併得到。至此,我們解決了第乙個問題,如何獲取核心執行緒接收到的訊號。

接下來解決另乙個問題,就是要獲取系統重啟時核心執行緒接收到的訊號。reboot之後系統會重啟,重啟之後模組中輸出的資訊沒有儲存在系統日誌/var/log/messages中,需要想別的辦法來拿到重啟時模組輸出的日誌資訊。google了一番沒有什麼收穫,最後想到使用kdump+crash來拿到重啟時系統日誌資訊。kdump可以在核心崩潰時轉儲生成core檔案,crash用來分析生成的core檔案,在crash中使用dmesg命令可以看到系統崩潰時的日誌資訊。所以如果在我們的核心執行緒接收到訊號,列印完日誌資訊後讓核心崩潰就可以看到我們輸出的日誌資訊了。讓核心崩潰很簡單,使用bug()巨集或直接呼叫panic()函式。修改測試thread_process()函式,將if分支中的」break;「語句替換為bug()或panic(),**就沒必要貼了,直接上測試結果:  

底層由於接收到作業系統的訊號而停止

問題 除錯時出現核心段錯誤 分析方向 1.stack overflow 2.野指標 分析過程 1.錯誤現場 底層由於接收到作業系統的訊號而停止 訊號名稱 sigsegv 訊號含義 segmentation fault 2.進入除錯模式定位問題 case 0x80 noasdu apdu.noasdu...

底層由於接收到作業系統的訊號而停止

除錯qt程式的時候,出現 底層由於接收到作業系統的訊號而停止 訊號名稱 sigsegv 訊號意義 segmentation fault 這類現象都是因為記憶體錯誤導致的。分析一 記憶體未申請成功,卻使用了它。對策 在使用指標前,用q assert p 或者 q check ptr p 若p為null...

JSP中遍歷顯示從Action中接收到的list

假設在action中有乙個user型別的list userlist 我需要在jsp中遍歷顯示list中的user資訊,示例 如下 其中,id user user為遍歷後物件的別名,名稱無限制 value userlist userlist為遍歷物件在action中的名字,需要與action中的名稱保...