Linux基礎(chǔ)(二)——多進(jìn)程開(kāi)發(fā)
一、進(jìn)程概述

進(jìn)程是指在計(jì)算機(jī)中正在運(yùn)行的程序?qū)嶓w,它包含了程序代碼、數(shù)據(jù)和執(zhí)行狀態(tài)等信息。進(jìn)程是動(dòng)態(tài)的,它需要占用計(jì)算機(jī)的資源,例如內(nèi)存、CPU 時(shí)間、I/O 等。
因此,程序和進(jìn)程之間的關(guān)系可以類比為藍(lán)圖和建筑物的關(guān)系。程序就像是藍(lán)圖,描述了建筑物的設(shè)計(jì)和構(gòu)造,而進(jìn)程就像建筑物本身,是根據(jù)藍(lán)圖所描述的設(shè)計(jì)和構(gòu)造而實(shí)際建造出來(lái)的。
總之,程序是靜態(tài)的,而進(jìn)程是動(dòng)態(tài)的。程序需要被加載到內(nèi)存中并被操作系統(tǒng)管理成為進(jìn)程,才能夠在計(jì)算機(jī)上運(yùn)行。

單道程序設(shè)計(jì)是指在計(jì)算機(jī)系統(tǒng)中每次只能運(yùn)行一個(gè)程序,程序運(yùn)行結(jié)束后才能運(yùn)行下一個(gè)程序。這種方式需要用戶手動(dòng)將程序輸入計(jì)算機(jī),計(jì)算機(jī)完成一個(gè)程序的執(zhí)行后,用戶再將下一個(gè)程序輸入計(jì)算機(jī)。這種方式效率低下,且無(wú)法充分利用計(jì)算機(jī)資源。
而多道程序設(shè)計(jì)是指在計(jì)算機(jī)系統(tǒng)中同時(shí)運(yùn)行多個(gè)程序,這些程序可以共享計(jì)算機(jī)的資源(如CPU、內(nèi)存等),通過(guò)操作系統(tǒng)的調(diào)度算法來(lái)控制各個(gè)程序的執(zhí)行順序和資源使用。多道程序設(shè)計(jì)能夠更好地利用計(jì)算機(jī)資源,提高計(jì)算機(jī)系統(tǒng)的吞吐量和響應(yīng)速度。
在多道程序設(shè)計(jì)中,操作系統(tǒng)將所有需要執(zhí)行的程序分成多個(gè)作業(yè),每個(gè)作業(yè)都包含一個(gè)或多個(gè)獨(dú)立的程序,這些程序可以并發(fā)執(zhí)行。作業(yè)之間的切換由操作系統(tǒng)負(fù)責(zé),操作系統(tǒng)會(huì)根據(jù)程序的優(yōu)先級(jí)、資源需求等因素來(lái)分配計(jì)算機(jī)資源,以達(dá)到最優(yōu)的執(zhí)行效果。
總之,單道程序設(shè)計(jì)和多道程序設(shè)計(jì)是兩種不同的程序設(shè)計(jì)方式,前者每次只能運(yùn)行一個(gè)程序,效率低下,而后者可以同時(shí)運(yùn)行多個(gè)程序,提高計(jì)算機(jī)系統(tǒng)的效率和吞吐量。

時(shí)間片的引入可以使得多個(gè)進(jìn)程在同一時(shí)間內(nèi)并發(fā)運(yùn)行,并且可以讓每個(gè)進(jìn)程都有足夠的機(jī)會(huì)使用CPU資源。時(shí)間片的大小通常由操作系統(tǒng)根據(jù)系統(tǒng)負(fù)載、進(jìn)程優(yōu)先級(jí)等因素動(dòng)態(tài)調(diào)整,以保證系統(tǒng)的響應(yīng)速度和吞吐量。
使用時(shí)間片的優(yōu)點(diǎn)是:
提高系統(tǒng)的并發(fā)性和響應(yīng)速度,避免進(jìn)程長(zhǎng)時(shí)間等待資源。
充分利用CPU資源,提高系統(tǒng)的吞吐量。
可以讓每個(gè)進(jìn)程都有機(jī)會(huì)使用CPU資源,避免某些進(jìn)程長(zhǎng)時(shí)間占用CPU,導(dǎo)致其他進(jìn)程無(wú)法運(yùn)行。
但是,時(shí)間片過(guò)小會(huì)導(dǎo)致進(jìn)程頻繁切換,增加系統(tǒng)開(kāi)銷;時(shí)間片過(guò)大會(huì)導(dǎo)致進(jìn)程響應(yīng)速度變慢,影響用戶體驗(yàn)。因此,操作系統(tǒng)需要根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整時(shí)間片的大小,以達(dá)到最優(yōu)的系統(tǒng)性能。

并行(Parallel)指的是多個(gè)任務(wù)同時(shí)執(zhí)行,即多個(gè)任務(wù)在同一時(shí)刻同時(shí)進(jìn)行。在Linux中,如果系統(tǒng)有多個(gè)CPU或CPU核心,那么多個(gè)進(jìn)程或線程可以在不同的CPU或核心上同時(shí)執(zhí)行,從而實(shí)現(xiàn)并行處理,提高系統(tǒng)的處理速度和吞吐量。
并發(fā)(Concurrency)指的是多個(gè)任務(wù)交替執(zhí)行,即多個(gè)任務(wù)在一段時(shí)間內(nèi)交替進(jìn)行。在Linux中,多個(gè)進(jìn)程或線程可以在同一CPU或核心上交替執(zhí)行,從而實(shí)現(xiàn)并發(fā)處理。在并發(fā)處理中,操作系統(tǒng)會(huì)根據(jù)調(diào)度算法來(lái)決定哪個(gè)進(jìn)程或線程優(yōu)先執(zhí)行,從而實(shí)現(xiàn)任務(wù)的調(diào)度和管理。
可以看出,并行和并發(fā)都是多任務(wù)處理的方式,但是它們的實(shí)現(xiàn)方式不同。并行需要多個(gè)CPU或核心的支持,而并發(fā)可以在單個(gè)CPU或核心上實(shí)現(xiàn)。并行處理可以提高系統(tǒng)的處理能力和效率,但需要硬件支持;而并發(fā)處理可以提高系統(tǒng)的響應(yīng)速度和資源利用率,但需要考慮并發(fā)訪問(wèn)帶來(lái)的同步和競(jìng)爭(zhēng)問(wèn)題。
總之,并行和并發(fā)是兩種不同的多任務(wù)處理方式,它們都有著自己的優(yōu)點(diǎn)和適用場(chǎng)景,需要根據(jù)實(shí)際情況選擇合適的方式來(lái)進(jìn)行多任務(wù)處理。

在Linux中,PCB是由內(nèi)核動(dòng)態(tài)分配的,當(dāng)進(jìn)程創(chuàng)建時(shí),內(nèi)核會(huì)為進(jìn)程分配一塊內(nèi)存,用來(lái)存儲(chǔ)PCB信息。每個(gè)PCB都有一個(gè)唯一的進(jìn)程ID,用來(lái)標(biāo)識(shí)該進(jìn)程。當(dāng)進(jìn)程執(zhí)行時(shí),內(nèi)核會(huì)根據(jù)PCB中的信息來(lái)管理和控制進(jìn)程的行為,例如調(diào)度進(jìn)程、分配資源、進(jìn)行系統(tǒng)調(diào)用等等。
Linux中的PCB包含了很多信息,這些信息可以分為以下幾個(gè)方面:
進(jìn)程標(biāo)識(shí)信息:進(jìn)程ID、進(jìn)程優(yōu)先級(jí)、進(jìn)程所屬用戶等。
進(jìn)程狀態(tài)信息:進(jìn)程狀態(tài)、進(jìn)程調(diào)度信息、CPU時(shí)間等。
進(jìn)程內(nèi)存信息:進(jìn)程地址空間、虛擬內(nèi)存映射等。
進(jìn)程文件信息:進(jìn)程打開(kāi)的文件、文件描述符等。
通過(guò)PCB,內(nèi)核可以對(duì)進(jìn)程進(jìn)行管理和控制,例如對(duì)進(jìn)程進(jìn)行調(diào)度、分配資源、中斷處理等。同時(shí),PCB也是進(jìn)程之間通信和同步的重要手段,進(jìn)程可以通過(guò)PCB來(lái)訪問(wèn)和修改其他進(jìn)程的信息,實(shí)現(xiàn)進(jìn)程之間的通信和同步。
二、進(jìn)程狀態(tài)轉(zhuǎn)換

