《程序員的自我修養(yǎng)》讀書筆記——系統(tǒng)調(diào)用、API

這一篇介紹一下系統(tǒng)調(diào)用优妙,熟悉一下流程竞穷。很多做客戶端的同學(xué)根本不知道這些內(nèi)容。建議花時(shí)間看看相關(guān)的知識(shí)鳞溉。最好的方式還是去看源碼瘾带,反匯編,才能深刻的理解熟菲。

系統(tǒng)調(diào)用

程序運(yùn)行的時(shí)候看政,本身是沒有權(quán)限訪問多少系統(tǒng)資源的。系統(tǒng)資源有限抄罕,如果操作系統(tǒng)不進(jìn)行控制允蚣,那么各個(gè)程序難免會(huì)產(chǎn)生沖突。線程操作系統(tǒng)都將可能產(chǎn)生沖突的系統(tǒng)資源保護(hù)起來呆贿,阻止程序直接訪問嚷兔。比如文件、網(wǎng)絡(luò)做入、IO冒晰、各種設(shè)備等。

比如無論在Windows還是Linux中竟块,程序員都不能直接去訪問硬盤的某扇區(qū)上的數(shù)據(jù)壶运,必須通過文件系統(tǒng),也不能擅自修改任意文件浪秘。所有這些操作必須經(jīng)過操作系統(tǒng)規(guī)定的方式進(jìn)行蒋情。比如用fopen打開沒有權(quán)限的文件就會(huì)失敗埠况。

比如:想要程序延遲執(zhí)行一段時(shí)間,不借助操作系統(tǒng)就是使用循環(huán)棵癣,這樣會(huì)白白消耗CPU辕翰,造成資源浪費(fèi)。如果使用操作系統(tǒng)提供的定時(shí)器就可以方便有效狈谊。

系統(tǒng)調(diào)用涵蓋的功能很廣金蜀,有程序運(yùn)行鎖必須的支持,如創(chuàng)建和退出進(jìn)程的畴、線程渊抄,進(jìn)程內(nèi)存管理,對(duì)系統(tǒng)資源的訪問等丧裁。

Linux系統(tǒng)調(diào)用

在x86下护桦,系統(tǒng)調(diào)用是通過0x80中斷完成,各個(gè)通用寄存器用于傳遞參數(shù)煎娇。EAX寄存器用于表示系統(tǒng)調(diào)用的接口號(hào)二庵。

比如:EAX=1表示退出進(jìn)程,EAX=2表示創(chuàng)建進(jìn)程缓呛,EAX=3表示讀文件或IO催享,EAX=4表示寫文件或IO。每個(gè)系統(tǒng)調(diào)用對(duì)應(yīng)到內(nèi)核源碼中的一個(gè)函數(shù)哟绊,他們都是以sys_開頭的因妙,比如exit調(diào)用對(duì)應(yīng)內(nèi)核中的sys_exit函數(shù)

Linux內(nèi)核提供了幾百個(gè)系統(tǒng)調(diào)用,下面列舉部分


所以完全可以不使用glibc封裝的fopen票髓、fread攀涵、fclose操作文件,而直接使用系統(tǒng)函數(shù)open,read,close實(shí)現(xiàn)洽沟。

技巧:Linux中可以使用man查看系統(tǒng)調(diào)用詳情以故,使用參數(shù)2表示系統(tǒng)調(diào)用手冊(cè)(比如 man 2 read)

如果直接使系統(tǒng)調(diào)用會(huì)有非常多的問題:

  • 使用不方便,操作系統(tǒng)提供的系統(tǒng)調(diào)用接口往往過于原始裆操。程序員需要了解很多跟操作系統(tǒng)相關(guān)的細(xì)節(jié)
  • 各個(gè)操作系統(tǒng)之間系統(tǒng)調(diào)用不兼容

于是增加一個(gè)層來解決怒详,系統(tǒng)調(diào)用與程序之間增加一個(gè)抽象層。這個(gè)層就是前面所說的glibc踪区,或者API.

系統(tǒng)調(diào)用原理

這里單單以Linux為例昆烁,至于Windows調(diào)用原理暫時(shí)省略。

用戶態(tài)朽缴、內(nèi)核態(tài)及中斷

現(xiàn)代操作系統(tǒng)中有兩種特權(quán)級(jí)別善玫,分為用戶模式和內(nèi)核模式水援。

