前情回顧:
通過系統調用進入內核空間的這個蟲洞我終於弄清楚了,可我的冒險還要繼續······詳情參見:內核地址空間大冒險:系統調用
除0錯誤
我是一個線程,出生在Linux帝國,今天我的任務是去執行一段人類用C語言編寫的代碼。
開始的工作很順利,一共執行了18次系統調用,對於來往於用戶空間與內核空間的那個蟲洞我已經輕車熟路,再也不是萌新一枚。
后來,我拿到了一段數學運算的代碼,來來往往地奔波於內存與寄存器之間,把我累得夠嗆,熱的滿頭大汗,電腦風扇都轉的飛快給我降溫。
沒多久,一條除法指令擺在我的面前,我瞟了一眼除數居然是0,一種不好的預感涌上心頭。沒有辦法,硬着頭皮也得上啊,准備開始執行這個除法。
突然!眼前閃過一道白光,然后變得漆黑,這不是執行系統調用的蟲洞嗎?可是我並沒有執行系統調用啊,怎么跑到這里來了。
我心里開始犯嘀咕,打算等會問問原來的白胡子老頭究竟是怎么回事。
不久,光亮開始出現,來到了一個陌生的地方,白霧茫茫。
繼續前行,霧逐漸散去,一座大門出現在我面前,我定睛一看,上面寫着:0:divide_error。
除法錯誤?我越發的緊張起來,這是到哪里了?
中斷&異常
“年輕人,歡迎來到內核地址空間”,熟悉的問候語響起,走過來一位白發老頭,卻不是我在系統調用時見過的那位,拄着一根木棍,掛着一只葫蘆,看起來年紀比系統調用那個老頭還要大一些。
“敢問老先生,我怎么到這里來了,我並沒有執行系統調用啊”,我向老頭打聽情況。
“這里並不是系統調用的入口,因為你執行了除數為0的除法,觸發了異常,所以來到了這里”,老頭說完喝了一口葫蘆里的酒。
“異常,這又是什么意思?”,今天又聽到一個新的名詞。
只見老頭木棍一揮,大霧完全散去,我這才注意到,這里還有好多大門,它們一個挨着一個,形成了一面門牆。
“老先生,這些都是什么啊,這到底是什么地方?”,我對眼前的景象感到越發的好奇。
“這里是中斷描述符表——IDT,是所有中斷和異常發生時,你們會來到的地方”,老頭用了一堆我不懂的話來回答我。
“中斷又是什么?和異常又是什么關系?IDT又是做什么的?”,我向老頭發出了靈魂三問。
“中斷就是有重要的事情發生,要打斷你們線程手頭的工作,讓出CPU必須去處理”
“什么事情,這么重要?”
“比如說有鍵盤按鍵被按下,鼠標被移動或點擊,網絡中有數據包到來等等情況”。
“那異常呢?”
“異常就是你們這些線程在執行代碼指令的時候出現了一些錯誤的情況,比如做除法的時候除數為0,又比如訪問的內存地址錯誤等這些情況,那遇到這些情況怎么辦呢?CPU會發現有問題,強制改變你們的執行流,去處理這些異常”。
“聽起來,跟中斷差不多嘛!“
“確實差不多,所以它們都用IDT來一起記錄嘛!不過實際上差別還是很大的哦。最大的區別在於中斷是異步,而異常是同步的!“
“這是為什么?”
“因為中斷什么時候來你是不知道的,你是被迫被打斷的,而異常是你們執行指令主動造成的”
“那IDT又是做什么的?”
“剛才我不是說發生中斷和異常你們就會被打斷嘛!那打斷后該去那里呢?IDT就是把所有中斷和異常發生后要去的地方記錄成了一個表,也就是你眼前所看到的這一面門牆了,總共256扇門,你現在觸發的是除0錯誤,該抓緊時間去0號門里去處理異常了!”
信號投遞
拜別老先生,我走進divide_error這扇門,接着又穿過了common_exception和do_divide_error,
越往前走,周遭越發的陰森,直到來到了force_sig_info,我停下了腳步,想找人打聽下情況。
正在這時,從force_sig_info里面走出一人,瞧着年紀長我幾歲。
“大哥,前面是什么地界,為何這般陰森”,我上前請教。
“前面就是給你所屬進程投遞信號的地方了,你是准備去投遞什么信號?”
“信號,什么信號?”我不太聽得懂大哥的話。
“就是你手里的第一個參數,讓我看一下。咦,是個SIGFPE信號,你是遇到除數是0的除法了嗎?”大哥居然看出了我的來歷。
“不錯,我確實是因為除了一下0才來到這里的,不知大哥是如何得知的?”
“因為你手里是SIGFPE,這是在數學運算出錯時才會給進程發送的信號,而通常情況下都是除法除以0時候發生,所以我才猜中的。”
“大哥,您口中一直所說的信號,到底是個什么意思?”
“這個信號就是Signal,用來告訴進程有事情發生了。比如常用的CTRL+C進程就是發送SIGINT信號,kill殺進程就是SIGTERM信號,你現在手里的SIGFPE就是表示有數學運算錯誤。總而言之,這就是個通知而已”
“那這通知發送到了哪里呢?又是什么時候去處理呢?”,我有些好奇。
“這就先不告訴你了,等會你自己去就知道了,快去吧,再見了”,大哥揮手離開。
歇息過后便又起身繼續前行,進入force_sig_info后,又先后跨過幾個函數來到send_signal,我看到我所屬進程的task_struct中有一個專門存放信號的隊列,原來信號是放在了這里。
我准備了一個信號對象加入到了進程的信號隊列中,大功告成,准備返回。
返回前夕
很快回到了見到白發老頭的地方,我一下難住了,我是通過異常這個蟲洞來到這里的,現在我該回哪里去呢?
“年輕人,事情都忙完了?”,老頭又一次出現了。
“老先生,嗯,我都忙完了,可是我現在該怎么回去呢?”
“你現在看看你的內核堆棧上面存了什么?”
我低頭看了一眼我的內核堆棧,發現上面居然保存了除0指令之后那條指令的地址,這正是我要回去的地方。
“這是什么時候存進去的,我不記得我執行過push保存啊”
“在你剛來到這里的時候就存進去了,確實不是你push進去的,而是當你通過異常這個蟲洞進入內核空間時,CPU自動完成的”
“原來如此,我知道我要去哪里了,可是我該怎么打開蟲洞回去呢?”
“你看前面,有一條iret指令,通過它,你就能開啟蟲洞之門,回到用戶態空間了”,老頭向我指了指方向。
“ret指令我倒是經常執行,就是函數返回嘛,這個iret是什么,能有這么強大能力?”
“iret就是interrupt return的意思,專門用於被中斷或異常打斷的線程處理完畢后返回用戶空間使用的。”
“明白了,感謝老先生,我就先告辭了,下次再見”,再次向老頭拜別,准備回到我原來的地方,來這里太久了,都有點想念了。
“等一下,少年,你現在還不能回去”,老頭攔下了我。
“不能回去?為什么?”
“回去之前還有件事要去處理哦!”
“到底是什么事情啊?”
“你所在的進程有信號來了,需要先去處理!”
“納尼?那信號是我放的啊?”,我回頭一看,老先生竟然已經走遠。
“別走啊,老先生請留步......”
未完待續·······
彩蛋
就在我准備起身去處理信號的時候,一道黑影從我眼前掠過,我抬頭一看,IDT門牆的20號門有過絲絲晃動,仔細一瞧,這門上的地址和我來時所見似有不同······
欲知后事如何,請關注后續精彩......
精彩回顧: