1 Linux系統(tǒng)和POSIX 標(biāo)準(zhǔn)入門
本書介紹了Linux以及我們?nèi)绾卧贚inux環(huán)境中使用C++來管理關(guān)鍵資源。我們想花一些時(shí)間在本章中加深對(duì)操作系統(tǒng)(OS)的基本了解。 您將更多地了解一些特定技術(shù)、系統(tǒng)調(diào)用接口和可移植操作系統(tǒng)接口(POSIX Portable Operating System Interface)的起源。
在Linux或其他基于Unix的操作系統(tǒng)環(huán)境中編程相當(dāng)常見。 無論您的專業(yè)知識(shí)位于何處——從物聯(lián)網(wǎng) (IoT) 設(shè)備和嵌入式軟件開發(fā)到移動(dòng)設(shè)備、超級(jí)計(jì)算或航天器——您很有可能在某個(gè)時(shí)候接觸到Linux發(fā)行版。
本章內(nèi)容:
- 熟悉操作系統(tǒng)的概念
- 了解Linux 內(nèi)核
- 介紹系統(tǒng)調(diào)用接口和系統(tǒng)編程
- 瀏覽文件、進(jìn)程和線程
- 使用init和systemd運(yùn)行服務(wù)
- POSIX
技術(shù)要求
為了熟悉編程環(huán)境,讀者必須準(zhǔn)備以下內(nèi)容:
1.1 熟悉操作系統(tǒng)的概念
現(xiàn)代操作系統(tǒng)是一個(gè)復(fù)雜的實(shí)體。 它還具有附加功能,如統(tǒng)計(jì)數(shù)據(jù)收集、多媒體處理、系統(tǒng)安全保障、整體穩(wěn)定性、可靠的錯(cuò)誤處理等。
雖然操作系統(tǒng)有義務(wù)執(zhí)行所有這些任務(wù),但程序員仍然有必要注意系統(tǒng)的細(xì)節(jié)和要求。 例如,通過虛擬機(jī)從更高的抽象層次進(jìn)行工作并不意味著放棄了解我們的代碼如何影響系統(tǒng)行為的需要。 更接近操作系統(tǒng)層的程序員也需要有效地管理系統(tǒng)資源。 這是操作系統(tǒng)提供應(yīng)用程序編程接口(API)的原因之一。 了解如何使用此類API以及它們提供的好處是非常寶貴的專業(yè)知識(shí)。
1.1.1 操作系統(tǒng)類型
通用操作系統(tǒng) (GPOS general-purpose operating systems) 最初是作為分時(shí)操作系統(tǒng)開始的。 歷史上,還有另一種操作系統(tǒng),與分時(shí)操作系統(tǒng)起源于同一時(shí)期——實(shí)時(shí)操作系統(tǒng)(RTOS real-time operating systems)。我們將討論任務(wù)優(yōu)先級(jí)、定時(shí)器值、外設(shè)速度、中斷和信號(hào)處理程序、多線程和動(dòng)態(tài)內(nèi)存分配等屬性如何導(dǎo)致系統(tǒng)行為的變化。 有時(shí)這些是不可預(yù)測(cè)的。 這就是為什么我們認(rèn)識(shí)兩種類型的RTOS:硬RTOS和軟RTOS。 硬RTOS通常與給定的硬件嚴(yán)格相關(guān)。 系統(tǒng)開發(fā)人員熟悉終端設(shè)備的要求。 盡管設(shè)備的輸入仍然被視為異步且不可預(yù)測(cè),但可以初步評(píng)估和編程任務(wù)執(zhí)行時(shí)間。 本書的重點(diǎn)仍然是具有一些軟RTOS功能的GPOS編程。
在硬RTOS中,保證實(shí)時(shí)任務(wù)按時(shí)執(zhí)行。系統(tǒng)反應(yīng)期限通常是預(yù)先定義的,關(guān)鍵任務(wù)數(shù)據(jù)存儲(chǔ)在ROM中,因此無法在運(yùn)行時(shí)更新。 虛擬內(nèi)存等功能通常被刪除。 一些現(xiàn)代CPU內(nèi)核提供所謂的緊耦合存儲(chǔ)器 (TCM tightly coupled memory),在系統(tǒng)啟動(dòng)時(shí)將常用的數(shù)據(jù)和代碼行從非易失性存儲(chǔ)器(NVM non-volatile memory)加載到其中。 系統(tǒng)的行為是預(yù)先編寫好的腳本。 這些操作系統(tǒng)的作用與機(jī)器控制有關(guān),禁止用戶輸入。
軟RTOS為關(guān)鍵任務(wù)提供最高優(yōu)先級(jí),直至完成且不會(huì)中斷。 盡管如此,實(shí)時(shí)任務(wù)還是希望能夠及時(shí)完成,而不應(yīng)該無休止地等待。 顯然,這種類型的操作系統(tǒng)不能用于關(guān)鍵任務(wù):工廠機(jī)器、機(jī)器人、車輛等。 但它可以用來控制整個(gè)系統(tǒng)行為,因此這種類型的操作系統(tǒng)常見于多媒體和研究項(xiàng)目、人工智能、計(jì)算機(jī)圖形、虛擬現(xiàn)實(shí)設(shè)備等中。 由于這些RTOS與GPOS不沖突,因此可以與其集成。 它們的功能也可以在某些Linux發(fā)行版中找到。 QNX是對(duì)此的一個(gè)有趣的實(shí)現(xiàn)。
1.1.2 Linux簡(jiǎn)介
Linux是一個(gè)類Unix操作系統(tǒng),這意味著它提供了與Unix類似(有時(shí)是相同)的接口 - 它的功能,尤其是API,被設(shè)計(jì)為與Unix的功能相匹配。 但它不是基于Unix的操作系統(tǒng)。 它們的功能不是以相同的方式實(shí)現(xiàn)的。 對(duì)FreeBSD-macOS關(guān)系的理解也存在類似的誤解。 盡管兩者共享很大一部分代碼,但它們的方法完全不同,包括內(nèi)核的結(jié)構(gòu)方式。
記住這些事實(shí)很重要,因?yàn)椴⒎俏覀儗⒃诒緯惺褂玫乃泄δ芏即嬖诨蛟谒蓄怳nix操作系統(tǒng)上都可以訪問。 我們專注于 Linux,只要滿足每章各自的技術(shù)要求,我們的示例就可以工作。
做出這個(gè)決定有幾個(gè)原因。 首先,Linux是開源的,你可以輕松查看其內(nèi)核代碼:https://github.com/torvalds/linux。 您應(yīng)該能夠輕松閱讀它,因?yàn)樗怯肅編寫的。盡管C不是面向?qū)ο蟮恼Z言,但Linux內(nèi)核遵循許多面向?qū)ο缶幊?(OOP object-oriented programming) 范例。 操作系統(tǒng)本身由許多獨(dú)立的設(shè)計(jì)塊組成,稱為模塊。 您可以根據(jù)您的系統(tǒng)需求輕松配置、集成和應(yīng)用它們。 Linux給你使用實(shí)時(shí)系統(tǒng)(在本章后面描述)和并行代碼執(zhí)行的能力。 簡(jiǎn)而言之 – Linux 易于適應(yīng)、擴(kuò)展和配置; 我們可以輕松地利用這一點(diǎn)來發(fā)揮我們的優(yōu)勢(shì)。 但具體在哪里呢?
好吧,我們可以開發(fā)接近操作系統(tǒng)的應(yīng)用程序,或者我們甚至可以自己生成一些模塊,這些模塊可以在運(yùn)行時(shí)加載或卸載。 這樣的例子是文件系統(tǒng)或設(shè)備驅(qū)動(dòng)程序。 我們將在第2章深入探討流程實(shí)體時(shí)重新討論這個(gè)主題。 現(xiàn)在,假設(shè)模塊看起來很像OOP設(shè)計(jì):它們是可構(gòu)造和可破壞的; 有時(shí),根據(jù)內(nèi)核的需要,可以將公共代碼概括為一個(gè)模塊,并且這些模塊具有層次依賴性。 盡管如此,Linux內(nèi)核仍被認(rèn)為是整體的; 例如,它具有復(fù)雜的功能,但整個(gè)操作系統(tǒng)都在內(nèi)核空間中運(yùn)行。 相比之下,微內(nèi)核(QNX、MINIX或L4)構(gòu)成了運(yùn)行操作系統(tǒng)的最低限度。 在這種情況下,附加功能是通過在內(nèi)核本身之外工作的模塊提供的。 這讓我們對(duì)Linux內(nèi)核的可能性有了一個(gè)稍微混亂但總體清晰的認(rèn)識(shí)。
1.2 了解Linux內(nèi)核
Linux系統(tǒng)中看到的三個(gè)主要層:用戶空間(正在運(yùn)行的進(jìn)程及其線程)、內(nèi)核空間(正在運(yùn)行的內(nèi)核本身,通常是它自己的進(jìn)程)和計(jì)算機(jī)——這可以是任何類型的計(jì)算設(shè)備,例如 PC、平板電腦、 智能手機(jī)、超級(jí)計(jì)算機(jī)、物聯(lián)網(wǎng)設(shè)備等。 正如我們?cè)诮酉聛淼恼鹿?jié)中解釋的那樣,圖中觀察到的所有術(shù)語都會(huì)一一落實(shí)到位,所以如果您現(xiàn)在不熟悉所有術(shù)語,請(qǐng)不要擔(dān)心。
上圖中的一些相互依賴關(guān)系可能已經(jīng)給您留下了深刻的印象。 例如,查看設(shè)備驅(qū)動(dòng)程序、各個(gè)設(shè)備和中斷之間的關(guān)系。 設(shè)備驅(qū)動(dòng)程序是字符設(shè)備驅(qū)動(dòng)程序、塊設(shè)備驅(qū)動(dòng)程序和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的概括。 請(qǐng)注意中斷與任務(wù)調(diào)度的關(guān)系。 這是一個(gè)微不足道但基本的機(jī)制,在驅(qū)動(dòng)程序的實(shí)現(xiàn)中大量使用。 它是操作系統(tǒng)和硬件的初始通信和控制機(jī)制。
僅舉一個(gè)例子:假設(shè)您想要從磁盤 (NVM) 恢復(fù)和讀取文件,并且您通過某種標(biāo)準(zhǔn)編程功能請(qǐng)求它。 read() 調(diào)用將在后臺(tái)執(zhí)行,然后轉(zhuǎn)換為文件系統(tǒng)操作。 文件系統(tǒng)調(diào)用設(shè)備驅(qū)動(dòng)程序來查找并檢索給定文件描述符后面的內(nèi)容,然后將其與文件系統(tǒng)已知的地址相關(guān)聯(lián)。 所需設(shè)備 (NVM) 開始搜索數(shù)據(jù)片段——文件。 在操作完成之前,如果調(diào)用者進(jìn)程是單線程進(jìn)程并且沒有其他事情可做,則它將被停止。 另一個(gè)進(jìn)程將開始工作,直到設(shè)備找到并返回指向文件地址的指針。 然后觸發(fā)中斷,這有助于操作系統(tǒng)調(diào)用調(diào)度程序。 我們的初始進(jìn)程將使用新加載的數(shù)據(jù)再次啟動(dòng),第二個(gè)進(jìn)程現(xiàn)在將停止。
此任務(wù)示例演示了如何通過一個(gè)小的、無關(guān)緊要的操作來影響系統(tǒng)的行為 - 這是您將在第一個(gè)編程課程中學(xué)習(xí)的編碼。 在系統(tǒng)的生命周期中,許多進(jìn)程將一直重新安排。 操作系統(tǒng)的工作就是在不中斷的情況下實(shí)現(xiàn)這一點(diǎn)。
但中斷是一項(xiàng)繁重的操作,可能會(huì)導(dǎo)致不必要的內(nèi)存訪問和無用的應(yīng)用程序狀態(tài)切換?,F(xiàn)在,想想如果系統(tǒng)過載會(huì)發(fā)生什么——CPU使用率達(dá)到99%,或者磁盤收到很多請(qǐng)求而無法及時(shí)處理它們。 如果該系統(tǒng)是飛機(jī)嵌入式設(shè)備的一部分怎么辦? 當(dāng)然,這在現(xiàn)實(shí)中不太可能,因?yàn)轱w機(jī)需要滿足嚴(yán)格的技術(shù)要求和高質(zhì)量標(biāo)準(zhǔn)。 但為了便于討論,請(qǐng)考慮如何防止類似情況發(fā)生,或者如何保證代碼在任何用戶場(chǎng)景中成功執(zhí)行。
1.3 系統(tǒng)調(diào)用接口和系統(tǒng)編程
NVM數(shù)據(jù)請(qǐng)求是一個(gè)受益于系統(tǒng)調(diào)用接口的過程,因?yàn)椴僮飨到y(tǒng)有義務(wù)將此請(qǐng)求轉(zhuǎn)換為應(yīng)用程序二進(jìn)制接口 (ABI application binary interface) 調(diào)用,引用相應(yīng)的設(shè)備驅(qū)動(dòng)程序。 這種操作稱為系統(tǒng)調(diào)用。使用系統(tǒng)調(diào)用來實(shí)現(xiàn)或執(zhí)行操作系統(tǒng)提供的功能稱為系統(tǒng)編程。 系統(tǒng)調(diào)用是內(nèi)核服務(wù)的唯一入口點(diǎn)。 它們通常由glibc等庫包裝,并且不直接調(diào)用。
換句話說,系統(tǒng)調(diào)用定義了程序員的接口,通過該接口可以使用所有內(nèi)核服務(wù)。 操作系統(tǒng)更多地可以被視為內(nèi)核服務(wù)和硬件之間的中介。 除非你喜歡擺弄硬件引腳和底層平臺(tái)指令,或者你自己就是模塊架構(gòu)師,否則你應(yīng)該勇敢地將細(xì)節(jié)留給操作系統(tǒng)。操作系統(tǒng)負(fù)責(zé)處理特定的計(jì)算機(jī)物理接口操作。應(yīng)用程序有責(zé)任使用正確的系統(tǒng)調(diào)用。軟件工程師的任務(wù)是了解它們對(duì)系統(tǒng)整體行為的影響。 請(qǐng)記住,使用系統(tǒng)調(diào)用是有代價(jià)的。
正如示例中所觀察到的,操作系統(tǒng)在檢索文件時(shí)會(huì)執(zhí)行很多操作。當(dāng)動(dòng)態(tài)分配內(nèi)存或由多個(gè)線程訪問單個(gè)內(nèi)存塊時(shí),甚至?xí)瓿筛喙ぷ?。我們將在接下來的章?jié)中進(jìn)一步討論這一點(diǎn),并將強(qiáng)調(diào)盡可能謹(jǐn)慎地使用系統(tǒng)調(diào)用,無論是自愿還是非自愿。 簡(jiǎn)而言之,系統(tǒng)調(diào)用不是簡(jiǎn)單的函數(shù)調(diào)用,因?yàn)樗鼈儾皇窃谟脩艨臻g中執(zhí)行的。 系統(tǒng)調(diào)用不會(huì)進(jìn)入程序堆棧中的下一個(gè)過程,而是觸發(fā)模式切換,從而導(dǎo)致跳轉(zhuǎn)到內(nèi)核內(nèi)存堆棧中的例程。 從文件中讀取可以可視化如下:
那么我們什么時(shí)候應(yīng)該使用系統(tǒng)調(diào)用呢? 簡(jiǎn)而言之,當(dāng)我們想要非常精確地執(zhí)行某些操作系統(tǒng)任務(wù)時(shí),這些任務(wù)通常與設(shè)備管理、文件管理、進(jìn)程控制或通信基礎(chǔ)設(shè)施相關(guān)。 我們將在后面的章節(jié)中介紹這些角色的許多示例,但簡(jiǎn)而言之,歡迎您閱讀更多內(nèi)容并熟悉以下內(nèi)容:
- syscall()
- fork()
- exec()
- exit()
- wait()
- kill())
參考:https://www.kernel.org/doc/man-pages/。
有用系統(tǒng)調(diào)用的簡(jiǎn)短列表可以在以下鏈接中找到:https://man7.org/linux/man-pages/man2/syscalls.2.xhtml。
您可能已經(jīng)猜到,使用系統(tǒng)調(diào)用接口也會(huì)給系統(tǒng)帶來安全風(fēng)險(xiǎn)。 距離內(nèi)核和設(shè)備控制如此之近,為惡意軟件滲透您的軟件提供了絕佳的機(jī)會(huì)。 當(dāng)您的軟件影響系統(tǒng)行為時(shí),另一個(gè)程序可能會(huì)嗅探并收集有價(jià)值的數(shù)據(jù)。 您至少可以做的就是設(shè)計(jì)代碼,使用戶界面與關(guān)鍵過程(尤其是系統(tǒng)調(diào)用)很好地隔離。 100%安全是不可能的,雖然有許多關(guān)于安全問題的綜合書籍,但保護(hù)系統(tǒng)安全的藝術(shù)本身就是一個(gè)不斷發(fā)展的過程。
1.4 瀏覽文件、進(jìn)程和線程
1.4.1 文件
簡(jiǎn)而言之,我們需要文件來代表系統(tǒng)上的多種資源。 我們編寫的程序也是文件。 編譯后的代碼,例如可執(zhí)行二進(jìn)制文件(.bin、.exe)和庫都是文件(.o、.so、.lib、.dll 等)。 此外,我們還需要它們來實(shí)現(xiàn)通信機(jī)制和存儲(chǔ)管理。 你知道Linux上可以識(shí)別哪些類型的文件嗎? 讓我們快速向您介紹一下:
- 普通或常規(guī)文件:系統(tǒng)上幾乎所有存儲(chǔ)數(shù)據(jù)的文件都被視為常規(guī)文件:文本、媒體、代碼等。
- 目錄:用于構(gòu)建文件系統(tǒng)的層次結(jié)構(gòu)。 它們不存儲(chǔ)數(shù)據(jù),而是存儲(chǔ)其他文件的位置。
- 特殊(設(shè)備)文件:您可以在 /dev 目錄下找到它們,代表您的所有硬件設(shè)備。
- 鏈接:我們使用它們來允許訪問不同位置的另一個(gè)文件。 實(shí)際上,它們是真實(shí)文件的替換,通過它們可以直接訪問這些文件。 這與 Windows的快捷方式不同。 它們是特定的文件類型,需要應(yīng)用程序來支持它們 - 首先處理快捷方式元數(shù)據(jù),然后指向資源,因此文件不會(huì)被一次性訪問。
- 套接字:這是進(jìn)程交換數(shù)據(jù)(包括與其他系統(tǒng))的通信端點(diǎn)。
- 命名管道:我們使用命名管道在系統(tǒng)上當(dāng)前運(yùn)行的兩個(gè)進(jìn)程之間交換雙向數(shù)據(jù)。
1.4.2 進(jìn)程和線程
進(jìn)程是程序的一個(gè)實(shí)例,準(zhǔn)確地說是一個(gè)執(zhí)行實(shí)例。 它有自己的地址空間并與其他進(jìn)程保持隔離。 這意味著每個(gè)進(jìn)程都有操作系統(tǒng)分配給它的一系列(通常是虛擬的)地址。 Linux將它們視為任務(wù)。 一般用戶無法觀察到它們。 這就是內(nèi)核完成其工作的方式。 每個(gè)任務(wù)都通過在include/linux/sched.h中定義的task_struct實(shí)體進(jìn)行描述。 系統(tǒng)管理員和系統(tǒng)程序員通過進(jìn)程表觀察進(jìn)程,進(jìn)程表通過每個(gè)進(jìn)程的特定進(jìn)程標(biāo)識(shí)符(pid )進(jìn)行散列。 此方法用于快速查找進(jìn)程-使用終端中的 ps 命令查看系統(tǒng)上的進(jìn)程狀態(tài),然后鍵入以下命令查看單個(gè)進(jìn)程的具體信息:
ps -p <required pid>
新進(jìn)程是通過當(dāng)前進(jìn)程屬性的副本創(chuàng)建的,并且屬于進(jìn)程組。 一個(gè)或多個(gè)組創(chuàng)建一個(gè)會(huì)話。 每個(gè)會(huì)話都與一個(gè)終端相關(guān)。 小組和會(huì)議都有流程領(lǐng)導(dǎo)者。 屬性的克隆主要用于資源共享。 如果兩個(gè)進(jìn)程共享相同的虛擬內(nèi)存空間,它們將被視為單個(gè)進(jìn)程中的兩個(gè)線程并進(jìn)行管理,但它們不像進(jìn)程量級(jí)。
我們關(guān)心四個(gè)實(shí)體:第一個(gè)是可執(zhí)行文件,因?yàn)樗且獔?zhí)行的指令的單元載體。 其次是進(jìn)程——執(zhí)行這些指令的工作單元。 第三,我們需要這些指令作為處理和管理系統(tǒng)資源的工具。 第四是線程——最小的指令序列,由操作系統(tǒng)獨(dú)立管理,是進(jìn)程的一部分。請(qǐng)記住,每個(gè)操作系統(tǒng)的進(jìn)程和線程的實(shí)現(xiàn)都不同,因此在使用它們之前請(qǐng)先進(jìn)行研究。
從內(nèi)核的角度來看,進(jìn)程的主線程是任務(wù)組組長,在代碼中標(biāo)識(shí)為group_leader。 由組領(lǐng)導(dǎo)者產(chǎn)生的所有線程都可以通過 thread_node 進(jìn)行迭代。實(shí)際上,它們存儲(chǔ)在一個(gè)單鏈表中,thread_node是它的頭。生成的線程攜帶一個(gè)指向 group_leader 工具的指針。 進(jìn)程創(chuàng)建者的task_struct對(duì)象由它指向。 你可能已經(jīng)猜對(duì)了,它與組長的task_struct相同。
如果一個(gè)進(jìn)程生成另一個(gè)進(jìn)程,例如通過 fork(),新創(chuàng)建的進(jìn)程(稱為子進(jìn)程)通過父指針了解其創(chuàng)建者。 它們還通過兄弟指針了解其兄弟進(jìn)程,該指針是指向父進(jìn)程的其他子進(jìn)程的列表節(jié)點(diǎn)。 每個(gè)父級(jí)都通過子級(jí)了解其子級(jí)——指向列表頭的指針,存儲(chǔ)子級(jí)并提供對(duì)它們的訪問。
如下圖所示,線程沒有定義任何其他數(shù)據(jù)結(jié)構(gòu):
我們已經(jīng)多次提到 fork(),但它到底是什么? 嗯,簡(jiǎn)單來說,它是一個(gè)系統(tǒng)函數(shù),用于創(chuàng)建進(jìn)程調(diào)用者的進(jìn)程副本。 它向父進(jìn)程提供新進(jìn)程的 ID 并啟動(dòng)子進(jìn)程的執(zhí)行。
在幕后,fork()被替換為clone()。 通過標(biāo)志提供不同的選項(xiàng),但如果全部設(shè)置為零,clone()的行為類似于 fork()。更多參考:https://man7.org/linux/man-pages/man2/clone.2.xhtml。
您可能會(huì)問自己為什么這種實(shí)現(xiàn)更可取。 這樣想:當(dāng)內(nèi)核在進(jìn)程之間進(jìn)行切換時(shí),它會(huì)檢查當(dāng)前進(jìn)程在虛擬內(nèi)存中的地址,確切地說是頁目錄。 如果與新執(zhí)行的進(jìn)程相同,則它們共享相同的地址空間。 那么,switch只是一個(gè)簡(jiǎn)單的指針跳轉(zhuǎn)指令,通常是到程序的入口點(diǎn)。 這意味著預(yù)計(jì)重新安排會(huì)更快。 請(qǐng)小心–進(jìn)程可能共享相同的地址空間,但不共享相同的程序堆棧。 clone()負(fù)責(zé)為每個(gè)進(jìn)程創(chuàng)建不同的堆棧。
1.4.3 基于運(yùn)行模式的進(jìn)程類型
某些流程需要啟動(dòng)或交互用戶交互。它們被稱為前臺(tái)進(jìn)程。 但正如您可能已經(jīng)發(fā)現(xiàn)的那樣,有些進(jìn)程獨(dú)立于我們或任何其他用戶的活動(dòng)運(yùn)行。此類進(jìn)程稱為后臺(tái)進(jìn)程。除非另有說明,否則作為程序執(zhí)行調(diào)用或用戶命令的終端輸入默認(rèn)被視為前臺(tái)進(jìn)程。要在后臺(tái)運(yùn)行進(jìn)程,只需將&放在用于啟動(dòng)該進(jìn)程的命令行末尾即可。 例如,讓我們調(diào)用已知的測(cè)試,完成后,我們?cè)诮K端中看到以下內(nèi)容:
$ ./test &
[1] 62934
[1] + done ./test
$ ./test &
[1] 63388
$ kill 63388
[1] + terminated./test
終止進(jìn)程和讓它自行終止是兩件不同的事情,終止進(jìn)程可能會(huì)導(dǎo)致不可預(yù)測(cè)的系統(tǒng)行為或無法訪問某些資源,例如未關(guān)閉的文件或套接字。
一些進(jìn)程在無人值守的情況下運(yùn)行。它們被稱為守護(hù)進(jìn)程并在后臺(tái)持續(xù)運(yùn)行。他們預(yù)計(jì)將始終可用。守護(hù)進(jìn)程通常通過系統(tǒng)的啟動(dòng)腳本啟動(dòng)并運(yùn)行直到系統(tǒng)關(guān)閉。它們通常提供系統(tǒng)服務(wù),并且多個(gè)用戶依賴它們。 因此,啟動(dòng)時(shí)的守護(hù)進(jìn)程通常由ID為 0的用戶(通常是 root)啟動(dòng),并且可能以root權(quán)限運(yùn)行。
Linux系統(tǒng)上擁有最高權(quán)限的用戶稱為root用戶,或簡(jiǎn)稱為root。 此權(quán)限級(jí)別允許執(zhí)行與安全相關(guān)的任務(wù)。該角色對(duì)系統(tǒng)的完整性有直接影響,因此所有其他用戶必須設(shè)置盡可能最小的權(quán)限級(jí)別,直到需要更高的權(quán)限級(jí)別。
僵尸進(jìn)程是已終止但仍可通過其pid識(shí)別的進(jìn)程。 它沒有地址空間。 只要其父進(jìn)程運(yùn)行,僵尸進(jìn)程就會(huì)繼續(xù)存在。 這意味著,在我們退出主進(jìn)程、關(guān)閉系統(tǒng)或重新啟動(dòng)之前,僵尸進(jìn)程在ps列出時(shí)仍將顯示為
$ ps
PID TTY TIME CMD
…
64690 ttys000 0:00.00 <defunct>
$ top
t–p - 07:58:26 up 100 days, 2:34, 2 users, load average: 1.20, 1.12, 1.68
Tasks: 200 total, 1 running, 197 sleeping, 1 stopped, 1 zombie
1.5 用init和systemd 運(yùn)行服務(wù)
是初始進(jìn)程,在Linux系統(tǒng)上由內(nèi)核執(zhí)行,其pid始終為1:
$ ps -p 1
PID TTY TIME CMD
1 ? 04:53:20 systemd
它被稱為系統(tǒng)上所有進(jìn)程的父進(jìn)程,因?yàn)樗糜诔跏蓟?、管理和跟蹤其他服?wù)和守護(hù)程序。 Linux的第一個(gè)init守護(hù)進(jìn)程稱為Init,它定義了六種系統(tǒng)狀態(tài)。 所有系統(tǒng)服務(wù)都分別映射到這些狀態(tài)。 它的腳本用于按預(yù)定義的順序啟動(dòng)進(jìn)程,系統(tǒng)程序員偶爾會(huì)使用它。 使用它的一個(gè)可能的原因是減少系統(tǒng)的啟動(dòng)持續(xù)時(shí)間。 要?jiǎng)?chuàng)建服務(wù)或編輯腳本,您可以修改/etc/init.d,ls命令并查看可以通過init運(yùn)行的所有服務(wù)。
$ ls /etc/init.d/
acpid
alsa-utils
anacron
...
ufw
unidd
x11-common
每個(gè)腳本都遵循相同的代碼模板來執(zhí)行和維護(hù):
您可以自己生成相同的模板,并通過以下命令閱讀有關(guān)init腳本源代碼的更多信息:“$ man init-d-script”
您可以通過以下命令列出可用服務(wù)的狀態(tài):
$ service --status-all
[ + ] acpid
[ - ] alsa-utils
[ - ] anacron
...
[ + ] ufw
[ - ] uuidd
[ - ] x11-common
我們可以停止防火墻服務(wù) – ufw:
$ service ufw stop
現(xiàn)在,讓我們檢查一下它的狀態(tài):
$ service ufw status
● ufw.service - Uncomplicated firewall
...
$ service ufw start
$ service ufw status
● ufw.service - Uncomplicated firewall
...
以類似的方式,您可以創(chuàng)建自己的服務(wù)并使用service 命令啟動(dòng)它。 在現(xiàn)代、全面的Linux系統(tǒng)上,init被認(rèn)為是一種過時(shí)的方法。 盡管如此與systemd不同,它可以在每個(gè)基于Unix的操作系統(tǒng)上找到,因此系統(tǒng)程序員會(huì)期望將其用作服務(wù)的通用接口。 因此,我們更多地使用它作為一個(gè)簡(jiǎn)單的示例和服務(wù)來自哪里的解釋。 如果我們想使用最新的方法,我們必須轉(zhuǎn)向 systemd。
systemd是一個(gè)init守護(hù)進(jìn)程,它代表了在Linu 系統(tǒng)上運(yùn)行服務(wù)的現(xiàn)代方法。 它提供了并行系統(tǒng)服務(wù)啟動(dòng)功能,這進(jìn)一步加快了初始化過程。每個(gè)服務(wù)都存儲(chǔ)在/lib/systemd/system或/etc/systemd/system目錄下的.service 文件中。 /lib 中的服務(wù)是系統(tǒng)啟動(dòng)服務(wù)的定義,/etc中的服務(wù)是系統(tǒng)運(yùn)行時(shí)啟動(dòng)的服務(wù)的定義。 讓我們列出它們:
$ ls /lib/systemd/system
accounts-daemon.service
acpid.path
acpid.service
...
$ ls /etc/systemd/system
bluetooth.target.wants
display-manager.service
…
timers.target.wants
vmtoolsd.service
systemd的接口比init復(fù)雜得多。 我們鼓勵(lì)您花時(shí)間單獨(dú)檢查它,因?yàn)槲覀儫o法在這里對(duì)其進(jìn)行簡(jiǎn)短總結(jié)。 但是如果您列出systemd目錄,您可能會(huì)觀察到許多類型的文件。 在守護(hù)進(jìn)程的上下文中,它們被稱為單元。它們每個(gè)都提供不同的接口,因?yàn)樗鼈兠總€(gè)都與systemd管理的某個(gè)實(shí)體相關(guān)。 每個(gè)文件內(nèi)的腳本描述了設(shè)置的選項(xiàng)以及給定服務(wù)的作用。 單位名稱響亮。.timer 用于計(jì)時(shí)器管理,.service 用于指定給定服務(wù)的啟動(dòng)方式及其依賴項(xiàng),.path 描述給定服務(wù)的基于路徑的激活,等等。
讓我們創(chuàng)建一個(gè)簡(jiǎn)單的systemd 服務(wù),其目的是監(jiān)視給定文件是否被修改。
首先,讓我們通過一個(gè)簡(jiǎn)單的文本編輯器創(chuàng)建一些虛擬文件。 讓我們想象這是一個(gè)真實(shí)的配置。 打印出來給出以下內(nèi)容:
$ cat /etc/test_config/config
test test
讓我們準(zhǔn)備一個(gè)腳本來描述文件更改時(shí)需要執(zhí)行的過程。 同樣,為了本示例的目的,讓我們通過一個(gè)簡(jiǎn)單的文本編輯器創(chuàng)建它 - 它將如下所示:
$ cat ~/sniff_printer.sh
echo "File /etc/test_config/config changed!"
當(dāng)調(diào)用腳本時(shí),會(huì)出現(xiàn)一條消息,表明文件已更改。 當(dāng)然,您可以將任何程序放在這里。 我們將其稱為sniff_printer,因?yàn)槲覀冋谕ㄟ^該服務(wù)嗅探文件更改,并且我們將打印一些數(shù)據(jù)。
那么這是怎么發(fā)生的呢? 首先,我們通過unit – myservice_test.service定義新服務(wù),并實(shí)現(xiàn)以下腳本:
# /etc/systemd/system/myservice_test.service
[Unit]
Description=This service is triggered through a file change
[Service]
Type=oneshot
ExecStart=bash /home/oem/sniff_printer.sh
[Install]
WantedBy=multi-user.target
其次,我們通過另一個(gè)名為myservice_test.path的單元描述我們正在監(jiān)視的文件路徑,該單元通過以下代碼實(shí)現(xiàn):
# /etc/systemd/system/myservice_test.path
[Unit]
Description=Path unit for watching for changes in "config"
[Path]
PathModified=/etc/test_config/config
Unit=myservice_test.service
[Install]
WantedBy=multi-user.target
將所有這些部分組合在一起,我們得到一個(gè)可以打印出簡(jiǎn)單消息的服務(wù)。 每當(dāng)提供的文件更新時(shí)都會(huì)觸發(fā)它。 讓我們看看進(jìn)展如何。 當(dāng)我們向服務(wù)目錄添加新文件時(shí),我們必須執(zhí)行重新加載:
$ systemctl daemon-reload
$ systemctl enable myservice_test
Created symlink /etc/systemd/system/multi-user.target.wants/myservice_test.service → /etc/systemd/system/myservice_test.service.
(base) andrew@andrew-YTF-XXX:~/code$ sudo systemctl start myservice_test
(base) andrew@andrew-YTF-XXX:~/code$ systemctl status myservice_test
○ myservice_test.service - This service is triggered through a file change
Loaded: loaded (/etc/systemd/system/myservice_test.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Wed 2023-12-06 17:26:22 CST; 10s ago
Process: 1854871 ExecStart=bash /home/andrew/sniff_printer.sh (code=exited, status=0/SUCCESS)
Main PID: 1854871 (code=exited, status=0/SUCCESS)
CPU: 5ms
12月 06 17:26:22 andrew-YTF-XXX systemd[1]: Starting This service is triggered through a file change...
12月 06 17:26:22 andrew-YTF-XXX bash[1854871]: File /etc/test_config/config changed!
12月 06 17:26:22 andrew-YTF-XXX systemd[1]: myservice_test.service: Deactivated successfully.
12月 06 17:26:22 andrew-YTF-XXX systemd[1]: Finished This service is triggered through a file change.
$ systemctl start myservice_test
$ systemctl status myservice_test
○ myservice_test.service - This service is triggered through a file change
Loaded: loaded (/etc/systemd/system/myservice_test.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Wed 2023-12-06 17:47:54 CST; 16s ago
Process: 1855368 ExecStart=bash /home/andrew/sniff_printer.sh (code=exited, status=0/SUCCESS)
Main PID: 1855368 (code=exited, status=0/SUCCESS)
CPU: 4ms
12月 06 17:47:54 andrew-YTF-XXX systemd[1]: Starting This service is triggered through a file change...
12月 06 17:47:54 andrew-YTF-XXX bash[1855368]: File /etc/test_config/config changed!
12月 06 17:47:54 andrew-YTF-XXX systemd[1]: myservice_test.service: Deactivated successfully.
12月 06 17:47:54 andrew-YTF-XXX systemd[1]: Finished This service is triggered through a file change.
我們需要通過一些文本編輯器更新該文件,例如:
$ vim /etc/test_config/config
$ systemctl status myservice_test
● myservice_test.service
...
但該進(jìn)程不再處于活動(dòng)狀態(tài),因?yàn)榉?wù)單元的類型為oneshot,因此只有另一個(gè)文件更新才會(huì)重新觸發(fā)它。 我們相信這個(gè)示例提供了如何在系統(tǒng)運(yùn)行時(shí)創(chuàng)建和啟動(dòng)守護(hù)進(jìn)程的簡(jiǎn)單解釋。 請(qǐng)隨意嘗試自己并嘗試不同的單位類型或選項(xiàng)。
參考資料
- 軟件測(cè)試精品書籍文檔下載持續(xù)更新 https://github.com/china-testing/python-testing-examples 請(qǐng)點(diǎn)贊,謝謝!
- 本文涉及的python測(cè)試開發(fā)庫 謝謝點(diǎn)贊! https://github.com/china-testing/python_cn_resouce
- python精品書籍下載 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品書籍下載 https://www.cnblogs.com/testing-/p/17438558.html
- https://wiki.qt.io/Building_Qt_Creator_from_Git_on_Ubuntu_22.04
1.6 POSIX
POSIX標(biāo)準(zhǔn)的主要任務(wù)是維護(hù)不同操作系統(tǒng)之間的兼容性。 因此,POSIX 被標(biāo)準(zhǔn)應(yīng)用軟件開發(fā)人員和系統(tǒng)程序員頻繁使用。 如今,它不僅可以在類Unix操作系統(tǒng)上找到,還可以在Windows環(huán)境中找到,例如Cygwin、MinGW和 Windows Subsystem for Linux (WSL)。 POSIX定義了系統(tǒng)級(jí)和用戶級(jí)API,但有一點(diǎn)是:使用POSIX,程序員不需要區(qū)分系統(tǒng)調(diào)用和庫函數(shù)。
POSIX API經(jīng)常在C編程語言中使用。 因此它可以與C++編譯。 在系統(tǒng)編程的幾個(gè)重要領(lǐng)域中,為系統(tǒng)調(diào)用接口提供了附加功能:文件操作、內(nèi)存管理、進(jìn)程和線程控制、網(wǎng)絡(luò)和通信以及正則表達(dá)式 - 正如您所看到的,它幾乎涵蓋了已經(jīng)提供的所有內(nèi)容。 現(xiàn)有的系統(tǒng)調(diào)用可以。 只是不要感到困惑并認(rèn)為情況總是如此。
與每個(gè)標(biāo)準(zhǔn)一樣,POSIX有多個(gè)版本,您必須了解系統(tǒng)中存在哪一個(gè)版本。 它還可以是某些環(huán)境子系統(tǒng)的一部分,例如Windows的Microsoft POSIX 子系統(tǒng)。 這是一個(gè)關(guān)鍵的評(píng)論,因?yàn)榄h(huán)境本身可能不會(huì)向您公開整個(gè)界面。 原因之一可能是系統(tǒng)的安全評(píng)估。
隨著POSIX的發(fā)展,代碼質(zhì)量的規(guī)則已經(jīng)建立。 其中一些與多線程內(nèi)存訪問、同步機(jī)制和并發(fā)執(zhí)行、安全和訪問限制以及類型安全有關(guān)。 POSIX軟件需求中的一個(gè)著名概念是“一次編寫,隨處采用”。
標(biāo)準(zhǔn)定義和目標(biāo)它的應(yīng)用有四個(gè)主要領(lǐng)域,稱為卷:
- 基本定義:規(guī)范的主要定義:語法、概念、術(shù)語和服務(wù)操作
- 系統(tǒng)接口:接口描述和定義的可用性
- 實(shí)用程序:Shell、命令和實(shí)用程序描述
- 理由:版本信息和歷史數(shù)據(jù)
盡管如此,在本書中我們的重點(diǎn)主要是POSIX作為系統(tǒng)調(diào)用的一種不同方法。 在接下來的章節(jié)中,我們將看到使用消息隊(duì)列、信號(hào)量、共享內(nèi)存或線程等對(duì)象的通用模式的好處。 一個(gè)顯著的改進(jìn)是函數(shù)調(diào)用及其命名約定的簡(jiǎn)單性。 例如shm_open()、mq_open()和sem_open() 分別用于創(chuàng)建和打開共享內(nèi)存對(duì)象、消息隊(duì)列和信號(hào)量。 他們的相似之處是顯而易見的。POSIX 中類似的想法受到系統(tǒng)程序員的歡迎。API 也是公開的,并且有大量的社區(qū)貢獻(xiàn)。 此外POSIX還提供了一個(gè)對(duì)象接口,例如互斥體,這在Unix上并不常見。 然而,在后面的章節(jié)中,我們將建議讀者更多地關(guān)注C++20功能,這是有充分理由的,所以請(qǐng)耐心等待。
使用POSIX允許軟件工程師概括他們的操作系統(tǒng)相關(guān)代碼并將其聲明為不特定于操作系統(tǒng)。 這樣可以更輕松、更快速地重新集成軟件,從而縮短上市時(shí)間。 系統(tǒng)程序員還可以輕松地從一個(gè)系統(tǒng)切換到另一個(gè)系統(tǒng),同時(shí)仍然編寫相同類型的代碼。文章來源:http://www.zghlxwxcb.cn/news/detail-750637.html
1.7 概括
在本章中,我們介紹了與操作系統(tǒng)相關(guān)的基本概念的定義。 您已經(jīng)了解了Linux的主要內(nèi)核結(jié)構(gòu)及其對(duì)軟件設(shè)計(jì)的期望。 簡(jiǎn)要介紹了實(shí)時(shí)操作系統(tǒng),還介紹了系統(tǒng)調(diào)用的定義、系統(tǒng)調(diào)用接口和POSIX。 我們還奠定了多處理和多線程的基礎(chǔ)。 在下一章中,我們將討論作為主要資源用戶和管理者的進(jìn)程。 我們將從一些C++20代碼開始。 通過本文,您將了解Linux的進(jìn)程內(nèi)存布局、操作系統(tǒng)的進(jìn)程調(diào)度機(jī)制,以及多處理如何運(yùn)行以及它帶來的挑戰(zhàn)。 您還將了解一些有關(guān)原子操作的有趣事實(shí)。文章來源地址http://www.zghlxwxcb.cn/news/detail-750637.html
到了這里,關(guān)于Linux系統(tǒng)C++程序設(shè)計(jì)1-Linux系統(tǒng)和POSIX 標(biāo)準(zhǔn)入門的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!