成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一文搞懂Linux進(jìn)程:解鎖操作系統(tǒng)的核心秘密

系統(tǒng) Linux
在Linux 的世界里,進(jìn)程就像是一個(gè)個(gè)活躍的小生命,它們?cè)谙到y(tǒng)的舞臺(tái)上各司其職,共同演繹著復(fù)雜而精彩的計(jì)算交響樂(lè)。

在Linux 的世界里,進(jìn)程就像是一個(gè)個(gè)活躍的小生命,它們?cè)谙到y(tǒng)的舞臺(tái)上各司其職,共同演繹著復(fù)雜而精彩的計(jì)算交響樂(lè)。簡(jiǎn)單來(lái)說(shuō),進(jìn)程是程序的一次執(zhí)行實(shí)例。當(dāng)你在 Linux 系統(tǒng)中運(yùn)行一個(gè)程序,比如用命令行啟動(dòng)一個(gè) Python 腳本,系統(tǒng)就會(huì)創(chuàng)建一個(gè)進(jìn)程來(lái)執(zhí)行這個(gè)腳本。程序本身只是存儲(chǔ)在磁盤上的一組靜態(tài)指令和數(shù)據(jù),而進(jìn)程則是這些指令和數(shù)據(jù)在內(nèi)存中的動(dòng)態(tài)運(yùn)行過(guò)程。進(jìn)程具有一些關(guān)鍵屬性,這些屬性就像是它們的 “身份標(biāo)簽” 和 “行為準(zhǔn)則”。

比如,每個(gè)進(jìn)程都有一個(gè)唯一的進(jìn)程標(biāo)識(shí)符(PID),這就好比每個(gè)人的身份證號(hào)碼,系統(tǒng)通過(guò) PID 來(lái)識(shí)別和管理不同的進(jìn)程。進(jìn)程還有自己的狀態(tài),常見(jiàn)的狀態(tài)包括運(yùn)行態(tài)(R)、睡眠態(tài)(S)、停止態(tài)(T)和僵尸態(tài)(Z)等 。

  • 運(yùn)行態(tài)表示進(jìn)程正在 CPU 上執(zhí)行或準(zhǔn)備執(zhí)行;
  • 睡眠態(tài)則是進(jìn)程在等待某個(gè)事件的完成,比如等待 I/O 操作結(jié)束;
  • 停止態(tài)是進(jìn)程被暫停,通常是因?yàn)榻邮盏搅颂囟ǖ男盘?hào);

僵尸態(tài)比較特殊,當(dāng)子進(jìn)程結(jié)束運(yùn)行但父進(jìn)程沒(méi)有回收其資源時(shí),子進(jìn)程就會(huì)進(jìn)入僵尸態(tài)。進(jìn)程的優(yōu)先級(jí)也很重要,它決定了進(jìn)程獲取 CPU 時(shí)間的先后順序。優(yōu)先級(jí)高的進(jìn)程會(huì)優(yōu)先被調(diào)度執(zhí)行,就像在醫(yī)院里,急診病人會(huì)比普通病人優(yōu)先得到救治一樣。在 Linux 系統(tǒng)中,進(jìn)程的優(yōu)先級(jí)可以通過(guò) nice 值來(lái)調(diào)整,nice 值的范圍是 -20 到 19,值越小優(yōu)先級(jí)越高。不過(guò),普通用戶只能在一定范圍內(nèi)調(diào)整自己進(jìn)程的 nice 值,而 root 用戶則擁有更大的權(quán)限。

一、Linux進(jìn)程概念

從理論角度看,是對(duì)正在運(yùn)行的程序過(guò)程的抽象;

從實(shí)現(xiàn)角度看,是一種數(shù)據(jù)結(jié)構(gòu),目的在于清晰地刻畫動(dòng)態(tài)系統(tǒng)的內(nèi)在規(guī)律,有效管理和調(diào)度進(jìn)入計(jì)算機(jī)系統(tǒng)主存儲(chǔ)器運(yùn)行的程序。

1. 什么是進(jìn)程?

  • 狹義定義:進(jìn)程就是一段程序的執(zhí)行過(guò)程。
  • 廣義定義:進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。它是操作系統(tǒng)動(dòng)態(tài)執(zhí)行的基本單元,在傳統(tǒng)的操作系統(tǒng)中,進(jìn)程既是基本的分配單元,也是基本的執(zhí)行單元。
  • 進(jìn)程有怎么樣的特征?
  • 動(dòng)態(tài)性:進(jìn)程的實(shí)質(zhì)是程序在多道程序系統(tǒng)中的一次執(zhí)行過(guò)程,進(jìn)程是動(dòng)態(tài)產(chǎn)生,動(dòng)態(tài)消亡的。
  • 并發(fā)性:任何進(jìn)程都可以同其他進(jìn)程一起并發(fā)執(zhí)行
  • 獨(dú)立性:進(jìn)程是一個(gè)能獨(dú)立運(yùn)行的基本單位,同時(shí)也是系統(tǒng)分配資源和調(diào)度的獨(dú)立單位;
  • 異步性:由于進(jìn)程間的相互制約,使進(jìn)程具有執(zhí)行的間斷性,即進(jìn)程按各自獨(dú)立的、不可預(yù)知的速度向前推 進(jìn)
  • 結(jié)構(gòu)特征:進(jìn)程由程序、數(shù)據(jù)和進(jìn)程控制塊三部分組成;
  • 多個(gè)不同的進(jìn)程可以包含相同的程序:一個(gè)程序在不同的數(shù)據(jù)集里就構(gòu)成不同的進(jìn)程,能得到不同的結(jié)果;但是執(zhí)行過(guò)程中,程序不能發(fā)生改變。

2. Linux進(jìn)程結(jié)構(gòu)?

Linux進(jìn)程結(jié)構(gòu):可由三部分組成:代碼段、數(shù)據(jù)段、堆棧段。也就是程序、數(shù)據(jù)、進(jìn)程控制塊PCB(Process Control Block)組成。進(jìn)程控制塊是進(jìn)程存在的惟一標(biāo)識(shí),系統(tǒng)通過(guò)PCB的存在而感知進(jìn)程的存在。

系統(tǒng)通過(guò)PCB對(duì)進(jìn)程進(jìn)行管理和調(diào)度。PCB包括創(chuàng)建進(jìn)程、執(zhí)行程序、退出進(jìn)程以及改變進(jìn)程的優(yōu)先級(jí)等。而進(jìn)程中的PCB用一個(gè)名為task_struct的結(jié)構(gòu)體來(lái)表示,定義在include/linux/sched.h中,每當(dāng)創(chuàng)建一新進(jìn)程時(shí),便在內(nèi)存中申請(qǐng)一個(gè)空的task_struct結(jié)構(gòu),填入所需信息,同時(shí),指向該結(jié)構(gòu)的指針也被加入到task數(shù)組中,所有進(jìn)程控制塊都存儲(chǔ)在task[]數(shù)組中。

進(jìn)程的三種基本狀態(tài):

  • 就緒狀態(tài):進(jìn)程已獲得除處理器外的所需資源,等待分配處理器資源;只要分配了處理器進(jìn)程就可執(zhí)行。就緒進(jìn)程可以按多個(gè)優(yōu)先級(jí)來(lái)劃分隊(duì)列。例如,當(dāng)一個(gè)進(jìn)程由于時(shí)間片用完而進(jìn)入就緒狀態(tài)時(shí),排入低優(yōu)先級(jí)隊(duì)列;當(dāng)進(jìn)程由I/O操作完成而進(jìn)入就緒狀態(tài)時(shí),排入高優(yōu)先級(jí)隊(duì)列。
  • 運(yùn)行狀態(tài):進(jìn)程占用處理器資源;處于此狀態(tài)的進(jìn)程的數(shù)目小于等于處理器的數(shù)目。在沒(méi)有其他進(jìn)程可以 執(zhí)行時(shí)(如所有進(jìn)程都在阻塞狀態(tài)),通常會(huì)自動(dòng)執(zhí)行系統(tǒng)的空閑進(jìn)程。
  • 阻塞狀態(tài):由于進(jìn)程等待某種條件(如I/O操作或進(jìn)程同步),在條件滿足之前無(wú)法繼續(xù)執(zhí)行。該事件發(fā)生 前即使把處理機(jī)分配給該進(jìn)程,也無(wú)法運(yùn)行。

