偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Linux系統(tǒng)調(diào)用全面解析:連接用戶與內(nèi)核的橋梁

系統(tǒng) Linux
fork系統(tǒng)調(diào)用用于創(chuàng)建新的進程,使得系統(tǒng)能夠同時運行多個任務(wù) 。exec系列系統(tǒng)調(diào)用(這里使用的是execvp)用于在子進程中執(zhí)行新的程序,實現(xiàn)了程序的替換和執(zhí)行 。wait系統(tǒng)調(diào)用用于父進程等待子進程的結(jié)束,以便父進程能夠獲取子進程的執(zhí)行結(jié)果,進行后續(xù)的處理 。這些系統(tǒng)調(diào)用相互配合,實現(xiàn)了進程的創(chuàng)建、執(zhí)行和管理,是 Linux 系統(tǒng)多進程編程的基礎(chǔ) 。

其實系統(tǒng)調(diào)用沒那么復(fù)雜:它就像用戶態(tài)與內(nèi)核態(tài)之間的 “橋梁”,每一次調(diào)用都是一次 “合規(guī)的資源申請”。搞懂它的原理,不僅能幫你快速定位errno錯誤、優(yōu)化程序性能,更能理解 Linux 系統(tǒng) “隔離與協(xié)作” 的設(shè)計思想。當(dāng)我們在 Linux 系統(tǒng)上運行程序時,程序經(jīng)常需要與操作系統(tǒng)內(nèi)核進行交互,以獲取某些資源或執(zhí)行特定的任務(wù),比如讀寫文件、創(chuàng)建進程、分配內(nèi)存等 。

而 Linux 系統(tǒng)調(diào)用,就是用戶程序與操作系統(tǒng)內(nèi)核之間溝通的橋梁,是一種讓用戶程序能夠訪問操作系統(tǒng)內(nèi)核提供的服務(wù)的機制。它在 Linux 系統(tǒng)中處于極為關(guān)鍵的地位,對于理解操作系統(tǒng)的工作原理、編寫高效穩(wěn)定的應(yīng)用程序,以及進行系統(tǒng)級的開發(fā)和優(yōu)化都起著不可或缺的作用。接下來,就讓我們一起深入探索 Linux 系統(tǒng)調(diào)用的奧秘吧。

一、Linux 系統(tǒng)調(diào)用是什么?

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

在 Linux 系統(tǒng)中,系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核為用戶空間的程序提供的一組接口,它就像是一扇特殊的 “門”,讓用戶程序能夠請求內(nèi)核執(zhí)行一些特權(quán)操作,比如訪問硬件資源、管理文件系統(tǒng)、控制進程等 。簡單來說,系統(tǒng)調(diào)用是用戶程序與操作系統(tǒng)內(nèi)核之間進行交互的一種機制。

你可以把 Linux 系統(tǒng)想象成一個龐大且復(fù)雜的工廠,內(nèi)核就是工廠的核心管理部門,負責(zé)調(diào)配所有的資源和運作。而用戶程序則像是工廠里的普通工人,他們需要完成各種任務(wù),但沒有直接調(diào)配核心資源的權(quán)限。當(dāng)工人(用戶程序)需要某些特殊資源或執(zhí)行特定任務(wù)時,就必須通過系統(tǒng)調(diào)用這個 “傳話筒” 向管理部門(內(nèi)核)提出請求 。

1.2系統(tǒng)調(diào)用的重要性

系統(tǒng)調(diào)用在操作系統(tǒng)中占據(jù)著舉足輕重的地位,是操作系統(tǒng)提供服務(wù)和實現(xiàn)功能的基礎(chǔ)機制,在資源管理、進程控制、文件操作、設(shè)備管理等眾多方面發(fā)揮著關(guān)鍵作用。

在資源管理方面,系統(tǒng)調(diào)用允許用戶程序請求內(nèi)核分配和管理系統(tǒng)資源,如內(nèi)存、CPU 時間片、文件描述符等。通過系統(tǒng)調(diào)用,內(nèi)核可以根據(jù)系統(tǒng)的整體狀態(tài)和資源使用情況,合理地分配資源,確保各個進程能夠公平、有效地使用資源,避免資源沖突和浪費。比如,當(dāng)一個程序需要使用內(nèi)存時,它會通過系統(tǒng)調(diào)用向內(nèi)核請求分配一定大小的內(nèi)存空間,內(nèi)核會在內(nèi)存管理模塊中進行相應(yīng)的處理,為程序分配合適的內(nèi)存塊,并返回一個指向該內(nèi)存塊的指針,供程序使用。

進程控制是操作系統(tǒng)的核心功能之一,系統(tǒng)調(diào)用為進程的創(chuàng)建、執(zhí)行、終止和等待等操作提供了接口。例如,fork () 系統(tǒng)調(diào)用用于創(chuàng)建一個新的進程,它是一個與父進程幾乎完全相同的子進程,擁有自己獨立的進程標識符(PID)。父進程可以通過 wait () 或 waitpid () 系統(tǒng)調(diào)用等待子進程結(jié)束,并獲取子進程的退出狀態(tài),以便進行后續(xù)的處理。這些系統(tǒng)調(diào)用使得開發(fā)者能夠方便地控制進程的生命周期,實現(xiàn)多進程編程,提高程序的并發(fā)處理能力。

文件操作是日常計算機使用中最常見的任務(wù)之一,系統(tǒng)調(diào)用為文件的打開、關(guān)閉、讀取、寫入、定位等操作提供了統(tǒng)一的接口。應(yīng)用程序通過 open () 系統(tǒng)調(diào)用打開一個文件,并獲得一個文件描述符,后續(xù)對文件的所有操作都通過這個文件描述符進行。write () 系統(tǒng)調(diào)用用于向文件中寫入數(shù)據(jù),read () 系統(tǒng)調(diào)用則用于從文件中讀取數(shù)據(jù)。這些系統(tǒng)調(diào)用屏蔽了底層文件系統(tǒng)的復(fù)雜性,使得開發(fā)者可以專注于文件操作的邏輯,而不必關(guān)心具體的文件存儲和訪問方式。

在設(shè)備管理方面,系統(tǒng)調(diào)用為應(yīng)用程序提供了訪問硬件設(shè)備的能力。無論是磁盤、打印機、網(wǎng)絡(luò)接口等設(shè)備,都可以通過相應(yīng)的系統(tǒng)調(diào)用來進行控制和交互。例如,ioctl () 系統(tǒng)調(diào)用常用于對設(shè)備進行控制操作,如設(shè)置設(shè)備的參數(shù)、獲取設(shè)備的狀態(tài)信息等。通過系統(tǒng)調(diào)用,應(yīng)用程序可以與各種硬件設(shè)備進行通信,實現(xiàn)設(shè)備的驅(qū)動和管理,使得操作系統(tǒng)能夠充分發(fā)揮硬件設(shè)備的功能。

1.3為什么需要系統(tǒng)調(diào)用

linux內(nèi)核中設(shè)置了一組用于實現(xiàn)系統(tǒng)功能的子程序,稱為系統(tǒng)調(diào)用。系統(tǒng)調(diào)用和普通庫函數(shù)調(diào)用非常相似,只是系統(tǒng)調(diào)用由操作系統(tǒng)核心提供,運行于內(nèi)核態(tài),而普通的函數(shù)調(diào)用由函數(shù)庫或用戶自己提供,運行于用戶態(tài)。

一般的,進程是不能訪問內(nèi)核的。它不能訪問內(nèi)核所占內(nèi)存空間也不能調(diào)用內(nèi)核函數(shù)。CPU硬件決定了這些(這就是為什么它被稱作“保護模式”)。

為了和用戶空間上運行的進程進行交互,內(nèi)核提供了一組接口。透過該接口,應(yīng)用程序可以訪問硬件設(shè)備和其他操作系統(tǒng)資源。這組接口在應(yīng)用程序和內(nèi)核之間扮演了使者的角色,應(yīng)用程序發(fā)送各種請求,而內(nèi)核負責(zé)滿足這些請求(或者讓應(yīng)用程序暫時擱置)。實際上提供這組接口主要是為了保證系統(tǒng)穩(wěn)定可靠,避免應(yīng)用程序肆意妄行,惹出大麻煩。

系統(tǒng)調(diào)用在用戶空間進程和硬件設(shè)備之間添加了一個中間層,該層主要作用有三個:

  1. 它為用戶空間提供了一種統(tǒng)一的硬件的抽象接口。比如當(dāng)需要讀些文件的時候,應(yīng)用程序就可以不去管磁盤類型和介質(zhì),甚至不用去管文件所在的文件系統(tǒng)到底是哪種類型。
  2. 系統(tǒng)調(diào)用保證了系統(tǒng)的穩(wěn)定和安全。作為硬件設(shè)備和應(yīng)用程序之間的中間人,內(nèi)核可以基于權(quán)限和其他一些規(guī)則對需要進行的訪問進行裁決。舉例來說,這樣可以避免應(yīng)用程序不正確地使用硬件設(shè)備,竊取其他進程的資源,或做出其他什么危害系統(tǒng)的事情。
  3. 每個進程都運行在虛擬系統(tǒng)中,而在用戶空間和系統(tǒng)的其余部分提供這樣一層公共接口,也是出于這種考慮。如果應(yīng)用程序可以隨意訪問硬件而內(nèi)核又對此一無所知的話,幾乎就沒法實現(xiàn)多任務(wù)和虛擬內(nèi)存,當(dāng)然也不可能實現(xiàn)良好的穩(wěn)定性和安全性。在Linux中,系統(tǒng)調(diào)用是用戶空間訪問內(nèi)核的惟一手段;除異常和中斷外,它們是內(nèi)核惟一的合法入口。

二、用戶空間與內(nèi)核空間

對 32 位操作系統(tǒng)而言,它的尋址空間(虛擬地址空間,或叫線性地址空間)為 4G(2的32次方)。也就是說一個進程的最大地址空間為 4G。操作系統(tǒng)的核心是內(nèi)核(kernel),它獨立于普通的應(yīng)用程序,可以訪問受保護的內(nèi)存空間,也有訪問底層硬件設(shè)備的所有權(quán)限。為了保證內(nèi)核的安全,現(xiàn)在的操作系統(tǒng)一般都強制用戶進程不能直接操作內(nèi)核。

具體的實現(xiàn)方式基本都是由操作系統(tǒng)將虛擬地址空間劃分為兩部分,一部分為內(nèi)核空間,另一部分為用戶空間。針對 Linux 操作系統(tǒng)而言,最高的 1G 字節(jié)(從虛擬地址 0xC0000000 到 0xFFFFFFFF)由內(nèi)核使用,稱為內(nèi)核空間。而較低的 3G 字節(jié)(從虛擬地址 0x00000000 到 0xBFFFFFFF)由各個進程使用,稱為用戶空間。

圖片圖片

2.1用戶空間

用戶空間是應(yīng)用程序運行的區(qū)域,它為應(yīng)用程序提供了一個相對獨立和受保護的執(zhí)行環(huán)境。在 32 位的 Linux 系統(tǒng)中,通常將虛擬地址空間的低 3GB 劃分為用戶空間,每個用戶進程都擁有自己獨立的用戶空間,這使得不同進程之間的地址空間相互隔離,一個進程的崩潰不會影響其他進程的正常運行,就像一個個獨立的小房間,每個房間里的活動互不干擾 。

用戶空間的應(yīng)用程序具有受限的訪問權(quán)限,它們無法直接操作硬件資源,如CPU、內(nèi)存、I/O設(shè)備等。這是因為硬件資源的直接操作需要較高的權(quán)限,而用戶空間的應(yīng)用程序運行在較低的特權(quán)級別(如 x86 架構(gòu)中的 Ring 3),以防止應(yīng)用程序?qū)ο到y(tǒng)造成破壞。如果把操作系統(tǒng)比作一個大型工廠,硬件資源就是工廠里的核心生產(chǎn)設(shè)備,而用戶空間的應(yīng)用程序就像是工廠里的普通工人,他們不能隨意操作核心生產(chǎn)設(shè)備,必須通過特定的流程(系統(tǒng)調(diào)用)向管理層(內(nèi)核)提出申請 。

2.2內(nèi)核空間

內(nèi)核空間是操作系統(tǒng)內(nèi)核的執(zhí)行區(qū)域,是操作系統(tǒng)的核心部分,負責(zé)管理系統(tǒng)的硬件資源和提供各種基本服務(wù)。在 32 位的 Linux 系統(tǒng)中,虛擬地址空間的高 1GB 通常被劃分為內(nèi)核空間,所有進程共享這部分內(nèi)核空間。內(nèi)核空間就像是工廠的管理層,擁有最高的權(quán)限,可以直接訪問和控制所有硬件資源,執(zhí)行特權(quán)指令。

內(nèi)核空間擁有完全控制硬件資源的權(quán)限,它可以直接操作 CPU、內(nèi)存、I/O 設(shè)備等,負責(zé)進程調(diào)度、內(nèi)存管理、文件系統(tǒng)管理、設(shè)備驅(qū)動等關(guān)鍵功能。例如,在內(nèi)核空間中,內(nèi)核可以根據(jù)進程的優(yōu)先級和資源需求,合理地分配 CPU 時間片,確保各個進程能夠公平地使用 CPU 資源;內(nèi)核還負責(zé)管理物理內(nèi)存和虛擬內(nèi)存之間的映射關(guān)系,為進程分配和回收內(nèi)存;此外,內(nèi)核還通過設(shè)備驅(qū)動程序與各種硬件設(shè)備進行交互,實現(xiàn)對硬件設(shè)備的控制和管理。

2.3為什么需要區(qū)分內(nèi)核空間與用戶空間

在 CPU 的所有指令中,有些指令是非常危險的,如果錯用,將導(dǎo)致系統(tǒng)崩潰,比如清內(nèi)存、設(shè)置時鐘等。如果允許所有的程序都可以使用這些指令,那么系統(tǒng)崩潰的概率將大大增加。所以,CPU 將指令分為特權(quán)指令和非特權(quán)指令,對于那些危險的指令,只允許操作系統(tǒng)及其相關(guān)模塊使用,普通應(yīng)用程序只能使用那些不會造成災(zāi)難的指令。比如 Intel 的 CPU 將特權(quán)等級分為 4 個級別:Ring0~Ring3。

其實 Linux 系統(tǒng)只使用了 Ring0 和 Ring3 兩個運行級別(Windows 系統(tǒng)也是一樣的)。當(dāng)進程運行在 Ring3 級別時被稱為運行在用戶態(tài),而運行在 Ring0 級別時被稱為運行在內(nèi)核態(tài)。

當(dāng)進程運行在內(nèi)核空間時就處于內(nèi)核態(tài),而進程運行在用戶空間時則處于用戶態(tài)。

在內(nèi)核態(tài)下,進程運行在內(nèi)核地址空間中,此時 CPU 可以執(zhí)行任何指令。運行的代碼也不受任何的限制,可以自由地訪問任何有效地址,也可以直接進行端口的訪問。

在用戶態(tài)下,進程運行在用戶地址空間中,被執(zhí)行的代碼要受到 CPU 的諸多檢查,它們只能訪問映射其地址空間的頁表項中規(guī)定的在用戶態(tài)下可訪問頁面的虛擬地址,且只能對任務(wù)狀態(tài)段(TSS)中 I/O 許可位圖(I/O Permission Bitmap)中規(guī)定的可訪問端口進行直接訪問。

對于以前的 DOS 操作系統(tǒng)來說,是沒有內(nèi)核空間、用戶空間以及內(nèi)核態(tài)、用戶態(tài)這些概念的??梢哉J為所有的代碼都是運行在內(nèi)核態(tài)的,因而用戶編寫的應(yīng)用程序代碼可以很容易的讓操作系統(tǒng)崩潰掉。

對于 Linux 來說,通過區(qū)分內(nèi)核空間和用戶空間的設(shè)計,隔離了操作系統(tǒng)代碼(操作系統(tǒng)的代碼要比應(yīng)用程序的代碼健壯很多)與應(yīng)用程序代碼。即便是單個應(yīng)用程序出現(xiàn)錯誤也不會影響到操作系統(tǒng)的穩(wěn)定性,這樣其它的程序還可以正常的運行(Linux 可是個多任務(wù)系統(tǒng)??!)。

所以,區(qū)分內(nèi)核空間和用戶空間本質(zhì)上是要提高操作系統(tǒng)的穩(wěn)定性及可用性

三、系統(tǒng)調(diào)用的執(zhí)行過程

Linux 系統(tǒng)調(diào)用的執(zhí)行過程涉及多個關(guān)鍵步驟,從用戶程序發(fā)起調(diào)用,到內(nèi)核進行處理,再到最終返回結(jié)果給用戶程序,每一步都至關(guān)重要。接下來,我們將詳細探討 Linux 系統(tǒng)調(diào)用的執(zhí)行過程。

3.1準備階段

在用戶程序調(diào)用系統(tǒng)調(diào)用之前,首先需要設(shè)置相關(guān)參數(shù),這些參數(shù)包括系統(tǒng)調(diào)用號以及其他與具體系統(tǒng)調(diào)用相關(guān)的參數(shù)。系統(tǒng)調(diào)用號是一個唯一的標識符,用于標識不同的系統(tǒng)調(diào)用。每個系統(tǒng)調(diào)用都被分配了一個特定的編號,就像圖書館里的每一本書都有一個唯一的編號一樣,方便快速查找和識別 。

在 x86 架構(gòu)中,系統(tǒng)調(diào)用號通常通過 EAX 寄存器傳遞給內(nèi)核。例如,對于常見的 read 系統(tǒng)調(diào)用,其系統(tǒng)調(diào)用號為 3(在不同架構(gòu)和內(nèi)核版本中可能會有所不同)。除了系統(tǒng)調(diào)用號,還需要傳遞其他參數(shù),如 read 系統(tǒng)調(diào)用需要傳遞文件描述符(通過 EBX 寄存器傳遞)、緩沖區(qū)指針(通過 ECX 寄存器傳遞)和讀取的字節(jié)數(shù)(通過 EDX 寄存器傳遞) 。

3.2觸發(fā)系統(tǒng)調(diào)用

