Linux:進程模型和進程管理

来源:https://www.cnblogs.com/orion-orion/archive/2023/04/11/17308288.html
-Advertisement-
Play Games

在Linux系統中,執行一個程式或命令就可以觸發一個進程,系統會給予這個進程一個ID,稱為PID,同時根據觸發這個進程的用戶與相關屬性關係,基於這個PID一組有效的許可權設置。舉個常見的例子,我們要操作系統的時候通常是利用ssh連接程式或直接在主機上登錄,然後獲取shell。預設的shell是bash... ...


1 進程與程式

在Linux系統中,執行一個程式或命令就可以觸發一個進程,系統會給予這個進程一個ID,稱為PID,同時根據觸發這個進程的用戶與相關屬性關係,基於這個PID一組有效的許可權設置。如下圖所示(圖片來自《鳥哥的Linux私房菜》[1]):

舉個常見的例子,我們要操作系統的時候通常是利用ssh連接程式或直接在主機上登錄,然後獲取shell。預設的shell是bash,對應的路徑為/bin/bash,那麼同時間的每個人登錄都是執行/bin/bash,不過每個人獲取的許可權不同,如下圖所示:

也就是說,當我們的登錄並執行/bin/bash程式時,系統已經給了我們一個PID,這個PID就是根據登陸者的UID/GID(/etc/passwd)而來,而所謂用戶的許可權就是這個bash進程的許可權。當這個進程執行其它作業時,比如我們在shell中執行touch命令時,這個進程觸發出來的其它進程也會沿用這個進程的相關許可權

我們對程式與進程做一個總結:

  • 程式(program):通常為二進位程式,存儲在存儲媒介中(如磁碟等),以物理文件的形式存在。
  • 進程(process):程式被觸發後,執行者的許可權與屬性、程式的代碼與所需數據等都會被載入到記憶體中,操作系統給予這個記憶體中的單元一個標識符(PID),可以說進程就是一個正在運行中的程式

進程彼此之間是有關係的。從下圖來看,連續執行兩個bash後,第二個bash的父進程就是前一個bash,通過Parent PID(PPID)可獲取其父進程的PID:

此外,子進程可以獲取父進程的環境變數。

下麵這個例子我們會展示子進程和父進程的關係。我們在當前的bash環境下,再觸發一次bash,並用ps -l命令查看進程相關的輸出信息,

bash-3.2$ ps -l
  UID   PID  PPID        F CPU PRI NI       SZ    RSS WCHAN     S     ADDR TTY           TIME CMD
  501  9892  9827     4006   0  31  0 408650672   1968 -      S           0 ttys001    0:00.01 /bin/bash
  501  9905  9892     4006   0  31  0 408657840   2736 -      S           0 ttys001    0:00.01 /bin/bash

可以看到第一個bash的PID和第二個bash的PPID都是9892,這是因為第二個bash是來自第一個所產生的。

很多常常會發現,“咦,我們們將有問題的進程關閉了(比如Ctrl+C殺掉),怎麼過一陣子它又自動產生了?而且新產生進程的PID還與原先不同這是怎麼回事呢?”如果不是crontab計劃任務的影響,那麼肯定有一個父進程存在,所以我們殺掉子進程後,父進程又會主動再生成一個。那怎麼辦呢?“擒賊先擒王”。我們用ps auxf找出那個父進程,然後將它殺掉即可(後文會提到)。

fork and exec:進程調用的流程

子進程和父進程之間的關係比較複雜,最大的複雜點在於進程之間的調用。Linux程式的調用通常稱為fork-and-exec流程。進程都會藉由父進程以複製(fork)的方式產生一個一模一樣的子進程,然後被覆制出來的子進程再以exec的方式來執行時機要執行的代碼,最終就成為一個子進程。整個流程有點像下麵這張圖:

  • 系統先以fork的方式複製一個與父進程相同的臨時進程,這個進程與父進程唯一的差別就是PID不同,但這個臨時進程還會多一個PPID參數。
  • 然後臨時進程開始以exec的方式載入實際要執行的進程。以上圖來講,新的程式名稱為qqq,最終子進程的進程代碼就會變成qqq了。