3. 進(jìn)程和程序的區(qū)別?

  • 程序是指令和數(shù)據(jù)的有序集合,是一個(gè)靜態(tài)的概念。而進(jìn)程是程序在處理機(jī)上的一次執(zhí)行過(guò)程,它是一個(gè) 動(dòng)態(tài)的概念。
  • 程序可以作為一種軟件資料長(zhǎng)期存在,而進(jìn)程是有一定生命期的。程序是永久的,進(jìn)程是暫時(shí)的。
  • 進(jìn)程是由進(jìn)程控制塊、程序段、數(shù)據(jù)段三部分組成;
  • 進(jìn)程具有創(chuàng)建其他進(jìn)程的功能,而程序沒(méi)有。
  • 同一程序同時(shí)運(yùn)行于若干個(gè)數(shù)據(jù)集合上,它將屬于若干個(gè)不同的進(jìn)程,也就是說(shuō)同一程序可以對(duì)應(yīng)多個(gè)進(jìn) 程。
  • 在傳統(tǒng)的操作系統(tǒng)中,程序并不能獨(dú)立運(yùn)行,作為資源分配和獨(dú)立運(yùn)行的基本單元都是進(jìn)程。

二、進(jìn)程的查看與監(jiān)控

在 Linux 系統(tǒng)中,了解進(jìn)程的運(yùn)行狀態(tài)是系統(tǒng)管理和調(diào)試的重要環(huán)節(jié)。就像一位交通警察需要時(shí)刻了解道路上車輛的行駛情況一樣,系統(tǒng)管理員和開發(fā)者需要通過(guò)各種工具來(lái)查看和監(jiān)控進(jìn)程。下面我們就來(lái)介紹幾個(gè)常用的命令。

1. ps命令

ps 命令就像是進(jìn)程世界的 “普查員”,它可以讓我們查看系統(tǒng)中正在運(yùn)行的進(jìn)程的詳細(xì)信息。通過(guò)不同的選項(xiàng)組合,ps 命令能夠展現(xiàn)出進(jìn)程的豐富細(xì)節(jié)。

比如,使用ps -aux命令,它會(huì)列出系統(tǒng)中所有用戶的進(jìn)程信息。這里的a表示顯示所有用戶的進(jìn)程(包括其他用戶的進(jìn)程),u是以用戶友好的格式顯示進(jìn)程信息(包括用戶名、CPU 和內(nèi)存使用率等詳細(xì)信息),x表示顯示沒(méi)有控制終端的進(jìn)程(通常是后臺(tái)進(jìn)程) 。在我的 Linux 服務(wù)器上執(zhí)行這個(gè)命令,得到的部分輸出如下:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  16120  3340?        Ss   08:21   0:01 /sbin/init splash
root         2  0.0  0.0      0     0?        S    08:21   0:00 [kthreadd]
root         3  0.0  0.0      0     0?        S    08:21   0:00 [ksoftirqd/0]

在這些輸出信息中,USER表示進(jìn)程所屬的用戶;PID是進(jìn)程的唯一標(biāo)識(shí)符;%CPU顯示進(jìn)程使用的 CPU 百分比;%MEM是進(jìn)程使用的內(nèi)存百分比;VSZ代表進(jìn)程使用的虛擬內(nèi)存大小(單位:KB);RSS是進(jìn)程使用的物理內(nèi)存大小(單位:KB);TTY表示進(jìn)程所連接的終端設(shè)備;STAT是進(jìn)程狀態(tài),常見(jiàn)的如S表示睡眠態(tài),R表示運(yùn)行態(tài)等;START是進(jìn)程啟動(dòng)時(shí)間;TIME是進(jìn)程占用 CPU 的累計(jì)時(shí)間;COMMAND則是啟動(dòng)進(jìn)程的命令名稱和參數(shù)。

而ps -ef命令同樣用于顯示所有進(jìn)程的詳細(xì)信息,這里的-e表示顯示所有進(jìn)程,包括其他用戶的進(jìn)程,-f是以完整格式顯示進(jìn)程信息 。它的輸出格式與ps -aux略有不同,但包含的核心信息是一致的。例如:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:21?        00:00:01 /sbin/init splash
root         2     0  0 08:21?        00:00:00 [kthreadd]
root         3     2  0 08:21?        00:00:00 [ksoftirqd/0]

其中,UID是進(jìn)程所有者的用戶 ID,PPID是父進(jìn)程 ID,C表示 CPU 使用率,STIME是進(jìn)程啟動(dòng)時(shí)間,TTY是進(jìn)程關(guān)聯(lián)的終端設(shè)備,TIME是進(jìn)程占用的 CPU 時(shí)間,CMD是啟動(dòng)進(jìn)程的命令名稱和參數(shù)。通過(guò)這些信息,我們可以清晰地了解每個(gè)進(jìn)程的基本情況。

2. top命令

top 命令則像是一個(gè) “實(shí)時(shí)監(jiān)控器”,它為我們提供了系統(tǒng)進(jìn)程的動(dòng)態(tài)實(shí)時(shí)視圖,讓我們可以實(shí)時(shí)了解系統(tǒng)中各個(gè)進(jìn)程的資源占用狀況,就像在觀看一場(chǎng)實(shí)時(shí)的比賽直播一樣,隨時(shí)掌握進(jìn)程的 “戰(zhàn)況”。

當(dāng)我們?cè)诿钚休斎雝op命令后,會(huì)看到一個(gè)動(dòng)態(tài)更新的界面,它每隔一段時(shí)間(默認(rèn) 3 秒)就會(huì)自動(dòng)刷新,展示最新的進(jìn)程狀態(tài)。界面的頂部顯示了系統(tǒng)的一些關(guān)鍵信息,如系統(tǒng)的運(yùn)行時(shí)間、當(dāng)前登錄用戶數(shù)、系統(tǒng)負(fù)載(load average)等 。接下來(lái)是進(jìn)程統(tǒng)計(jì)信息,包括總進(jìn)程數(shù)、正在運(yùn)行的進(jìn)程數(shù)、睡眠的進(jìn)程數(shù)、停止的進(jìn)程數(shù)和僵尸進(jìn)程數(shù)等。再下面是 CPU 使用情況的詳細(xì)統(tǒng)計(jì),包括用戶空間占用 CPU 百分比、內(nèi)核空間占用 CPU 百分比、空閑 CPU 百分比等 。還有內(nèi)存和交換分區(qū)的使用情況統(tǒng)計(jì)。