新建狀態(tài)(New):當(dāng)一個(gè)進(jìn)程被創(chuàng)建時(shí),它處于新建狀態(tài)。此時(shí),操作系統(tǒng)會(huì)為該進(jìn)程分配資源并初始化進(jìn)程控制塊(PCB)。
就緒狀態(tài)(Ready):當(dāng)進(jìn)程已經(jīng)準(zhǔn)備好運(yùn)行,但由于CPU資源正在被其他進(jìn)程占用,因此它不能立即運(yùn)行,此時(shí)進(jìn)程處于就緒狀態(tài)。
運(yùn)行狀態(tài)(Running):當(dāng)進(jìn)程被分配到CPU資源并開(kāi)始執(zhí)行時(shí),它處于運(yùn)行狀態(tài)。此時(shí)進(jìn)程正在使用CPU資源進(jìn)行計(jì)算、I/O操作等。
阻塞狀態(tài)(Blocked):當(dāng)進(jìn)程需要等待某些事件發(fā)生(如I/O操作完成、信號(hào)到達(dá)等),而這些事件不能立即發(fā)生時(shí),進(jìn)程會(huì)進(jìn)入阻塞狀態(tài)。此時(shí),操作系統(tǒng)會(huì)將進(jìn)程從CPU資源中移除,直到事件發(fā)生后才會(huì)重新將其調(diào)入就緒狀態(tài)。
終止?fàn)顟B(tài)(Terminated):當(dāng)進(jìn)程執(zhí)行完成或被強(qiáng)制終止時(shí),它會(huì)進(jìn)入終止?fàn)顟B(tài)。此時(shí),操作系統(tǒng)會(huì)釋放進(jìn)程占用的資源,并從系統(tǒng)進(jìn)程列表中刪除該進(jìn)程。
在進(jìn)程的生命周期中,進(jìn)程狀態(tài)會(huì)不斷地發(fā)生變化,通常的狀態(tài)轉(zhuǎn)換為:
New -> Ready -> Running -> Blocked -> Ready -> Running -> ... -> Terminated
其中,就緒狀態(tài)、運(yùn)行狀態(tài)和阻塞狀態(tài)都是進(jìn)程運(yùn)行的三種基本狀態(tài),它們之間的切換由操作系統(tǒng)的調(diào)度算法來(lái)決定,以實(shí)現(xiàn)對(duì)進(jìn)程的管理和調(diào)度。
總之,Linux中進(jìn)程狀態(tài)轉(zhuǎn)換是進(jìn)程生命周期中重要的一部分,了解進(jìn)程狀態(tài)的變化可以幫助我們更好地理解進(jìn)程的執(zhí)行過(guò)程和操作系統(tǒng)的工作原理。

top:用于實(shí)時(shí)監(jiān)控進(jìn)程的狀態(tài)和系統(tǒng)資源使用情況,可以顯示CPU、內(nèi)存、I/O等信息。
kill:用于向進(jìn)程發(fā)送信號(hào),可以用來(lái)終止進(jìn)程、重啟進(jìn)程、暫停進(jìn)程等。
pkill:用于根據(jù)進(jìn)程名或其他屬性殺死進(jìn)程,可以通過(guò)正則表達(dá)式等方式指定進(jìn)程。
killall:用于根據(jù)進(jìn)程名殺死進(jìn)程,可以殺死所有同名進(jìn)程。
nice:用于設(shè)置進(jìn)程的優(yōu)先級(jí),可以讓進(jìn)程使用更多或更少的CPU資源。
renice:用于修改已經(jīng)運(yùn)行的進(jìn)程的優(yōu)先級(jí)。
top、htop:用于監(jiān)控系統(tǒng)資源使用情況和進(jìn)程狀態(tài),可以實(shí)時(shí)顯示進(jìn)程的CPU、內(nèi)存、I/O等信息。
vmstat:用于監(jiān)控系統(tǒng)資源使用情況,可以顯示CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)等信息。
pidof:用于根據(jù)進(jìn)程名獲取進(jìn)程ID。

在Linux中,進(jìn)程號(hào)的獲取和管理可以通過(guò)以下幾個(gè)系統(tǒng)調(diào)用函數(shù)來(lái)實(shí)現(xiàn):
getpid():獲取當(dāng)前進(jìn)程的進(jìn)程ID。
getppid():獲取當(dāng)前進(jìn)程的父進(jìn)程ID。
fork():創(chuàng)建一個(gè)新的進(jìn)程,新進(jìn)程的進(jìn)程ID不同于父進(jìn)程的進(jìn)程ID。
exec():在當(dāng)前進(jìn)程中執(zhí)行一個(gè)新的程序,進(jìn)程ID不會(huì)改變。
wait():等待指定進(jìn)程退出,獲取該進(jìn)程的退出狀態(tài)。
kill():向指定進(jìn)程發(fā)送信號(hào),可以用來(lái)終止進(jìn)程、重啟進(jìn)程、暫停進(jìn)程等。
signal():設(shè)置或處理信號(hào)處理函數(shù),可以用來(lái)處理進(jìn)程接收到的信號(hào)。
除了以上這些系統(tǒng)調(diào)用函數(shù),還有一些其他的函數(shù)可以用來(lái)獲取和管理進(jìn)程信息,例如:
ps:用于顯示當(dāng)前系統(tǒng)中的進(jìn)程信息,包括進(jìn)程ID、進(jìn)程狀態(tài)等。
top:用于實(shí)時(shí)監(jiān)控進(jìn)程的狀態(tài)和系統(tǒng)資源使用情況。
htop:類似于top,但是可以使用鼠標(biāo)和鍵盤進(jìn)行交互。
pstree:用于顯示進(jìn)程之間的關(guān)系,以樹狀結(jié)構(gòu)的形式展示。
lsof:用于顯示系統(tǒng)中打開(kāi)的文件和進(jìn)程信息。
通過(guò)這些函數(shù)和命令,我們可以獲取和管理進(jìn)程信息,了解系統(tǒng)的運(yùn)行狀態(tài)和資源使用情況,進(jìn)行調(diào)試和優(yōu)化。
三、進(jìn)程創(chuàng)建

當(dāng)進(jìn)程調(diào)用fork()時(shí),操作系統(tǒng)會(huì)為子進(jìn)程創(chuàng)建一個(gè)新的進(jìn)程描述符,并為其分配一個(gè)唯一的進(jìn)程ID。子進(jìn)程將繼承父進(jìn)程的打開(kāi)文件描述符、信號(hào)處理程序等信息。父進(jìn)程和子進(jìn)程之間的區(qū)別在于返回值:在父進(jìn)程中,fork()將返回子進(jìn)程的進(jìn)程ID,而在子進(jìn)程中,返回值將為0。
在子進(jìn)程中,通常緊接著會(huì)調(diào)用exec()系列函數(shù),以便將子進(jìn)程替換為另一個(gè)可執(zhí)行文件。這將導(dǎo)致子進(jìn)程的地址空間被新程序所占用。
四、父子進(jìn)程虛擬地址空間情況

在創(chuàng)建子進(jìn)程時(shí),操作系統(tǒng)將為子進(jìn)程復(fù)制父進(jìn)程的整個(gè)虛擬地址空間,包括代碼段、數(shù)據(jù)段、堆和棧等內(nèi)容。子進(jìn)程的虛擬地址空間是父進(jìn)程虛擬地址空間的一個(gè)副本,但是子進(jìn)程的虛擬地址空間是獨(dú)立的,因此父進(jìn)程和子進(jìn)程之間不會(huì)相互干擾。
當(dāng)一個(gè)進(jìn)程修改它的虛擬地址空間時(shí),它的修改只會(huì)影響到它自己的虛擬地址空間,不會(huì)影響到其他進(jìn)程的虛擬地址空間。例如,當(dāng)一個(gè)進(jìn)程分配內(nèi)存時(shí),它只會(huì)修改自己的虛擬地址空間,不會(huì)影響其他進(jìn)程的虛擬地址空間。因此,每個(gè)進(jìn)程都可以獨(dú)立地管理它自己的虛擬地址空間,這是多個(gè)進(jìn)程能夠同時(shí)運(yùn)行的重要基礎(chǔ)。
在父進(jìn)程和子進(jìn)程中,相同的虛擬地址通常都會(huì)映射到相同的物理內(nèi)存區(qū)域。這意味著,如果一個(gè)進(jìn)程修改了它的虛擬地址空間中的某個(gè)變量,那么另一個(gè)進(jìn)程也會(huì)看到這個(gè)變量的值發(fā)生了改變。但是,由于每個(gè)進(jìn)程都有自己的虛擬地址空間,因此它們之間的變量并不是真正共享的,它們只是在物理內(nèi)存中的同一位置上有相同的副本而已。
五、父子進(jìn)程關(guān)系及GDB多進(jìn)程調(diào)試


