實驗樓 Linux進程之初步瞭解

1、概念的理解

1.1 進程與程序概念的理解

首先程序與進程是什麼?程序與進程又有什麼區別?nginx

程序(procedure):不太精確地說,程序就是執行一系列有邏輯、有順序結構的指令,幫咱們達成某個結果。就如咱們去餐館,給服務員說我要牛肉蓋澆飯,她執行了作牛肉蓋澆飯這麼一個程序,最後咱們獲得了這麼一盤牛肉蓋澆飯。它須要去執行,否則它就像一本武功祕籍,放在那裏等人翻看。shell

進程(process):進程是程序在一個數據集合上的一次執行過程,在早期的UNIX、Linux 2.4及更早的版本中,它是系統進行資源分配和調度的獨立基本單位。同上一個例子,就如咱們去了餐館,給服務員說我要牛肉蓋澆飯,她執行了作牛肉蓋澆飯這麼一個程序,而裏面作飯的是一個進程,作牛肉湯汁的是一個進程,把牛肉湯汁與飯混合在一塊兒的是一個進程,把飯端上桌的是一個進程。它就像是咱們在看武功祕籍這麼一個過程,而後一個篇章一個篇章地去練。apache

簡單來講,程序是爲了完成某種任務而設計的軟件,好比 vim 是程序。什麼是進程呢?進程就是運行中的程序。ubuntu

程序只是一些列指令的集合,是一個靜止的實體,而進程不一樣,進程有如下的特性:vim

  • 動態性:進程的實質是一次程序執行的過程,有建立、撤銷等狀態的變化。而程序是一個靜態的實體。
  • 併發性:進程能夠作到在一個時間段內,有多個程序在運行中。程序只是靜態的實體,因此不存在併發性。
  • 獨立性:進程能夠獨立分配資源,獨立接受調度,獨立地運行。
  • 異步性:進程以不可預知的速度向前推動。
  • 結構性:進程擁有代碼段、數據段、PCB(進程控制塊,進程存在的惟一標誌)。也正是由於有結構性,進程才能夠作到獨立地運行。

併發:在一個時間段內,宏觀來看有多個程序都在活動,有條不紊的執行(每一瞬間只有一個在執行,只是在一段時間有多個程序都執行過)bash

並行:在每個瞬間,都有多個程序都在同時執行,這個必須有多個 CPU 才行session

引入進程是由於傳統意義上的程序已經不足以描述 OS 中各類活動之間的動態性、併發性、獨立性還有相互制約性。程序就像一個公司,只是一些證書,文件的堆積(靜態實體)。而當公司運做起來就有各個部門的區分,財務部,技術部,銷售部等等,就像各個進程,各個部門之間能夠獨立運作,也能夠有交互(獨立性、併發性)。數據結構

而隨着程序的發展越作越大,又會繼續細分,從而引入了線程的概念,當代多數操做系統、Linux 2.6及更新的版本中,進程自己不是基本運行單位,而是線程的容器。就像上述所說的,每一個部門又會細分爲各個工做小組(線程),而工做小組須要的資源須要向上級(進程)申請。多線程

線程(thread)是操做系統可以進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運做單位。一條線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程並行執行不一樣的任務。由於線程中幾乎不包含系統資源,因此執行更快、更有效率。併發

簡而言之,一個程序至少有一個進程,一個進程至少有一個線程。線程的劃分尺度小於進程,使得多線程程序的併發性高。另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。就以下圖所示:

實驗樓

2、進程的屬性

2.1 進程的分類

大概明白進程是個什麼樣的存在後,咱們須要進一步瞭解的就是進程分類。能夠從兩個角度來分:

  • 以進程的功能與服務的對象來分;
  • 以應用程序的服務類型來分;

第一個角度來看,咱們能夠分爲用戶進程與系統進程:

  • 用戶進程:經過執行用戶程序、應用程序或稱之爲內核以外的系統程序而產生的進程,此類進程能夠在用戶的控制下運行或關閉。
  • 系統進程:經過執行系統內核程序而產生的進程,好比能夠執行內存資源分配和進程切換等相對底層的工做;並且該進程的運行不受用戶的干預,即便是 root 用戶也不能干預系統進程的運行。

第二角度來看,咱們能夠將進程分爲交互進程、批處理進程、守護進程

  • 交互進程:由一個 shell 終端啓動的進程,在執行過程當中,須要與用戶進行交互操做,能夠運行於前臺,也能夠運行在後臺。
  • 批處理進程:該進程是一個進程集合,負責按順序啓動其餘的進程。
  • 守護進程:守護進程是一直運行的一種進程,在 Linux 系統啓動時啓動,在系統關閉時終止。它們獨立於控制終端而且週期性的執行某種任務或等待處理某些發生的事件。例如 httpd 進程,一直處於運行狀態,等待用戶的訪問。還有常常用的 cron(在 centOS 系列爲 crond)進程,這個進程爲 crontab 的守護進程,能夠週期性的執行用戶設定的某些任務。