在 top 命令的交互界面中,我們可以通過(guò)一些按鍵來(lái)進(jìn)行各種操作。比如,按下P鍵,進(jìn)程列表會(huì)按照 CPU 使用率從高到低進(jìn)行排序,這樣我們就能快速找到那些 “吃 CPU 大戶” 的進(jìn)程;按下M鍵,會(huì)按照內(nèi)存使用率排序,幫助我們定位占用內(nèi)存較多的進(jìn)程;按下N鍵,會(huì)以 PID 排序 。如果我們想要監(jiān)控特定用戶的進(jìn)程,可以按下u鍵,然后輸入用戶名,界面就會(huì)只顯示該用戶的進(jìn)程。當(dāng)我們發(fā)現(xiàn)某個(gè)進(jìn)程占用資源過(guò)高且不需要它繼續(xù)運(yùn)行時(shí),可以按下k鍵,然后輸入該進(jìn)程的 PID,再按下回車鍵,就可以終止該進(jìn)程(默認(rèn)發(fā)送的是 15 信號(hào),即正常終止進(jìn)程,如果進(jìn)程沒(méi)有響應(yīng),可以再次輸入 9,發(fā)送強(qiáng)制終止信號(hào)) 。按下q鍵則可以退出 top 命令界面。

3. pstree命令

pstree 命令就像是一位 “家族譜繪制師”,它以樹狀結(jié)構(gòu)展示進(jìn)程之間的關(guān)系,讓我們可以一目了然地看到各個(gè)進(jìn)程之間的父子關(guān)系,就像查看一個(gè)家族的族譜一樣清晰。

當(dāng)我們執(zhí)行pstree命令時(shí),它會(huì)以init進(jìn)程(在 systemd 系統(tǒng)中通常是systemd進(jìn)程)為根節(jié)點(diǎn),展示整個(gè)系統(tǒng)的進(jìn)程樹。例如,在我的系統(tǒng)上執(zhí)行pstree命令,得到的部分輸出如下:

systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager─┬─dhclient
        │                ├─dnsmasq───2*[{dnsmasq}]
        │                └─2*[{NetworkManager}]
        ├─agetty
        ├─atd
        ├─auditd───{auditd}
        ├─chronyd

從這個(gè)輸出中,我們可以清晰地看到systemd是很多進(jìn)程的父進(jìn)程,比如ModemManager、NetworkManager等,而NetworkManager又有自己的子進(jìn)程,如dhclient、dnsmasq等 。如果我們想要查看某個(gè)特定進(jìn)程 ID 的進(jìn)程樹,可以使用pstree -p PID的形式,其中PID是具體的進(jìn)程 ID。例如,pstree -p 1會(huì)以init進(jìn)程(PID 通常為 1)為根節(jié)點(diǎn),展示其完整的進(jìn)程樹,并在每個(gè)進(jìn)程名稱旁邊顯示其 PID 。如果我們只想查看某個(gè)用戶的所有進(jìn)程及其關(guān)系,可以使用pstree -u username的形式,其中username是具體的用戶名。

三、進(jìn)程的控制與管理

1. 進(jìn)程的啟動(dòng)與終止

在 Linux 系統(tǒng)中,啟動(dòng)進(jìn)程是一項(xiàng)常見(jiàn)的操作。最基本的方式就是在命令行直接輸入命令并回車,此時(shí)進(jìn)程會(huì)在前臺(tái)運(yùn)行 。例如,輸入ls命令,系統(tǒng)會(huì)立即列出當(dāng)前目錄下的文件和文件夾,在這個(gè)過(guò)程中,命令行被該進(jìn)程占用,直到ls命令執(zhí)行完畢,才會(huì)返回命令提示符,在此期間我們無(wú)法在同一命令行執(zhí)行其他命令。

造成進(jìn)程產(chǎn)生的主要事件有:

  • 系統(tǒng)初始化
  • 執(zhí)行進(jìn)程創(chuàng)立程序
  • 用戶請(qǐng)求創(chuàng)立新進(jìn)程

造成進(jìn)程消亡的事件:

  • 進(jìn)程運(yùn)行完成而退出。
  • 進(jìn)程因錯(cuò)誤而自行退出
  • 進(jìn)程被其他進(jìn)程所終止
  • 進(jìn)程因異常而被強(qiáng)行終結(jié)

如果我們希望命令在后臺(tái)運(yùn)行,不占用命令行,以便可以同時(shí)執(zhí)行其他操作,可以在命令后面加上&符號(hào) 。比如,我們想要運(yùn)行一個(gè)長(zhǎng)時(shí)間的計(jì)算任務(wù),如python long_computation.py &,這樣該 Python 腳本就會(huì)在后臺(tái)啟動(dòng),命令行馬上返回提示符,我們可以繼續(xù)執(zhí)行其他命令 。通過(guò)這種方式,多個(gè)命令可以并行執(zhí)行,提高了工作效率。需要注意的是,使用&符號(hào)將命令放到后臺(tái)運(yùn)行后,該進(jìn)程的父進(jìn)程還是當(dāng)前終端 shell 的進(jìn)程。一旦父進(jìn)程退出,它會(huì)發(fā)送 hangup 信號(hào)給所有子進(jìn)程,子進(jìn)程收到 hangup 信號(hào)以后也會(huì)退出。

對(duì)于已經(jīng)在前臺(tái)執(zhí)行的命令,如果想要將其放到后臺(tái)運(yùn)行,可以先按下Ctrl+Z組合鍵暫停該進(jìn)程,然后使用bg命令將其放到后臺(tái)繼續(xù)運(yùn)行 。例如,我們?cè)谇芭_(tái)執(zhí)行vim編輯文件時(shí),突然需要執(zhí)行其他命令,就可以按下Ctrl+Z暫停vim進(jìn)程,然后輸入bg命令將vim放到后臺(tái),此時(shí)可以執(zhí)行其他命令。若要將后臺(tái)進(jìn)程恢復(fù)到前臺(tái)運(yùn)行,可以使用fg命令,加上對(duì)應(yīng)的作業(yè)編號(hào),作業(yè)編號(hào)可以通過(guò)jobs命令查看,jobs命令會(huì)顯示所有暫停以及后臺(tái)執(zhí)行的命令及其編號(hào)。

當(dāng)我們不再需要某個(gè)進(jìn)程運(yùn)行時(shí),就需要終止它。在 Linux 中,常用kill命令和killall命令來(lái)終止進(jìn)程 。kill命令用于終止指定進(jìn)程 ID(PID)的進(jìn)程。首先,我們需要通過(guò)ps等命令獲取要終止進(jìn)程的 PID,例如ps -aux | grep process_name,然后使用kill PID來(lái)終止該進(jìn)程。如果進(jìn)程比較頑固,無(wú)法通過(guò)正常方式終止,可以使用kill -9 PID來(lái)強(qiáng)制終止,-9表示發(fā)送 SIGKILL 信號(hào),這是一種強(qiáng)制終止進(jìn)程的方式,進(jìn)程無(wú)法忽略該信號(hào),但可能會(huì)導(dǎo)致數(shù)據(jù)丟失等問(wèn)題,所以要謹(jǐn)慎使用 。比如,當(dāng)一個(gè)程序出現(xiàn)死鎖或者無(wú)響應(yīng)時(shí),我們可以使用kill -9來(lái)結(jié)束它。

killall命令則是根據(jù)進(jìn)程名稱來(lái)終止所有匹配該名稱的進(jìn)程 。它的使用相對(duì)簡(jiǎn)單,不需要先查找進(jìn)程 ID,直接使用killall process_name即可。例如,要終止所有的nginx進(jìn)程,只需輸入killall nginx 。不過(guò),使用killall命令時(shí)要特別小心,因?yàn)樗鼤?huì)終止所有匹配名稱的進(jìn)程,如果名稱不唯一,可能會(huì)誤殺其他有用的進(jìn)程。

2. 進(jìn)程優(yōu)先級(jí)調(diào)整