parent:跟蹤父進(jìn)程,子進(jìn)程將在后臺(tái)運(yùn)行;
child:跟蹤子進(jìn)程,父進(jìn)程將在后臺(tái)運(yùn)行;
ask:每次fork()調(diào)用時(shí)詢問(wèn)用戶應(yīng)該跟蹤哪個(gè)進(jìn)程。
默認(rèn)情況下,GDB使用parent選項(xiàng)來(lái)跟蹤進(jìn)程。如果要跟蹤子進(jìn)程,可以使用set follow-fork-mode child命令。
在GDB中進(jìn)行多進(jìn)程調(diào)試時(shí),可以使用set detach-on-fork命令來(lái)控制子進(jìn)程的行為。如果將其設(shè)置為off,則子進(jìn)程將繼續(xù)在GDB中運(yùn)行;如果將其設(shè)置為on,則子進(jìn)程將自動(dòng)與GDB斷開(kāi)連接,并在后臺(tái)運(yùn)行。默認(rèn)情況下,set detach-on-fork命令的值為on。
在多進(jìn)程調(diào)試中,可以使用info inferiors命令來(lái)列出所有的進(jìn)程。使用inferior命令可以選擇當(dāng)前要調(diào)試的進(jìn)程。在選擇了進(jìn)程后,可以使用info threads命令來(lái)列出該進(jìn)程中的所有線程。使用thread命令可以選擇要調(diào)試的線程。在選擇了線程后,可以像調(diào)試單個(gè)進(jìn)程一樣使用GDB進(jìn)行調(diào)試。
六、exec函數(shù)族

exec()函數(shù)族包括以下函數(shù):
int execl(const char *path, const char *arg, ...);:通過(guò)指定可執(zhí)行文件的路徑和命令行參數(shù),執(zhí)行一個(gè)新程序。arg參數(shù)和后續(xù)的可變參數(shù)都是新程序的命令行參數(shù),最后一個(gè)參數(shù)必須是NULL。
int execv(const char *path, char *const argv[]);:與execl()函數(shù)類似,但是參數(shù)以字符串?dāng)?shù)組的形式傳遞給新程序。
int execle(const char *path, const char *arg, ..., char *const envp[]);:與execl()函數(shù)類似,但是可以指定新程序的環(huán)境變量。
int execve(const char *path, char *const argv[], char *const envp[]);:與execv()函數(shù)類似,但是可以指定新程序的環(huán)境變量。
int execlp(const char *file, const char *arg, ...);:與execl()函數(shù)類似,但是可執(zhí)行文件的路徑不需要指定絕對(duì)路徑,而是在PATH環(huán)境變量中搜索可執(zhí)行文件。
int execvp(const char *file, char *const argv[]);:與execv()函數(shù)類似,但是可執(zhí)行文件的路徑不需要指定絕對(duì)路徑,而是在PATH環(huán)境變量中搜索可執(zhí)行文件。
exec()函數(shù)族的返回值只有在執(zhí)行失敗時(shí)才會(huì)返回,如果執(zhí)行成功,則不會(huì)返回。如果exec()函數(shù)執(zhí)行失敗,它將返回-1,并設(shè)置errno變量來(lái)指示錯(cuò)誤的類型。
注意,在exec()函數(shù)族中,新程序?qū)?huì)繼承當(dāng)前進(jìn)程的一些屬性,例如文件描述符和信號(hào)處理程序等。因此,在調(diào)用exec()函數(shù)族之前,通常需要使用fork()函數(shù)創(chuàng)建一個(gè)新進(jìn)程,以便在子進(jìn)程中調(diào)用exec()函數(shù)族,從而保持父進(jìn)程不受影響。
七、進(jìn)程退出、孤兒進(jìn)程、僵尸進(jìn)程



僵尸進(jìn)程并不會(huì)占用系統(tǒng)資源,但是它們會(huì)占用一定的進(jìn)程ID和進(jìn)程描述符等資源。如果系統(tǒng)中有大量的僵尸進(jìn)程,就會(huì)導(dǎo)致系統(tǒng)資源的浪費(fèi)。為了避免僵尸進(jìn)程的產(chǎn)生,父進(jìn)程通常需要在子進(jìn)程退出時(shí)調(diào)用wait()或waitpid()函數(shù)來(lái)獲取它的退出狀態(tài)信息,并且釋放子進(jìn)程的資源。如果父進(jìn)程不再需要子進(jìn)程的狀態(tài)信息,也可以使用waitpid()函數(shù)的WNOHANG選項(xiàng)來(lái)非阻塞地獲取子進(jìn)程的狀態(tài)信息。如果父進(jìn)程不希望等待子進(jìn)程退出,也可以使用SIGCHLD信號(hào)來(lái)異步地處理子進(jìn)程的退出狀態(tài)信息。
八、wait函數(shù)

Linux中的進(jìn)程回收是通過(guò)父進(jìn)程調(diào)用wait()或waitpid()函數(shù)來(lái)實(shí)現(xiàn)的。這些函數(shù)會(huì)阻塞父進(jìn)程,直到一個(gè)子進(jìn)程退出,然后返回子進(jìn)程的進(jìn)程ID和退出狀態(tài)信息。父進(jìn)程可以使用這些信息來(lái)釋放子進(jìn)程的資源,并且避免子進(jìn)程成為僵尸進(jìn)程。如果父進(jìn)程不希望阻塞,也可以使用waitpid()函數(shù)的WNOHANG選項(xiàng)來(lái)非阻塞地獲取子進(jìn)程的狀態(tài)信息。
當(dāng)一個(gè)進(jìn)程退出時(shí),它的退出狀態(tài)信息會(huì)被保存在內(nèi)核中,直到它的父進(jìn)程調(diào)用wait()或waitpid()函數(shù)來(lái)獲取。如果一個(gè)進(jìn)程沒(méi)有父進(jìn)程,或者其父進(jìn)程已經(jīng)退出,那么這個(gè)進(jìn)程就會(huì)被init進(jìn)程接管,init進(jìn)程將會(huì)周期性地調(diào)用wait()函數(shù)來(lái)獲取孤兒進(jìn)程的退出狀態(tài)信息,并且釋放它們的資源。
進(jìn)程回收還包括釋放進(jìn)程占用的內(nèi)存空間和其他資源。當(dāng)一個(gè)進(jìn)程退出時(shí),它占用的內(nèi)存空間會(huì)被操作系統(tǒng)回收,這些空間可以被分配給其他進(jìn)程使用。同時(shí),打開(kāi)的文件描述符等其他資源也會(huì)被釋放,以便下一個(gè)進(jìn)程可以使用它們。
總之,進(jìn)程回收是Linux系統(tǒng)中一個(gè)重要的機(jī)制,它可以幫助操作系統(tǒng)有效地管理進(jìn)程和資源,從而提高系統(tǒng)的穩(wěn)定性和性能。
九、waitpid函數(shù)

WEXITSTATUS(status):如果進(jìn)程正常退出,則返回進(jìn)程的退出狀態(tài)碼。
WIFSIGNALED(status):如果進(jìn)程是因?yàn)樾盘?hào)而退出,則返回非零值。
WTERMSIG(status):如果進(jìn)程是因?yàn)樾盘?hào)而退出,則返回導(dǎo)致進(jìn)程退出的信號(hào)編號(hào)。
WCOREDUMP(status):如果進(jìn)程是因?yàn)樾盘?hào)而退出,并且產(chǎn)生了核心轉(zhuǎn)儲(chǔ)文件,則返回非零值。
WIFSTOPPED(status):如果進(jìn)程被暫停,則返回非零值。
WSTOPSIG(status):如果進(jìn)程被暫停,則返回導(dǎo)致進(jìn)程暫停的信號(hào)編號(hào)。
WIFCONTINUED(status):如果進(jìn)程被恢復(fù),則返回非零值。

status參數(shù)是一個(gè)整型指針,用于保存子進(jìn)程的退出狀態(tài)信息。如果不關(guān)心子進(jìn)程的退出狀態(tài)信息,可以將status設(shè)置為NULL。
options參數(shù)指定等待子進(jìn)程的行為。常用的選項(xiàng)包括:
WNOHANG:非阻塞模式,即如果沒(méi)有子進(jìn)程退出,則立即返回0,而不會(huì)阻塞。
WUNTRACED:如果子進(jìn)程處于暫停狀態(tài),則也返回狀態(tài)信息。
WCONTINUED:如果子進(jìn)程被恢復(fù),則也返回狀態(tài)信息。
waitpid()函數(shù)的返回值是退出的子進(jìn)程的進(jìn)程ID,如果沒(méi)有子進(jìn)程退出,則返回0。如果出錯(cuò),則返回-1,并設(shè)置全局變量errno表示錯(cuò)誤原因。
十、進(jìn)程間通信簡(jiǎn)介

