【Linux系统编程】进程介绍
進程
我們平時寫的 C 語言代碼,通過編譯器編譯,最終它會成為一個可執行程序,當這個可執行程序運行起來后(沒有結束之前),它就成為了一個進程。
程序是存放在存儲介質上的一個可執行文件,而進程是程序執行的過程。進程的狀態是變化的,其包括進程的創建、調度和消亡。程序是靜態的,進程是動態的。
在 Linux 系統中,操作系統是通過進程去完成一個一個的任務,進程是管理事務的基本單元。進程擁有自己獨立的處理環境(如:當前需要用到哪些環境變量,程序運行的目錄在哪,當前是哪個用戶在運行此程序等)和系統資源(如:處理器 CPU 占用率、存儲器、I/O設備、數據、程序)。我們可以這么理解,公司相當于操作系統,部門相當于進程,公司通過部門來管理(系統通過進程管理),對于各個部門,每個部門有各自的資源,如人員、電腦設備、打印機等。
進程狀態
我們現在的電腦基本上都是多任務,我們聊著 QQ 的時候,同時可以看著視頻,這里相當于 QQ 和視頻兩個程序同時運行著(兩個進程)。早期的時候,電腦的 CPU 是單核的(單核理論上只運行操作一個任務),那它是如何做到多任務的呢?這就涉及到進程的調度策略。現在給大家舉這么一個例子,有 A,B,C 三個進程,在我們單 CPU 的情況下,每一個時刻只有一個進程在運行,如果 A 運行完,B 運行,B 運行完,C 運行,C 運行完,A 運行,而 CPU 的運算速度足夠快,A 兩次運行時間間隔足夠短,從宏觀上就我們就看到 A,B,C 好像同時運行,這就是實現單 CPU 運行多個任務的核心原理,通過時間片輪詢調度策略實現多任務(更多詳情,請看《Linux 進程調度淺析》)。
從上面的例子,我們可以得知,對于 A 進程而言,有時候在運行,有時候沒有運行,兩個狀態不一樣,所以,進程是有狀態的,同時,狀態是可以相互進行轉換的,從執行的狀態轉換為不執行的狀態,這里,我們可以把進程運行的整個生命周期簡單劃分為三種狀態(實際上不指這三種狀態):就緒態、執行態、等待態。
就緒態:
進程已經具備執行的一切條件,正在等待分配 CPU 的處理時間。
執行態:
該進程正在占用 CPU 運行。
等待態:
進程因不具備某些執行條件而暫時無法繼續執行的狀態。
這里需要注意,就緒態和等待態都是不執行,但它們是有區別的,就緒態是指滿足條件,時間沒到,等待態是不滿足條件。
同樣的,進程的這三種狀態可以相互轉換:
為了讓大家更好地這三種狀態的轉換,給大家舉一個買火車票的例子。
Mike 匆忙地趕去火車站買火車票,太著急了,到了售票廳才發現忘記帶身份證,這時候,就算 Mike 排隊也沒用,因為 Mike 不具備買票的條件(沒帶身份證),這時候的 Mike 屬于等待態。
Mike 給它對象打電話,讓她把身份證帶過來,等會,身份證送到了,這時候,Mike 可以去排隊買票了,只是時間到,Mike 就可以買票了,這時,Mike 屬于就緒態。而這過程是由等待態轉換到就緒態。
等了 10 分鐘,終于到 Mike 了,Mike 開始買票,這時候, Mike 屬于執行態。而這過程是由就緒態轉換為執行態。
而在買票的過程中,Mike 的對象打電話給他,讓 Mike 也幫她買一張火車票,但是, Mike 沒有她對象的身份證,接著,Mike 繼續等他對象送身份證,這時候,Mike由執行態轉換為等待態。
假如是這么一種情況,Mike 買火車票是給公司的同事買的(需要買 100 多張票),在買著票的過程中(執行態),后面還有很多人在排隊,后面排隊的人肯定不爽,這時售票員就說,20分鐘后,如果你還沒處理完,請你到后面排隊。結果,Mike 花了 20 分鐘還是沒有處理完,于是,乖乖地到后面重新排隊,這時候,Mike由執行態轉換為就緒態。
進程控制塊
對于操作系統而言,它需要控制很多進程,同時,每個進程都有不同的狀態,系統如何知道 A 執行完到 B 執行而不是 C?系統如何協調控制進程呢?
當我們運行一個程序使它成為一個進程時,系統會開辟一段內存空間存放與此進程相關的數據信息,而這個數據信息是通過結構體(?task_struct,打開 /usr/include/linux/sched.h 可以找到 task_struct 的定義?)來存放,我們把這個存放進程相關數據信息的結構體稱為進程控制塊。操作系統就是通過這個進程控制塊來操作控制進程。更多詳情,請看《?Linux 進程管理》。
進程控制塊是操作系統中最重要的記錄型數據結構。進程控制塊記錄了用于描述進程進展情況及控制進程運行所需的全部信息,它是進程存在的唯一標志。進程控制塊里有很多信息,其中比較重要的是進程號,至于其他的一些信息我們不在這詳細討論。
進程號
每個進程都由一個進程號來標識,其類型為 pid_t(無符號整型),進程號的范圍:0~32767。進程號總是唯一的,但進程號可以重用。當一個進程終止后,其進程號就可以再次使用。
系統允許一個進程創建新進程,新進程即為子進程,子進程還可以創建新的子進程,形成進程樹結構模型。整個 Linux 系統的所有進程也是一個樹形結構。樹根是系統自動構造的,即在內核態下執行的 0 號進程,它是所有進程的祖先。進程號為 0 的進程通常是調度進程,常被稱為交換進程( swapper )。由 0 號進程創建 1 號進程(內核態),1 號負責執行內核的部分初始化工作及進行系統配置,并創建若干個用于高速緩存和虛擬主存管理的內核線程。隨后,1 號進程調用 execve() 運行可執行程序 init,并演變成用戶態 1 號進程,即 init 進程。
所以,在 Linux 下面所有的進程都由 init 進程直接或者間接創建。
接下來,再給大家介紹三個不同的進程號。
進程號(PID):
標識進程的一個非負整型數。
父進程號(PPID):
任何進程( 除 init 進程)都是由另一個進程創建,該進程稱為被創建進程的父進程,對應的進程號稱為父進程號(PPID)。如,A 進程創建了 B 進程,A 的進程號就是 B 進程的父進程號。
進程組號(PGID):
進程組是一個或多個進程的集合。他們之間相互關聯,進程組可以接收同一終端的各種信號,關聯的進程有一個進程組號(PGID) 。這個過程有點類似于 QQ 群,組相當于 QQ 群,各個進程相當于各個好友,把各個好友都拉入這個 QQ 群里,主要是方便管理,特別是通知某些事時,只要在群里吼一聲,所有人都收到,簡單粗暴。但是,這個進程組號和 QQ 群號是有點區別的,默認的情況下,當前的進程號會當做當前的進程組號。
Linux 操作系統提供了三個獲得進程號的函數?getpid()、getppid()、getpgid()。
所需頭文件:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:
獲取本進程號(PID)
參數:
無
返回值:
本進程號
pid_t getppid(void);
功能:
獲取調用此函數的進程的父進程號(PPID)
參數:
無
返回值:
調用此函數的進程的父進程號(PPID)
pid_t getpgid(pid_t pid);
功能:
獲取進程組號(PGID)
參數:
pid:進程號
返回值:
參數為 0 時返回當前進程組號,否則返回參數指定的進程的進程組號
示例代碼如下:
#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main(int argc, char *argv[]) {pid_t pid, ppid, pgid;pid = getpid();printf("pid = %d\n", pid);ppid = getppid();printf("ppid = %d\n", ppid);pgid = getpgid(pid);printf("pgid = %d\n", pgid);return 0; }運行結果如下:
總結
以上是生活随笔為你收集整理的【Linux系统编程】进程介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】 文件描述符的复制
- 下一篇: 【Linux系统编程】Linux 可执行