進(jìn)程優(yōu)先級(jí)決定了進(jìn)程在競(jìng)爭(zhēng)系統(tǒng)資源(如 CPU 時(shí)間)時(shí)的先后順序。在 Linux 系統(tǒng)中,進(jìn)程優(yōu)先級(jí)的范圍是 -20 到 19,數(shù)值越小優(yōu)先級(jí)越高 。例如,一個(gè)優(yōu)先級(jí)為 -10 的進(jìn)程會(huì)比優(yōu)先級(jí)為 5 的進(jìn)程更優(yōu)先獲得 CPU 時(shí)間。

我們可以通過(guò)ps -l命令查看進(jìn)程的優(yōu)先級(jí)相關(guān)信息,其中PRI字段表示進(jìn)程的實(shí)際優(yōu)先級(jí),NI字段表示進(jìn)程的 nice 值 。nice 值不是進(jìn)程的優(yōu)先級(jí)本身,而是用于調(diào)整優(yōu)先級(jí)的修正數(shù)據(jù),進(jìn)程的實(shí)際優(yōu)先級(jí)(PRI)會(huì)根據(jù) nice 值進(jìn)行調(diào)整,計(jì)算公式為PRI = PRI(old) + nice,通常初始的 PRI 值為 80 。比如,一個(gè)進(jìn)程的 nice 值為 0,那么它的實(shí)際優(yōu)先級(jí) PRI 就是 80;如果將其 nice 值調(diào)整為 5,那么新的實(shí)際優(yōu)先級(jí) PRI 就變?yōu)?85,優(yōu)先級(jí)相對(duì)降低。

如果想要改變進(jìn)程的優(yōu)先級(jí),可以使用nice命令和renice命令 。nice命令主要用于在啟動(dòng)進(jìn)程時(shí)設(shè)置其優(yōu)先級(jí)。例如,我們要以較高的優(yōu)先級(jí)啟動(dòng)一個(gè)top命令來(lái)實(shí)時(shí)監(jiān)控系統(tǒng)資源,可以使用nice -n -10 top,這里-n選項(xiàng)后面的 -10 表示設(shè)置的 nice 值,數(shù)值越小優(yōu)先級(jí)越高 。這樣啟動(dòng)的top進(jìn)程就會(huì)比普通啟動(dòng)的top進(jìn)程更優(yōu)先獲得 CPU 資源,能更及時(shí)地反映系統(tǒng)狀態(tài)。

renice命令則用于修改已經(jīng)在運(yùn)行的進(jìn)程的優(yōu)先級(jí) 。假設(shè)我們有一個(gè)正在運(yùn)行的進(jìn)程,其 PID 為 12345,現(xiàn)在想要將它的優(yōu)先級(jí)降低,可以使用renice -n 5 12345,這會(huì)將該進(jìn)程的 nice 值設(shè)置為 5,從而調(diào)整其實(shí)際優(yōu)先級(jí) 。在實(shí)際應(yīng)用中,比如當(dāng)我們發(fā)現(xiàn)某個(gè)后臺(tái)進(jìn)程占用過(guò)多 CPU 資源,影響到其他更重要的進(jìn)程運(yùn)行時(shí),就可以使用renice命令降低它的優(yōu)先級(jí),確保系統(tǒng)資源能合理分配給各個(gè)進(jìn)程。

3. 進(jìn)程間通信

在 Linux 系統(tǒng)中,多個(gè)進(jìn)程常常需要相互協(xié)作、交換信息,這就需要進(jìn)程間通信(IPC,Inter-Process Communication)機(jī)制 。比如,一個(gè)網(wǎng)絡(luò)服務(wù)器進(jìn)程可能需要與多個(gè)客戶端進(jìn)程進(jìn)行數(shù)據(jù)交互,一個(gè)圖形界面程序可能需要與后臺(tái)的數(shù)據(jù)處理進(jìn)程通信來(lái)獲取和展示數(shù)據(jù)。下面介紹幾種常見(jiàn)的進(jìn)程間通信方式。

(1) 管道(Pipe)

管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動(dòng),而且通常只能在具有親緣關(guān)系(如父子進(jìn)程)的進(jìn)程間使用 。它在內(nèi)核中維護(hù)了一塊內(nèi)存作為緩沖區(qū),有讀端和寫端 。我們可以使用pipe函數(shù)創(chuàng)建無(wú)名管道,父進(jìn)程創(chuàng)建管道后通過(guò)fork子進(jìn)程,子進(jìn)程會(huì)繼承父進(jìn)程的管道文件描述符,從而實(shí)現(xiàn)父子進(jìn)程間的通信 。例如,父進(jìn)程向管道寫端寫入數(shù)據(jù),子進(jìn)程從管道讀端讀取數(shù)據(jù)。

管道的讀寫遵循一定規(guī)則,當(dāng)管道的寫端不存在時(shí),讀操作會(huì)認(rèn)為已經(jīng)讀到數(shù)據(jù)末尾,返回的讀出字節(jié)數(shù)為 0;當(dāng)管道的寫端存在時(shí),如果請(qǐng)求讀取的字節(jié)數(shù)目大于管道緩沖區(qū)的大小(PIPE_BUF),則返回管道中現(xiàn)有的數(shù)據(jù)字節(jié)數(shù),如果請(qǐng)求的字節(jié)數(shù)目不大于 PIPE_BUF,則返回管道中現(xiàn)有數(shù)據(jù)字節(jié)數(shù)(當(dāng)管道中數(shù)據(jù)量小于請(qǐng)求的數(shù)據(jù)量時(shí))或者返回請(qǐng)求的字節(jié)數(shù)(當(dāng)管道中數(shù)據(jù)量不小于請(qǐng)求的數(shù)據(jù)量時(shí)) 。向管道中寫入數(shù)據(jù)時(shí),如果管道緩沖區(qū)已滿,寫操作將一直阻塞,直到有數(shù)據(jù)被讀取,緩沖區(qū)有空閑空間 。例如:

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

int main(void) {
    char buf[32] = {0};
    pid_t pid;
    int fd[2];

    // 創(chuàng)建無(wú)名管道
    if (pipe(fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父進(jìn)程
        close(fd[0]); // 關(guān)閉讀端
        write(fd[1], "hello", 5); // 向?qū)懚藢懭霐?shù)據(jù)
        close(fd[1]); // 關(guān)閉寫端
        wait(NULL); // 等待子進(jìn)程結(jié)束
    } else {
        // 子進(jìn)程
        close(fd[1]); // 關(guān)閉寫端
        read(fd[0], buf, 32); // 從讀端讀取數(shù)據(jù)
        printf("buf is %s\n", buf);
        close(fd[0]); // 關(guān)閉讀端
    }

    return 0;
}

在這個(gè)例子中,父進(jìn)程創(chuàng)建管道后,通過(guò)fork創(chuàng)建子進(jìn)程,然后父進(jìn)程關(guān)閉讀端,向?qū)懚藢懭?“hello”,子進(jìn)程關(guān)閉寫端,從讀端讀取數(shù)據(jù)并打印。

(2) 命名管道(Named Pipe,F(xiàn)IFO)

命名管道也是半雙工的通信方式,但它允許無(wú)親緣關(guān)系的進(jìn)程間通信 。它的實(shí)質(zhì)是內(nèi)核維護(hù)的一塊內(nèi)存,表現(xiàn)為一個(gè)有名字的文件 。我們可以使用mkfifo函數(shù)創(chuàng)建命名管道,不同進(jìn)程通過(guò)打開同一個(gè)命名管道文件來(lái)進(jìn)行通信 。比如,一個(gè)進(jìn)程以寫模式打開命名管道,另一個(gè)進(jìn)程以讀模式打開,就可以實(shí)現(xiàn)數(shù)據(jù)的傳輸 。例如,有兩個(gè)進(jìn)程writer.c和reader.c,writer.c負(fù)責(zé)向命名管道寫入數(shù)據(jù),reader.c負(fù)責(zé)從命名管道讀取數(shù)據(jù):