2.2 進程的衍生

進程有這麼多的種類,那麼進程之間定是有相關性的,而這些有關聯性的進程又是如何產生的,如何衍生的?

就好比咱們啓動了終端,就是啓動了一個 bash 進程,咱們能夠在 bash 中再輸入 bash 則會再啓動一個 bash 的進程,此時第二個 bash 進程就是由第一個 bash 進程建立出來的,他們直接又是個什麼關係?

咱們通常稱呼第一個 bash 進程是第二 bash 進程的父進程,第二 bash 進程是第一個 bash 進程的子進程,這層關係是如何得來的呢?

關於父進程與子進程便會說起這兩個系統調用 fork()exec()

fork-exec是由 Dennis M. Ritchie 創造的

fork() 是一個系統調用(system call),它的主要做用就是爲當前的進程建立一個新的進程,這個新的進程就是它的子進程,這個子進程除了父進程的返回值和 PID 之外其餘的都如出一轍,如進程的執行代碼段,內存信息,文件描述,寄存器狀態等等

exec() 也是系統調用,做用是切換子進程中的執行程序也就是替換其從父進程複製過來的代碼段與數據段

子進程就是父進程經過系統調用 fork() 而產生的複製品,fork() 就是把父進程的 PCB 等進程的數據結構信息直接複製過來,只是修改了 PID,因此如出一轍,只有在執行 exec() 以後纔會不一樣,而早先的 fork() 比較消耗資源後來進化成 vfork(),效率高了很多,感興趣的同窗能夠查查爲何。

這就是子進程產生的由來。簡單的實現邏輯就以下方所示【註釋1】

pid_t p; p = fork(); if (p == (pid_t) -1) /* ERROR */ else if (p == 0) /* CHILD */ else /* PARENT */ 

既然子進程是經過父進程而衍生出來的,那麼子進程的退出與資源的回收定然與父進程有很大的相關性。當一個子進程要正常的終止運行時,或者該進程結束時它的主函數 main() 會執行 exit(n); 或者 return n,這裏的返回值 n 是一個信號,系統會把這個 SIGCHLD 信號傳給其父進程,固然如果異常終止也每每是由於這個信號。

在將要結束時的子進程代碼執行部分已經結束執行了,系統的資源也基本歸還給系統了,但如果其進程的進程控制塊(PCB)仍駐留在內存中,而它的 PCB 還在,表明這個進程還存在(由於 PCB 就是進程存在的惟一標誌,裏面有 PID 等消息),並無消亡,這樣的進程稱之爲殭屍進程(Zombie)。

如圖中第四列標題是 S,S 表示的是進程的狀態,而在下屬的第三行的 Z 表示的是 Zombie 的意思。( ps 命令將在後續詳解)

實驗樓

正常狀況下,父進程會收到兩個返回值:exit code(SIGCHLD 信號)與 reason for termination 。以後,父進程會使用 wait(&status) 系統調用以獲取子進程的退出狀態,而後內核就能夠從內存中釋放已結束的子進程的 PCB;而如若父進程沒有這麼作的話,子進程的 PCB 就會一直駐留在內存中,一直留在系統中成爲殭屍進程(Zombie)。

雖然殭屍進程是已經放棄了幾乎全部內存空間,沒有任何可執行代碼,也不能被調度,在進程列表中保留一個位置,記載該進程的退出狀態等信息供其父進程收集,從而釋放它。可是 Linux 系統中能使用的 PID 是有限的,若是系統中存在有大量的殭屍進程,系統將會由於沒有可用的 PID 從而致使不能產生新的進程。

另外若是父進程結束(非正常的結束),未能及時收回子進程,子進程仍在運行,這樣的子進程稱之爲孤兒進程。在 Linux 系統中,孤兒進程通常會被 init 進程所「收養」,成爲 init 的子進程。由 init 來作善後處理,因此它並不至於像殭屍進程那樣無人問津,無論不顧,大量存在會有危害。

進程 0 是系統引導時建立的一個特殊進程,也稱之爲內核初始化,其最後一個動做就是調用 fork() 建立出一個子進程運行 /sbin/init 可執行文件,而該進程就是 PID=1 的進程 1,而進程 0 就轉爲交換進程(也被稱爲空閒進程),進程 1 (init 進程)是第一個用戶態的進程,再由它不斷調用 fork() 來建立系統裏其餘的進程,因此它是全部進程的父進程或者祖先進程。同時它是一個守護程序,直到計算機關機纔會中止。

經過如下的命令咱們能夠很明顯的看到這樣的結構

pstree 

實驗樓

或者今後圖咱們能夠更加形象的看清子父進程的關係

實驗樓

經過以上的顯示結果咱們能夠看的很清楚,init 爲全部進程的父進程或者說是祖先進程

咱們還可使用這樣一個命令來看,其中 pid 就是該進程的一個惟一編號,ppid 就是該進程的父進程的 pid,command 表示的是該進程經過執行什麼樣的命令或者腳本而產生的