多個(gè)模式存在密强,那么操作系統(tǒng)就可以讓不同代碼運(yùn)行在不同模式下茅郎,進(jìn)而限制代碼的權(quán)限,提高穩(wěn)定性或渤、安全性系冗。一般普通程序在用戶態(tài),操作會(huì)受到限制薪鹦。系統(tǒng)調(diào)用運(yùn)行在內(nèi)核態(tài)掌敬,應(yīng)用程序基本都是運(yùn)行在用戶態(tài)被限制。

用戶態(tài)的程序通過中斷來運(yùn)行內(nèi)核態(tài)的代碼池磁,進(jìn)而從用戶態(tài)切換到內(nèi)核態(tài)奔害。

中斷

中斷是操作系統(tǒng)的一個(gè)概念。中斷是一個(gè)硬件或者軟件發(fā)出的請(qǐng)求地熄,要求CPU暫停當(dāng)前的工作轉(zhuǎn)手去執(zhí)行更加重要的事情华临。

比如在編輯文本的時(shí)候,敲擊鍵盤那CPU如何得知道呢端考?一種是輪詢雅潭,CPU每隔一小段時(shí)間就去詢問鍵盤是否有鍵按下,但是除非一直打字却特,否則大部分輪訓(xùn)都是得到?jīng)]有鍵按下的結(jié)果扶供。這樣就白白浪費(fèi)掉了很多資源。

另一種方式就是當(dāng)鍵盤按下的時(shí)候裂明,鍵盤芯片發(fā)一個(gè)信號(hào)給CPU椿浓,然后CPU接收到信號(hào)之后就只到鍵盤按下了。然后再去詢鍵盤具體是哪個(gè)鍵被按下闽晦,這樣的信號(hào)就是一種中斷轰绵。——硬件中斷

中斷一般有兩個(gè)屬性尼荆,一個(gè)是中斷號(hào)(從0開始)左腔,一個(gè)稱為中斷處理程序(ISR),不同中斷具有不同的中斷號(hào),一個(gè)中斷號(hào)對(duì)應(yīng)一個(gè)中斷處理程序捅儒。在內(nèi)核中保存了一個(gè)數(shù)組液样,叫做中斷向量表,這個(gè)數(shù)組第n項(xiàng)包含了指向第n個(gè)中斷號(hào)的中斷處理程序的指針巧还。當(dāng)中斷來的時(shí)候鞭莽,CPU就會(huì)暫停當(dāng)前執(zhí)行的代碼,根據(jù)中斷的中斷號(hào)麸祷,在中斷向量表中找到對(duì)應(yīng)的中斷處理程序澎怒,并且調(diào)用他,處理完之后阶牍,CPU繼續(xù)執(zhí)行之前的代碼喷面。

中斷有硬件中斷星瘾,這種來至于硬件的異常或者其他時(shí)間的發(fā)生比如斷電惧辈,鍵盤按下琳状。另一種是軟件中斷,軟件中斷一般是一條指令(i386下是int)盒齿,帶有一個(gè)參數(shù)記錄中斷號(hào)念逞,這個(gè)指令用戶可以手動(dòng)觸發(fā)某個(gè)中斷并執(zhí)行中斷處理程序。比如int 0x80這條指令就會(huì)調(diào)用第0x80號(hào)的中斷處理程序边翁。

中斷號(hào)是有限的翎承,不可能每一個(gè)系統(tǒng)調(diào)用都對(duì)應(yīng)一個(gè)中斷號(hào),更加合理的是用一個(gè)或少數(shù)幾個(gè)中斷號(hào)來對(duì)應(yīng)所有的系統(tǒng)滴啊用符匾。比如Linux中int 0x80來觸發(fā)所有的系統(tǒng)調(diào)用审洞。

系統(tǒng)調(diào)用會(huì)有一個(gè)系統(tǒng)調(diào)用號(hào),表明是哪一個(gè)系統(tǒng)調(diào)用待讳,這個(gè)系統(tǒng)調(diào)用通常就是系統(tǒng)調(diào)用在系統(tǒng)調(diào)用表中的位置芒澜。比如Linux中的fork函數(shù)調(diào)用號(hào)是2,這個(gè)系統(tǒng)調(diào)用號(hào)在執(zhí)行int指令前就會(huì)被放到某個(gè)固定的寄存器中创淡,對(duì)應(yīng)的中斷代碼會(huì)取得這個(gè)系統(tǒng)調(diào)用號(hào)痴晦,并且調(diào)用正確的函數(shù)。