用戶程序設(shè)置好參數(shù)后,需要通過特定的指令來觸發(fā)系統(tǒng)調(diào)用,從而進入內(nèi)核態(tài)執(zhí)行相應(yīng)的操作。在 x86 架構(gòu)中,傳統(tǒng)上常用的指令是 int 0x80,這是一條軟中斷指令。當(dāng) CPU 執(zhí)行到 int 0x80 指令時,會產(chǎn)生一個軟件中斷,從而觸發(fā)系統(tǒng)調(diào)用機制。隨著技術(shù)的發(fā)展,x86 架構(gòu)從內(nèi)核 2.6.11 版本開始引入了 syscall 指令,它比 int 0x80 指令具有更高的性能。syscall 指令通過特定的寄存器(如 RAX、RDI、RSI 等)來傳遞系統(tǒng)調(diào)用號和參數(shù),能夠更高效地實現(xiàn)從用戶態(tài)到內(nèi)核態(tài)的切換 。

除了 x86 架構(gòu),其他架構(gòu)也有各自對應(yīng)的觸發(fā)系統(tǒng)調(diào)用的方式。在 ARM 架構(gòu)中,通常使用 svc(Supervisor Call)指令來觸發(fā)系統(tǒng)調(diào)用,該指令會導(dǎo)致處理器進入特權(quán)模式,從而執(zhí)行內(nèi)核中的系統(tǒng)調(diào)用處理程序 。

3.3進入內(nèi)核態(tài)

當(dāng)用戶程序觸發(fā)系統(tǒng)調(diào)用后,CPU 會從用戶態(tài)切換到內(nèi)核態(tài),這是一個關(guān)鍵的狀態(tài)轉(zhuǎn)換過程。在這個過程中,CPU 會保存當(dāng)前用戶程序的上下文信息,包括寄存器的值、程序計數(shù)器(PC)等,以便在系統(tǒng)調(diào)用完成后能夠恢復(fù)用戶程序的執(zhí)行。

CPU 通過中斷向量表來確定系統(tǒng)調(diào)用處理程序的入口地址。中斷向量表是一個存儲中斷處理程序入口地址的表格,每個中斷向量對應(yīng)一個特定的中斷類型。對于系統(tǒng)調(diào)用,會有一個特定的中斷向量(如 x86 架構(gòu)中 int 0x80 對應(yīng)的中斷向量),CPU 根據(jù)這個中斷向量在中斷向量表中查找對應(yīng)的系統(tǒng)調(diào)用處理程序的入口地址,然后跳轉(zhuǎn)到該地址開始執(zhí)行內(nèi)核代碼 。

3.4參數(shù)檢查與處理

在內(nèi)核開始執(zhí)行系統(tǒng)調(diào)用之前,需要對用戶程序傳遞過來的參數(shù)進行合法性檢查,這是確保系統(tǒng)安全性和穩(wěn)定性的重要環(huán)節(jié)。內(nèi)核會檢查參數(shù)的類型、范圍、有效性等,以防止用戶程序傳遞非法參數(shù)導(dǎo)致系統(tǒng)崩潰或出現(xiàn)安全漏洞。

對于涉及指針的參數(shù),內(nèi)核會檢查指針是否指向合法的內(nèi)存地址,并且該內(nèi)存地址是否屬于用戶程序的地址空間,防止用戶程序通過傳遞非法指針來訪問內(nèi)核空間或其他進程的內(nèi)存空間。對于文件描述符參數(shù),內(nèi)核會檢查文件描述符是否有效,是否指向一個已經(jīng)打開的文件,以及當(dāng)前進程是否具有對該文件的相應(yīng)操作權(quán)限 。

3.5執(zhí)行系統(tǒng)調(diào)用函數(shù)

內(nèi)核完成參數(shù)檢查后,會根據(jù)系統(tǒng)調(diào)用號從系統(tǒng)調(diào)用表中找到對應(yīng)的系統(tǒng)調(diào)用函數(shù),并執(zhí)行該函數(shù)。系統(tǒng)調(diào)用表是一個存儲系統(tǒng)調(diào)用函數(shù)指針的數(shù)組,每個系統(tǒng)調(diào)用號對應(yīng)數(shù)組中的一個元素,該元素指向相應(yīng)的系統(tǒng)調(diào)用函數(shù)。

在 x86 架構(gòu)的 32 位系統(tǒng)中,系統(tǒng)調(diào)用表位于 arch/x86/kernel/syscall_table_32.S 文件中;在 64 位系統(tǒng)中,系統(tǒng)調(diào)用表位于 arch/x86/entry/syscalls/syscall_64.tbl 文件中。例如,對于 read 系統(tǒng)調(diào)用,內(nèi)核會在系統(tǒng)調(diào)用表中找到對應(yīng)的函數(shù)指針,然后調(diào)用該函數(shù)來執(zhí)行實際的讀取操作。在執(zhí)行過程中,內(nèi)核會根據(jù)用戶傳遞的參數(shù),如文件描述符、緩沖區(qū)指針和讀取字節(jié)數(shù),來完成文件讀取任務(wù),并將讀取的數(shù)據(jù)存儲到用戶指定的緩沖區(qū)中 。

3.6返回用戶態(tài)

當(dāng)系統(tǒng)調(diào)用函數(shù)執(zhí)行完成后,內(nèi)核會將結(jié)果返回給用戶程序,并將 CPU 從內(nèi)核態(tài)切換回用戶態(tài)。內(nèi)核將系統(tǒng)調(diào)用的返回值存儲在特定的寄存器中(如 x86 架構(gòu)中通過 EAX 寄存器返回),然后恢復(fù)之前保存的用戶程序的上下文信息,包括寄存器的值、程序計數(shù)器等。

CPU 根據(jù)恢復(fù)的程序計數(shù)器的值,跳轉(zhuǎn)到用戶程序中系統(tǒng)調(diào)用指令的下一條指令處繼續(xù)執(zhí)行,從而完成系統(tǒng)調(diào)用的整個過程。如果系統(tǒng)調(diào)用執(zhí)行過程中發(fā)生錯誤,內(nèi)核會返回一個錯誤碼,用戶程序可以通過檢查返回值來判斷系統(tǒng)調(diào)用是否成功,并根據(jù)錯誤碼進行相應(yīng)的錯誤處理 。

四、Linux 系統(tǒng)調(diào)用的分類

Linux 系統(tǒng)調(diào)用涵蓋了眾多功能領(lǐng)域,根據(jù)其功能特點,可以大致分為以下幾類。

4.1文件操作系統(tǒng)調(diào)用

文件操作系統(tǒng)調(diào)用主要用于對文件和文件系統(tǒng)進行操作,是實現(xiàn)文件管理和數(shù)據(jù)存儲的基礎(chǔ)。其中,open函數(shù)用于打開或創(chuàng)建文件,在打開或創(chuàng)建文件時可以指定文件的屬性及用戶的權(quán)限等各種參數(shù)。比如我們想要打開一個名為 “example.txt” 的文件進行讀寫操作,可以這樣使用:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }
    // 后續(xù)對文件的操作
    close(fd);
    return 0;
}

在這個例子中,O_RDWR表示以讀寫方式打開文件,O_CREAT表示如果文件不存在則創(chuàng)建它,0644是文件的權(quán)限設(shè)置。

read函數(shù)用于從文件中讀取數(shù)據(jù),write函數(shù)則用于向文件寫入數(shù)據(jù)。例如,我們從一個文件中讀取數(shù)據(jù)并寫入到另一個文件中:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024

int main() {
    int source_fd, target_fd;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read, bytes_written;

    source_fd = open("source.txt", O_RDONLY);
    if (source_fd == -1) {
        perror("Error opening source file");
        return 1;
    }

    target_fd = open("target.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (target_fd == -1) {
        perror("Error opening target file");
        close(source_fd);
        return 1;
    }

    while ((bytes_read = read(source_fd, buffer, BUFFER_SIZE)) > 0) {
        bytes_written = write(target_fd, buffer, bytes_read);
        if (bytes_written == -1) {
            perror("Error writing to target file");
            break;
        }
    }

    if (bytes_read == -1) {
        perror("Error reading source file");
    }

    close(source_fd);
    close(target_fd);
    return 0;
}

close函數(shù)用于關(guān)閉一個已打開的文件,釋放文件描述符,確保系統(tǒng)資源的合理利用。在上述代碼中,我們在文件操作完成后,都使用close函數(shù)關(guān)閉了文件。

4.2進程控制系統(tǒng)調(diào)用

進程控制系統(tǒng)調(diào)用用于管理進程的生命周期,包括進程的創(chuàng)建、執(zhí)行、等待和終止等操作,是實現(xiàn)多任務(wù)處理和進程間協(xié)作的關(guān)鍵。fork函數(shù)是用于創(chuàng)建新進程的系統(tǒng)調(diào)用,它會創(chuàng)建一個與調(diào)用進程(父進程)幾乎完全相同的子進程。子進程復(fù)制父進程的代碼段、數(shù)據(jù)段、堆、棧等資源,并擁有自己獨立的進程 ID(PID),但與父進程共享一些資源,如打開的文件描述符 。例如:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    pid_t pid;

    pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子進程
        printf("I am the child process, my pid is %d, my parent's pid is %d\n", getpid(), getppid());
    } else {
        // 父進程
        printf("I am the parent process, my pid is %d, and my child's pid is %d\n", getpid(), pid);
    }

    return 0;
}