// writer.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    int fd;
    char cont_w[] = "hello sundy";

    // 創(chuàng)建命名管道
    if (mkfifo("myfifo", 0666) == -1) {
        perror("mkfifo");
        return 1;
    }

    // 以寫模式打開命名管道
    fd = open("myfifo", O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    write(fd, cont_w, sizeof(cont_w));
    close(fd);

    return 0;
}
// reader.c
/*
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    int fd;
    char buf[32] = {0};

    // 以讀模式打開命名管道
    fd = open("myfifo", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    read(fd, buf, sizeof(buf));
    printf("buf is %s\n", buf);
    close(fd);

    return 0;
} */

在這個(gè)例子中,writer.c創(chuàng)建命名管道 “myfifo” 并寫入數(shù)據(jù),reader.c打開該命名管道讀取數(shù)據(jù)并打印。

(3) 共享內(nèi)存(Shared Memory)

共享內(nèi)存是一種高效的進(jìn)程間通信方式,它允許多個(gè)進(jìn)程直接訪問(wèn)同一塊內(nèi)存區(qū)域 。共享內(nèi)存的實(shí)現(xiàn)是通過(guò)將內(nèi)存段映射到共享它的進(jìn)程的地址空間,這樣進(jìn)程間的數(shù)據(jù)傳送不再需要通過(guò)內(nèi)核進(jìn)行多次數(shù)據(jù)復(fù)制,大大提高了通信效率 。使用共享內(nèi)存時(shí),通常需要配合信號(hào)量等機(jī)制來(lái)實(shí)現(xiàn)進(jìn)程間的同步和互斥,防止多個(gè)進(jìn)程同時(shí)訪問(wèn)共享內(nèi)存時(shí)出現(xiàn)數(shù)據(jù)沖突 。例如,一個(gè)進(jìn)程創(chuàng)建共享內(nèi)存段并寫入數(shù)據(jù),其他進(jìn)程可以通過(guò)映射該共享內(nèi)存段來(lái)讀取數(shù)據(jù) 。下面是一個(gè)簡(jiǎn)單的示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define SHM_SIZE 1024

int main() {
    key_t key;
    int shmid;
    char *shm, *s;

    // 生成一個(gè)唯一的鍵值
    key = ftok(".", 'a');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 創(chuàng)建共享內(nèi)存段
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    // 將共享內(nèi)存段映射到進(jìn)程地址空間
    shm = (char *)shmat(shmid, NULL, 0);
    if (shm == (char *)-1) {
        perror("shmat");
        return 1;
    }

    s = shm;
    // 向共享內(nèi)存寫入數(shù)據(jù)
    for (int i = 0; i < 10; i++) {
        sprintf(s, "This is %d\n", i);
        s += strlen(s);
    }

    // 等待其他進(jìn)程讀取數(shù)據(jù)
    while (*shm != '*') {
        sleep(1);
    }

    // 取消映射
    if (shmdt(shm) == -1) {
        perror("shmdt");
        return 1;
    }

    // 刪除共享內(nèi)存段
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        return 1;
    }

    return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define SHM_SIZE 1024

int main() {
    key_t key;
    int shmid;
    char *shm, *s;

    // 生成相同的鍵值
    key = ftok(".", 'a');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 獲取共享內(nèi)存段
    shmid = shmget(key, SHM_SIZE, 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }

    // 將共享內(nèi)存段映射到進(jìn)程地址空間
    shm = (char *)shmat(shmid, NULL, 0);
    if (shm == (char *)-1) {
        perror("shmat");
        return 1;
    }

    // 從共享內(nèi)存讀取數(shù)據(jù)
    s = shm;
    while (*s != '\0') {
        printf("%s", s);
        s += strlen(s);
    }

    // 標(biāo)記數(shù)據(jù)已讀取
    *shm = '*';

    // 取消映射
    if (shmdt(shm) == -1) {
        perror("shmdt");
        return 1;
    }

    return 0;
}

在這個(gè)示例中,第一個(gè)進(jìn)程創(chuàng)建共享內(nèi)存并寫入數(shù)據(jù),然后等待第二個(gè)進(jìn)程讀取數(shù)據(jù)并標(biāo)記,第二個(gè)進(jìn)程獲取共享內(nèi)存并讀取數(shù)據(jù),讀取完成后標(biāo)記數(shù)據(jù)已讀取。

(4) 消息隊(duì)列(Message Queue)

消息隊(duì)列是由消息的鏈表組成,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí) 。它克服了信號(hào)傳遞信息少、管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn) 。進(jìn)程可以向消息隊(duì)列發(fā)送消息,也可以從消息隊(duì)列接收消息 。消息隊(duì)列中的消息具有類型,接收進(jìn)程可以根據(jù)消息類型有選擇地接收消息 。例如,在一個(gè)多進(jìn)程協(xié)作的系統(tǒng)中,不同的進(jìn)程可以根據(jù)自己的需求向消息隊(duì)列發(fā)送不同類型的消息,其他進(jìn)程可以根據(jù)自身需要從消息隊(duì)列中獲取特定類型的消息進(jìn)行處理 。下面是一個(gè)簡(jiǎn)單的消息隊(duì)列使用示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_SIZE 128

// 消息結(jié)構(gòu)
typedef struct msgbuf {
    long mtype;
    char mtext[MSG_SIZE];
} msgbuf;

int main() {
    key_t key;
    int msgid;
    msgbuf msg;

    // 生成一個(gè)唯一的鍵值
    key = ftok(".", 'b');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 創(chuàng)建消息隊(duì)列
    msgid = msgget(key, IPC_CREAT | 0666);
    if (msgid == -1) {
        perror("msgget");
        return 1;
    }

    // 填充消息
    msg.mtype = 1;
    strcpy(msg.mtext, "Hello, message queue!");

    // 發(fā)送消息
    if (msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0) == -1) {
        perror("msgsnd");
        return 1;
    }

    printf("Message sent.\n");

    return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_SIZE 128

// 消息結(jié)構(gòu)
typedef struct msgbuf {
    long mtype;
    char mtext[MSG_SIZE];
} msgbuf;

int main() {
    key_t key;
    int msgid;
    msgbuf msg;

    // 生成相同的鍵值
    key = ftok(".", 'b');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 獲取消息隊(duì)列
    msgid = msgget(key, 0666);
    if (msgid == -1) {
        perror("msgget");
        return 1;
    }

    // 接收消息
    if (msgrcv(msgid, &msg, MSG_SIZE, 1, 0) == -1) {
        perror("msgrcv");
        return 1;
    }

    printf("Received message: %s\n", msg.mtext);

    return 0;
}

在這個(gè)示例中,第一個(gè)進(jìn)程創(chuàng)建消息隊(duì)列并發(fā)送一條類型

四、特殊進(jìn)程剖析

1. 守護(hù)進(jìn)程

守護(hù)進(jìn)程,也被稱為精靈進(jìn)程,是 Linux 系統(tǒng)中一類特殊的進(jìn)程,它們?nèi)缤刈o(hù)系統(tǒng)的 “幕后英雄”,在后臺(tái)持續(xù)運(yùn)行,執(zhí)行著各種重要的系統(tǒng)任務(wù) 。與普通進(jìn)程不同,守護(hù)進(jìn)程脫離于終端運(yùn)行,這意味著它們不受終端的控制,也不會(huì)在終端上顯示輸出信息,即使終端關(guān)閉,守護(hù)進(jìn)程依然能夠繼續(xù)運(yùn)行 。就像一位忠誠(chéng)的守衛(wèi),無(wú)論周圍環(huán)境如何變化,始終堅(jiān)守崗位。