系統或網路服務:常駐在記憶體里的進程

一般的Linux命令(如lstouchrm等)都是執行完就結束,也就是說該項命令被觸發後所產生的PID很快就會被終止,那麼有沒有一直在執行的進程呢?

當然有,我們把在後臺啟動並一直持續不斷地運行,也即常駐在記憶體當中的進程稱為守護進程(daemon)。常見的服務包括系統本身所需要的服務(例如crond、atd、rsyslogd等)和負責網路連接的服務(例如apache、named、postfix、vsftpd等)。網路服務比較有趣的地方在於,它會啟動一個可以複雜網路監聽的埠(port),以提供外部客戶端(client)的連接請求。

PS1:在Linux系統中,一般daemon類型的進程都會在文件名後面加上d。

PS2:“守護進程”這個概念由麻省理工學院MAC項目的程式員發明。費南多·柯巴托於1963年在MAC項目任務。根據他的說法,他的團隊最早採用daemon這個概念,其靈感來源於麥克斯韋妖——一種物理學和熱力學中虛構的介質,能幫助排列分子。他對此表示:“我們別出心裁地開始使用daemon這個詞來描述後臺進程,它們不知疲倦地處理系統中的雜務。”Unix系統繼承了這個術語。作為一種在後臺起作用的超自然存在,麥克斯韋妖與古希臘神話中的代蒙一致[2]。關於麥克斯韋妖的更多有趣信息可以參見梅拉妮·米歇爾的《複雜》[3]一書。

2 進程管理

想要查看系統上正在運行中的進程,可以利用靜態的ps或者是動態的top命令,還可以利用pstree來查看進程樹之間的關係。

ps:將某個時間點的進程運行情況擷取下來
ps aux可查看系統中所有的進程(註意,沒有-號):

~/Orion-Orion # ps -aux                                                                                         root@qi
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4504    48 pts/0    Ss    2022   0:01 sh /root/start.sh
root         7  0.0  0.0  65520   420 ?        Ss    2022   0:07 /usr/sbin/sshd
root         8  0.0  0.0  20052   264 pts/0    S+    2022   0:00 /bin/bash
...
root     39717  0.1  0.0  93648  7608 ?        Ss   08:40   0:02 sshd: root@notty
root     39727  0.0  0.0   9752  2924 ?        Ss   08:40   0:00 bash
root     40140  0.5  0.0  95296  9220 ?        Rs   08:54   0:02 sshd: root@notty
root     40150  0.0  0.0   9752  2832 ?        Ss   08:54   0:00 bash

ps -l則可以僅查看自己的bash相關的進程:

~/Orion-Orion # ps -l                                                                                           root@qi
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S     0 42331 41909  0  80   0 -  5015 wait   pts/312  00:00:00 bash
0 R     0 42379 42331  0  80   0 -  6920 -      pts/312  00:00:00 ps

