日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
為什么Ctrl-C會中斷當前運行程序

0x01 問題演示

站在用戶的角度思考問題,與客戶深入溝通,找到河曲網(wǎng)站設計與河曲網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設計與互聯(lián)網(wǎng)技術結合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都網(wǎng)站設計、網(wǎng)站制作、外貿營銷網(wǎng)站建設、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)絡空間、企業(yè)郵箱。業(yè)務覆蓋河曲地區(qū)。

下面是用rust寫的一段測試程序,邏輯非常簡單,就是讀取用戶輸入,然后將其輸出。

圖片

運行這個程序,然后按Ctrl-C:

圖片

由上圖可見,該程序沒有收到任何輸入,當然也沒有任何輸出,這個程序就退出了。

為什么Ctrl-C會導致當前運行程序退出呢?

0x02 程序退出原因

上面的測試程序之所以會退出,是因為Ctrl-C會告訴linux內核,讓其發(fā)送SIGINT信號給當前運行程序,該信號的默認行為是殺掉目標進程,所以就有了上面的現(xiàn)象。

但是,SIGINT信號,以及其他的各種信號,都是可以捕獲的,這樣我們就可以修改信號的默認行為,比如將SIGINT信號的默認行為,修改成輸出一些日志,而不是殺掉當前進程。

將上面的測試代碼改成下面的樣子:

圖片

上圖中我們捕獲了SIGINT信號,并且在收到該信號后,輸出 got SIGINT。

運行看下:

圖片

這次再按Ctrl-C,程序就不會退出了,而且還會輸出 got SIGINT。

由上可見,Ctrl-C導致程序退出,確實是因為該按鍵使內核發(fā)送了SIGINT信號到目標進程,進而導致目標進程被殺死。

那為什么Ctrl-C會觸發(fā)SIGINT信號呢?

在回答這個問題之前,我們要先了解下terminal emulator,即終端模擬器,的運行機制。

0x03 Terminal Emulator的運行機制

我當前使用的terminal emulator為            alacritty,后面如果涉及到terminal emulator的源碼分析,就是基于這個項目。

當然,以下講的terminal emulator的運行機制,對于其他terminal emulator也同樣適用。

當我們在圖形化界面,打開一個terminal emulator時,terminal emulator會調用openpty函數(shù),向linux內核申請一個pty數(shù)據(jù)通道,當該pty數(shù)據(jù)通道創(chuàng)建成功后,linux內核會返回兩個文件描述符,即兩個fd,給terminal            emulator,這兩個fd,就代表了新創(chuàng)建pty數(shù)據(jù)通道的兩端,分別為master端和slave端。

當向master端的fd寫數(shù)據(jù)時,該數(shù)據(jù)就可以從slave端的fd讀出來,當向slave端的fd寫數(shù)據(jù)時,該數(shù)據(jù)就能從master端的fd讀出來。

當pty數(shù)據(jù)通道創(chuàng)建完畢后,terminalemulator就會調用fork函數(shù),啟動一個子進程,該子進程用來運行shell程序,比如bash、zsh等,同時會將該shell的標準輸入,標準輸出,標準錯誤輸出,都設置為上面通過openpty函數(shù)獲取的slave端的fd。

shell在啟動成功后,會一直等待著從標準輸入,即slave fd,里接收要執(zhí)行的命令。

當terminal emulator啟動成功后,我們就可以在其內部輸入命令了,我們輸入的命令,會被terminal emulator寫入到master fd里,這樣在shell子進程中,就可以通過標準輸入,即slave fd,接收到這個命令,并開始執(zhí)行。

shell在執(zhí)行接收到的命令時,也是通過fork函數(shù),創(chuàng)建一個子進程,然后在子進程里執(zhí)行該命令對應的程序。

不過,這里需要注意的是,子進程中運行的命令程序,其標準輸入,標準輸出、標準錯誤輸出都是繼承自shell,即它的標準輸入,標準輸出、標準錯誤輸出的值都是slave fd。

這樣,當命令程序寫日志到標準輸出時,其實際上是寫到了slave fd里,如此,在terminal emulator里,就可以通過master fd,讀取到這些日志信息,并在terminal emulator里顯示出來。

同樣的道理,當我們此時在terminal emulator里輸入內容時,該內容會被terminal emulator寫入到master fd,進而就可以被命令程序進程,從標準輸入,即slave fd,里讀出來。

這里大家可能會有個疑問,即shell進程和當前運行的命令程序進程,他們的標準輸入都是slave fd,那為什么我們寫入到terminal emulator里的內容,是被命令程序進程讀出來,而不是被shell進程讀出來呢?

這個就涉及到terminal emulator使用權的概念了。

當shell進程剛啟動成功后,terminal emulator的使用權自然是shell的,此時我們在terminal emulator里輸入的內容,會被shell從標準輸入,即slave fd,里讀出來。

當shell啟動一個子進程,并用該進程運行命令程序時,它會把terminal emulator的使用權,轉交給該命令程序進程,此時我們在terminal emulator里輸入的內容,會被該命令程序進程從它的標準輸入,即slave fd,里讀出來的。

當該命令程序進程退出后,linux內核會通知shell進程,告知它啟動的子命令進程已經(jīng)結束了,此時shell會把terminal emulator的使用權轉回給自己,進而shell又可以開始從terminal emulator接收新的命令了。