ps -fxo user,ppid,pid,pgid,command 

實驗樓

能夠在圖中看見咱們執行的 ps 就是由 zsh 經過 fork-exec 建立的子進程而執行的

使用這樣的一個命令咱們也能清楚的看見 init 如上文所說是由進程 0 這個初始化進程來建立而出的子進程,而其餘的進程基本是由 init 建立的子進程,或者是由它的子進程建立出來的子進程。因此 init 是用戶進程的第一個進程也是全部用戶進程的父進程或者祖先進程。(ps 命令將在後續課程詳解)

就像一個樹狀圖,而 init 進程就是這棵樹的根,其餘進程由根不斷的發散,開枝散葉

2.3 進程組與 Sessions

每個進程都會是一個進程組的成員,並且這個進程組是惟一存在的,他們是依靠 PGID(process group ID)來區別的,而每當一個進程被建立的時候,它便會成爲其父進程所在組中的一員。

通常狀況,進程組的 PGID 等同於進程組的第一個成員的 PID,而且這樣的進程稱爲該進程組的領導者,也就是領導進程,進程通常經過使用 getpgrp() 系統調用來尋找其所在組的 PGID,領導進程能夠先終結,此時進程組依然存在,並持有相同的PGID,直到進程組中最後一個進程終結。

與進程組相似,每當一個進程被建立的時候,它便會成爲其父進程所在 Session 中的一員,每個進程組都會在一個 Session 中,而且這個 Session 是惟一存在的,

Session 主要是針對一個 tty 創建,Session 中的每一個進程都稱爲一個工做(job)。每一個會話能夠鏈接一個終端(control terminal)。當控制終端有輸入輸出時,都傳遞給該會話的前臺進程組。Session 意義在於將多個 jobs 囊括在一個終端,並取其中的一個 job 做爲前臺,來直接接收該終端的輸入輸出以及終端信號。 其餘 jobs 在後臺運行。

前臺(foreground)就是在終端中運行,能與你有交互的

後臺(background)就是在終端中運行,可是你並不能與其任何的交互,也不會顯示其執行的過程

2.4 工做管理

bash(Bourne-Again shell)支持工做控制(job control),而 sh(Bourne shell)並不支持。

而且每一個終端或者說 bash 只能管理當前終端的中的 job,不能管理其餘終端中的 job。好比我當前存在兩個 bash 分別爲 bash一、bash2,bash1 只能管理其本身裏面的 job 並不能管理 bash2 裏面的 job

咱們都知道當一個進程在前臺運做時咱們能夠用 ctrl + c 來終止它,可是如果在後臺的話就不行了。

咱們能夠經過 & 這個符號,讓咱們的命令在後臺中運行

ls & 

實驗樓

圖中所顯示的 [1] 236分別是該 job 的 job number 與該進程的 PID,而最後一行的 Done 表示該命令已經在後臺執行完畢。

咱們還能夠經過 ctrl + z 使咱們的當前工做中止並丟到後臺中去

實驗樓

被中止並放置在後臺的工做咱們可使用這個命令來查看

jobs 

實驗樓

其中第一列顯示的爲被放置後臺 job 的編號,而第二列的 表示最近(剛剛、最後)被放置後臺的 job,同時也表示預設的工做,也就是如果有什麼針對後臺 job 的操做,首先對預設的 job,- 表示倒數第二(也就是在預設以前的一個)被放置後臺的工做,倒數第三個(再以前的)之後都不會有這樣的符號修飾,第三列表示它們的狀態,而最後一列表示該進程執行的命令

咱們能夠經過這樣的一個命令將後臺的工做拿到前臺來

#後面不加參數提取預設工做,加參數提取指定工做的編號 #ubuntu 在 zsh 中須要 %,在 bash 中不須要 % fg [%jobnumber] 

實驗樓

實驗樓

以前咱們經過 ctrl + z 使得工做中止放置在後臺,如果咱們想讓其在後臺運做咱們就使用這樣一個命令

#與fg相似,加參則指定,不加參則取預設 bg [%jobnumber] 

實驗樓

既然有方法將被放置在後臺的工做提至前臺或者讓它從中止變成繼續運行在後臺,固然也有方法刪除一個工做,或者重啓等等

#kill的使用格式以下 kill -signal %jobnumber #signal從1-64個信號值能夠選擇,能夠這樣查看 kill -l 

其中經常使用的有這些信號值

信號值 做用
-1 從新讀取參數運行,相似與restart
-2 如同 ctrl+c 的操做退出 
-9 強制終止該任務   
-15 正常的方式終止該任務

實驗樓

注意

如果在使用kill+信號值而後直接加 pid,你將會對 pid 對應的進程進行操做

如果在使用kill+信號值而後 %jobnumber,這時所操做的對象是 job,這個數字就是就當前 bash 中後臺的運行的 job 的 ID

相關文章
相關標籤/搜索