如何記錄鍵盤SIGQUIT次數

Unix信號

在計算機科學中,信號(英語:Signals)是Unix、類Unix以及其他POSIX兼容的操作系統中進程間通訊的一種有限制的方式。它是一種異步的通知機制,用來提醒進程一個事件已經發生。當一個信號發送給一個進程,操作系統中斷了進程正常的控制流程,此時,任何非原子操作都將被中斷。如果進程定義了信號的處理函數,那麼它將被執行,否則就執行默認的處理函數。

信號類似於中斷,不同之處在於中斷由處理器調解並由內核處理,而信號由內核調解(可能通過系統調用)並由進程處理。內核可以將中斷作爲信號傳遞給導致中斷的進程(典型的例子有SIGSEGV、SIGBUS、SIGILL和SIGFPE)。

發送信號

在一個運行的程序的控制終端鍵入特定的組合鍵可以向它發送某些信號:

  • Ctrl-C發送INT信號(SIGINT);默認情況下,這會導致進程終止。
  • Ctrl-Z發送TSTP信號(SIGTSTP);默認情況下,這會導致進程掛起。
  • Ctrl-\發送QUIT信號(SIGQUIT);默認情況下,這會導致進程終止並且將內存中的信息轉儲到硬盤(核心轉儲)。

kill()系統調用會在權限允許的情況下向進程發送特定的信號,類似地,kill命令允許用戶向進程發送信號。raise(int signal)庫函數可以將特定信號發送給當前進程。

關於SIGQUIT的官網文檔解釋

文檔在這裏https://developer.apple.com/library/archive/technotes/tn2151/_index.html,搜索SIGQUIT即可快速定位到。翻譯大概是這樣的。

該進程在另一個具有管理該進程生命週期特權的進程請求下停止的。SIGQUIT不意味着該進程已崩潰,但是它確實被檢測出來發生錯誤了。

在iOS中,如果鍵盤加載時間過長,宿主進程會給鍵盤擴展發送SIGQUIT信號。崩潰日誌中顯示的調用棧回溯信息沒有太多的用途。最有可能發生SIGQUIT的原因是,鍵盤擴展的啓動過程的代碼花費了很長時間才能完成,但是確實在時間限制內完成了,但當SIGQUIT發出SIGQUIT信號時,代碼已經執行到日誌中顯示的調用棧回溯信息中了。您應該對鍵盤擴展進行分析,以更好的瞭解鍵盤啓動時都做了些什麼事情,然後將一些不必要的事情移到子線程或者推遲到鍵盤加載完畢之後再執行。

處理信號

信號處理函數可以通過signal()系統調用來設置。如果沒有爲一個信號設置對應的處理函數,就會使用默認的處理函數,否則信號就被進程截獲並調用相應的處理函數。在沒有處理函數的情況下,程序可以指定兩種行爲:忽略這個信號(SIG_IGN)或者用默認的處理函數(SIG_DFL)。但是有兩個信號是無法被截獲並處理的:SIGKILL和SIGSTOP。

風險

因爲競態條件的存在,信號的處理是有弱點的。因爲信號是異步的,所以在處理一個信號的過程中,進程可能收到另一個信號(甚至是相同的信號)。sigprocmask()系統調用可以用來阻塞和恢復信號的傳遞。信號可以造成進程中系統調用的中斷,並在信號處理完後重新開始未完成的系統調用。信號處理函數應該沒有任何不想要的副作用,比如,errno的改變、信號掩碼的改變、信號處理方法的改變,以及其他全局進程性質的改變。在信號處理函數內使用不可重入函數,如mallocprintf,也是不安全的。

測試iOS鍵盤的SIGQUIT信號處理機制是SIG_IGN還是SIG_DFL

上面我們說到:在沒有處理函數的情況下,程序可以指定兩種行爲:忽略這個信號(SIG_IGN)或者用默認的處理函數(SIG_DFL)。那我們來測試iOS鍵盤的SIGQUIT信號處理機制是SIG_IGN還是SIG_DFL

測試方案:新建一個空的鍵盤項目,我們在ViewDidLoad函數中,隨機做一些時長爲0ms~3000ms的耗時操作。同時在+(void)load函數中進行打印。在iOS11設備上進行測試。測試流程爲頻繁的調起鍵盤並收起鍵盤,最終通過log查看結果。

經測試在已有的三類設備中:iOS11設備復現SIGQUIT的機率較高,iOS9和iOS13復現的機率極低。

當我們不做任何signal處理時