比如Linux中int 0x80為例琳彩,系統(tǒng)調(diào)用號(hào)使用eax來傳入誊酌,用戶將系統(tǒng)調(diào)用號(hào)保存在eax,然后使用int 0x80調(diào)用中斷露乏,中斷服務(wù)傳給信號(hào)就可以從eax取得系統(tǒng)調(diào)用號(hào)碧浊,進(jìn)行調(diào)用相應(yīng)的函數(shù)。

基于int的Linux經(jīng)典系統(tǒng)調(diào)用

下面以fork為例

觸發(fā)中斷

首先當(dāng)程序在代碼里面調(diào)用一個(gè)系統(tǒng)調(diào)用時(shí)瘟仿,是以一個(gè)函數(shù)的形式調(diào)用的箱锐,比如fork:

int main() {
 fork();
}

fork函數(shù)是對(duì)系統(tǒng)調(diào)用fork的封裝,可以用下面的宏定義:

_syscall0(pid_t, fork)

_syscall0是一個(gè)宏劳较,用于定義一個(gè)沒有參數(shù)的系統(tǒng)調(diào)用封裝驹止。他的第一個(gè)參數(shù)為這個(gè)系統(tǒng)調(diào)用的返回值類型,pid_t代表進(jìn)程的id观蜗。第二個(gè)參數(shù)是系統(tǒng)調(diào)用的名字臊恋。_syscall0展開之后 會(huì)形成一個(gè)與系統(tǒng)調(diào)用名稱同名的函數(shù)。下面是i386的syscall0