管道(Pipe):管道是一種半雙工的通信方式,它只能在具有父子關(guān)系的進(jìn)程之間使用。管道有兩種類型:有名管道和無(wú)名管道。有名管道可以在不具有父子關(guān)系的進(jìn)程之間使用,而無(wú)名管道只能在具有父子關(guān)系的進(jìn)程之間使用。
命名管道(FIFO):命名管道是一種有名管道,它允許不具有父子關(guān)系的進(jìn)程之間進(jìn)行通信。命名管道可以在文件系統(tǒng)中創(chuàng)建一個(gè)特殊文件來(lái)實(shí)現(xiàn)。
信號(hào)(Signal):信號(hào)是一種異步通信方式,它允許進(jìn)程向其他進(jìn)程發(fā)送信號(hào),以通知它們發(fā)生了某些事件。信號(hào)可以用于進(jìn)程間的同步和異步通信。
共享內(nèi)存(Shared Memory):共享內(nèi)存是一種通過(guò)共享內(nèi)存區(qū)域來(lái)實(shí)現(xiàn)進(jìn)程間通信的方式,多個(gè)進(jìn)程可以訪問(wèn)同一個(gè)內(nèi)存區(qū)域,從而實(shí)現(xiàn)數(shù)據(jù)共享。共享內(nèi)存通常需要配合信號(hào)量來(lái)進(jìn)行同步和互斥操作,以保證數(shù)據(jù)的一致性和安全性。
消息隊(duì)列(Message Queue):消息隊(duì)列是一種通過(guò)隊(duì)列來(lái)實(shí)現(xiàn)進(jìn)程間通信的方式,發(fā)送進(jìn)程將消息放入隊(duì)列中,接收進(jìn)程從隊(duì)列中取出消息。消息隊(duì)列可以用于進(jìn)程之間的異步通信。
信號(hào)量(Semaphore):信號(hào)量是一種用于進(jìn)程同步和互斥的機(jī)制,它可以控制多個(gè)進(jìn)程對(duì)共享資源的訪問(wèn)。信號(hào)量通常用于共享內(nèi)存和消息隊(duì)列等進(jìn)程間通信方式中。

套接字(Socket):套接字是一種通用的進(jìn)程間通信方式,它可以在不同主機(jī)之間進(jìn)行通信。套接字通常使用TCP或UDP協(xié)議來(lái)實(shí)現(xiàn)數(shù)據(jù)傳輸。
遠(yuǎn)程過(guò)程調(diào)用(RPC):RPC是一種通過(guò)網(wǎng)絡(luò)調(diào)用遠(yuǎn)程計(jì)算機(jī)上的程序的機(jī)制。它可以讓不同主機(jī)之間的進(jìn)程像調(diào)用本地程序一樣調(diào)用遠(yuǎn)程程序。
遠(yuǎn)程方法調(diào)用(RMI):RMI是一種Java語(yǔ)言特有的進(jìn)程間通信方式,它允許Java程序在不同主機(jī)之間進(jìn)行通信。
十一、匿名管道概述


父子進(jìn)程通信:管道只能在具有父子關(guān)系的進(jìn)程之間使用。這是因?yàn)楣艿朗怯梢粋€(gè)進(jìn)程創(chuàng)建的,另一個(gè)進(jìn)程只能通過(guò)繼承文件描述符的方式來(lái)使用該管道。
數(shù)據(jù)傳輸有序:管道中的數(shù)據(jù)傳輸是有序的,即寫入管道中的數(shù)據(jù)會(huì)按照先后順序被讀取出來(lái)。在同一時(shí)刻,只有一個(gè)進(jìn)程可以對(duì)管道進(jìn)行讀寫操作,因此不會(huì)出現(xiàn)數(shù)據(jù)交錯(cuò)的情況。
數(shù)據(jù)傳輸有限制:管道有一個(gè)固定的緩沖區(qū)大小,當(dāng)緩沖區(qū)已滿時(shí),寫入進(jìn)程會(huì)被阻塞,直到有足夠的空間可用。同樣地,當(dāng)緩沖區(qū)為空時(shí),讀取進(jìn)程也會(huì)被阻塞,直到有數(shù)據(jù)可用。
匿名管道:在Linux中,管道分為匿名管道和命名管道兩種形式。匿名管道沒(méi)有文件名和文件系統(tǒng)中的實(shí)體,只能在創(chuàng)建它的進(jìn)程和其子進(jìn)程之間使用。


文件描述符:管道的文件描述符是一個(gè)包含兩個(gè)整數(shù)的數(shù)組,其中一個(gè)整數(shù)用于讀取數(shù)據(jù),另一個(gè)整數(shù)用于寫入數(shù)據(jù)。當(dāng)進(jìn)程打開(kāi)管道時(shí),會(huì)返回一個(gè)文件描述符,該文件描述符可以用于讀取或?qū)懭牍艿乐械臄?shù)據(jù)。
管道的狀態(tài):管道的狀態(tài)包括一些基本的屬性,如緩沖區(qū)大小、讀取進(jìn)程數(shù)量、寫入進(jìn)程數(shù)量等。在Linux中,可以使用fstat()系統(tǒng)調(diào)用來(lái)獲取管道的狀態(tài)信息。
管道的鎖:由于管道是多個(gè)進(jìn)程共享的資源,因此需要使用鎖來(lái)保護(hù)管道的操作。在Linux中,可以使用互斥鎖和條件變量來(lái)保護(hù)管道的讀寫操作。
十二、父子進(jìn)程通過(guò)匿名管道通信

創(chuàng)建子進(jìn)程:在父進(jìn)程中,可以使用fork()系統(tǒng)調(diào)用來(lái)創(chuàng)建一個(gè)新的子進(jìn)程。子進(jìn)程會(huì)繼承父進(jìn)程的文件描述符。
關(guān)閉不需要的文件描述符:在子進(jìn)程中,由于子進(jìn)程也繼承了父進(jìn)程的文件描述符,因此需要關(guān)閉不需要的文件描述符,即寫入文件描述符或讀取文件描述符。
進(jìn)行通信:在父進(jìn)程中,可以使用寫入文件描述符向管道中寫入數(shù)據(jù);在子進(jìn)程中,可以使用讀取文件描述符從管道中讀取數(shù)據(jù)。
關(guān)閉文件描述符:在進(jìn)程使用完文件描述符后,需要將其關(guān)閉,以釋放系統(tǒng)資源。
十三、匿名管道通信案例

2. 創(chuàng)建子進(jìn)程:通常,匿名管道是由一個(gè)父進(jìn)程和一個(gè)子進(jìn)程之間進(jìn)行通信。因此,父進(jìn)程將創(chuàng)建一個(gè)子進(jìn)程來(lái)與其通信。在創(chuàng)建子進(jìn)程時(shí),父進(jìn)程需要將管道文件描述符復(fù)制到子進(jìn)程的文件描述符中。
3. 進(jìn)程通信:在子進(jìn)程中,它可以通過(guò)管道的寫入端將數(shù)據(jù)寫入管道。父進(jìn)程可以從管道的讀取端讀取這些數(shù)據(jù)。同樣,父進(jìn)程也可以通過(guò)管道的寫入端將數(shù)據(jù)寫入管道,而子進(jìn)程可以從管道的讀取端讀取這些數(shù)據(jù)。
4. 關(guān)閉管道:當(dāng)進(jìn)程完成通信時(shí),它們都應(yīng)該關(guān)閉管道。在父進(jìn)程和子進(jìn)程中,當(dāng)讀取到管道尾部時(shí),也應(yīng)該關(guān)閉管道。
十四、管道的讀寫特點(diǎn)和管道設(shè)置為非阻塞

1. 打開(kāi)需要設(shè)置為非阻塞的管道
2. 使用fcntl函數(shù)獲取管道的當(dāng)前標(biāo)志位,可以使用F_GETFL命令來(lái)實(shí)現(xiàn)
3. 將獲取到的標(biāo)志位與O_NONBLOCK位進(jìn)行或操作,以設(shè)置管道為非阻塞模式
4. 使用fcntl函數(shù)將設(shè)置后的標(biāo)志位設(shè)置回管道中,可以使用F_SETFL命令來(lái)實(shí)現(xiàn)
十五、有名管道介紹及使用