守護(hù)進(jìn)程具有一些顯著的特點(diǎn)。首先,它們?cè)诤笈_(tái)長(zhǎng)期運(yùn)行,通常在系統(tǒng)啟動(dòng)時(shí)就被自動(dòng)啟動(dòng),并且一直持續(xù)運(yùn)行到系統(tǒng)關(guān)閉 。比如,系統(tǒng)日志守護(hù)進(jìn)程syslogd,它負(fù)責(zé)收集和記錄系統(tǒng)中的各種日志信息,從系統(tǒng)啟動(dòng)的那一刻起,它就開始默默地工作,記錄著系統(tǒng)運(yùn)行過(guò)程中的點(diǎn)點(diǎn)滴滴,為系統(tǒng)管理員提供了重要的故障排查和系統(tǒng)監(jiān)控依據(jù) 。其次,守護(hù)進(jìn)程沒(méi)有控制終端,這使得它們能夠獨(dú)立運(yùn)行,不依賴于用戶的交互操作 。

例如,網(wǎng)絡(luò)守護(hù)進(jìn)程sshd,它允許用戶通過(guò) SSH 協(xié)議遠(yuǎn)程登錄到系統(tǒng),即使沒(méi)有用戶在本地終端進(jìn)行操作,sshd依然在后臺(tái)監(jiān)聽網(wǎng)絡(luò)端口,等待用戶的連接請(qǐng)求,保障了遠(yuǎn)程管理的便捷性 。另外,守護(hù)進(jìn)程通常以系統(tǒng)權(quán)限運(yùn)行,這賦予了它們?cè)L問(wèn)系統(tǒng)關(guān)鍵資源和執(zhí)行重要任務(wù)的能力 。比如,cron守護(hù)進(jìn)程,它負(fù)責(zé)執(zhí)行周期性的任務(wù),如定時(shí)備份文件、更新系統(tǒng)軟件等,需要具備足夠的權(quán)限來(lái)操作相關(guān)的文件和目錄 。

在 Linux 系統(tǒng)中,有許多常見(jiàn)的守護(hù)進(jìn)程。除了前面提到的syslogd、sshd和cron,還有httpd(Apache Web 服務(wù)器守護(hù)進(jìn)程),它負(fù)責(zé)處理 Web 服務(wù)器的請(qǐng)求,使得我們能夠在瀏覽器中訪問(wèn)網(wǎng)站;mysqld(MySQL 數(shù)據(jù)庫(kù)服務(wù)器守護(hù)進(jìn)程),它管理著 MySQL 數(shù)據(jù)庫(kù),為各種應(yīng)用程序提供數(shù)據(jù)存儲(chǔ)和檢索服務(wù) 。這些守護(hù)進(jìn)程在系統(tǒng)中各司其職,共同維持著系統(tǒng)的穩(wěn)定運(yùn)行 。

對(duì)于守護(hù)進(jìn)程的管理,我們可以使用不同的工具和方法 。在基于 Systemd 的系統(tǒng)中,使用systemctl命令來(lái)管理守護(hù)進(jìn)程非常方便 。比如,要啟動(dòng)httpd守護(hù)進(jìn)程,可以使用systemctl start httpd命令;要停止它,使用systemctl stop httpd命令;要查看其狀態(tài),使用systemctl status httpd命令 。如果希望httpd守護(hù)進(jìn)程在系統(tǒng)啟動(dòng)時(shí)自動(dòng)啟動(dòng),可以使用systemctl enable httpd命令;如果不想讓它自動(dòng)啟動(dòng),則使用systemctl disable httpd命令 。另外,對(duì)于一些簡(jiǎn)單的守護(hù)進(jìn)程,我們也可以使用nohup命令來(lái)啟動(dòng),它可以讓進(jìn)程在后臺(tái)運(yùn)行,并且忽略掛斷信號(hào) 。

例如,nohup my_daemon &可以將my_daemon這個(gè)守護(hù)進(jìn)程在后臺(tái)啟動(dòng),輸出信息會(huì)被重定向到nohup.out文件中 。還有supervisor,它是一個(gè)功能強(qiáng)大的進(jìn)程管理工具,可以方便地管理和監(jiān)控守護(hù)進(jìn)程 。通過(guò)配置supervisor的配置文件,我們可以定義守護(hù)進(jìn)程的啟動(dòng)命令、自動(dòng)重啟策略、日志輸出等 。比如,在/etc/supervisor/conf.d/目錄下創(chuàng)建一個(gè)守護(hù)進(jìn)程的配置文件,然后使用supervisorctl命令來(lái)啟動(dòng)、停止、重啟守護(hù)進(jìn)程,還可以查看其狀態(tài)和日志 。

2. 僵尸進(jìn)程與孤兒進(jìn)程

在 Linux 進(jìn)程的世界里,僵尸進(jìn)程和孤兒進(jìn)程是兩種比較特殊的進(jìn)程狀態(tài),它們有著獨(dú)特的產(chǎn)生原因和特點(diǎn)。

僵尸進(jìn)程是指子進(jìn)程已經(jīng)終止運(yùn)行,但父進(jìn)程沒(méi)有調(diào)用wait或waitpid函數(shù)來(lái)獲取子進(jìn)程的退出狀態(tài)信息,此時(shí)子進(jìn)程就會(huì)變成僵尸進(jìn)程 。簡(jiǎn)單來(lái)說(shuō),就像孩子已經(jīng)長(zhǎng)大離開家(子進(jìn)程結(jié)束),但家長(zhǎng)(父進(jìn)程)卻沒(méi)有去了解孩子的情況,孩子就一直處于一種 “懸而未決” 的狀態(tài) 。

從系統(tǒng)的角度看,僵尸進(jìn)程雖然已經(jīng)不再占用 CPU 等運(yùn)行資源,但它的進(jìn)程描述符仍然保留在系統(tǒng)中,占用著進(jìn)程表的一個(gè)位置 。這就好比一個(gè)已經(jīng)畢業(yè)的學(xué)生,雖然不再在學(xué)校上課(不占用運(yùn)行資源),但學(xué)校的學(xué)籍系統(tǒng)里還保留著他的學(xué)籍信息(占用進(jìn)程表位置) 。如果系統(tǒng)中存在大量的僵尸進(jìn)程,會(huì)導(dǎo)致進(jìn)程號(hào)資源被大量占用,因?yàn)橄到y(tǒng)所能使用的進(jìn)程號(hào)是有限的,當(dāng)進(jìn)程號(hào)耗盡時(shí),系統(tǒng)將無(wú)法創(chuàng)建新的進(jìn)程,這會(huì)對(duì)系統(tǒng)的正常運(yùn)行造成嚴(yán)重影響 。

例如,我們來(lái)看下面這段 C 代碼:

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

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子進(jìn)程
        printf("Child process, PID: %d\n", getpid());
        exit(0);
    } else {
        // 父進(jìn)程
        printf("Parent process, PID: %d\n", getpid());
        while (1) {
            sleep(1);
        }
    }
    return 0;
}

在這段代碼中,父進(jìn)程創(chuàng)建子進(jìn)程后,子進(jìn)程很快退出,但父進(jìn)程沒(méi)有調(diào)用wait或waitpid來(lái)處理子進(jìn)程的退出,此時(shí)子進(jìn)程就會(huì)變成僵尸進(jìn)程 。我們可以通過(guò)ps -aux命令查看進(jìn)程狀態(tài),會(huì)發(fā)現(xiàn)處于僵尸狀態(tài)的子進(jìn)程,其狀態(tài)字段顯示為Z 。