下面我們來看一些具體的例子:

圖片

上圖是用ps命令輸出的信息,看圖中的選中行,第一行為alacritty進程,即我們最開始啟動的terminal            emulator,第二行為alacritty啟動的子進程,在該子進程中,運行的是bash程序,第三行為bash啟動的子進程,在該子進程中,運行的是我們文章最開始時使用的測試程序hello。

這三個進程的層級關系,和我們上面的描述是一致的。

我們再來看下alacritty啟動bash子進程的相關代碼:

圖片

上圖中make_pty函數(shù)內會調用openpty函數(shù)獲取master fd和slave fd,在獲取到master fd和slave fd后,slave fd被賦值到builder的stdin           stderr里,這樣,在下面執(zhí)行builder.spawn函數(shù)啟動shell子進程時,其標準輸入、標準輸入、標準錯誤輸出就都指向slave fd了。, stdout,

另外,在上圖中,master fd被保存到了Pty里,并和其他信息一起返回給該函數(shù)的調用方,這樣,alacritty如果想要發(fā)送數(shù)據(jù)給shell時,就從Pty里獲取到master fd,然后將數(shù)據(jù)寫入到master fd里就好了。

0x04 Ctrl-C是如何處理的

上面我們講過,我們在terminal emulator中輸入的內容,會被terminal emulator寫到內核的pty數(shù)據(jù)通道中,進而這些數(shù)據(jù)會被轉發(fā)給shell進程,或者是在shell中運行的子進程。

那在terminal emulator里按Ctrl-C,也是這么處理的嗎?

首先,Ctrl-C確實是被當作一個字符來處理的,且terminal emulator在接收到這個字符后,會直接寫入到內核的pty數(shù)據(jù)通道,并不做特殊處理。

但是,在內核的pty數(shù)據(jù)通道里,有一個組件叫做 line discipline,它會檢查要被傳輸?shù)淖址?,如果字符流中包含Ctrl-C,它就會把Ctrl-C這個特殊字符從字符流中移除掉,并生成一個SIGINT信號,發(fā)送給目標進程。

如果目標進程沒有捕獲該信號,內核就會執(zhí)行該信號的默認行為,即殺掉目標進程。

以下是生成SIGINT信號的內核代碼:

圖片

上圖中,光標所在行就是在判斷該字符是否是Ctrl-C,如果是,則發(fā)送SIGINT信號給目標進程。

由上圖我們還可以看到,其實不止Ctrl-C這個特殊字符會轉化成信號,QUIT字符Ctrl-\,SUSP字符Ctrl-Z等,都會被轉化成對應的信號。

0x05 精致全景圖

以上講了很多理論,下面我們來畫一幅圖,來梳理下完整流程。

圖片

首先,在terminal emulator啟動成功后,我們在其中輸入./hello命令,該命令沿著terminal emulator中的第一個輸出箭頭,即第一條虛線,經(jīng)由內核pty數(shù)據(jù)通道,到達bash進程的stdin。

然后,bash從標準輸入中讀取到要執(zhí)行的命令./hello,fork一個新的子進程,并在子進程中開始執(zhí)行hello程序,此時bash也把terminal emulator的使用權交給了hello進程。

hello程序在開始運行后,就嘗試從標準輸入中讀取數(shù)據(jù)。

接著,我們在terminal emulator中再輸入hello world,該數(shù)據(jù)會沿著terminal emulator中的第二個輸出箭頭,即第一條實線,經(jīng)由pty數(shù)據(jù)通道,到達hello進程的stdin。

hello進程從標準輸入中讀到hello world字符串,然后直接將其寫入到了標準輸出,該數(shù)據(jù)又經(jīng)由內核pty數(shù)據(jù)通道,到達terminal emulator的master fd端。

terminal emulator從master fd中讀取到hello world字符串,并將其顯示在界面中。

hello進程在寫完hello world字符串后,自己主動退出,bash檢測到hello進程退出后,又把terminal emulator的使用權轉回給自己。

bash寫命令提示符 > 到標準輸出,該數(shù)據(jù)再經(jīng)由pty數(shù)據(jù)通道到達terminal emulator的master fd端。

terminal emulator從master fd中讀取到bash的命令提示符,并將其顯示在界面上,提示用戶可以輸入下一條命令了。

以上就是在terminal emulator中執(zhí)行hello程序的完整流程。

從上圖中我們還可以看到,假設我們在terminal emulator中按Ctrl-C,該數(shù)據(jù)在到達內核pty數(shù)據(jù)通道時,line discipline組件會將其轉換成SIGINT信號,并發(fā)給目標進程。

這個也解答了我們此篇文章的疑問,現(xiàn)在你應該豁然開朗了吧。

另外,內核中 line discripline 組件的能力也是可以被修改的,比如我們可以修改成按Ctrl-B觸發(fā)SIGINT信號,甚至是直接關閉SIGINT信號的生成,具體方式,可以查看stty命令的man文檔。

這篇文章就這些內容,希望對你有所幫助。


文章名稱:為什么Ctrl-C會中斷當前運行程序
分享鏈接:http://www.5511xx.com/article/ccdhdji.html