1. 有名管道是在文件系統(tǒng)中存在的文件,并且可以在多個(gè)進(jìn)程之間共享。
2. 有名管道可以用于不相關(guān)的進(jìn)程之間的通信,而匿名管道只能在父子進(jìn)程之間使用。
3. 有名管道的創(chuàng)建和刪除是由文件系統(tǒng)完成的,而匿名管道只是由進(jìn)程創(chuàng)建的一組文件描述符。
4. 有名管道可以用于實(shí)現(xiàn)進(jìn)程間的同步。

1. 有名管道是在文件系統(tǒng)中存在的文件,并且可以在多個(gè)進(jìn)程之間共享。
2. 有名管道可以用于不相關(guān)的進(jìn)程之間的通信,而匿名管道只能在父子進(jìn)程之間使用。
3. 有名管道的創(chuàng)建和刪除是由文件系統(tǒng)完成的,而匿名管道只是由進(jìn)程創(chuàng)建的一組文件描述符。
4. 有名管道可以用于實(shí)現(xiàn)進(jìn)程間的同步。

1. FIFO是一種阻塞式I/O,當(dāng)沒(méi)有數(shù)據(jù)可用時(shí),讀取和寫入FIFO的進(jìn)程都會(huì)阻塞。
2. 在寫入FIFO之前,必須有一個(gè)進(jìn)程打開(kāi)FIFO進(jìn)行寫入操作。同樣,在讀取FIFO之前,必須有一個(gè)進(jìn)程打開(kāi)FIFO進(jìn)行讀取操作。
3. FIFO通常是用于單向通信的(例如,從一個(gè)進(jìn)程讀取數(shù)據(jù),或?qū)?shù)據(jù)寫入另一個(gè)進(jìn)程)。如果需要進(jìn)行雙向通信,通常需要?jiǎng)?chuàng)建兩個(gè)FIFO,一個(gè)用于讀取,另一個(gè)用于寫入。
4. 當(dāng)不再需要FIFO時(shí),必須顯式地刪除它。這可以通過(guò)使用rm命令或unlink()系統(tǒng)調(diào)用來(lái)完成。
FIFO是一種非常有用的IPC機(jī)制,可用于在不同的進(jìn)程之間進(jìn)行通信。在Linux中,F(xiàn)IFO通常被用于實(shí)現(xiàn)多個(gè)進(jìn)程之間的協(xié)作和同步。

十六、有名管道實(shí)現(xiàn)簡(jiǎn)單版聊天功能

十七、內(nèi)存映射

在Linux中,內(nèi)存映射主要通過(guò)mmap()系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)。

內(nèi)存限制:mmap將文件映射到內(nèi)存中,因此需要注意內(nèi)存的使用量。如果文件很大,可能會(huì)導(dǎo)致內(nèi)存不足,從而導(dǎo)致系統(tǒng)性能下降或崩潰。
文件訪問(wèn)權(quán)限:使用mmap需要確保文件具有適當(dāng)?shù)脑L問(wèn)權(quán)限。如果文件不可讀或不可寫,將無(wú)法使用mmap。
內(nèi)存對(duì)齊:mmap將文件映射到內(nèi)存時(shí)需要進(jìn)行內(nèi)存對(duì)齊,通常情況下是按頁(yè)面大?。ㄍǔ?KB)進(jìn)行對(duì)齊。因此,在使用mmap時(shí)需要確保訪問(wèn)的內(nèi)存地址與文件偏移量是頁(yè)面對(duì)齊的。
文件修改:如果使用mmap進(jìn)行寫操作,需要注意對(duì)文件進(jìn)行同步操作,以確保數(shù)據(jù)已經(jīng)寫入磁盤。否則,如果系統(tǒng)崩潰或出現(xiàn)其他異常情況,可能會(huì)導(dǎo)致數(shù)據(jù)丟失或損壞。
跨平臺(tái)兼容性:在跨平臺(tái)使用mmap時(shí)需要注意不同操作系統(tǒng)的實(shí)現(xiàn)差異,例如內(nèi)存對(duì)齊方式和文件訪問(wèn)權(quán)限等。需要確保代碼能夠在不同的操作系統(tǒng)上正確運(yùn)行。
錯(cuò)誤處理:使用mmap時(shí)需要注意錯(cuò)誤處理,例如文件不存在、內(nèi)存映射失敗等情況。需要根據(jù)具體情況進(jìn)行適當(dāng)?shù)奶幚恚员苊獬绦虺霈F(xiàn)崩潰等問(wèn)題。

十八、信號(hào)概述

信號(hào)的目的是在運(yùn)行中的進(jìn)程之間傳遞信息,例如通知進(jìn)程某個(gè)事件的發(fā)生,向進(jìn)程發(fā)送中斷請(qǐng)求等。Linux系統(tǒng)中定義了多種信號(hào),每個(gè)信號(hào)都有一個(gè)唯一的編號(hào)和特定的含義。例如,SIGINT信號(hào)表示中斷進(jìn)程,通常是由用戶在終端上按下Ctrl+C鍵發(fā)送的。
信號(hào)的特點(diǎn)如下:
異步事件:信號(hào)是異步事件,可以在任何時(shí)刻被發(fā)送和接收。進(jìn)程無(wú)法預(yù)測(cè)何時(shí)會(huì)接收到信號(hào),因此需要謹(jǐn)慎處理。
中斷處理:當(dāng)進(jìn)程接收到信號(hào)時(shí),操作系統(tǒng)中斷進(jìn)程的正常執(zhí)行流程,轉(zhuǎn)而執(zhí)行信號(hào)處理函數(shù)。信號(hào)處理函數(shù)通常是一段短小的代碼片段,用于處理特定的事件。
優(yōu)先級(jí):在Linux系統(tǒng)中,信號(hào)具有優(yōu)先級(jí)。當(dāng)多個(gè)信號(hào)同時(shí)發(fā)送給進(jìn)程時(shí),系統(tǒng)會(huì)根據(jù)信號(hào)的優(yōu)先級(jí)確定信號(hào)的處理順序。
可靠性:信號(hào)的發(fā)送和接收都是可靠的。如果進(jìn)程向其他進(jìn)程發(fā)送信號(hào),系統(tǒng)會(huì)保證信號(hào)被傳遞到目標(biāo)進(jìn)程。如果進(jìn)程接收到一個(gè)信號(hào),系統(tǒng)會(huì)保證該信號(hào)不會(huì)丟失。

SIGHUP:掛起信號(hào),通常在終端會(huì)話結(jié)束時(shí)發(fā)送給進(jìn)程。
SIGINT:中斷信號(hào),通常由用戶在終端上按下Ctrl+C鍵發(fā)送給進(jìn)程。
SIGQUIT:退出信號(hào),通常由用戶在終端上按下Ctrl+\鍵發(fā)送給進(jìn)程。
SIGILL:非法指令信號(hào),通常表示進(jìn)程執(zhí)行了不合法的指令或操作。
SIGABRT:終止信號(hào),通常由程序自身調(diào)用abort函數(shù)發(fā)送給自己,表示程序出現(xiàn)了致命錯(cuò)誤。
SIGFPE:浮點(diǎn)異常信號(hào),通常表示進(jìn)程執(zhí)行了一條非法的浮點(diǎn)運(yùn)算指令。
SIGKILL:殺死信號(hào),可以立即終止進(jìn)程的運(yùn)行,無(wú)法被忽略或捕獲。
SIGSEGV:段錯(cuò)誤信號(hào),通常表示進(jìn)程試圖訪問(wèn)未分配或不允許訪問(wèn)的內(nèi)存地址。
SIGPIPE:管道破裂信號(hào),通常表示進(jìn)程試圖向已關(guān)閉的管道寫入數(shù)據(jù)。
SIGALRM:鬧鐘信號(hào),通常用于實(shí)現(xiàn)定時(shí)器功能。
SIGTERM:終止信號(hào),可以請(qǐng)求進(jìn)程正常退出,并允許進(jìn)程進(jìn)行清理操作。
SIGUSR1/SIGUSR2:用戶定義信號(hào),通常由進(jìn)程自定義使用。
SIGCHLD:子進(jìn)程狀態(tài)改變信號(hào),通常由父進(jìn)程接收,用于通知父進(jìn)程子進(jìn)程的狀態(tài)改變。
SIGCONT:繼續(xù)信號(hào),通常用于繼續(xù)之前被暫停的進(jìn)程。
SIGSTOP:停止信號(hào),可以暫停進(jìn)程的運(yùn)行,無(wú)法被忽略或捕獲。
SIGTSTP:掛起信號(hào),通常由用戶在終端上按下Ctrl+Z鍵發(fā)送給進(jìn)程,用于將進(jìn)程暫停并放入后臺(tái)。