默認	10:55:16.767137+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8239
默認	10:55:22.399583+0800	Keyboard	----- SGIDebug viewDidLoad time : 594.86ms processID:8239
默認	10:55:22.589510+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8241
默認	10:55:30.203572+0800	Keyboard	----- SGIDebug viewDidLoad time : 1762.28ms processID:8241
默認	10:55:32.229868+0800	Keyboard	----- SGIDebug viewDidLoad time : 1898.79ms processID:8241
默認	10:55:33.230580+0800	Keyboard	----- SGIDebug viewDidLoad time : 409.31ms processID:8241
默認	10:55:34.979780+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8243
默認	10:55:43.241684+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8244
默認	10:55:46.390089+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8246
默認	10:55:56.152045+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8247
默認	10:55:59.780951+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8249
默認	10:56:04.449111+0800	Keyboard	----- SGIDebug viewDidLoad time : 1887.10ms processID:8249
默認	10:56:08.151585+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8250
默認	10:56:15.609184+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8252
默認	10:56:18.662599+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8253
默認	10:56:23.697057+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8255
默認	10:56:26.876691+0800	Keyboard	----- SGIDebug viewDidLoad time : 1146.67ms processID:8255

我們可以看到,很多關於是+(void)load的log,同時進程的pid發生變化,說明進程重啓了。然後我們再通過系統設置-隱私-分析數據中查看,發現了10個關於SIGQUIT的崩潰日誌,正好對應後10次的+(void)load的log。

當我們設置signal(SIGQUIT, SIG_DFL)

默認	11:04:36.417079+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8351
默認	11:04:39.071723+0800	Keyboard	----- SGIDebug viewDidLoad time : 956.85ms processID:8351
默認	11:04:41.899271+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8391
默認	11:04:45.559485+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8392
默認	11:04:47.438328+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8393
默認	11:04:49.725146+0800	Keyboard	----- SGIDebug viewDidLoad time : 1334.17ms processID:8393
默認	11:04:51.523124+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8395
默認	11:04:57.324517+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8397
默認	11:05:01.870287+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8398
默認	11:05:04.226078+0800	Keyboard	----- SGIDebug viewDidLoad time : 328.21ms processID:8398
默認	11:05:07.329288+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8400
默認	11:05:09.780019+0800	Keyboard	----- SGIDebug viewDidLoad time : 473.52ms processID:8400
默認	11:05:16.478241+0800	Keyboard	----- SGIDebug viewDidLoad time : 1075.12ms processID:8400
默認	11:05:20.206613+0800	Keyboard	----- SGIDebug viewDidLoad time : 1962.51ms processID:8400
默認	11:05:20.704045+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8402
默認	11:05:27.016272+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8403
默認	11:05:30.646437+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8404

我們發現日誌與不做任何signal處理時的日誌打印基本一致。同樣在數據分析中也找到了10條SIGQUIT的崩潰日誌。

當我們設置signal(SIGQUIT, SIG_IGN)

默認	11:09:46.018568+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8536
默認	11:09:51.788130+0800	Keyboard	----- SGIDebug viewDidLoad time : 1844.02ms processID:8536
默認	11:09:52.554243+0800	Keyboard	----- SGIDebug viewDidLoad time : 761.81ms processID:8536
默認	11:09:55.039405+0800	Keyboard	----- SGIDebug viewDidLoad time : 2263.81ms processID:8536
默認	11:09:56.335422+0800	Keyboard	----- SGIDebug viewDidLoad time : 1068.47ms processID:8536
默認	11:09:57.533077+0800	Keyboard	----- SGIDebug viewDidLoad time : 1194.43ms processID:8536
默認	11:10:03.510833+0800	Keyboard	----- SGIDebug viewDidLoad time : 1017.10ms processID:8536
默認	11:10:04.666973+0800	Keyboard	----- SGIDebug viewDidLoad time : 799.31ms processID:8536
默認	11:10:07.115586+0800	Keyboard	----- SGIDebug viewDidLoad time : 1961.38ms processID:8536
默認	11:10:08.250567+0800	Keyboard	----- SGIDebug viewDidLoad time : 1131.50ms processID:8536
默認	11:10:11.414621+0800	Keyboard	----- SGIDebug viewDidLoad time : 1123.73ms processID:8536
默認	11:10:14.375550+0800	Keyboard	----- SGIDebug viewDidLoad time : 2107.79ms processID:8536
默認	11:10:15.833425+0800	Keyboard	----- SGIDebug viewDidLoad time : 738.65ms processID:8536
默認	11:10:17.686347+0800	Keyboard	----- SGIDebug viewDidLoad time : 1864.35ms processID:8536
默認	11:10:18.873011+0800	Keyboard	----- SGIDebug viewDidLoad time : 875.18ms processID:8536
默認	11:10:20.903778+0800	Keyboard	----- SGIDebug viewDidLoad time : 1565.67ms processID:8536
默認	11:10:22.704031+0800	Keyboard	----- SGIDebug viewDidLoad time : 675.52ms processID:8536
默認	11:10:25.583807+0800	Keyboard	----- SGIDebug viewDidLoad time : 1263.46ms processID:8536
默認	11:10:29.533700+0800	Keyboard	----- SGIDebug viewDidLoad time : 2070.15ms processID:8536
默認	11:10:32.017082+0800	Keyboard	----- SGIDebug viewDidLoad time : 1547.02ms processID:8536
默認	11:10:34.044078+0800	Keyboard	----- SGIDebug viewDidLoad time : 2024.01ms processID:8536
默認	11:10:37.525322+0800	Keyboard	----- SGIDebug viewDidLoad time : 1290.21ms processID:8536
默認	11:10:39.553857+0800	Keyboard	----- SGIDebug viewDidLoad time : 1747.27ms processID:8536