在這個例子中,fork函數(shù)被調(diào)用后,會返回兩次,一次在父進程中返回子進程的 PID,一次在子進程中返回 0。通過判斷返回值,我們可以區(qū)分父子進程,并執(zhí)行不同的代碼邏輯。

exec系列函數(shù)用于在當(dāng)前進程的上下文中執(zhí)行一個新的程序,它會用新程序的代碼和數(shù)據(jù)替換當(dāng)前進程的代碼和數(shù)據(jù) 。例如,我們可以使用execlp函數(shù)來執(zhí)行一個外部命令:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    if (fork() == 0) {
        // 子進程
        if (execlp("ls", "ls", "-l", NULL) < 0) {
            perror("execlp error");
            exit(1);
        }
    }
    return 0;
}

在這個例子中,execlp函數(shù)在子進程中執(zhí)行了 “l(fā)s -l” 命令,列出當(dāng)前目錄下的文件和目錄信息。如果execlp函數(shù)執(zhí)行成功,它不會返回;只有在執(zhí)行失敗時,才會返回并設(shè)置錯誤信息。

wait系列函數(shù)用于等待子進程的結(jié)束,并獲取子進程的退出狀態(tài) 。例如,父進程可以使用wait函數(shù)來等待子進程結(jié)束:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main() {
    pid_t pid;
    int status;

    pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子進程
        sleep(2);
        printf("Child process is exiting\n");
        exit(0);
    } else {
        // 父進程
        wait(&status);
        if (WIFEXITED(status)) {
            printf("Child process exited normally with status %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("Child process was terminated by signal %d\n", WTERMSIG(status));
        }
    }

    return 0;
}

在這個例子中,父進程調(diào)用wait函數(shù)后,會被阻塞,直到子進程結(jié)束。wait函數(shù)返回時,通過WIFEXITED和WEXITSTATUS宏可以判斷子進程是否正常退出,并獲取其退出狀態(tài);通過WIFSIGNALED和WTERMSIG宏可以判斷子進程是否被信號終止,并獲取終止信號。

exit函數(shù)用于終止當(dāng)前進程的執(zhí)行,并將控制權(quán)返回給操作系統(tǒng) 。當(dāng)進程執(zhí)行到exit函數(shù)時,會進行一些清理工作,如關(guān)閉打開的文件描述符、釋放內(nèi)存等,然后終止進程。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("Before exit\n");
    exit(0);
    printf("This line will not be executed\n");
    return 0;
}

在這個例子中,當(dāng)執(zhí)行到exit(0)時,進程會立即終止,后面的 “This line will not be executed” 不會被輸出。

4.3內(nèi)存管理系統(tǒng)調(diào)用

內(nèi)存管理系統(tǒng)調(diào)用負責(zé)管理進程的內(nèi)存空間,包括內(nèi)存的分配、釋放和映射等操作,是確保程序高效運行和內(nèi)存資源合理利用的重要手段。mmap函數(shù)用于將文件或設(shè)備的內(nèi)容映射到進程的地址空間中,實現(xiàn)文件的內(nèi)存映射訪問或共享內(nèi)存 。例如,我們可以使用mmap函數(shù)將一個文件映射到內(nèi)存中,然后直接對內(nèi)存進行讀寫操作,而不需要通過read和write系統(tǒng)調(diào)用:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#define FILE_SIZE 1024

int main() {
    int fd;
    char *file_contents;
    struct stat file_stat;

    fd = open("example.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }

    if (fstat(fd, &file_stat) == -1) {
        perror("Error getting file status");
        close(fd);
        return 1;
    }

    file_contents = (char *)mmap(0, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (file_contents == MAP_FAILED) {
        perror("mmap error");
        close(fd);
        return 1;
    }

    // 對映射的內(nèi)存進行操作
    sprintf(file_contents, "This is a test\n");

    if (munmap(file_contents, FILE_SIZE) == -1) {
        perror("munmap error");
    }

    close(fd);
    return 0;
}

在這個例子中,mmap函數(shù)將 “example.txt” 文件的內(nèi)容映射到進程的地址空間中,返回一個指向映射內(nèi)存區(qū)域的指針file_contents。我們可以像操作普通內(nèi)存一樣對file_contents進行讀寫操作,操作完成后,使用munmap函數(shù)解除內(nèi)存映射。

brk和sbrk函數(shù)用于改變進程數(shù)據(jù)段的大小,從而實現(xiàn)內(nèi)存的分配和釋放 。brk函數(shù)通過設(shè)置進程數(shù)據(jù)段的結(jié)束地址來增加或減少內(nèi)存空間,sbrk函數(shù)則是在當(dāng)前數(shù)據(jù)段的基礎(chǔ)上,增加或減少指定大小的內(nèi)存空間。例如,使用sbrk函數(shù)分配一塊內(nèi)存:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    char *new_memory;

    new_memory = (char *)sbrk(1024);
    if (new_memory == (void *)-1) {
        perror("sbrk error");
        return 1;
    }

    // 使用分配的內(nèi)存
    sprintf(new_memory, "Allocated memory");
    printf("%s\n", new_memory);

    // 釋放內(nèi)存,這里通過將數(shù)據(jù)段大小減少1024字節(jié)來實現(xiàn)
    if (sbrk(-1024) == (void *)-1) {
        perror("sbrk error");
    }

    return 0;
}

在這個例子中,sbrk(1024)函數(shù)分配了 1024 字節(jié)的內(nèi)存空間,并返回指向該內(nèi)存區(qū)域的指針new_memory。我們可以對new_memory進行操作,操作完成后,通過sbrk(-1024)函數(shù)將數(shù)據(jù)段大小減少 1024 字節(jié),從而釋放之前分配的內(nèi)存。不過需要注意的是,brk和sbrk函數(shù)的使用相對底層,容易出錯,在實際應(yīng)用中,通常會使用更高級的內(nèi)存分配函數(shù),如malloc和free,它們是對brk和sbrk的封裝,提供了更方便和安全的內(nèi)存管理方式。

4.4設(shè)備控制系統(tǒng)調(diào)用

設(shè)備控制系統(tǒng)調(diào)用用于控制硬件設(shè)備的操作,實現(xiàn)設(shè)備與用戶程序之間的通信和交互,是拓展計算機系統(tǒng)功能和實現(xiàn)設(shè)備特定操作的重要途徑。ioctl函數(shù)是設(shè)備控制系統(tǒng)調(diào)用中的一個重要函數(shù),它允許用戶程序?qū)υO(shè)備進行特定的控制操作 。例如,對于串口設(shè)備,我們可以使用ioctl函數(shù)來設(shè)置串口的波特率、數(shù)據(jù)位、停止位等參數(shù):

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

int main() {
    int fd;
    struct termios options;

    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("Error opening serial port");
        return 1;
    }

    if (tcgetattr(fd, &options) == -1) {
        perror("Error getting serial port attributes");
        close(fd);
        return 1;
    }

    cfsetispeed(&options, B9600);  // 設(shè)置波特率為9600
    cfsetospeed(&options, B9600);

    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;  // 無校驗位
    options.c_cflag &= ~CSTOPB;  // 1位停止位
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;      // 8位數(shù)據(jù)位

    if (tcsetattr(fd, TCSANOW, &options) == -1) {
        perror("Error setting serial port attributes");
    }

    close(fd);
    return 0;
}

在這個例子中,ioctl函數(shù)通過tcgetattr和tcsetattr函數(shù)來獲取和設(shè)置串口設(shè)備的屬性。tcgetattr函數(shù)用于獲取串口的當(dāng)前屬性,存儲在options結(jié)構(gòu)體中;然后我們對options結(jié)構(gòu)體中的參數(shù)進行修改,設(shè)置波特率、校驗位、停止位和數(shù)據(jù)位等;最后使用tcsetattr函數(shù)將修改后的屬性設(shè)置到串口設(shè)備上。

對于其他設(shè)備,如攝像頭、網(wǎng)卡等,ioctl函數(shù)也有類似的應(yīng)用。例如,在視頻采集設(shè)備中,使用ioctl函數(shù)來設(shè)置攝像頭的分辨率、幀率等參數(shù);在網(wǎng)卡設(shè)備中,使用ioctl函數(shù)來配置網(wǎng)絡(luò)接口的 IP 地址、子網(wǎng)掩碼等 。不同的設(shè)備驅(qū)動程序會定義各自的ioctl命令,用戶程序通過這些命令來實現(xiàn)對設(shè)備的特定控制。

五、Linux下系統(tǒng)調(diào)用的三種方法

5.1通過 glibc 提供的庫函數(shù)

glibc 是 Linux 下使用的開源的標準 C 庫,它是 GNU 發(fā)布的 libc 庫,即運行時庫。glibc 為程序員提供豐富的 API(Application Programming Interface),除了例如字符串處理、數(shù)學(xué)運算等用戶態(tài)服務(wù)之外,最重要的是封裝了操作系統(tǒng)提供的系統(tǒng)服務(wù),即系統(tǒng)調(diào)用的封裝。那么glibc提供的系統(tǒng)調(diào)用API與內(nèi)核特定的系統(tǒng)調(diào)用之間的關(guān)系是什么呢?

通常情況,每個特定的系統(tǒng)調(diào)用對應(yīng)了至少一個 glibc 封裝的庫函數(shù),如系統(tǒng)提供的打開文件系統(tǒng)調(diào)用 sys_open 對應(yīng)的是 glibc 中的 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)用。