為了避免僵尸進(jìn)程的產(chǎn)生,我們可以采取一些措施 。一種方法是在父進(jìn)程中調(diào)用wait或waitpid函數(shù)來(lái)等待子進(jìn)程的結(jié)束,并獲取其退出狀態(tài)信息 。wait函數(shù)會(huì)使父進(jìn)程阻塞,直到有子進(jìn)程結(jié)束;waitpid函數(shù)則更加靈活,可以指定等待特定的子進(jìn)程,并且可以設(shè)置非阻塞模式 。例如:

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

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子進(jìn)程
        printf("Child process, PID: %d\n", getpid());
        exit(0);
    } else {
        // 父進(jìn)程
        printf("Parent process, PID: %d\n", getpid());
        int status;
        waitpid(pid, &status, 0);
        printf("Child process has exited\n");
    }
    return 0;
}

在這個(gè)改進(jìn)的代碼中,父進(jìn)程調(diào)用了waitpid函數(shù)等待子進(jìn)程結(jié)束,這樣就不會(huì)產(chǎn)生僵尸進(jìn)程 。

另一種避免僵尸進(jìn)程的方法是利用信號(hào)機(jī)制 。當(dāng)子進(jìn)程結(jié)束時(shí),會(huì)向父進(jìn)程發(fā)送SIGCHLD信號(hào),我們可以在父進(jìn)程中注冊(cè)SIGCHLD信號(hào)的處理函數(shù),在處理函數(shù)中調(diào)用wait或waitpid來(lái)處理子進(jìn)程的退出 。例如:

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

void sigchld_handler(int signo) {
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        printf("Child process %d has exited\n", pid);
    }
}

int main() {
    struct sigaction sa;
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子進(jìn)程
        printf("Child process, PID: %d\n", getpid());
        exit(0);
    } else {
        // 父進(jìn)程
        printf("Parent process, PID: %d\n", getpid());
        while (1) {
            sleep(1);
        }
    }
    return 0;
}

在這段代碼中,通過(guò)sigaction函數(shù)注冊(cè)了SIGCHLD信號(hào)的處理函數(shù)sigchld_handler,當(dāng)子進(jìn)程結(jié)束時(shí),會(huì)調(diào)用該處理函數(shù)來(lái)處理子進(jìn)程的退出,從而避免了僵尸進(jìn)程的產(chǎn)生 。

孤兒進(jìn)程則是指父進(jìn)程在子進(jìn)程之前退出,導(dǎo)致子進(jìn)程失去了父進(jìn)程的管理,此時(shí)子進(jìn)程就成為了孤兒進(jìn)程 。這就像孩子還在成長(zhǎng)(子進(jìn)程還在運(yùn)行),但家長(zhǎng)卻提前離開了(父進(jìn)程先退出) 。在 Linux 系統(tǒng)中,孤兒進(jìn)程會(huì)被init進(jìn)程(在 systemd 系統(tǒng)中通常是systemd進(jìn)程)收養(yǎng),init進(jìn)程會(huì)負(fù)責(zé)回收孤兒進(jìn)程的資源 。例如,下面這段代碼:

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

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子進(jìn)程
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
        sleep(5);
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
    } else {
        // 父進(jìn)程
        printf("Parent process, PID: %d\n", getpid());
        exit(0);
    }
    return 0;
}

在這個(gè)例子中,父進(jìn)程創(chuàng)建子進(jìn)程后立即退出,子進(jìn)程在睡眠 5 秒前后查看自己的父進(jìn)程 ID,會(huì)發(fā)現(xiàn)父進(jìn)程 ID 變成了init進(jìn)程的 ID(通常為 1),說(shuō)明子進(jìn)程已經(jīng)被init進(jìn)程收養(yǎng),成為了孤兒進(jìn)程 。

孤兒進(jìn)程本身對(duì)系統(tǒng)并沒(méi)有太大的危害,因?yàn)閕nit進(jìn)程會(huì)妥善處理它們的資源回收 。但在某些情況下,我們可能需要對(duì)孤兒進(jìn)程進(jìn)行特殊的處理或監(jiān)控 。比如,如果我們希望在孤兒進(jìn)程中執(zhí)行一些特定的清理操作,可以在子進(jìn)程中檢測(cè)父進(jìn)程是否已經(jīng)退出,如果發(fā)現(xiàn)父進(jìn)程退出,就執(zhí)行相應(yīng)的清理代碼 。例如:

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

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子進(jìn)程
        sleep(1);
        if (getppid() == 1) {
            printf("I am an orphan process. Performing clean-up...\n");
            // 執(zhí)行清理操作
        }
        while (1) {
            sleep(1);
        }
    } else {
        // 父進(jìn)程
        printf("Parent process, PID: %d\n", getpid());
        exit(0);
    }
    return 0;
}

在這段代碼中,子進(jìn)程在睡眠 1 秒后檢查自己的父進(jìn)程 ID,如果發(fā)現(xiàn)父進(jìn)程 ID 為 1,說(shuō)明自己成為了孤兒進(jìn)程,然后執(zhí)行相應(yīng)的清理操作 。

五、Linux進(jìn)程實(shí)戰(zhàn)演練

1. 案例一:優(yōu)化系統(tǒng)性能

在日常的 Linux 系統(tǒng)運(yùn)維中,我們常常會(huì)遇到系統(tǒng)性能下降的情況,這時(shí)候就需要通過(guò)各種手段來(lái)找出問(wèn)題所在并進(jìn)行優(yōu)化。其中,找出占用資源高的進(jìn)程并進(jìn)行相應(yīng)處理是一個(gè)重要的優(yōu)化步驟。

假設(shè)我們?cè)谝慌_(tái)運(yùn)行著多個(gè)服務(wù)的 Linux 服務(wù)器上,突然發(fā)現(xiàn)系統(tǒng)響應(yīng)變得遲緩,用戶反饋一些應(yīng)用程序加載緩慢。這時(shí)候,我們首先想到的就是使用top命令來(lái)查看系統(tǒng)中各個(gè)進(jìn)程的資源占用情況。在終端中輸入top命令后,我們會(huì)看到一個(gè)動(dòng)態(tài)更新的界面,其中%CPU和%MEM列分別顯示了每個(gè)進(jìn)程占用 CPU 和內(nèi)存的百分比。通過(guò)觀察這兩列數(shù)據(jù),我們發(fā)現(xiàn)一個(gè)名為big_computation.py的 Python 進(jìn)程占用了高達(dá) 80% 的 CPU 資源,同時(shí)占用了大量的內(nèi)存 。

top - 10:15:23 up 2 days,  1:23,  2 users,  load average: 2.50, 2.00, 1.50
Tasks: 200 total,   2 running, 198 sleeping,   0 stopped,   0 zombie
%Cpu(s): 80.0 us, 10.0 sy,  0.0 ni,  5.0 id,  5.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   7914.2 total,    123.4 free,   6543.2 used,   1247.6 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   1024.0 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
12345 user1     20   0  512340 204800  12340 R  80.0  2.5   5:10.00 python big_computation.py

這個(gè)進(jìn)程可能是導(dǎo)致系統(tǒng)性能下降的 “罪魁禍?zhǔn)住薄H绻@個(gè)進(jìn)程是一個(gè)臨時(shí)的計(jì)算任務(wù),且目前并非急需它運(yùn)行,我們可以考慮終止它以釋放系統(tǒng)資源。通過(guò)ps -ef命令進(jìn)一步確認(rèn)該進(jìn)程的詳細(xì)信息,獲取其 PID 為 12345,然后使用kill命令來(lái)終止它 。

ps -ef | grep big_computation.py
user1    12345     1  80 10:00 ?        00:05:10 python big_computation.py
kill 12345