我們可以看到,系統整體運行的進程是非常多的,但使用ps -l僅會列出與你的操作環境(bash)有關的進程,即最上層的父進程會是你自己的bash而沒有擴展到systemd(後續會介紹)這個進程中。我們接下來來看看ps -l顯示出來的數據有哪些?

  • F:表示這個進程標識(process flags),說明這個進程的許可權,常見號碼有:
    • 若為4表示此進程的許可權為root。
    • 若為表示此進程僅執行複製(fork)而沒有實際執行(exec)。
    • 若為0表示進程標識沒有設置。
  • S: 代表這個進程的狀態(STAT),主要的狀態有:
    • R(Running):該進程正在運行中(running) 或是可運行的(runnable)
    • S(Sleep):進程處於可被喚醒(signal)的睡眠狀態,也即所謂空閑狀態(idle)。這種狀態一般是進程主動進入的。
    • D:進程處於不可被喚醒的睡眠狀態,通常這個進程可能在等待I/O的情況(例如列印)。這種狀態一般是進程被動進入的。
    • T(Stopped)停止狀態,可能是在任務控制(後臺暫停)或跟蹤(traced)狀態。
    • Z(Zombie)僵屍狀態(即所謂defunct),進程已經終止但卻無法被刪除至記憶體外。
  • UID/PID/PPID:代表進程被該UID所擁有/進程的PID號碼/此進程的父進程PID號碼。
  • C:代表CPU使用率,單位為百分比。
  • PRI/NI:Priority/Nice的縮寫,代表此進程被CPU所執行的優先順序,數值越小代表該進程越快被CPU執行。詳細的PRI與NI將在下一小節說明。
  • ADDR/SZ/WCHAN:都與記憶體相關,ADDR是kernel function,指出該進程在記憶體的哪個部分,如果是個running的進程,一般就會顯示-;SZ代表此進程用掉多少記憶體;WCHAN表示目前進場是否運行,同樣的,若為-表示正在運行中。
  • TTY:登錄者的終端位置,若為遠程登錄則使用動態終端介面名稱(pts/n)。
  • TIME:使用CPU的時間,註意是進程實際花費CPU的時間,而不是運行時間。關於這兩個時間之間的區別可參見我的博客《Python:對程式做性能分析及計時統計》
  • CMD:就是command的縮寫,表示觸發此進程的命令是什麼。

所以你看到的ps -l輸出信息中,它說明的是:bash進程屬於UID為0的用戶,狀態為睡眠(Sleeping),之所以為睡眠,是因為它觸發了ps(狀態為Running)。此進程的PID是42331,執行優先順序為80,執行bash所獲取的終端介面為pts/0。運行狀態為等待(wait)。

接下來我們用ps auxf列出類似進程樹的進程顯示:

(base) root@qi:~/Orion-Orion# ps -auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4504    48 pts/0    Ss    2022   0:01 sh /root/start.sh
root         7  0.0  0.0  65520   420 ?        Ss    2022   0:07 /usr/sbin/sshd
root     38677  0.0  0.0  93284  7412 ?        Ss   07:55   0:00  \_ sshd: root@notty
root     38687  0.0  0.0   9756  2824 ?        Ss   07:55   0:00  |   \_ bash
root     38765  0.0  0.0   4504  1712 ?        S    07:55   0:00  |       \_ sh /root/.vscode-server/bin/b7886d7461186a5
root     41898  0.1  0.0 661960 58144 ?        Sl   10:38   0:06  |       |       \_ /root/.vscode-server/bin/b7886d7461
root     42331  0.0  0.0  20060  3788 pts/312  S    10:40   0:00  |       |           \_ bash
root     43592  0.0  0.0  36276  3248 pts/312  R+   11:41   0:00  |       |                \_ ps -auxf
...
root     39717  0.0  0.0  93648  7608 ?        Ss   08:40   0:02  \_ sshd: root@notty
root     39727  0.0  0.0   9752  2924 ?        Ss   08:40   0:00  |   \_ bash
root     43493  0.0  0.0   4376   672 ?        S    11:40   0:00  |       \_ sleep 180
root     41362  0.0  0.0  93296  7360 ?        Ss   10:33   0:01  \_ sshd: root@notty
root     41372  0.0  0.0   9752  2824 ?        Ss   10:33   0:00      \_ bash
root     43492  0.0  0.0   4376   700 ?        S    11:39   0:00          \_ sleep 180
root         8  0.0  0.0  20052   264 pts/0    S+    2022   0:00 /bin/bash

因為我是用ssh網路連接進入伺服器來執行一些測試的,可以看出進程之間是相關性的。從上面的例子來看,我是通過sshd提供的網路服務獲取的一個進程,該進程提供bash給我使用,而我通過bash再去執行VSCode-Server服務啟動腳本,以運行VSCode-Server服務進程,然後該進程再提供給我一個bash, 我通過這個bash再去執行ps auxf(瘋狂套娃哈哈哈)。

這裡說一個題外話,sshd進程是我們前面提到的deamon,是不能隨意殺掉的哦! 殺掉了不僅你會馬上斷開連接,下次再用ssh連你也連不上了。

除了f這個選項,我們還可以使用pstree來完全查看這個進程樹。