舉例來說,我們通過 glibc 提供的chmod 函數(shù)來改變文件 etc/passwd 的屬性為 444:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>

int main()
{
        int rc;

        rc = chmod("/etc/passwd", 0444);
        if (rc == -1)
                fprintf(stderr, "chmod failed, errno = %d\n", errno);
        else
                printf("chmod success!\n");
        return 0;
}

在普通用戶下編譯運用,輸出結(jié)果為:

chmod failed, errno = 1

上面系統(tǒng)調(diào)用返回的值為-1,說明系統(tǒng)調(diào)用失敗,錯誤碼為1,在 /usr/include/asm-generic/errno-base.h 文件中有如下錯誤代碼說明:

#define EPERM       1                /* Operation not permitted */

即無權(quán)限進行該操作,我們以普通用戶權(quán)限是無法修改 /etc/passwd 文件的屬性的,結(jié)果正確。

5.2使用 syscall 直接調(diào)用

使用上面的方法有很多好處,首先你無須知道更多的細節(jié),如 chmod 系統(tǒng)調(diào)用號,你只需了解 glibc 提供的 API 的原型;其次,該方法具有更好的移植性,你可以很輕松將該程序移植到其他平臺,或者將 glibc 庫換成其它庫,程序只需做少量改動。

但有點不足是,如果 glibc 沒有封裝某個內(nèi)核提供的系統(tǒng)調(diào)用時,我就沒辦法通過上面的方法來調(diào)用該系統(tǒng)調(diào)用。如我自己通過編譯內(nèi)核增加了一個系統(tǒng)調(diào)用,這時 glibc 不可能有你新增系統(tǒng)調(diào)用的封裝 API,此時我們可以利用 glibc 提供的syscall 函數(shù)直接調(diào)用。該函數(shù)定義在 unistd.h 頭文件中,函數(shù)原型如下:

long int syscall (long int sysno, ...)

sysno 是系統(tǒng)調(diào)用號,每個系統(tǒng)調(diào)用都有唯一的系統(tǒng)調(diào)用號來標識。在 sys/syscall.h 中有所有可能的系統(tǒng)調(diào)用號的宏定義。

... 為剩余可變長的參數(shù),為系統(tǒng)調(diào)用所帶的參數(shù),根據(jù)系統(tǒng)調(diào)用的不同,可帶0~5個不等的參數(shù),如果超過特定系統(tǒng)調(diào)用能帶的參數(shù),多余的參數(shù)被忽略。

返回值 該函數(shù)返回值為特定系統(tǒng)調(diào)用的返回值,在系統(tǒng)調(diào)用成功之后你可以將該返回值轉(zhuǎn)化為特定的類型,如果系統(tǒng)調(diào)用失敗則返回 -1,錯誤代碼存放在 errno 中。

還以上面修改 /etc/passwd 文件的屬性為例,這次使用 syscall 直接調(diào)用:

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
        int rc;
        rc = syscall(SYS_chmod, "/etc/passwd", 0444);

        if (rc == -1)
                fprintf(stderr, "chmod failed, errno = %d\n", errno);
        else
                printf("chmod succeess!\n");
        return 0;
}

在普通用戶下編譯執(zhí)行,輸出的結(jié)果與上例相同。

5.3通過 int 指令陷入

如果我們知道系統(tǒng)調(diào)用的整個過程的話,應(yīng)該就能知道用戶態(tài)程序通過軟中斷指令int 0x80 來陷入內(nèi)核態(tài)(在Intel Pentium II 又引入了sysenter指令),參數(shù)的傳遞是通過寄存器,eax 傳遞的是系統(tǒng)調(diào)用號,ebx、ecx、edx、esi和edi 來依次傳遞最多五個參數(shù),當(dāng)系統(tǒng)調(diào)用返回時,返回值存放在 eax 中。

仍然以上面的修改文件屬性為例,將調(diào)用系統(tǒng)調(diào)用那段寫成內(nèi)聯(lián)匯編代碼:

#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
        long rc;
        char *file_name = "/etc/passwd";
        unsigned short mode = 0444;

        asm(
                "int $0x80"
                : "=a" (rc)
                : "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
        );

        if ((unsigned long)rc >= (unsigned long)-132) {
                errno = -rc;
                rc = -1;
        }

        if (rc == -1)
                fprintf(stderr, "chmode failed, errno = %d\n", errno);
        else
                printf("success!\n");

        return 0;
}

如果 eax 寄存器存放的返回值(存放在變量 rc 中)在 -1~-132 之間,就必須要解釋為出錯碼(在/usr/include/asm-generic/errno.h 文件中定義的最大出錯碼為 132),這時,將錯誤碼寫入 errno 中,置系統(tǒng)調(diào)用返回值為 -1;否則返回的是 eax 中的值。

六、系統(tǒng)調(diào)用的實現(xiàn)機制

6.1系統(tǒng)調(diào)用的觸發(fā)

用戶程序運行在用戶態(tài),具有受限的權(quán)限,無法直接訪問操作系統(tǒng)內(nèi)核的資源和執(zhí)行特權(quán)操作。當(dāng)用戶程序需要操作系統(tǒng)提供的服務(wù)時,就需要觸發(fā)系統(tǒng)調(diào)用 。在 Linux 系統(tǒng)中,用戶程序通常通過軟中斷指令來觸發(fā)系統(tǒng)調(diào)用,將控制權(quán)交給內(nèi)核。

在 x86 架構(gòu)的 Linux 系統(tǒng)中,傳統(tǒng)的方式是使用int 0x80指令來觸發(fā)系統(tǒng)調(diào)用 。當(dāng) CPU 執(zhí)行到int 0x80指令時,會產(chǎn)生一個軟中斷,CPU 會暫停當(dāng)前用戶程序的執(zhí)行,保存當(dāng)前的程序狀態(tài)(如寄存器的值、程序計數(shù)器等),然后跳轉(zhuǎn)到內(nèi)核中預(yù)先定義好的中斷處理程序入口。在這個過程中,CPU 會從用戶態(tài)切換到內(nèi)核態(tài),從而使得內(nèi)核能夠處理系統(tǒng)調(diào)用請求。例如,在一個簡單的 C 程序中,如果要調(diào)用write系統(tǒng)調(diào)用來輸出一些信息到控制臺,在底層實現(xiàn)中,就可能會通過int 0x80指令來觸發(fā)系統(tǒng)調(diào)用,將write系統(tǒng)調(diào)用的相關(guān)參數(shù)傳遞給內(nèi)核,讓內(nèi)核執(zhí)行實際的寫操作 。

隨著硬件技術(shù)的發(fā)展,x86_64 架構(gòu)引入了更高效的syscall指令來觸發(fā)系統(tǒng)調(diào)用 。syscall指令相比int 0x80指令,具有更低的開銷和更快的執(zhí)行速度。它通過特定的寄存器來傳遞系統(tǒng)調(diào)用號和參數(shù),避免了int 0x80指令中一些復(fù)雜的中斷處理過程。例如,在 x86_64 架構(gòu)下,系統(tǒng)調(diào)用號會被存放在rax寄存器中,參數(shù)會被存放在rdi、rsi、rdx等寄存器中,然后執(zhí)行syscall指令,CPU 會直接跳轉(zhuǎn)到內(nèi)核中對應(yīng)的系統(tǒng)調(diào)用處理函數(shù),大大提高了系統(tǒng)調(diào)用的效率 。

6.2系統(tǒng)調(diào)用的流程

用戶程序發(fā)起調(diào)用:用戶程序通常不會直接調(diào)用系統(tǒng)調(diào)用的內(nèi)核函數(shù),而是通過調(diào)用標準庫函數(shù)來發(fā)起系統(tǒng)調(diào)用請求。例如,在 C 語言中,我們使用open函數(shù)來打開文件,open函數(shù)就是標準庫提供的接口 。當(dāng)用戶程序調(diào)用open函數(shù)時,標準庫會對函數(shù)參數(shù)進行處理和封裝,為后續(xù)的系統(tǒng)調(diào)用做好準備。這些標準庫函數(shù)就像是一層 “包裝”,隱藏了系統(tǒng)調(diào)用的底層細節(jié),使得用戶程序的編寫更加簡單和方便。

參數(shù)傳遞與軟中斷:在用戶程序調(diào)用標準庫函數(shù)后,標準庫會將系統(tǒng)調(diào)用的參數(shù)傳遞給操作系統(tǒng) 。如前所述,在 x86 架構(gòu)中,傳統(tǒng)方式是通過寄存器傳遞參數(shù),然后執(zhí)行int 0x80指令;在 x86_64 架構(gòu)中,則是將系統(tǒng)調(diào)用號放入rax寄存器,參數(shù)放入rdi、rsi等寄存器,然后執(zhí)行syscall指令 。通過這些軟中斷指令,程序的控制權(quán)從用戶態(tài)切換到內(nèi)核態(tài),操作系統(tǒng)內(nèi)核開始接管程序的執(zhí)行。