執(zhí)行kill命令后,再次查看top命令的輸出,發(fā)現(xiàn)系統(tǒng)的 CPU 使用率和內(nèi)存占用率都有了明顯的下降,系統(tǒng)響應(yīng)速度也恢復(fù)正常 。

如果這個(gè)進(jìn)程是一個(gè)長(zhǎng)期運(yùn)行的服務(wù),我們不能直接終止它,但可以嘗試調(diào)整其優(yōu)先級(jí),讓它少占用一些 CPU 資源,以保證其他更重要的進(jìn)程能夠正常運(yùn)行 。比如,使用renice命令將其優(yōu)先級(jí)降低。假設(shè)我們希望將其 nice 值調(diào)整為 10(nice 值越大優(yōu)先級(jí)越低) 。

renice -n 10 12345

執(zhí)行上述命令后,再次觀察top命令的輸出,會(huì)發(fā)現(xiàn)該進(jìn)程的優(yōu)先級(jí)降低,CPU 使用率也有所下降,系統(tǒng)的整體性能得到了優(yōu)化 。通過(guò)這樣的實(shí)戰(zhàn)操作,我們能夠更好地掌握 Linux 系統(tǒng)中進(jìn)程的監(jiān)控和優(yōu)化技巧,確保系統(tǒng)的穩(wěn)定高效運(yùn)行 。

2. 案例二:進(jìn)程間通信實(shí)現(xiàn)

進(jìn)程間通信在 Linux 系統(tǒng)中有著廣泛的應(yīng)用場(chǎng)景,下面我們通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)展示使用管道實(shí)現(xiàn)進(jìn)程間數(shù)據(jù)傳遞的方法。

假設(shè)我們有一個(gè)數(shù)據(jù)生成進(jìn)程和一個(gè)數(shù)據(jù)處理進(jìn)程,數(shù)據(jù)生成進(jìn)程不斷生成一些隨機(jī)數(shù),數(shù)據(jù)處理進(jìn)程需要接收這些隨機(jī)數(shù)并進(jìn)行平方計(jì)算 。我們可以使用管道來(lái)實(shí)現(xiàn)這兩個(gè)進(jìn)程之間的數(shù)據(jù)傳遞。

首先,我們創(chuàng)建一個(gè) C 語(yǔ)言程序來(lái)實(shí)現(xiàn)數(shù)據(jù)生成進(jìn)程(producer.c) :

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

int main() {
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    } else if (pid == 0) {
        // 子進(jìn)程,作為數(shù)據(jù)處理進(jìn)程
        close(pipe_fd[1]); // 關(guān)閉寫端
        int num;
        while (read(pipe_fd[0], &num, sizeof(int)) > 0) {
            int result = num * num;
            printf("Processed result: %d\n", result);
        }
        close(pipe_fd[0]); // 關(guān)閉讀端
    } else {
        // 父進(jìn)程,作為數(shù)據(jù)生成進(jìn)程
        close(pipe_fd[0]); // 關(guān)閉讀端
        srand(time(NULL));
        for (int i = 0; i < 5; i++) {
            int num = rand() % 100;
            write(pipe_fd[1], &num, sizeof(int));
            printf("Generated number: %d\n", num);
            sleep(1);
        }
        close(pipe_fd[1]); // 關(guān)閉寫端
        wait(NULL); // 等待子進(jìn)程結(jié)束
    }

    return 0;
}

在這個(gè)程序中,首先創(chuàng)建一個(gè)管道,然后通過(guò)fork函數(shù)創(chuàng)建子進(jìn)程。父進(jìn)程作為數(shù)據(jù)生成進(jìn)程,關(guān)閉管道的讀端,生成 5 個(gè)隨機(jī)數(shù)并通過(guò)管道的寫端發(fā)送給子進(jìn)程 。子進(jìn)程作為數(shù)據(jù)處理進(jìn)程,關(guān)閉管道的寫端,從管道的讀端讀取數(shù)據(jù),對(duì)讀取到的數(shù)據(jù)進(jìn)行平方計(jì)算并打印結(jié)果 。

編譯并運(yùn)行這個(gè)程序:

gcc -o producer producer.c
./producer

運(yùn)行結(jié)果如下:

Generated number: 34
Processed result: 1156
Generated number: 78
Processed result: 6084
Generated number: 12
Processed result: 144
Generated number: 91
Processed result: 8281
Generated number: 56
Processed result: 3136

從運(yùn)行結(jié)果可以清晰地看到,數(shù)據(jù)生成進(jìn)程不斷生成隨機(jī)數(shù)并發(fā)送給數(shù)據(jù)處理進(jìn)程,數(shù)據(jù)處理進(jìn)程成功接收并進(jìn)行了相應(yīng)的處理 。通過(guò)這個(gè)簡(jiǎn)單的案例,我們展示了使用管道實(shí)現(xiàn)進(jìn)程間通信的基本方法,在實(shí)際應(yīng)用中,可以根據(jù)具體需求對(duì)這個(gè)示例進(jìn)行擴(kuò)展和優(yōu)化,以滿足更復(fù)雜的進(jìn)程間通信場(chǎng)景 。

責(zé)任編輯:趙寧寧 來(lái)源: 深度Linux
相關(guān)推薦

2022-04-12 09:05:30

Linux時(shí)鐘

2022-03-28 19:19:45

Linux時(shí)間子系統(tǒng)

2020-09-27 08:02:47

操作系統(tǒng)

2020-09-03 06:35:44

Linux權(quán)限文件

2020-12-19 16:12:58

操作系統(tǒng)計(jì)算機(jī)科學(xué)

2023-03-27 09:08:11

Linux

2022-03-24 08:51:48

Redis互聯(lián)網(wǎng)NoSQL

2024-04-12 12:19:08

語(yǔ)言模型AI

2023-12-15 15:55:24

Linux線程同步

2023-12-21 08:02:21

CPUJava8列表

2025-04-23 00:00:00

2021-07-10 14:32:30

Python導(dǎo)入模塊

2016-12-23 14:08:30

物聯(lián)網(wǎng)操作系統(tǒng)開源

2023-09-15 12:00:01

API應(yīng)用程序接口

2023-09-08 08:20:46

ThreadLoca多線程工具

2021-03-22 10:05:59

netstat命令Linux

2021-01-06 13:52:19

zookeeper開源分布式

2025-04-28 02:22:00

2022-01-06 18:21:00

Hadoop生態(tài)系統(tǒng)

2021-05-06 05:38:48

Python文件操作異常模塊
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 久久精品国产一区二区电影 | av免费网站在线观看 | 在线一区视频 | 日韩欧美在线免费观看视频 | 久久婷婷国产 | 国产精品二区三区 | 精品亚洲一区二区三区 | 国产天天操 | av网站免费| 免费特黄视频 | 日韩成人在线一区 | 米奇成人网 | 成人免费视频网站在线看 | 成人性生交大片 | 久久一区二区三区四区五区 | 免费观看成人性生生活片 | 999久久久| 男女羞羞在线观看 | 日批免费看 | 91在线精品秘密一区二区 | 日本不卡免费新一二三区 | 91在线视频一区 | 天天干天天色 | www.888www看片 | 综合在线视频 | 免费观看一级毛片 | 免费观看成人鲁鲁鲁鲁鲁视频 | 黄色网址av | 国产成人99久久亚洲综合精品 | 天天操夜夜艹 | 久久久久久国产精品免费免费男同 | 日本人爽p大片免费看 | a在线观看| 日本三级全黄三级三级三级口周 | 欧美毛片免费观看 | 视频一区二区三区中文字幕 | 精品久久香蕉国产线看观看亚洲 | 午夜电影网站 | 久久精品小视频 | 国产小u女发育末成年 | 伊人网影院 |