從執行過程中的現象來看,我們觀察到鍵盤會被切換成系統鍵盤。但是log中,我們沒有發現任何進程重啓的現象。同樣在分析數據中,沒有任何SIGQUIT的崩潰日誌。

當我們設置signal(SIGQUIT, signalQuitHandler)

void signalQuitHandler(int sig)
{
    NSLog(@"----- SGIDebug %s processID:%d", __FUNCTION__, [NSProcessInfo processInfo].processIdentifier);
}

當我們設置爲自定義函數時,我們來看看log是什麼樣的。

默認	11:15:44.751765+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:8928
默認	11:15:48.469873+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:48.470165+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:48.697348+0800	Keyboard	----- SGIDebug viewDidLoad time : 1295.26ms processID:8928
默認	11:15:49.236325+0800	Keyboard	----- SGIDebug viewDidLoad time : 531.80ms processID:8928
默認	11:15:50.643042+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:50.643121+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:51.772356+0800	Keyboard	----- SGIDebug viewDidLoad time : 2100.50ms processID:8928
默認	11:15:53.984736+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:53.984787+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:15:54.592767+0800	Keyboard	----- SGIDebug viewDidLoad time : 2128.13ms processID:8928
默認	11:15:57.876701+0800	Keyboard	----- SGIDebug viewDidLoad time : 1378.18ms processID:8928
默認	11:15:59.463721+0800	Keyboard	----- SGIDebug viewDidLoad time : 1018.97ms processID:8928
默認	11:16:01.940692+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:01.940831+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:02.344180+0800	Keyboard	----- SGIDebug viewDidLoad time : 2100.48ms processID:8928
默認	11:16:03.536956+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:03.537108+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:04.308509+0800	Keyboard	----- SGIDebug viewDidLoad time : 1961.05ms processID:8928
默認	11:16:07.489481+0800	Keyboard	----- SGIDebug viewDidLoad time : 999.39ms processID:8928
默認	11:16:08.996407+0800	Keyboard	----- SGIDebug viewDidLoad time : 634.44ms processID:8928
默認	11:16:11.203330+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:11.203435+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:11.945976+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:11.946227+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:11.994779+0800	Keyboard	----- SGIDebug viewDidLoad time : 2102.37ms processID:8928
默認	11:16:13.564042+0800	Keyboard	----- SGIDebug viewDidLoad time : 1574.79ms processID:8928
默認	11:16:15.888691+0800	Keyboard	----- SGIDebug viewDidLoad time : 1308.88ms processID:8928
默認	11:16:17.058090+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:17.058131+0800	Keyboard	----- SGIDebug signalQuitHandler processID:8928
默認	11:16:18.114001+0800	Keyboard	----- SGIDebug viewDidLoad time : 1808.26ms processID:8928
默認	11:16:18.987198+0800	Keyboard	----- SGIDebug viewDidLoad time : 869.59ms processID:8928

通過log看,我們確實處理的SIGQUIT,並且鍵盤進程沒有重啓,分析數據中沒有SIGQUIT的崩潰日誌。

以上評測的結論

  • 當我們不做任何signal處理時,鍵盤默認走的是SIG_DFL的邏輯。鍵盤進程會重啓,並且在數據分析中會產生SIGQUIT相關的崩潰日誌,進程重啓的次數與SIGQUIT崩潰的次數一致。
  • 當我們設置SIG_IGN和自定義函數時,鍵盤進程不會重啓,並且在數據分析中不會產生SIGQUIT相關的崩潰日誌。

統計SIGQUIT次數

通過上面的評測,我們瞭解到,如果我們要統計SIGQUIT的次數,就必須要自定義處理信號的函數。而自定義處理信號,就會要忽略掉這個信號,導致鍵盤不會重啓,與鍵盤默認處理信號的邏輯不一致,可能會出現其他問題。