內(nèi)核執(zhí)行調(diào)用:當(dāng)內(nèi)核接收到系統(tǒng)調(diào)用請求后,會根據(jù)系統(tǒng)調(diào)用號來判斷用戶程序需要執(zhí)行的具體操作 。每個系統(tǒng)調(diào)用都有一個唯一的系統(tǒng)調(diào)用號,就像是一個 “身份證號碼”,內(nèi)核通過這個號碼來識別不同的系統(tǒng)調(diào)用。內(nèi)核會根據(jù)系統(tǒng)調(diào)用號查找系統(tǒng)調(diào)用表(后面會詳細介紹系統(tǒng)調(diào)用表),找到對應(yīng)的內(nèi)核函數(shù),并執(zhí)行該函數(shù)來完成系統(tǒng)調(diào)用的功能 。例如,如果系統(tǒng)調(diào)用號對應(yīng)的是write系統(tǒng)調(diào)用,內(nèi)核就會執(zhí)行sys_write函數(shù),進行實際的文件寫入操作,包括檢查文件權(quán)限、定位文件位置、寫入數(shù)據(jù)等步驟。

返回結(jié)果:當(dāng)系統(tǒng)調(diào)用在內(nèi)核中執(zhí)行完成后,會將執(zhí)行結(jié)果返回給用戶程序 。返回結(jié)果可能是成功執(zhí)行的返回值,比如文件操作中返回的文件描述符、讀取或?qū)懭氲淖止?jié)數(shù)等;也可能是錯誤碼,表示系統(tǒng)調(diào)用執(zhí)行過程中出現(xiàn)了問題 。內(nèi)核會將返回值存放在特定的寄存器中(如 x86 架構(gòu)中的eax寄存器,x86_64 架構(gòu)中的rax寄存器),然后通過特定的指令(如iret或sysret指令)將控制權(quán)交還給用戶程序,程序從內(nèi)核態(tài)切換回用戶態(tài),繼續(xù)執(zhí)行用戶程序的后續(xù)代碼 。用戶程序可以根據(jù)返回結(jié)果來判斷系統(tǒng)調(diào)用是否成功,并進行相應(yīng)的處理。

6.3內(nèi)核態(tài)與用戶態(tài)的切換

在計算機系統(tǒng)中,為了保證系統(tǒng)的安全性和穩(wěn)定性,CPU 提供了不同的特權(quán)級別,Linux 系統(tǒng)主要使用用戶態(tài)和內(nèi)核態(tài)這兩種狀態(tài) 。用戶態(tài)是程序運行的一種受限狀態(tài),在用戶態(tài)下,程序只能執(zhí)行一些受限的操作,不能直接訪問硬件資源和執(zhí)行特權(quán)指令,這就像是一個普通員工,只能在規(guī)定的權(quán)限范圍內(nèi)工作 。而內(nèi)核態(tài)則具有最高權(quán)限,操作系統(tǒng)內(nèi)核運行在內(nèi)核態(tài),它可以直接訪問硬件資源,執(zhí)行特權(quán)指令,管理系統(tǒng)的各種資源和進程,相當(dāng)于公司的管理層,擁有最高的決策權(quán)和資源調(diào)配權(quán) 。

當(dāng)用戶程序發(fā)起系統(tǒng)調(diào)用時,就需要 進行從用戶態(tài)到內(nèi)核態(tài)的 切換 。這是因為系統(tǒng)調(diào)用涉及到對硬件資源的訪問或特權(quán)操作,需要在內(nèi)核態(tài)下才能執(zhí)行。上下文切換是系統(tǒng)調(diào)用過程中的一個重要環(huán)節(jié),它指的是在切換狀態(tài)時,保存當(dāng)前狀態(tài)的相關(guān)信息(如寄存器的值、程序計數(shù)器的值等),以便在返回時 能夠恢復(fù)到原來的狀態(tài)繼續(xù)執(zhí)行 。

以read系統(tǒng)調(diào)用為例,當(dāng)用戶程序調(diào)用read函數(shù)時,首先會將read函數(shù)的參數(shù)(如文件描述符、緩沖區(qū)指針、讀取的字節(jié)數(shù)等)傳遞給標準庫 。標準庫將這些參數(shù)進一步傳遞給內(nèi)核,并通過軟中斷指令觸發(fā)系統(tǒng)調(diào)用 。此時,CPU 會保存當(dāng)前用戶態(tài)的上下文信息,包括寄存器的值、程序計數(shù)器的值等,然后切換到內(nèi)核態(tài) 。在內(nèi)核態(tài)下,內(nèi)核根據(jù)系統(tǒng)調(diào)用號找到對應(yīng)的sys_read函數(shù),并執(zhí)行該函數(shù)來完成文件讀取操作 。完成讀取后,內(nèi)核會將讀取的結(jié)果(如讀取的字節(jié)數(shù))存放在特定的寄存器中,然后恢復(fù)之前保存的用戶態(tài)上下文信息,將控制權(quán)交還給用戶程序,程序從內(nèi)核態(tài)切換回用戶態(tài),繼續(xù)執(zhí)行用戶程序的后續(xù)代碼 。

這種用戶態(tài)和內(nèi)核態(tài)的切換機制,有效地隔離了用戶程序和內(nèi)核,保證了系統(tǒng)的安全。如果沒有這種隔離機制,用戶程序就可能隨意訪問和修改系統(tǒng)資源,導(dǎo)致系統(tǒng)的不穩(wěn)定甚至崩潰 。

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

系統(tǒng)調(diào)用表是 Linux 內(nèi)核中一個非常重要的數(shù)據(jù)結(jié)構(gòu),它是一個函數(shù)指針數(shù)組,用于管理所有的系統(tǒng)調(diào)用 。每個系統(tǒng)調(diào)用在系統(tǒng)調(diào)用表中都有一個對應(yīng)的表項,表項中存儲著該系統(tǒng)調(diào)用對應(yīng)的內(nèi)核函數(shù)的指針 。可以把系統(tǒng)調(diào)用表想象成一個巨大的 “菜單”,每個系統(tǒng)調(diào)用就像是菜單上的一道 “菜”,而表項中的函數(shù)指針就是制作這道菜的 “菜譜”,內(nèi)核通過查找這個 “菜單”,就能找到對應(yīng)的 “菜譜” 來執(zhí)行系統(tǒng)調(diào)用 。

在 x86 架構(gòu)的 Linux 系統(tǒng)中,系統(tǒng)調(diào)用表通常定義在arch/x86/entry/syscall_64.c文件中 。例如,在 64 位 x86 架構(gòu)下,系統(tǒng)調(diào)用表的定義可能如下:

asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
    [0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};

這里,sys_call_table就是系統(tǒng)調(diào)用表,__NR_syscall_max表示系統(tǒng)調(diào)用的最大編號 。在初始化時,所有的表項都被初始化為指向sys_ni_syscall函數(shù),sys_ni_syscall是一個未實現(xiàn)系統(tǒng)調(diào)用的默認處理函數(shù) 。然后,通過編譯時腳本,會將實際的系統(tǒng)調(diào)用處理函數(shù)填充到對應(yīng)的表項中 。

每個系統(tǒng)調(diào)用都有一個唯一的系統(tǒng)調(diào)用號,這個系統(tǒng)調(diào)用號就像是 “菜單” 上菜品的編號 。當(dāng)內(nèi)核接收到系統(tǒng)調(diào)用請求時,會根據(jù)系統(tǒng)調(diào)用號作為索引,在系統(tǒng)調(diào)用表中查找對應(yīng)的函數(shù)指針 。例如,如果接收到的系統(tǒng)調(diào)用號是__NR_write(在 x86_64 架構(gòu)中,__NR_write的值通常為 1),內(nèi)核就會通過sys_call_table[__NR_write]找到sys_write函數(shù)的指針,并調(diào)用sys_write函數(shù)來執(zhí)行write系統(tǒng)調(diào)用 。通過這種方式,內(nèi)核能夠快速準確地找到并執(zhí)行用戶程序請求的系統(tǒng)調(diào)用服務(wù) 。

七、系統(tǒng)調(diào)用與庫函數(shù)的關(guān)系

7.1庫函數(shù)對系統(tǒng)調(diào)用的封裝

在 Linux 系統(tǒng)中,大多數(shù)系統(tǒng)調(diào)用是通過 C 標準庫函數(shù)進行封裝的 。C 標準庫為程序員提供了一系列方便易用的函數(shù)接口,這些函數(shù)在內(nèi)部通過調(diào)用系統(tǒng)調(diào)用來實現(xiàn)底層的功能 。以文件操作相關(guān)的函數(shù)為例,fopen、fread、fwrite、fclose等函數(shù)都是 C 標準庫提供的文件操作接口 。當(dāng)我們使用fopen函數(shù)打開一個文件時,fopen函數(shù)內(nèi)部會調(diào)用open系統(tǒng)調(diào)用,完成文件的打開操作,并返回一個FILE指針,用于后續(xù)的文件讀寫操作 。

同樣,fread函數(shù)用于從文件中讀取數(shù)據(jù),它會在內(nèi)部調(diào)用read系統(tǒng)調(diào)用,將數(shù)據(jù)從文件中讀取到用戶空間的緩沖區(qū)中 。fwrite函數(shù)用于向文件寫入數(shù)據(jù),它會調(diào)用write系統(tǒng)調(diào)用,將用戶空間緩沖區(qū)中的數(shù)據(jù)寫入到文件中 。fclose函數(shù)用于關(guān)閉文件,它會調(diào)用close系統(tǒng)調(diào)用,釋放文件相關(guān)的資源 。通過這些庫函數(shù)的封裝,程序員無需直接與系統(tǒng)調(diào)用打交道,避免了復(fù)雜的系統(tǒng)調(diào)用參數(shù)設(shè)置和底層細節(jié)處理,提高了編程的效率和代碼的可讀性 。