(base) root@qi:~/Orion-Orion# pstree 
sh─┬─bash
   └─sshd─┬─sshd───bash─┬─sh───node─┬─node───12*[{node}]
          │             │           ├─node─┬─node───10*[{node}]
          │             │           │      ├─node───11*[{node}]
          │             │           │      ├─python───{python}
          │             │           │      └─11*[{node}]
          │             │           ├─node───11*[{node}]
          │             │           ├─node─┬─bash───pstree
          │             │           │      └─11*[{node}]
          │             │           └─10*[{node}]
          │             └─sleep
          └─2*[sshd───bash───sleep]      

除此之外,我們必須要知道的是僵屍(zombie) 進程是什麼?通常,造成僵屍進程的原因在於該進程應該已經執行完畢,或是應該要終止了,但該進程的父進程卻無法完整地將該進程結束掉,而造成該進程一直存在記憶體中。如果你發現在某個進程的CMD/COMMAND後面接上了defunct時,就代表該進程是僵屍進程,例如:

apache  8683  0.0  0.9 83384 9992 ?   Z  14:33   0:00 /usr/sbin/httpd <defunct>

系統不穩定的時候就容易造成所謂的僵屍進程,可能是因為程式寫得不好,或是用戶的操作習慣不良等所造成的。如果你發現系統中有很多僵屍進程時,記得要找出該進程的父進程,然後好好做個追蹤,好好進行主機的環境優化,看看有什麼地方需要改善,而不是直接將它kill掉。不然萬一它一直產生就麻煩了。

事實上,通常僵屍進程都已經無法管理,而直接交給 systemd 這個進程來負責,偏偏systemd是系統第一個執行的進程,它是所有進程的父進程。我們是無法殺掉該進程的(殺掉它,系統就死掉了),所以如果產生僵屍進程,而系統過了一陣子還沒有辦法通過內核非經常性的特殊處理來將該進程刪除時,那你只好通過reboot的方式來將該進程kill掉。

systemd是目前Linux系統上主要的系統守護進程管理工具,由於init一方面對於進程的管理是串列化的,容易出現阻塞情況,另一方面init也僅僅是執行啟動腳本,並不能對服務本身進行更多的管理。所以最新系統(RedHat7,CentOS7,Ubuntu15…)大都由systemd取代了init作為預設的系統進程管理工具。

top:動態查看進程的變化

相對於ps是選取一個時間點的進程狀態,top可以持續監測進程運行的狀態,使用方式如下:

top [-d 數字] | top [-bnp]

它的選項與參數如下:

  • -d:後面可以接秒數,就是整個進程界面更新的秒數,預設是5秒。
  • -b:以批量的方式執行top,還有更多的參數可以使用,通常會搭配數據重定向來將批量的結果輸出為文件。
  • -n:與-b搭配,意義是需要執行幾次top的輸出結果。
  • -p:指定某些PID來執行查看檢測。

top的執行過程中可以使用下列的按鍵命令:

  • ?:顯示在top中可以輸入的按鍵命令
  • P:以CPU的使用排序顯示。
  • M:以Memory的使用排序顯示。
  • N:以PID來排序。
  • T:由該進程使用的CPU時間累積(TIME+)排序
  • k:給予某個PID一個信號(signal)。
  • r:給予某個PID重新制定一個nice值。
  • q:退出top的按鍵。

接下來我們實際查看一下如何使用toptop的界面。比如以下是我們輸入top -d 2命令得到的結果,該命令表示每兩秒鐘更新一次top,查看整體信息。

top - 13:19:06 up 202 days,  5:00,  3 users,  load average: 89.81, 75.65, 68.67
Tasks:  74 total,   1 running,  73 sleeping,   0 stopped,   0 zombie
%Cpu(s): 32.2 us,  4.5 sy, 35.7 ni, 27.5 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem : 52701926+total, 52750784 free, 49904712 used, 42436377+buff/cache
KiB Swap:  8388604 total,  7983868 free,   404736 used. 46639996+avail Mem 
    <==如果加入k或r時,就會有相關的字樣出現在這裡。
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                         
38775 root      20   0 1106596 236232  37596 S   1.0  0.0   1:28.68 node                                                            
12191 root      20   0 1662128  61256  11176 S   0.5  0.0 205:36.04 python                                                          
    1 root      20   0    4504     48      0 S   0.0  0.0   0:01.03 sh                                                              
    7 root      20   0   65520    420      4 S   0.0  0.0   0:07.19 sshd    