經過測試,我們可以多次調用signal()函數,來設置處理信號的回調函數。那麼我們可以在首個自定義處理信號的函數中進行記錄,並且再將處理信號重新設置回SIG_DFL,這樣下次SIGQUIT時,就可以走正常的流程了。爲了將自定義處理信號的表現與SIG_DFL的表現一致,我們可以在將處理信號設置回SIG_DFL後,手動調用raise(SIGQUIT)來觸發一次SIGQUIT

//  代碼調用處
signal(SIGQUIT, signalQuitHandler);

//  signalQuitHandler函數定義
void signalQuitHandler(int sig)
{
    NSLog(@"----- SGIDebug %s processID:%d", __FUNCTION__, [NSProcessInfo processInfo].processIdentifier);
    signal(SIGQUIT, SIG_DFL);
    raise(SIGQUIT);
}

我們來看下執行結果:

默認	11:34:00.054070+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9068
默認	11:34:03.583420+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9068
默認	11:34:03.955060+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9070
默認	11:34:05.507952+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9070
默認	11:34:05.508435+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9070
默認	11:34:05.664773+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9071
默認	11:34:10.390774+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9071
默認	11:34:10.546643+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9072
默認	11:34:12.549918+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9072
默認	11:34:17.221589+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9073
默認	11:34:21.522573+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9073
默認	11:34:21.970238+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9075
默認	11:34:23.987462+0800	Keyboard	----- SGIDebug viewDidLoad time : 0.08ms processID:9075
默認	11:34:26.458283+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9075
默認	11:34:26.622109+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9076
默認	11:34:30.809283+0800	Keyboard	----- SGIDebug viewDidLoad time : 2086.79ms processID:9076
默認	11:34:34.996783+0800	Keyboard	----- SGIDebug viewDidLoad time : 729.69ms processID:9076
默認	11:34:37.540586+0800	Keyboard	----- SGIDebug viewDidLoad time : 1205.70ms processID:9076
默認	11:34:39.438812+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9076
默認	11:34:39.851161+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9078
默認	11:34:42.935266+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9078
默認	11:34:43.097889+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9079
默認	11:35:01.570907+0800	Keyboard	----- SGIDebug viewDidLoad time : 2068.85ms processID:9079
默認	11:35:04.184268+0800	Keyboard	----- SGIDebug viewDidLoad time : 1051.43ms processID:9079
默認	11:35:07.248899+0800	Keyboard	----- SGIDebug viewDidLoad time : 1810.91ms processID:9079
默認	11:35:08.981190+0800	Keyboard	----- SGIDebug viewDidLoad time : 767.95ms processID:9079
默認	11:35:09.025391+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9079
默認	11:35:09.115436+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9082
默認	11:35:12.548235+0800	Keyboard	----- SGIDebug signalQuitHandler processID:9082
默認	11:35:18.919975+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:9083

不難看出,這個如果去掉signalQuitHandler的log,其餘的log與SIG_DFL的log一致,且分析數據中的崩潰日誌數量,也可以跟+(void)load的數量對的上。所以如果我們需要統計SIGQUIT的次數,我們需要先自定義信號的處理函數,在處理函數裏面記錄SIGQUIT的次數,並在處理函數裏面通過singal(SIGQUIT, SIG_DFL)重新將處理函數設置爲默認函數,然後手動調用raise(SIGQUIT)來觸發SIGQUIT即可。

通過fishhook對raise()函數進行hook,同時不設置任何信號處理,得到的log。

默認	15:24:07.996198+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10847
默認	15:24:29.008861+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10871
默認	15:24:33.625747+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10872
默認	15:24:35.512911+0800	Keyboard	----- SGIDebug my_raise signal:3 processID:10872
默認	15:24:39.616856+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10873
默認	15:24:39.904361+0800	Keyboard	----- SGIDebug my_raise signal:3 processID:10873
默認	15:24:43.556727+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10875
默認	15:24:52.592057+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10877
默認	15:24:55.932611+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10878
默認	15:25:00.866935+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10879
默認	15:25:01.325544+0800	Keyboard	----- SGIDebug my_raise signal:3 processID:10879
默認	15:25:07.464576+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10880
默認	15:25:12.994054+0800	Keyboard	----- SGIDebug +[KeyboardViewController load] processID:10882

從log中,不難看出,系統在某些時刻,也是通過raise()函數來發SIGQUIT的。這些時刻基本上都是手動把輸入法從系統鍵盤切換到我們開發的鍵盤時出現的。也就是當我們自身的調起時間較長時,鍵盤本身會調用raise()來發送SIGQUIT信號。所以我們自身在程序中通過raise()函數來發SIGQUIT信號,應該也是沒有問題的。

在越獄的機器上驗證,通過命令行kill -SIGQUIT 鍵盤pid可以給鍵盤發送SIGQUIT信號。比較遺憾的是,沒有找到宿主進程是如何與鍵盤通訊,發送SIGQUIT信號給鍵盤的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章