Ign(忽略):當(dāng)進(jìn)程接收到該信號(hào)時(shí),進(jìn)程會(huì)直接忽略該信號(hào),繼續(xù)執(zhí)行。
Core(生成核心轉(zhuǎn)儲(chǔ)文件):當(dāng)進(jìn)程接收到該信號(hào)時(shí),進(jìn)程會(huì)生成一個(gè)核心轉(zhuǎn)儲(chǔ)文件,用于調(diào)試和分析程序錯(cuò)誤。
Stop(停止進(jìn)程,默認(rèn)處理動(dòng)作):當(dāng)進(jìn)程接收到該信號(hào)時(shí),進(jìn)程會(huì)被暫停,并等待被其他進(jìn)程繼續(xù)執(zhí)行。
Cont(繼續(xù)進(jìn)程):當(dāng)進(jìn)程接收到該信號(hào)時(shí),進(jìn)程會(huì)從之前被暫停的地方繼續(xù)執(zhí)行。
十九、kill、raise、abort函數(shù)

int kill(pid_t pid, int sig);其中,pid參數(shù)指定要發(fā)送信號(hào)的進(jìn)程或進(jìn)程組的ID,sig參數(shù)指定要發(fā)送的信號(hào)編號(hào)。raise函數(shù):可以向當(dāng)前進(jìn)程發(fā)送信號(hào)。其函數(shù)原型為:int raise(int sig);其中,sig參數(shù)指定要發(fā)送的信號(hào)編號(hào)。abort函數(shù):可以向當(dāng)前進(jìn)程發(fā)送SIGABRT信號(hào),使其自行終止。其函數(shù)原型為:void abort(void)。需要注意的是,使用kill、raise和abort函數(shù)向進(jìn)程發(fā)送信號(hào)時(shí),需要注意信號(hào)的含義和作用,避免信號(hào)的誤用導(dǎo)致不必要的麻煩或錯(cuò)誤。同時(shí),對(duì)于一些特殊的信號(hào),例如SIGKILL和SIGSTOP,無(wú)法被忽略或捕獲,因此需要謹(jǐn)慎使用。
二十、alarm函數(shù)

二十一、setitimer定時(shí)器函數(shù)

ITIMER_REAL:使用系統(tǒng)的實(shí)時(shí)時(shí)鐘來(lái)計(jì)時(shí),定時(shí)器到期時(shí)發(fā)送SIGALRM信號(hào)。
ITIMER_VIRTUAL:使用進(jìn)程的虛擬時(shí)鐘來(lái)計(jì)時(shí),定時(shí)器到期時(shí)發(fā)送SIGVTALRM信號(hào)。
ITIMER_PROF:使用進(jìn)程和系統(tǒng)的時(shí)間來(lái)計(jì)時(shí),定時(shí)器到期時(shí)發(fā)送SIGPROF信號(hào)。
new_value參數(shù)是一個(gè)指向結(jié)構(gòu)體itimerval的指針,用于設(shè)置新的定時(shí)器值。該結(jié)構(gòu)體包含了兩個(gè)成員:
it_interval:定時(shí)器的周期時(shí)間,即定時(shí)器到期后再次觸發(fā)的時(shí)間間隔。
it_value:定時(shí)器的初始時(shí)間,即定時(shí)器第一次到期的時(shí)間間隔。當(dāng)定時(shí)器到達(dá)指定時(shí)間間隔時(shí),操作系統(tǒng)會(huì)向當(dāng)前進(jìn)程發(fā)送相應(yīng)的信號(hào),可以通過(guò)信號(hào)處理函數(shù)來(lái)處理該信號(hào)。需要注意的是,使用setitimer函數(shù)設(shè)置定時(shí)器時(shí),需要確保定時(shí)器的設(shè)置和取消符合預(yù)期,避免產(chǎn)生不必要的麻煩或錯(cuò)誤。同時(shí),對(duì)于一些特殊的信號(hào),例如SIGKILL和SIGSTOP,無(wú)法被忽略或捕獲,因此需要謹(jǐn)慎使用。
二十二、signal信號(hào)捕捉函數(shù)

另外,為了避免信號(hào)處理函數(shù)與其他代碼產(chǎn)生競(jìng)爭(zhēng)條件,通常建議在信號(hào)處理函數(shù)中盡量避免使用不可重入的函數(shù),例如printf和malloc等。如果必須使用這些函數(shù),可以使用異步信號(hào)安全函數(shù)(async-signal-safe function)來(lái)代替。異步信號(hào)安全函數(shù)是指可以在信號(hào)處理函數(shù)中安全使用的函數(shù),它們不會(huì)產(chǎn)生競(jìng)爭(zhēng)條件,也不會(huì)因?yàn)橹袛喽鴮?dǎo)致不確定的行為。常見(jiàn)的異步信號(hào)安全函數(shù)包括write、_exit、read、fcntl等。
二十三、信號(hào)集及相關(guān)函數(shù)

可以使用以下函數(shù)來(lái)操作信號(hào)集:
sigemptyset函數(shù):用于清空信號(hào)集,將所有信號(hào)從信號(hào)集中移除。sigfillset函數(shù):用于將所有信號(hào)加入信號(hào)集中。sigaddset函數(shù):用于將指定的信號(hào)加入信號(hào)集中。sigdelset函數(shù):用于將指定的信號(hào)從信號(hào)集中移除。sigismember函數(shù):用于判斷指定的信號(hào)是否包含在信號(hào)集中。