7.2兩者的區(qū)別與聯(lián)系

系統(tǒng)調(diào)用和庫函數(shù)雖然都用于實現(xiàn)特定的功能,但它們在執(zhí)行環(huán)境、功能、開銷等方面存在明顯的區(qū)別 。從執(zhí)行環(huán)境來看,系統(tǒng)調(diào)用運行在內(nèi)核態(tài),它可以直接訪問硬件資源和執(zhí)行特權(quán)指令,因為系統(tǒng)調(diào)用涉及到對硬件資源的訪問或特權(quán)操作,需要在內(nèi)核態(tài)下才能執(zhí)行 。而庫函數(shù)運行在用戶態(tài),它只能執(zhí)行一些受限的操作,不能直接訪問硬件資源和執(zhí)行特權(quán)指令 。這就好比一個是工廠里的高級管理人員(內(nèi)核態(tài)),可以直接調(diào)配各種資源和下達重要指令;另一個是普通員工(用戶態(tài)),只能在規(guī)定的權(quán)限范圍內(nèi)工作 。

在功能方面,系統(tǒng)調(diào)用提供了對操作系統(tǒng)底層功能的訪問,如文件操作、進程控制、內(nèi)存管理、設(shè)備控制等,這些功能直接與操作系統(tǒng)內(nèi)核相關(guān) 。而庫函數(shù)則提供了更高級的功能和抽象,它們通常是對系統(tǒng)調(diào)用的封裝和擴展,以提供更方便、更易用的接口 。比如fopen函數(shù)相比open系統(tǒng)調(diào)用,不僅完成了文件的打開操作,還提供了文件流的概念,方便進行格式化讀寫等操作 。

在開銷方面,由于系統(tǒng)調(diào)用需要進行從用戶態(tài)到內(nèi)核態(tài)的上下文切換,涉及到保存當(dāng)前進程的狀態(tài)、加載新的內(nèi)核狀態(tài)、執(zhí)行內(nèi)核代碼、再切換回用戶態(tài)等過程,所以開銷相對較大 。而庫函數(shù)調(diào)用屬于普通的函數(shù)調(diào)用,在用戶態(tài)執(zhí)行,不涉及上下文切換,開銷相對較小 。這就像從普通員工(用戶態(tài))到高級管理人員(內(nèi)核態(tài))的身份轉(zhuǎn)變需要經(jīng)過一系列復(fù)雜的手續(xù)(上下文切換),而普通員工之間的協(xié)作(庫函數(shù)調(diào)用)則相對簡單 。

雖然系統(tǒng)調(diào)用和庫函數(shù)存在上述區(qū)別,但它們之間也有著密切的聯(lián)系 。庫函數(shù)通常是對系統(tǒng)調(diào)用的封裝,通過庫函數(shù),程序員可以更方便地使用系統(tǒng)調(diào)用提供的功能 。同時,系統(tǒng)調(diào)用是庫函數(shù)實現(xiàn)其功能的基礎(chǔ),許多庫函數(shù)最終都需要通過調(diào)用系統(tǒng)調(diào)用來完成底層的操作 。它們相互協(xié)作,共同為程序員提供了豐富的功能和便捷的編程接口 。

7.3系統(tǒng)調(diào)用的開銷與優(yōu)化

雖然系統(tǒng)調(diào)用為用戶程序提供了強大的功能,但它也存在一定的開銷。了解這些開銷產(chǎn)生的原因,對于優(yōu)化程序性能至關(guān)重要。系統(tǒng)調(diào)用過程中,最主要的開銷之一來自用戶態(tài)與內(nèi)核態(tài)的切換 。當(dāng)用戶程序發(fā)起系統(tǒng)調(diào)用時,需要從用戶態(tài)切換到內(nèi)核態(tài),完成系統(tǒng)調(diào)用后再切換回用戶態(tài) 。在這個過程中,CPU 需要保存當(dāng)前用戶態(tài)的上下文信息,包括寄存器的值、程序計數(shù)器的值等,以便在系統(tǒng)調(diào)用結(jié)束后能夠恢復(fù)到原來的狀態(tài)繼續(xù)執(zhí)行用戶程序 。然后加載內(nèi)核態(tài)的上下文信息,執(zhí)行內(nèi)核代碼,完成系統(tǒng)調(diào)用功能 。

這種上下文切換的過程涉及到多個步驟和操作,需要消耗一定的時間和資源 。例如,在 x86 架構(gòu)中,使用int 0x80指令觸發(fā)系統(tǒng)調(diào)用時,CPU 需要進行一系列的中斷處理操作,包括保存用戶態(tài)的寄存器值、查找中斷向量表、切換到內(nèi)核態(tài)等,這些操作都會帶來一定的開銷 。而在 x86_64 架構(gòu)中,雖然syscall指令提高了系統(tǒng)調(diào)用的效率,但仍然需要進行上下文切換,只是相對int 0x80指令來說,開銷有所降低 。

除了上下文切換的開銷,保存和恢復(fù)寄存器及堆棧上下文也是系統(tǒng)調(diào)用開銷的一部分 。在上下文切換時,需要保存當(dāng)前進程的寄存器值和堆棧指針等信息,以便在返回時能夠恢復(fù)到原來的狀態(tài) 。這些信息的保存和恢復(fù)需要進行內(nèi)存讀寫操作,也會消耗一定的時間 。不同的寄存器可能需要不同的操作來保存和恢復(fù),例如,一些浮點寄存器(如 FPU、MMX、SSE、AVX 等)的數(shù)據(jù)量較大,保存和恢復(fù)這些寄存器的狀態(tài)可能會增加幾 KB 的數(shù)據(jù),特別是 AVX 寄存器,AVX2 有 512 字節(jié),AVX - 512 有 2KB,這么大的數(shù)據(jù)量在上下文切換時還需要讀寫內(nèi)存,這里的內(nèi)存讀寫延遲(加上讀寫寄存器)引入的開銷是比較大的 。

雖然現(xiàn)在存在 “延遲狀態(tài)加載(lazy state load)” 這樣的機制,當(dāng) FPU、MMX、SSE、AVX 寄存器沒有加載數(shù)據(jù)時,能夠避免加載其中的部分或者全部寄存器數(shù)據(jù),從而一定程度上降低開銷 。但如果幾乎所有的任務(wù)都使用了這些寄存器狀態(tài)信息,那么延遲狀態(tài)加載的邏輯反而會變成累贅,增大開銷 。因為上下文切換過程中 “按需加載寄存器的判斷、執(zhí)行邏輯” 的開銷,可能會超出直接加載這些寄存器數(shù)據(jù)的開銷 。

針對系統(tǒng)調(diào)用的開銷,我們可以采取一些優(yōu)化方法來提高程序的性能:

減少系統(tǒng)調(diào)用次數(shù)是一種常見的優(yōu)化策略。由于系統(tǒng)調(diào)用的開銷相對較大,減少系統(tǒng)調(diào)用的次數(shù)可以顯著降低程序的運行時間。例如,在文件操作中,如果需要頻繁地讀取文件的小數(shù)據(jù)塊,可以一次性讀取較大的數(shù)據(jù)塊到內(nèi)存緩沖區(qū)中,然后在用戶態(tài)對緩沖區(qū)中的數(shù)據(jù)進行處理,而不是每次都調(diào)用read系統(tǒng)調(diào)用。在進行網(wǎng)絡(luò)通信時,也可以將多個小的網(wǎng)絡(luò)請求合并成一個大的請求,減少send和recv系統(tǒng)調(diào)用的次數(shù)。比如,在一個網(wǎng)絡(luò)傳輸?shù)膱鼍爸?,原本每次發(fā)送10 字節(jié)的數(shù)據(jù),需要調(diào)用10 次send系統(tǒng)調(diào)用,現(xiàn)在將數(shù)據(jù)合并成100 字節(jié),只需要調(diào)用1 次send系統(tǒng)調(diào)用,這樣就大大減少了系統(tǒng)調(diào)用的開銷。

批量處理數(shù)據(jù)也是一種有效的優(yōu)化方法 。許多系統(tǒng)調(diào)用支持批量操作,通過一次系統(tǒng)調(diào)用處理多個數(shù)據(jù)項,可以減少系統(tǒng)調(diào)用的次數(shù) 。例如,writev和readv系統(tǒng)調(diào)用可以在一次操作中寫入或讀取多個緩沖區(qū)的數(shù)據(jù) 。在處理大量文件描述符時,可以使用epoll機制來實現(xiàn)對多個文件描述符的高效管理,通過一次epoll_wait系統(tǒng)調(diào)用可以同時監(jiān)聽多個文件描述符的事件,而不是對每個文件描述符都進行單獨的select或poll系統(tǒng)調(diào)用 。以文件傳輸為例,如果要將多個文件的數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上,使用writev系統(tǒng)調(diào)用可以將多個文件的數(shù)據(jù)一次性發(fā)送出去,而不需要分別對每個文件調(diào)用write系統(tǒng)調(diào)用,提高了傳輸效率 。

