立項服務(wù)與進程管理
創(chuàng)建進程的系統(tǒng)調(diào)用叫作 fork。在 Linux 里慨蓝,當父進程調(diào)用 fork 創(chuàng)建進程的時候感混,子進程將各個子系統(tǒng)為父進程創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)也全部拷貝了一份,甚至連程序代碼也是拷貝過來的礼烈。按理說弧满,如果不進行特殊的處理,父進程和子進程都按相同的程序代碼進行下去此熬,這樣就沒有意義了庭呜。
所以,我們往往會這樣處理:對于 fork 系統(tǒng)調(diào)用的返回值犀忱,如果當前進程是子進程募谎,就返回 0;如果當前進程是父進程阴汇,就返回子進程的進程號数冬。這樣首先在返回值這里就有了一個區(qū)分,然后通過 if-else 語句判斷搀庶,如果是父進程拐纱,還接著做原來應(yīng)該做的事情;如果是子進程哥倔,需要請求另一個系統(tǒng)調(diào)用execve來執(zhí)行另一個程序戳玫,這個時候,子進程和父進程就徹底分道揚鑣了未斑,也即產(chǎn)生了一個分支(fork)了咕宿。
有時候,父進程要關(guān)心子進程的運行情況蜡秽,有個系統(tǒng)調(diào)用waitpid府阀,父進程可以調(diào)用它,將子進程的進程號作為參數(shù)傳給它芽突,這樣父進程就知道子進程運行完了沒有试浙,成功與否。
會議室管理與內(nèi)存管理
在操作系統(tǒng)中寞蚌,每個進程都有自己的內(nèi)存田巴,互相之間不干擾钠糊,有獨立的進程內(nèi)存空間。
對于進程的內(nèi)存空間來講壹哺,放程序代碼的這部分抄伍,我們稱為代碼段;放進程運行中產(chǎn)生數(shù)據(jù)的這部分管宵,我們稱為數(shù)據(jù)段截珍,其中局部變量的部分,在當前函數(shù)執(zhí)行的時候起作用箩朴,當進入另一個函數(shù)時岗喉,這個變量就釋放了;也有動態(tài)分配的炸庞,會較長時間保存钱床,指明才銷毀的,這部分稱為堆(Heap)埠居。
這里我們介紹兩個在堆里面分配內(nèi)存的系統(tǒng)調(diào)用诞丽,brk和mmap。
當分配的內(nèi)存數(shù)量比較小的時候拐格,使用 brk僧免,會和原來的堆的數(shù)據(jù)連在一起,這就像多分配兩三個工位捏浊,在原來的區(qū)域旁邊搬兩把椅子就行了懂衩。當分配的內(nèi)存數(shù)量比較大的時候,使用 mmap金踪,會重新劃分一塊區(qū)域浊洞,也就是說,當辦公空間需要太多的時候胡岔,索性來個一整塊法希。
檔案庫管理與文件管理
文件管理其實花樣不多,無非是創(chuàng)建靶瘸、打開苫亦、讀、寫等怨咪。對于文件的操作屋剑,下面這六個系統(tǒng)調(diào)用是最重要的:
對于已經(jīng)有的文件,可以使用open打開這個文件诗眨,close關(guān)閉這個文件唉匾;
對于沒有的文件,可以使用creat創(chuàng)建文件匠楚;
打開文件以后巍膘,可以使用lseek跳到文件的某個位置厂财;
可以對文件的內(nèi)容進行讀寫,讀的系統(tǒng)調(diào)用是read峡懈,寫是write璃饱。
Linux 里有一個特點,那就是一切皆文件逮诲。
啟動一個進程帜平,需要一個程序文件幽告,這是一個二進制文件梅鹦。
啟動的時候,要加載一些配置文件冗锁,例如 yml齐唆、properties 等,這是文本文件冻河;啟動之后會打印一些日志箍邮,如果寫到硬盤上,也是文本文件叨叙。
但是如果我想把日志打印到交互控制臺上锭弊,在命令行上打印出來,這其實也是一個文件擂错,是標準輸出stdout 文件味滞。
這個進程的輸出可以作為另一個進程的輸入,這種方式稱為管道钮呀,管道也是一個文件剑鞍。
進程可以通過網(wǎng)絡(luò)和其他進程進行通信,建立的Socket爽醋,也是一個文件蚁署。
進程需要訪問外部設(shè)備,設(shè)備也是一個文件蚂四。
文件都被存儲在文件夾里面光戈,文件夾也是一個文件。
進程運行起來遂赠,要想看到進程運行的情況田度,會在 /proc 下面有對應(yīng)的進程號,也是一系列文件解愤。
每個文件镇饺,Linux 都會分配一個文件描述符(File Descriptor),這是一個整數(shù)送讲。有了這個文件描述符奸笤,我們就可以使用系統(tǒng)調(diào)用惋啃,查看或者干預進程運行的方方面面。
文件操作是貫穿始終的监右,這也是“一切皆文件”的優(yōu)勢边灭,就是統(tǒng)一了操作的入口,提供了極大的便利健盒。
項目異常處理與信號處理
當項目遇到異常情況绒瘦,例如項目中斷,做到一半不做了扣癣。這時候就需要發(fā)送一個信號(Signal)給項目組惰帽。經(jīng)常遇到的信號有以下幾種:
在執(zhí)行一個程序的時候,在鍵盤輸入“CTRL+C”父虑,這就是中斷的信號该酗,正在執(zhí)行的命令就會中止退出;
如果非法訪問內(nèi)存士嚎,例如你跑到別人的會議室呜魄,可能會看到不該看的東西;
硬件故障莱衩,設(shè)備出了問題爵嗅,當然要通知項目組;
用戶進程通過kill函數(shù)笨蚁,將一個用戶信號發(fā)送給另一個進程睹晒。
當項目組收到信號的時候,項目組需要決定如何處理這些異常情況赚窃。
對于一些不嚴重的信號册招,可以忽略,該干啥干啥勒极,但是像 SIGKILL(用于終止一個進程的信號)和 SIGSTOP(用于中止一個進程的信號)是不能忽略的是掰,可以執(zhí)行對于該信號的默認動作。每種信號都定義了默認的動作辱匿,例如硬件故障键痛,默認終止;也可以提供信號處理函數(shù)匾七,可以通過sigaction系統(tǒng)調(diào)用絮短,注冊一個信號處理函數(shù)。
提供了信號處理服務(wù)昨忆,項目執(zhí)行過程中一旦有變動丁频,就可以及時處理了。
項目組間溝通與進程間通信
當某個項目比較大的時候,可能分成多個項目組席里,不同的項目組需要相互交流叔磷、相互配合才能完成,這就需要一個項目組之間的溝通機制奖磁。項目組之間的溝通方式有很多種改基,我們來一一規(guī)劃。
首先就是發(fā)個消息咖为,不需要一段很長的數(shù)據(jù)秕狰,這種方式稱為消息隊列(Message Queue)。由于一個公司內(nèi)的多個項目組溝通時躁染,這個消息隊列是在內(nèi)核里的鸣哀,我們可以通過msgget創(chuàng)建一個新的隊列,msgsnd將消息發(fā)送到消息隊列褐啡,而消息接收方可以使用msgrcv從隊列中取消息诺舔。
當兩個項目組需要交互的信息比較大的時候鳖昌,可以使用共享內(nèi)存的方式备畦,可以通過shmget創(chuàng)建一個共享內(nèi)存塊,通過shmat將共享內(nèi)存映射到自己的內(nèi)存空間许昨,然后就可以讀寫了懂盐。
但是,兩個項目組共同訪問一個會議室里的數(shù)據(jù)糕档,就會存在“競爭”問題莉恼。如果大家同時修改同一塊數(shù)據(jù)咋辦?這就需要有一種方式速那,讓不同的人能夠排他地訪問俐银,這就是信號量的機制 Semaphore。
這個機制比較復雜端仰,我這里說一種簡單的場景捶惜。對于只允許一個人訪問的需求,我們可以將信號量設(shè)為 1荔烧。當一個人要訪問的時候吱七,先調(diào)用sem_wait。如果這時候沒有人訪問鹤竭,則占用這個信號量踊餐,他就可以開始訪問了。如果這個時候另一個人要訪問臀稚,也會調(diào)用sem_wait吝岭。由于前一個人已經(jīng)在訪問了,所以后面這個人就必須等待上一個人訪問完之后才能訪問。當上一個人訪問完畢后窜管,會調(diào)用sem_post將信號量釋放酒觅,于是下一個人等待結(jié)束,可以訪問這個資源了微峰。
公司間溝通與網(wǎng)絡(luò)通信
這臺 Linux 要和另一臺 Linux 交流舷丹,這時候,我們就需要用到網(wǎng)絡(luò)服務(wù)蜓肆。不同機器的通過網(wǎng)絡(luò)相互通信颜凯,要遵循相同的網(wǎng)絡(luò)協(xié)議,即TCP/IP 網(wǎng)絡(luò)協(xié)議棧仗扬。Linux 內(nèi)核里有對于網(wǎng)絡(luò)協(xié)議棧的實現(xiàn)症概。如何暴露出服務(wù)給項目組使用呢?
網(wǎng)絡(luò)服務(wù)是通過套接字 Socket 來提供服務(wù)的早芭,在通信之前彼城,雙方都要建立一個 Socket。我們可以通過 Socket 系統(tǒng)調(diào)用建立一個 Socket退个。Socket 也是一個文件募壕,也有一個文件描述符,也可以通過讀寫函數(shù)進行通信语盈。
中介與 Glibc
為了對用戶更友好舱馅,我們還可以使用中介Glibc,有事情找它就行刀荒,它會轉(zhuǎn)換成為系統(tǒng)調(diào)用代嗤,幫你調(diào)用。
Glibc 是 Linux 下使用的開源的標準 C 庫缠借,它是 GNU 發(fā)布的 libc 庫干毅。Glibc 為程序員提供豐富的 API,除了例如字符串處理泼返、數(shù)學運算等用戶態(tài)服務(wù)之外硝逢,最重要的是封裝了操作系統(tǒng)提供的系統(tǒng)服務(wù),即系統(tǒng)調(diào)用的封裝符隙。
每個特定的系統(tǒng)調(diào)用對應(yīng)了至少一個 Glibc 封裝的庫函數(shù)趴捅,比如說,系統(tǒng)提供的打開文件系統(tǒng)調(diào)用 sys_open 對應(yīng)的 open 函數(shù)霹疫。
有時候拱绑,Glibc 一個單獨的 API 可能調(diào)用多個系統(tǒng)調(diào)用,比如說丽蝎,Glibc 提供的 printf 函數(shù)就會調(diào)用如 sys_open猎拨、sys_mmap膀藐、sys_write、sys_close 等等系統(tǒng)調(diào)用红省。
也有時候额各,多個 API 也可能只對應(yīng)同一個系統(tǒng)調(diào)用,如 Glibc 下實現(xiàn)的 malloc吧恃、calloc虾啦、free 等函數(shù)用來分配和釋放內(nèi)存,都利用了內(nèi)核的 sys_brk 的系統(tǒng)調(diào)用痕寓。
課堂練習
有個命令 strace傲醉,常用來跟蹤進程執(zhí)行時系統(tǒng)調(diào)用和所接收的信號。你可以試一下咱們學過的命令行呻率,看看都執(zhí)行了哪些系統(tǒng)調(diào)用硬毕。