匯編解釋:

  • __asm__是一個(gè)gcc關(guān)鍵字墓捻,表示接下來要嵌入?yún)R編代碼抖仅,volatile關(guān)鍵字告訴GCC對(duì)這段代碼不進(jìn)行任何優(yōu)化
  • __asm__第一個(gè)參數(shù)是一個(gè)字符串,代表匯編代碼的文本,這里匯編代煤制油int $0x80撤卢,表示要調(diào)用0x80號(hào)中斷
  • =a(__res)表示用eax(a表示eax)輸出返回?cái)?shù)據(jù)并存儲(chǔ)到_res中
  • 0(_NR ##name)表示用_NR ##name為輸入环凿,0指示由編譯器選擇和輸出相同的寄存器(eax)來傳遞參數(shù)。

__syscal_return是另外一個(gè)宏

最終fork函數(shù)匯編之后


當(dāng)系統(tǒng)調(diào)用如果有參數(shù)的話會(huì)用syscall1


相比syscall0多了個(gè)b凸丸,它表示把a(bǔ)rg1強(qiáng)制轉(zhuǎn)換為long拷邢,然后保存在ebx最為輸入袱院。

所以如果系統(tǒng)內(nèi)調(diào)用如果有1個(gè)參數(shù)屎慢,則參數(shù)通過ebx來傳遞。x86下的linux系統(tǒng)調(diào)用參數(shù)最多有6個(gè)忽洛。分別使用6個(gè)寄存器來傳遞腻惠。分別是ebx,ecx欲虚,ed想集灌,esi,edi和ebp复哆。

當(dāng)進(jìn)行系統(tǒng)調(diào)用的時(shí)候欣喧,CPU執(zhí)行到int $0x80時(shí),會(huì)保存現(xiàn)場以便恢復(fù)梯找。接著切換到內(nèi)核態(tài)唆阿,然后CPU查找中斷向量表低0x80元素。

切換堆棧

在實(shí)際執(zhí)行中斷向量表中的第0x80好中斷之前锈锤,CPU還要進(jìn)行堆棧的奇幻驯鳖,在Linx中,用戶態(tài)和內(nèi)核態(tài)使用的是不同的棧久免,兩者各自負(fù)責(zé)各自的函數(shù)調(diào)用浅辙,互不干擾。

在執(zhí)行0x80中斷的時(shí)候阎姥,程序從用戶態(tài)切換到內(nèi)核態(tài)记舆。這時(shí)相應(yīng)的棧也必須切換到內(nèi)核棧,從中斷處理函數(shù)中返回時(shí)呼巴,程序當(dāng)前棧需要從內(nèi)核棧切換到用戶棧氨淌。

當(dāng)前棧是指ESP值所在的棧空間伊磺,如果ESP的值位于用戶棧范圍能盛正,那么程序的當(dāng)前棧就是用戶棧。內(nèi)核棧同理屑埋。并且SS的值需要指向當(dāng)前棧所在的頁豪筝。所以用戶棧切換到內(nèi)核棧就是:

  • 保存當(dāng)前ESP、SS的值
  • 將ESP、SS的值設(shè)置為內(nèi)核棧的值

(反過來同理)

  • 恢復(fù)原理ESP续崖、SS的值
  • 用戶態(tài)的ESP和SS的值保存在內(nèi)核棧上敲街,

當(dāng)發(fā)生中斷的時(shí)候,CPU除了進(jìn)入內(nèi)核態(tài)之外還會(huì)做如下事情:

  • 找到當(dāng)前進(jìn)程的內(nèi)核棧(每一個(gè)進(jìn)程都有一個(gè)內(nèi)核棧)
  • 在內(nèi)核棧中一猜壓入用戶態(tài)的寄存器SS严望、ESP多艇、EFLAGS、CS像吻、EIP

當(dāng)內(nèi)核從系統(tǒng)調(diào)用中返回峻黍,則調(diào)動(dòng)iret指令回到用戶態(tài),iret指令會(huì)從內(nèi)核棧里面彈出SS拨匆、ESP姆涩、EFLAGS、CS惭每、EIP的值骨饿。使得棧恢復(fù)到用戶態(tài)的狀態(tài)台腥。


中斷處理程序

在int指令切換了棧之后宏赘,程序就切換到中斷向量表轉(zhuǎn)給你記錄0x80號(hào)中斷處理陳旭。下面是linux i386中斷服務(wù)流程

內(nèi)核的系統(tǒng)調(diào)用函數(shù)往往以sys_加上系統(tǒng)調(diào)用函數(shù)名了黎侈,比如sys_fork察署,sys_open等。

關(guān)于sys開頭的系統(tǒng)內(nèi)調(diào)用函數(shù)如何從用戶那里獲得參數(shù)的蜓竹。是通過寄存實(shí)現(xiàn)的箕母。我們知道用戶調(diào)用系統(tǒng)調(diào)用時(shí),根據(jù)系統(tǒng)電泳參數(shù)數(shù)量不同俱济,一次將參數(shù)放入EBX,EXC嘶是,EDX,ESI,EBP這6個(gè)寄存器蛛碌。如果一個(gè)參數(shù)的系統(tǒng)調(diào)用就是EBX聂喇,兩個(gè)參數(shù)的系統(tǒng)調(diào)用就是EBX和ECX

小結(jié)

通過閱讀,歸根結(jié)底還是要懂匯編并且去看源碼才能把整個(gè)過程分析正確蔚携。平時(shí)所使用的把所有的細(xì)節(jié)都已經(jīng)屏蔽了贡这。地下還深藏著許多玄機(jī)鬓催。——而這一點(diǎn)確實(shí)大都數(shù)開發(fā)人員的短板

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帮掉,一起剝皮案震驚了整個(gè)濱河市向胡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亡脑,老刑警劉巖堕澄,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邀跃,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛙紫,警方通過查閱死者的電腦和手機(jī)拍屑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坑傅,“玉大人僵驰,你說我怎么就攤上這事⊙涠荆” “怎么了蒜茴?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長枉证。 經(jīng)常有香客問我矮男,道長移必,這世上最難降的妖魔是什么室谚? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮崔泵,結(jié)果婚禮上秒赤,老公的妹妹穿的比我還像新娘。我一直安慰自己憎瘸,他們只是感情好入篮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幌甘,像睡著了一般潮售。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锅风,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天酥诽,我揣著相機(jī)與錄音,去河邊找鬼皱埠。 笑死肮帐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的边器。 我是一名探鬼主播训枢,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忘巧!你這毒婦竟也來了恒界?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤砚嘴,失蹤者是張志新(化名)和其女友劉穎十酣,沒想到半個(gè)月后眯勾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婆誓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年吃环,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洋幻。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡郁轻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出文留,到底是詐尸還是另有隱情好唯,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布燥翅,位于F島的核電站骑篙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏森书。R本人自食惡果不足惜靶端,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凛膏。 院中可真熱鬧杨名,春花似錦、人聲如沸猖毫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吁断。三九已至趁蕊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仔役,已是汗流浹背掷伙。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骂因,地道東北人炎咖。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像寒波,于是被迫代替她去往敵國和親乘盼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容