使用內(nèi)存映射(mmap)也是一種優(yōu)化系統(tǒng)調(diào)用開銷的方法 。mmap 函數(shù)可以將文件或設(shè)備的內(nèi)容映射到進程的地址空間中,使得進程可以直接訪問文件內(nèi)容,而不需要通過 read 和write 系統(tǒng)調(diào)用 。這種方式減少了數(shù)據(jù)在用戶空間和內(nèi)核空間之間的拷貝次數(shù),提高了數(shù)據(jù)訪問的效率 。例如,在處理大型文件時,使用mmap將文件映射到內(nèi)存中,程序可以像訪問內(nèi)存一樣直接訪問文件內(nèi)容,避免了頻繁的系統(tǒng)調(diào)用和數(shù)據(jù)拷貝 。在實現(xiàn)進程間共享內(nèi)存時,也可以使用mmap來創(chuàng)建共享內(nèi)存區(qū)域,多個進程可以通過映射同一個共享內(nèi)存區(qū)域來實現(xiàn)數(shù)據(jù)的共享和通信 。

八、實際應(yīng)用案例

8.1文件操作案例

下面是一個使用open、read、write、close等系統(tǒng)調(diào)用進行文件讀寫的示例代碼,通過這個案例,我們可以更直觀地了解這些系統(tǒng)調(diào)用的實際應(yīng)用 。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#define FILE_PATH "example.txt"
#define BUFFER_SIZE 1024

int main() {
    int file_descriptor;
    ssize_t bytes_read, bytes_written;
    char buffer[BUFFER_SIZE];
    char *content = "This is some sample content to write to the file.";

    // 打開文件,如果文件不存在則創(chuàng)建,權(quán)限為可讀可寫
    file_descriptor = open(FILE_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (file_descriptor == -1) {
        perror("Error opening file");
        return 1;
    }

    // 寫入文件
    bytes_written = write(file_descriptor, content, strlen(content));
    if (bytes_written == -1) {
        perror("Error writing to file");
        close(file_descriptor);
        return 1;
    }
    printf("Wrote %zd bytes to the file.\n", bytes_written);

    // 將文件指針移動到文件開頭,準備讀取文件
    if (lseek(file_descriptor, 0, SEEK_SET) == -1) {
        perror("Error seeking to the beginning of the file");
        close(file_descriptor);
        return 1;
    }

    // 讀取文件內(nèi)容
    bytes_read = read(file_descriptor, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("Error reading from file");
        close(file_descriptor);
        return 1;
    }
    buffer[bytes_read] = '\0'; // 添加字符串結(jié)束符
    printf("Read from file:\n%s\n", buffer);

    // 關(guān)閉文件
    if (close(file_descriptor) == -1) {
        perror("Error closing file");
        return 1;
    }

    return 0;
}

這段代碼實現(xiàn)了以下功能:

  1. 文件打開:使用open系統(tǒng)調(diào)用打開名為 “example.txt” 的文件 。O_RDWR參數(shù)表示以讀寫模式打開文件,O_CREAT參數(shù)表示如果文件不存在則創(chuàng)建它 。S_IRUSR | S_IWUSR設(shè)置文件的權(quán)限為用戶可讀可寫 。如果open調(diào)用失敗,會打印錯誤信息并退出程序 。
  2. 文件寫入:使用write系統(tǒng)調(diào)用將content字符串寫入文件 。write函數(shù)的第一個參數(shù)是文件描述符,第二個參數(shù)是要寫入的數(shù)據(jù)緩沖區(qū),第三個參數(shù)是要寫入的字節(jié)數(shù) 。如果寫入成功,會打印寫入的字節(jié)數(shù);如果失敗,會打印錯誤信息并關(guān)閉文件后退出程序 。
  3. 文件讀?。涸趯懭胛募螅褂胠seek系統(tǒng)調(diào)用將文件指針移動到文件開頭,以便從文件開頭讀取數(shù)據(jù) 。然后使用read系統(tǒng)調(diào)用從文件中讀取數(shù)據(jù)到buffer緩沖區(qū) 。read函數(shù)的第一個參數(shù)是文件描述符,第二個參數(shù)是用于存儲讀取數(shù)據(jù)的緩沖區(qū),第三個參數(shù)是要讀取的最大字節(jié)數(shù) 。讀取完成后,在緩沖區(qū)末尾添加字符串結(jié)束符'\0',并打印讀取到的文件內(nèi)容 。如果讀取失敗,會打印錯誤信息并關(guān)閉文件后退出程序 。
  4. 文件關(guān)閉:最后,使用close 系統(tǒng)調(diào)用關(guān)閉文件,釋放文件描述符和相關(guān)資源 。如果關(guān)閉失敗,會打印錯誤信息 。

通過這個案例,我們可以看到open、read、write、close等系統(tǒng)調(diào)用在文件操作中的具體使用方法,以及如何處理可能出現(xiàn)的錯誤 。

8.2進程控制案例

接下來,我們通過一個展示使用fork、exec、wait等系統(tǒng)調(diào)用創(chuàng)建子進程、執(zhí)行新程序和等待子進程結(jié)束的示例代碼,深入理解這些系統(tǒng)調(diào)用在進程控制中的作用 。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid;
    int status;

    // 創(chuàng)建子進程
    pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子進程
        char *args[] = {"/bin/ls", "-l", NULL};
        if (execvp(args[0], args) == -1) {
            perror("execvp error");
            exit(EXIT_FAILURE);
        }
    } else {
        // 父進程
        printf("Parent process, waiting for child to finish...\n");
        if (wait(&status) == -1) {
            perror("wait error");
            exit(EXIT_FAILURE);
        }
        if (WIFEXITED(status)) {
            printf("Child process exited normally with status %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("Child process was terminated by signal %d\n", WTERMSIG(status));
        }
    }

    return 0;
}

代碼執(zhí)行過程如下:

  1. 創(chuàng)建子進程:使用fork系統(tǒng)調(diào)用創(chuàng)建一個子進程 。fork函數(shù)會返回兩次,一次在父進程中,返回值是子進程的 PID;一次在子進程中,返回值是 0 。如果fork調(diào)用失敗,會打印錯誤信息并退出程序 。
  2. 子進程執(zhí)行新程序:在子進程中(pid == 0的分支),使用execvp系統(tǒng)調(diào)用執(zhí)行/bin/ls -l命令 。execvp函數(shù)會用新程序(這里是ls命令)替換子進程的代碼和數(shù)據(jù) 。如果execvp調(diào)用失敗,會打印錯誤信息并退出子進程 。
  3. 父進程等待子進程結(jié)束:在父進程中(pid > 0的分支),使用wait系統(tǒng)調(diào)用等待子進程結(jié)束 。wait函數(shù)會阻塞父進程,直到它的一個子進程結(jié)束 。當(dāng)子進程結(jié)束后,wait函數(shù)返回,通過WIFEXITED和WEXITSTATUS宏判斷子進程是否正常退出,并獲取其退出狀態(tài);通過WIFSIGNALED和WTERMSIG宏判斷子進程是否被信號終止,并獲取終止信號 。

在這個案例中,fork系統(tǒng)調(diào)用用于創(chuàng)建新的進程,使得系統(tǒng)能夠同時運行多個任務(wù) 。exec系列系統(tǒng)調(diào)用(這里使用的是execvp)用于在子進程中執(zhí)行新的程序,實現(xiàn)了程序的替換和執(zhí)行 。wait系統(tǒng)調(diào)用用于父進程等待子進程的結(jié)束,以便父進程能夠獲取子進程的執(zhí)行結(jié)果,進行后續(xù)的處理 。這些系統(tǒng)調(diào)用相互配合,實現(xiàn)了進程的創(chuàng)建、執(zhí)行和管理,是 Linux 系統(tǒng)多進程編程的基礎(chǔ) 。

責(zé)任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2025-07-28 03:00:00

2023-10-26 11:39:54

Linux系統(tǒng)CPU

2025-10-09 11:10:00

開發(fā)操作系統(tǒng)Linux

2009-12-17 14:34:24

Linux系統(tǒng)內(nèi)核

2010-03-09 13:10:08

Linux特殊用戶權(quán)限

2009-12-03 19:02:05

Linux

2009-12-17 15:01:21

Linux系統(tǒng)多用戶

2010-03-09 17:19:01

Linux時鐘

2009-12-23 11:05:28

Linux內(nèi)核

2021-01-08 05:59:39

Linux應(yīng)用程序Linux系統(tǒng)

2025-06-03 04:10:00

2009-12-25 16:47:04

Linux Make規(guī)

2010-03-08 17:27:56

Linux profi

2009-12-24 14:02:57

Linux集群技術(shù)

2010-01-06 17:12:57

Linux主要構(gòu)成

2009-02-17 16:07:29

Linux操作系統(tǒng)內(nèi)核啟動參數(shù)

2011-01-14 16:51:44

Linux內(nèi)核

2010-03-10 16:59:59

Linux數(shù)字文件權(quán)限

2010-03-09 14:59:02

Linux ifcon

2017-08-24 11:00:56

Linux用戶空間內(nèi)核空間
點贊
收藏

51CTO技術(shù)棧公眾號