阻塞信號(hào)集(Blocked signal set):用于管理當(dāng)前被阻塞的信號(hào)。當(dāng)進(jìn)程接收到一個(gè)被阻塞的信號(hào)時(shí),該信號(hào)不會(huì)被處理,而是被加入到阻塞信號(hào)集中,直到該信號(hào)被解除阻塞后才會(huì)被處理??梢允褂胹igprocmask函數(shù)來(lái)設(shè)置和獲取阻塞信號(hào)集。
未決信號(hào)集(Pending signal set):用于管理當(dāng)前未被處理的信號(hào)。當(dāng)進(jìn)程接收到一個(gè)信號(hào)時(shí),該信號(hào)會(huì)被加入到未決信號(hào)集中,直到該信號(hào)被處理后才會(huì)從未決信號(hào)集中刪除??梢允褂胹igpending函數(shù)來(lái)獲取當(dāng)前未決信號(hào)集中的信號(hào)。
可以使用以下函數(shù)來(lái)操作阻塞信號(hào)集:
sigprocmask函數(shù):用于設(shè)置和獲取阻塞信號(hào)集。int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
```
其中,how參數(shù)指定操作類型,可以是以下三個(gè)值之一:
- SIG_BLOCK:將set指定的信號(hào)集中的信號(hào)添加到當(dāng)前進(jìn)程的阻塞信號(hào)集中。
- SIG_UNBLOCK:將set指定的信號(hào)集中的信號(hào)從當(dāng)前進(jìn)程的阻塞信號(hào)集中移除。
- SIG_SETMASK:用set指定的信號(hào)集替換當(dāng)前進(jìn)程的阻塞信號(hào)集。
set參數(shù)為指向要設(shè)置的信號(hào)集的指針,oldset參數(shù)為指向用于存儲(chǔ)原阻塞信號(hào)集的指針。如果oldset參數(shù)不為NULL,則會(huì)將原阻塞信號(hào)集存儲(chǔ)到該指針指向的位置中。sigpending函數(shù):用于獲取當(dāng)前未決信號(hào)集中的信號(hào)。
#include <signal.h>
int sigpending(sigset_t *set);
其中,set參數(shù)為指向要存儲(chǔ)未決信號(hào)集的指針。該函數(shù)將當(dāng)前未決信號(hào)集存儲(chǔ)到set指向的位置中。
需要注意的是,阻塞信號(hào)集和未決信號(hào)集可以用于控制信號(hào)的處理方式,但在使用時(shí)需要特別小心,以免導(dǎo)致信號(hào)丟失或不被處理。此外,對(duì)于一些特殊的信號(hào),例如SIGKILL和SIGSTOP,無(wú)法被阻塞或捕獲。

二十四、sigprocmask 函數(shù)使用

sigprocmask函數(shù)的原型如下:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);其中,how參數(shù)指定操作類型,可以是以下三個(gè)值之一:
SIG_BLOCK:將set指定的信號(hào)集中的信號(hào)添加到當(dāng)前進(jìn)程的阻塞信號(hào)集中。
SIG_UNBLOCK:將set指定的信號(hào)集中的信號(hào)從當(dāng)前進(jìn)程的阻塞信號(hào)集中移除。
SIG_SETMASK:用set指定的信號(hào)集替換當(dāng)前進(jìn)程的阻塞信號(hào)集。
set參數(shù)為指向要設(shè)置的信號(hào)集的指針,oldset參數(shù)為指向用于存儲(chǔ)原阻塞信號(hào)集的指針。如果oldset參數(shù)不為NULL,則會(huì)將原阻塞信號(hào)集存儲(chǔ)到該指針指向的位置中。需要注意的是,阻塞信號(hào)集是對(duì)當(dāng)前進(jìn)程全局生效的,因此需要謹(jǐn)慎使用,避免對(duì)整個(gè)進(jìn)程的信號(hào)處理產(chǎn)生不良影響。同時(shí),在阻塞信號(hào)集中阻塞某個(gè)信號(hào)時(shí),該信號(hào)不會(huì)被丟棄,而是會(huì)被緩存起來(lái),直到解除阻塞后再被處理。因此,如果在某個(gè)時(shí)刻多個(gè)信號(hào)被阻塞,這些信號(hào)都將被緩存起來(lái),可能會(huì)導(dǎo)致后續(xù)處理時(shí)出現(xiàn)延遲。
二十五、sigaction 信號(hào)捕捉函數(shù)

sigaction函數(shù)的原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);其中,signum參數(shù)為要設(shè)置的信號(hào)編號(hào),act參數(shù)為指向要設(shè)置的信號(hào)處理方式的結(jié)構(gòu)體指針,oldact參數(shù)為指向用于存儲(chǔ)原信號(hào)處理方式的結(jié)構(gòu)體指針。如果oldact參數(shù)不為NULL,則會(huì)將原信號(hào)處理方式存儲(chǔ)到該指針指向的位置中。需要注意的是,信號(hào)處理函數(shù)應(yīng)該盡可能的簡(jiǎn)潔和快速,避免在信號(hào)處理函數(shù)中執(zhí)行復(fù)雜的操作。同時(shí),在信號(hào)處理函數(shù)中只能調(diào)用一些異步信號(hào)安全的函數(shù),不能調(diào)用一些可能會(huì)阻塞的函數(shù),否則可能會(huì)引起不可預(yù)知的問(wèn)題。

2.內(nèi)核檢查進(jìn)程的信號(hào)屏蔽字。在將信號(hào)發(fā)送給進(jìn)程之前,內(nèi)核會(huì)檢查進(jìn)程的信號(hào)屏蔽字,如果該信號(hào)在屏蔽字中,則該信號(hào)將被屏蔽,并不會(huì)發(fā)送給進(jìn)程。否則,內(nèi)核將繼續(xù)發(fā)送信號(hào)。
3.內(nèi)核發(fā)送信號(hào)給進(jìn)程。如果信號(hào)沒(méi)有被屏蔽,則內(nèi)核會(huì)將信號(hào)發(fā)送給進(jìn)程。在發(fā)送信號(hào)之前,內(nèi)核會(huì)暫停進(jìn)程的執(zhí)行,并將進(jìn)程的上下文保存到內(nèi)核數(shù)據(jù)結(jié)構(gòu)中,以便在信號(hào)處理函數(shù)執(zhí)行完畢后恢復(fù)進(jìn)程的執(zhí)行。
4.內(nèi)核執(zhí)行信號(hào)處理函數(shù)。當(dāng)進(jìn)程接收到信號(hào)時(shí),內(nèi)核會(huì)執(zhí)行該進(jìn)程的信號(hào)處理函數(shù)。內(nèi)核會(huì)先檢查信號(hào)處理函數(shù)是否存在,如果存在,則將進(jìn)程的上下文切換到信號(hào)處理函數(shù)中執(zhí)行。在執(zhí)行信號(hào)處理函數(shù)期間,內(nèi)核會(huì)將進(jìn)程的信號(hào)屏蔽字設(shè)置為信號(hào)處理函數(shù)中指定的信號(hào)屏蔽字,以避免其他信號(hào)的干擾。如果信號(hào)處理函數(shù)執(zhí)行完畢,則將進(jìn)程的上下文恢復(fù),并繼續(xù)執(zhí)行進(jìn)程。
二十六、SIGCHLD 信號(hào)

SIGCHLD信號(hào)的默認(rèn)處理方式是忽略該信號(hào)。因此,如果父進(jìn)程不捕捉該信號(hào),當(dāng)子進(jìn)程終止或停止時(shí),該信號(hào)不會(huì)對(duì)父進(jìn)程產(chǎn)生任何影響。
可以使用sigaction函數(shù)捕捉SIGCHLD信號(hào),并在信號(hào)處理函數(shù)中處理子進(jìn)程的狀態(tài)。當(dāng)捕捉到SIGCHLD信號(hào)時(shí),父進(jìn)程可以調(diào)用waitpid函數(shù)等待子進(jìn)程狀態(tài)的改變,以獲取子進(jìn)程的退出狀態(tài)碼和其他信息。需要注意的是,當(dāng)一個(gè)進(jìn)程的子進(jìn)程終止或停止時(shí),內(nèi)核只會(huì)向其父進(jìn)程發(fā)送一次SIGCHLD信號(hào),即使有多個(gè)子進(jìn)程同時(shí)終止或停止。因此,在SIGCHLD信號(hào)處理函數(shù)中,需要使用循環(huán)調(diào)用waitpid函數(shù)來(lái)處理所有的子進(jìn)程狀態(tài)。另外,由于SIGCHLD信號(hào)是異步發(fā)生的,因此在信號(hào)處理函數(shù)中需要注意使用異步信號(hào)安全的函數(shù),避免出現(xiàn)競(jìng)態(tài)條件等問(wèn)題。
二十七、共享內(nèi)存

高性能:共享內(nèi)存的訪問(wèn)速度比其他進(jìn)程間通信方式(如管道、消息隊(duì)列等)更快,因?yàn)楣蚕韮?nèi)存避免了數(shù)據(jù)的復(fù)制和系統(tǒng)調(diào)用的開(kāi)銷。
靈活性:共享內(nèi)存可以在不同進(jìn)程之間共享大量數(shù)據(jù),不僅可以用于進(jìn)程間通信,還可以用于在同一進(jìn)程的不同線程之間共享數(shù)據(jù)。
易用性:使用共享內(nèi)存的方式簡(jiǎn)單,只需要?jiǎng)?chuàng)建共享內(nèi)存區(qū)域、連接到共享內(nèi)存區(qū)域并訪問(wèn)共享內(nèi)存區(qū)域即可。
可擴(kuò)展性:共享內(nèi)存可以動(dòng)態(tài)地增加或減少內(nèi)存大小,使得應(yīng)用程序可以根據(jù)需要調(diào)整內(nèi)存大小。
穩(wěn)定性:共享內(nèi)存是在內(nèi)存中分配的,不像文件或管道等通信方式會(huì)受到硬盤容量或磁盤速度等因素的影響。因此,共享內(nèi)存通常更加穩(wěn)定和可靠。

創(chuàng)建共享內(nèi)存區(qū)域。可以使用shmget系統(tǒng)調(diào)用創(chuàng)建一個(gè)共享內(nèi)存區(qū)域,該調(diào)用會(huì)返回一個(gè)共享內(nèi)存標(biāo)識(shí)符。需要指定創(chuàng)建的共享內(nèi)存大小、權(quán)限等參數(shù)。
連接到共享內(nèi)存區(qū)域??梢允褂胹hmat系統(tǒng)調(diào)用將進(jìn)程連接到共享內(nèi)存區(qū)域,該調(diào)用會(huì)返回共享內(nèi)存區(qū)域的起始地址。需要指定共享內(nèi)存標(biāo)識(shí)符和連接方式等參數(shù)。
使用共享內(nèi)存。連接到共享內(nèi)存區(qū)域后,可以像訪問(wèn)普通內(nèi)存一樣訪問(wèn)共享內(nèi)存區(qū)域。多個(gè)進(jìn)程可以共享同一塊內(nèi)存區(qū)域,可以通過(guò)在內(nèi)存中存儲(chǔ)數(shù)據(jù)來(lái)進(jìn)行進(jìn)程間通信。
分離共享內(nèi)存區(qū)域??梢允褂胹hmdt系統(tǒng)調(diào)用將進(jìn)程與共享內(nèi)存區(qū)域分離,該調(diào)用會(huì)釋放進(jìn)程與共享內(nèi)存區(qū)域的連接。需要指定共享內(nèi)存區(qū)域的起始地址。
刪除共享內(nèi)存區(qū)域??梢允褂胹hmctl系統(tǒng)調(diào)用刪除共享內(nèi)存區(qū)域,該調(diào)用會(huì)釋放共享內(nèi)存區(qū)域并撤銷與該區(qū)域的所有連接。需要指定共享內(nèi)存標(biāo)識(shí)符和刪除方式等參數(shù)。

-m:顯示共享內(nèi)存的信息;
-q:顯示消息隊(duì)列的信息;
-s:顯示信號(hào)量的信息。ipcrm是Linux系統(tǒng)中的一個(gè)命令,用于刪除IPC資源,包括消息隊(duì)列、共享內(nèi)存和信號(hào)量等。ipcrm命令的使用方法如下:-m:刪除共享內(nèi)存;
-q:刪除消息隊(duì)列;
-s:刪除信號(hào)量。

二十八、進(jìn)程組、會(huì)話、進(jìn)程終端

物理終端:物理終端是指通過(guò)連接計(jì)算機(jī)的串口、并口、繪圖儀和控制臺(tái)等物理設(shè)備來(lái)提供終端功能,用戶通過(guò)這些設(shè)備與計(jì)算機(jī)進(jìn)行交互。在現(xiàn)代計(jì)算機(jī)中,物理終端已經(jīng)較少使用。
虛擬終端:虛擬終端是指通過(guò)軟件模擬的終端,提供與物理終端相同的終端功能。在Linux系統(tǒng)中,可以通過(guò)多個(gè)虛擬終端同時(shí)進(jìn)行工作,每個(gè)虛擬終端都有自己的命令行提示符和命令執(zhí)行環(huán)境,用戶可以在不同的虛擬終端中運(yùn)行不同的程序和命令。
在Linux系統(tǒng)中,可以使用以下命令來(lái)管理終端:
tty命令:用于顯示當(dāng)前終端的名稱。
who命令:用于顯示當(dāng)前登錄到系統(tǒng)的用戶信息,包括用戶名、登錄時(shí)間、登錄終端等。
login命令:用于登錄到系統(tǒng),需要輸入正確的用戶名和密碼。
logout或exit命令:用于退出當(dāng)前登錄的終端。
Ctrl+Alt+F1F6鍵:用于切換不同的虛擬終端,F(xiàn)1F6鍵分別對(duì)應(yīng)不同的虛擬終端。
reset命令:用于清屏并重置終端。
stty命令:用于設(shè)置終端屬性,如終端大小、回顯方式、流控制等。

ps命令:用于顯示當(dāng)前系統(tǒng)中的進(jìn)程信息,可以使用ps命令查看每個(gè)進(jìn)程的進(jìn)程ID(PID)和進(jìn)程組ID(PGID)等信息。
jobs命令:用于顯示當(dāng)前作業(yè)的狀態(tài),包括作業(yè)號(hào)、進(jìn)程ID、作業(yè)狀態(tài)等。
fg命令:用于將一個(gè)后臺(tái)作業(yè)移動(dòng)到前臺(tái)運(yùn)行。
bg命令:用于將一個(gè)前臺(tái)作業(yè)移動(dòng)到后臺(tái)運(yùn)行。
kill命令:用于向一個(gè)或多個(gè)進(jìn)程發(fā)送信號(hào),可以使用kill命令向進(jìn)程組發(fā)送信號(hào)。
setpgid函數(shù):用于設(shè)置進(jìn)程的進(jìn)程組ID。
getpgid函數(shù):用于獲取進(jìn)程的進(jìn)程組ID。

會(huì)話的主要作用是為了將一組相互關(guān)聯(lián)的進(jìn)程組織在一起,使它們能夠共享同一個(gè)控制終端,并且可以進(jìn)行作業(yè)控制和進(jìn)程組間通信等操作。
在Linux系統(tǒng)中,可以使用以下命令來(lái)管理會(huì)話:
login命令:用于登錄到系統(tǒng),并創(chuàng)建一個(gè)新的會(huì)話。
tty命令:用于顯示當(dāng)前終端的名稱,也可以用于查看當(dāng)前會(huì)話的名稱。
ps命令:用于顯示當(dāng)前系統(tǒng)中的進(jìn)程信息,可以使用ps命令查看每個(gè)進(jìn)程的會(huì)話ID(SID)等信息。
setsid命令:用于創(chuàng)建一個(gè)新的會(huì)話。
nohup命令:用于在后臺(tái)運(yùn)行一個(gè)進(jìn)程,并且不受終端關(guān)閉等影響。
需要注意的是,在使用會(huì)話時(shí)需要謹(jǐn)慎操作,避免誤操作導(dǎo)致系統(tǒng)數(shù)據(jù)丟失或系統(tǒng)崩潰等問(wèn)題。在進(jìn)行敏感操作時(shí),建議使用root用戶或具有足夠權(quán)限的用戶進(jìn)行操作。

一個(gè)進(jìn)程可以屬于一個(gè)進(jìn)程組,一個(gè)進(jìn)程組可以有多個(gè)進(jìn)程。
一個(gè)進(jìn)程組可以屬于一個(gè)會(huì)話,一個(gè)會(huì)話可以有多個(gè)進(jìn)程組。
一個(gè)會(huì)話與一個(gè)控制終端相互關(guān)聯(lián),一個(gè)控制終端可以與多個(gè)會(huì)話關(guān)聯(lián)。
一個(gè)會(huì)話中的第一個(gè)進(jìn)程被稱為控制進(jìn)程,控制進(jìn)程與控制終端相互關(guān)聯(lián)。
控制進(jìn)程可以通過(guò)控制終端與用戶進(jìn)行交互,同時(shí)可以對(duì)進(jìn)程組進(jìn)行作業(yè)控制和進(jìn)程組間通信等操作。

setpgid(pid, pgid):將進(jìn)程pid加入進(jìn)程組pgid中,如果pgid為0,則將pid所在的進(jìn)程組ID設(shè)置為pid本身的進(jìn)程ID。
getpgid(pid):獲取進(jìn)程pid所在的進(jìn)程組ID。
setsid():創(chuàng)建一個(gè)新的會(huì)話,并使調(diào)用進(jìn)程成為該會(huì)話的首進(jìn)程和進(jìn)程組組長(zhǎng)。
getpgrp():獲取當(dāng)前進(jìn)程所在的進(jìn)程組ID。
tcgetpgrp(fd):獲取與文件描述符fd關(guān)聯(lián)的終端的進(jìn)程組ID。
tcsetpgrp(fd, pgrp):將與文件描述符fd關(guān)聯(lián)的終端的進(jìn)程組ID設(shè)置為pgrp。
需要注意的是,這些函數(shù)需要在合適的上下文中使用,否則可能會(huì)導(dǎo)致意想不到的結(jié)果或者錯(cuò)誤。例如,在使用setsid函數(shù)創(chuàng)建一個(gè)新的會(huì)話之前,需要先關(guān)閉所有的文件描述符,否則會(huì)話可能會(huì)繼承該進(jìn)程打開(kāi)的文件描述符,導(dǎo)致意想不到的結(jié)果。
另外,Linux系統(tǒng)也提供了一些命令來(lái)進(jìn)行進(jìn)程組、會(huì)話操作,例如:
jobs:查看當(dāng)前shell會(huì)話中的作業(yè)信息。
fg:將一個(gè)后臺(tái)作業(yè)移動(dòng)到前臺(tái)運(yùn)行。
bg:將一個(gè)前臺(tái)作業(yè)移動(dòng)到后臺(tái)運(yùn)行。
kill:向一個(gè)或多個(gè)進(jìn)程發(fā)送信號(hào),可以使用kill命令向進(jìn)程組發(fā)送信號(hào)。
二十九、守護(hù)進(jìn)程

守護(hù)進(jìn)程通常在系統(tǒng)啟動(dòng)時(shí)自動(dòng)啟動(dòng),以root用戶身份運(yùn)行,并且在運(yùn)行過(guò)程中不受用戶登錄和注銷的影響。它們通常不會(huì)向控制臺(tái)輸出信息,而是將日志信息寫入日志文件中。

創(chuàng)建一個(gè)新的進(jìn)程,然后通過(guò)調(diào)用setsid函數(shù)創(chuàng)建一個(gè)新的會(huì)話,并使該進(jìn)程成為會(huì)話的首進(jìn)程。
將該進(jìn)程的umask設(shè)置為0,以便它可以創(chuàng)建任何文件和目錄。
關(guān)閉所有的文件描述符,以避免它們繼承自父進(jìn)程。
將標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出重定向到/dev/null或日志文件中,防止輸出到控制終端。
在守護(hù)進(jìn)程的主循環(huán)中執(zhí)行需要執(zhí)行的任務(wù)。