...
41787 root      20   0   20060   3700     32 S   0.0  0.0   0:00.01 bash                                                            
41898 root      20   0  968200  62276  33216 S   0.0  0.0   0:14.26 node  

可見,topps的靜態結果輸出不同,top這個進程可以持續地監測整個系統的進程任務狀態。在預設的情況下更新進程資源的時間為5秒,不過可以使用-d來執行修改。top主要分為兩部分界面,上面的界面為整個系統的資源使用狀態,基本上總共有六行。至於top下半部分的畫面,則是每個進程使用的資源情況。

top預設使用CPU使用率(%CPU)作為排序的依據,如果你想要使用記憶體使用率排序,則可以按下M鍵,若要恢復則按下P鍵即可。如果想要退出top,則按下q

參考

數學是符號的藝術,音樂是上界的語言。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多態 編譯時的多態:方法重載 運行時的多態:動態綁定 多態的三大前提 類之間要有繼承關係 要出現方法重寫 父類的引用指向了子類的對象 測試樣例 // 定義Person類 public class Person { public String name; public String sex; publ ...
  • 前言 Disruptor是一個高性能的無鎖併發框架,其主要應用場景是在高併發、低延遲的系統中,如金融領域的交易系統,游戲伺服器等。其優點就是非常快,號稱能支撐每秒600萬訂單。需要註意的是,Disruptor是單機框架,對標JDK中的Queue,而非可用於分散式系統的MQ 本文基於Disruptor ...
  • 本文將先介紹jdk動態代理的基本用法,並對其原理和註意事項予以說明。之後將以兩個最常見的應用場景為例,進行代碼實操。這兩個應用場景分別是攔截器和聲明性介面,它們在許多開發框架中廣泛使用。比如在spring和mybatis中均使用了攔截器模式,在mybatis中還利用動態代理來實現聲明性介面的功能。因 ...
  • 本文介紹在Anaconda環境下,安裝Python中柵格、矢量等地理數據處理庫GDAL的方法。 需要註意的是,本文介紹基於conda install命令直接聯網安裝GDAL庫的方法;這一方法有時不太穩定,且速度較慢。因此,如果有需要,大家可以參考Anaconda環境GDAL庫基於whl文件的配置方法 ...
  • 隨著技術的發展,ASP.NET Core MVC也推出了好長時間,經過不斷的版本更新迭代,已經越來越完善,本系列文章主要講解ASP.NET Core MVC開發B/S系統過程中所涉及到的相關內容,適用於初學者,在校畢業生,或其他想從事ASP.NET Core MVC 系統開發的人員。 經過前兩篇文章... ...
  • RelativeSource有四種類型 Self FindAncestor TemplatedParent PreviousData a.Self Self用於綁定源和綁定目標相同的場景中。對象的一個屬性與同一對象的另一個屬性綁定。 例如,讓我們取一個高度和寬度相同的橢圓。在XAML文件中添加下麵給 ...
  • 一、MQTT簡介 MQTT(Message Queuing Telemetry Transport)是一種輕量級的消息傳輸協議,主要用於物聯網設備間的通信。MQTT協議採用客戶端/伺服器架構,支持發佈/訂閱模式和點對點模式,具有高效、可靠、靈活等優點。 MQTT協議主要由三個要素構成:發佈者(pub ...
  • 前言 前幾天有群友在群里問如何在我之前的文章《ASP.NET Core WebApi返回結果統一包裝實踐》的時候有點疑問,主要的疑問點就是關於Respouse的讀取的問題。在之前的文章《深入探究ASP.NET Core讀取Request.Body的正確方式》曾分析過關於Request的讀取問題,需要 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...