Linux进程基本知识详解
目錄
前言
一、描述組織進程
1、描述進程
2、task_struct
2.1概念
2.2內容分類
3、組織進程
二、查看進程
三、通過系統調用創建進程
1、fork函數
2、父子進程分流執行
?四、進程狀態
1、Linux內核源碼
2、Linux狀態的說明
2.1 R運行狀態
2.2 S睡眠狀態
2.3 D磁盤休眠狀態
2.4 T停止狀態
2.5 Z僵尸狀態?
2.6 X死亡狀態
3、僵尸進程的危害
五、進程優先級?
1、基本概念
2、優先級的查看與調整
2.1查看系統進程
2.2 PRI and NI?
2.3調整優先級
六、環境變量?
1、常見環境變量
1.1 PATH
1.2 HOME
?1.3 SHELL
2、環境變量的組織方式?
2.1主函數的參數
?2.2通過代碼獲取環境變量
七、進程地址空間
1、虛擬地址
2、映射關系
3、mm_struct
4、地址空間的作用
?總結
前言
哈嘍,小伙伴們大家好,進程是操作系統中非常重要的一個概念,可以說我們與操作系統的唯一交互方式就是進程。今天我將在linux系統下帶大家了解進程的一些基本知識。事不宜遲,拿好小本本,我們趕緊開始吧~
一、描述組織進程
1、描述進程
進程概念:運行起來程序就是進程。
描述進程過程:?由于馮諾依曼體系要求,我們在執行一個程序的時候,需要先把程序加載到內存中去,CPU在從內存中依次提取代碼。那么,我們運行的程序都總只有一個嗎?顯示不是。大多數情況下,都是很多程序一起運行的,這時候就需要操作系統作進程管理。操作系統不會直接管理可執行程序,而是會管理可執行程序的數據。他會根據每一個可執行程序進行描述,形成一個結構體,這個結構體叫做進程控制塊,也叫做PCB(process control block)。然后操作系統再把所有的進程控制塊組織到一起,一起加載到內存中去。可以理解成進程是可執行程序和進程控制塊的集合,操作系統通過管理進程控制塊就可以起到管理進程的作用。
2、task_struct
2.1概念
概念:PCB是一個統稱,在Linux下控制進程的結構體叫做task_struct。下面我將介紹一下這個結構體中都包含哪些內容。
2.2內容分類
(1)標識符:標識符是描述一個進程的唯一標識,用來區別其它進程。相當于人的身份證。我們先運行一個進程,然后使用相關命令把進程的信息調出來。
查看系統中所有進程信息:? ?ps aux / ps axj 命令
?PID為運行進程的標識符,PPID為父進程的標識符。
用kil -9 ID號可以結束一個進程。
(2)狀態
?STAY下面對應的是這個進程的狀態,R代表正在運行,而加號代表這個進程是前臺進程。在同一時間,后臺進程有多個,而前臺進程只能由一個。在前臺進程運行時,我們在終端中輸入命令是無效的。如果想要進程在后臺運行,則在運行命令的后面加一個&即可。
?可以看到進程后臺運行依舊可以輸入命令行,并且進程狀態后面沒了加號。
(3)優先級
進程往往有很多個同時進行,但CPU資源是有限的,所以需要對進程進行優先級設置,需要排出放入CPU的先后順序。對進程的排序本質上是對PCB進行排隊。
(4)程序計數器
?概念:程序即將指向的下一條指令的地址。
在我們看書看到一半的時候,往往會夾一個書簽,方便下次找到整這個位置。進程執行時也同樣需要一個標簽,方便進程切換出去回來的時候能找到這個位置,所以需要程序計數器(PC指針)把這個位置記下來。
(5)內存指針
task_struct想要找到對應的執行程序,需要一個指針來保存地址,這稱為內存指針
(6)上下文數據
我們先來舉一個例子來解釋什么叫上下文數據。想必大家在上大學的時候都有同學在大一結束的時候去當兵吧,如果拿到了征兵和招募通知不在學校辦理相關手續就直接走,那大概率過兩年回來的時候已經被學校開除了。在走之前辦理相關手續是為了進行信息保留,把學籍、學分、考試成績等信息都保留下來,這樣等當兵回來之后可以繼續上大二。這些保留的信息在計算機中就叫做上下文數據。
CPU做的工作有三步:取指令,分析指令,執行指令。一個進程運行的時候會加載到內存中,CPU從內存中按順序取代碼進行計算,假設CPU是單核的,那么在某一時間它只能進行一個進程的計算。既然這樣,如果我們也一個死循環,CPU是否會卡死呢?答案是當然不會。 每個進程都有一個時間片,時間片實際就是一個時間段,時間片一到或者是有更高優先級的進程來了,CPU就會進行進程切換。在幾個進程之間循環切換就可以實現多個進程并行。
而CPU中只有一套寄存器,用來存放取到的指令和計算過程中的數據。那么如果計算進行到一半時,比如a=b+c;d=a+b;這個指令剛剛計算到一半,恰好需要進行進程切換怎么辦呢?難道下次切換回來的時候要進行重新計算嗎?答案是當然不會,剛剛計算過程中產生的數據叫做上下文數據,發生進程切換時CPU會把它們保存到task_struct中(不太準確,先簡單這么理解),等切換回來的時候再直接把上下文數據提取出來繼續運算就可以了。程序計數器也是存在CPU的寄存器中的,也可以理解成是一種上下文數據。
(7)I/O狀態信息
?包括顯示的I/O請求,分配給進程的I/O設備和被進程使用的文件列表。
(8)記賬信息
可能包括處理器時間總和,使用的時鐘數總和,時間限制,記賬號等。
3、組織進程
運行在系統里的進程數據以task_struct鏈表的形式儲存起來。
二、查看進程
在根目錄中,有proc(process)目錄是專門存放進程信息的。可以用/proc查看。
后面跟具體進程的PID可以查看對應進程的信息。
這里的cwd中存放的是進程的工作目錄,也就是說進程是知道自己在哪個目錄下工作的。所以我們之前進行文件操作時在能在對應的文件夾下完成讀寫。?
三、通過系統調用創建進程
我們之前創建進程的方式都是通過運行可執行程序形成進程。現在我們來講一下如何通過系統調用創建進程。
1、fork函數
我們可以通過fork函數來創建一個進程。
特點:
- fork()函數在進程中創建了一個子進程,父進程的代碼和數據可能是來自磁盤的,而子進程的代碼和數據是來自父進程的。
- fork()創建子進程,fork之前的代碼被父進程執行,fork之后的代碼父子都可以執行。也就是說fork之后父子共享代碼(那數據呢?之后再說)
- fork之后就有了父子兩個進程,這兩個進程誰先被調度是不確定的,完全取決于操作系統的調度算法。
2、父子進程分流執行
父子進程執行的指令往往是不同的,否則子進程就沒有存在的必要。
和我們以往接觸到的函數不同,fork()函數有兩個返回值,它會返回給子進程0,把子進程的PID返回給父進程。
看如下代碼,父進程和子進程會同時進行:
?
?四、進程狀態
進程包括幾個不同狀態,一般我們在學校學到的或者書中看到的都會說進程有五個狀態,分別是:創建,就緒,執行,阻塞,終止。但對這種敘述很多小伙伴都感覺似懂非懂,原因是這種描述是描述的所有操作系統的共性,所以感覺起來會有點抽象。今天我將以具體的一款操作系統為例,帶大家看看Linux的進程狀態是怎樣的。
1、Linux內核源碼
首先我想問大家一個問題,狀態可數據化嗎?答案是可以的,我們可以把每一個狀態和一個數字或字母對應起來,用數字或字母表示處于的狀態,就可以實現狀態的數據化。數據化后的狀態會保存到task_struct中。
下面是一段Linux的源碼,說明了Linux下的狀態定義:
static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };2、Linux狀態的說明
2.1 R運行狀態
運行狀態并不一定表示進程在運行中,也可以是在運行隊列中。
2.2 S睡眠狀態
意味著進程在等待某件事情完成(這里的睡眠有時候也可以叫做可中斷睡眠)。
2.3 D磁盤休眠狀態
也叫不可中斷睡眠,在這個狀態的進程通常會等待IO的結束。
S狀態和D狀態有什么區別呢?舉個例子,有一個進程叫張三,它負責取硬盤中拿東西,硬盤中和它對接的程序叫李四,李四說讓張三等一會,它需要把數據收集一下再給它,于是張三進入了S睡眠狀態等待李四。可這時候操作系統來了,操作系統感覺空間已經嚴重不足了,又看到張三在睡覺,于是就直接把張三殺掉了。過了一會李四收集好數據了開始找張三,結果張三已經掛了,無人應答。這種情況就尷尬了,是很容易引發問題的。于是就有了D狀態,D狀態和S狀態的區別就是D狀態時深度睡眠,操作系統無權把它殺掉或喚醒,所以張三只要進入D狀態就可以避免上面的問題。
2.4 T停止狀態
可以通過發送 SIGSTOP 信號給進程來停止(T)進程。這個被暫停的進程可
以通過發送 SIGCONT 信號讓進程繼續運行。
kill? ?-l? 可以列出所有命令:
2.5 Z僵尸狀態?
為了方便大家理解僵尸狀態,我來舉一個例子。假設馬路上出現一具尸體,尸體不會被馬上抬走,而是要等警察來清理現場,采集尸體上的有效信息,來確認是意外還是他殺,等著一系列的事情都辦完之后才會把人拉走。在這個例子里面,死人就好比進程,警察好比操作系統(父進程),等待警察采集尸體上有效信息這個過程就是僵尸狀態。
僵尸進程概念:一個進程已經退出了,但它的相關資源沒有被回收,這樣的進程叫做僵尸進程。
為什么要有僵尸狀態呢?
首先我們要清楚,一個進程創造出來是為了完成某個任務,為了判斷這個任務有沒有完成,進程退出時會釋放內存后,然后把退出的相關信息(退出碼等)保存到task_struct中,等待操作系統或父進程來讀取,如果沒有人來讀取,該進程的資源就不能被釋放。等待父進程取信息時的這個狀態就叫做僵尸狀態。僵尸進程本身并不占內存,但是用來保存信息的task_struct會占內存。僵尸狀態其實已經近似于一具尸體了,沒有人能夠再殺死它,kill? -9 也不行。只有等待父進程讀取信息才會釋放。
2.6 X死亡狀態
死亡狀態往往是一瞬間的事,在進程退出后相關信息被父進程讀取后立刻進入死亡狀態然后被釋放。死亡狀態很難觀測到。
3、僵尸進程的危害
- 如果子進程的退出信息一直不被讀取,就會一直處在僵尸狀態,信息儲存在task_struct中,需要PCB一直維護。
- 如果一個父進程創建了很多子進程,但就是不回收,就會造成內存資源的浪費。
- 如果在子進程還沒退出的時候父進程提前退出,子進程就會變成孤兒進程。孤兒進程很容易造成內存泄漏,所以會被操作系統領養,由操作系統來回收。?
五、進程優先級?
1、基本概念
進程優先級概念:由于CPU的資源是有限的,各個進程需要對CPU資源進行競爭,CPU資源分配的先后順序稱為進程優先級。
其它概念:
競爭性:cpu資源有限,所以進程之間是存在競爭性的。
獨立性:多進程運行時,進程與進程保持獨立,互不干擾。
并行:多個進程在多個cpu下分別同時進行運行,叫做并行。
并發:多個進程在一個cpu采用進程切換的方式,使多個進程同時推進,叫做并發。
2、優先級的查看與調整
2.1查看系統進程
查看當前shell產生的進程:ps? ?l
其中的信息含義如下:
- UID:執行者的身份
- PID:當前進程的標號
- PPID:父進程的標號
- PRI:代表這個進程可被執行的優先級,數字越小越先被執行
- NI:代表這個進程的nice值?
2.2 PRI and NI?
PRI很好理解,代表這個進程的優先級。NI代表nice值,那么什么是nice值呢?
nice值是一個修正優先級的數值,我們無法直接改變PRI,但可以通過改變nice值來改變PRI。? PRI(new)=PRI(old)+nice值。只要把nice值設為負數,PRI就會減小,優先級變高。nice值設為證書,PRI增大,優先級減小。nice的取值范圍是-20-19。
2.3調整優先級
調整優先級的本質是調整nice值。我們可以用top命令來更改nice值。
- top
- 進入top后按“r”–>輸入進程PID–>輸入nice值
?我們觀察myproc的進程信息
通過top命令把nice值調整為10,發現PRI和NI都發生了變化。
?
六、環境變量?
概念:環境變量一般是指操作系統中用來指定操作系統運行環境的一些參數。在系統中通常具有全局特性。
1、常見環境變量
命令:env查看所有環境變量
1.1 PATH
含義:命令的默認搜索路徑
通過之前的學習,我們了解到,我們輸入的命令行本質上也是一個進程。那為什么輸入命令行的時候可以直接輸入指令名,而運行其它進程的時候必須指定路徑,否則就會找不到呢?
?這是因為PATH這個環境變量中存了一系列的路徑,每個路徑之間用冒號隔開。我們輸入指令時如果不指明路徑操作系統會默認去這些路徑中搜索,而我們平常使用的命令行指令都存在這些路徑中,所以可以直接使用。代碼如下(用echo輸出環境變量的時候記得前面加$,否則會被當成字符串處理):
?如果我們想要一個進程不需要指明路徑就可以直接運行應該怎樣做呢?很簡單,只要把它也添加到默認搜索路徑中就可以了。例如我們用yum安裝軟件之后可以使用相應軟件的命令或者winodws下點一下快捷方式程序就能啟動,本質上都是因為這些軟件的路徑被安裝到了環境變量標定的路徑下。但我們自己寫的程序一般不建議添加到環境變量標定的路徑下,因為我們自己寫的程序質量和用途都參差不齊,容易污染系統中的工具集。
如果想要我們自己寫的程序可以直接運行,可以把當前程序所在路徑添加到環境變量中。
注意:環境變量只是一個變量,我們對它的改變是不會保存的。退出后再登陸系統會重新生成環境變量。
1.2 HOME
含義:用戶的主工作目錄(即用戶登陸到Linux系統的下默認工作目錄)
下面分別是普通用戶和root用戶中HOME的值,可以發現是不同的。
?1.3 SHELL
含義:當前操作系統中具體的shell。
2、環境變量的組織方式?
2.1主函數的參數
首先我們要先了解一個概念,main函數是可以有參數的。main函數是被操作系統調用的,當我們使用ls? -l這種指令時,實際上-l就是在給main函數傳參。
c語言規定main函數有兩個參數(也可以說是三個,這里我們先說前兩個),第一個是整型變量,代表傳入參數的個數,第二個是指向字符串的指針數組,里面保存傳入指令的地址。有了這兩個參數,我們就可以像命令行指令那樣,通過改變后綴來產生不同的效果。
?
?2.2通過代碼獲取環境變量
環境變量的獲取方式有兩種,一種是通過主函數的第三個參數,另一種是通過第三方變量environ。下面主要演示第二種。
每個程序都會收到一張環境表,環境表是字符指針數組,由二級指針environ調用。
環境變量具有全局屬性,我們可以在程序中把他們打印出來。
?這樣打印出來的是所有的環境變量,我們也可以用函數getenv根據環境變量名調出具體的環境變量。
#include <stdio.h> #include <stdlib.h> int main() { printf("%s\n", getenv("PATH")); return 0; }七、進程地址空間
1、虛擬地址
在c語言中,我們學過地址的概念,也了解過內存的空間分布,如下圖所示。但是我們對這些概念的理解可能并不準確,實際情況可我們想象的往往會有一些出入,今天我將從系統的角度對這些概念進行修正。
?我們先來看一個例子,代碼如下:
運行這個進程,發現在子進程對全局變量進行修改后,父進程和子進程打印出來的值并不一樣,但地址卻相同。
?從上面這個例子我們可以得到以下結論:
- 父子進程的的變量內容不一樣,所以父子進程輸出的絕對不是同一個變量。
- 但是他們的地址又相同,說明地址絕對不是真正意義上的物理地址,在linux下,我們把這種地址稱為虛擬地址。
- 我們在c/c++下看到的都是虛擬地址,物理地址一概看不到,由操作系統統一管理。
2、映射關系
?實際上我們之前提到的分區都不是真正意義上的數據內存分區,而是虛擬地址對應的分區。虛擬地址和內存中的實際物理地址都存在一張頁表中,并且是一一映射關系,可以根據虛擬地址找到物理地址。每個進程創建時,都會形成一個進程地址空間(也就是虛擬地址)和它的task_struct關聯起來。剛開始子進程和父進程的映射關系是完全一樣的,虛擬地址都指向相同的物理地址,但子進程改變了全局變量的值,由于進程之間具有獨立性,父進程中全局變量的值不能隨子進程的改變而改變,所以它會在內存中開辟一個新變量來保存改變后的值,并改變頁表中的映射關系,使虛擬地址能夠找到它。
3、mm_struct
進程地址空間實際上就是一個結構體,稱為mm_struct,mm_struct是對實際物理空間的數據化。舉個例子,我們上學的時候可能都劃過三八線,在畫三八線時要保證公平,需要用到尺子來量一下課桌的長度。實際上測量長度這一行為就是數據化的體現,假設量出來桌子長100厘米,那么規定0到50厘米歸左邊的小明,51到100厘米歸右邊的小紅,僅僅用一個長度區間就很好的描述了課桌的分區。
和劃分課桌相同,對一塊區域數據化的最好方法就是把他的起始位置和結束位置用數據表示出來,所以mm_struct中保存的是各個分區的始末位置。類似于下圖:
實際上不光進程是分段的,保存在磁盤中的可執行程序也是分段的。?
注意:mm_struct和task_struct一樣,都是保存在內存中的,并且task_struct中存了一個指針,用來指向mm_struct。
4、地址空間的作用
(1)保護內存
有了地址空間,就再也不會有系統層面的越界訪問了。我們平常說的指針越界其實都是地址空間的越界,并不會影響到系統。
(2)統一性
每個進程都認為看到的是相同的空間范圍(順序,構成)。
(3)更好的完成進程獨立性和合理使用空間
使用虛擬地址的話,每個進程都感覺自己在獨占內存,有助于系統更好的分配空間。舉個例子,我們每個人都往銀行存五萬塊錢,每個人都覺得自己擁有五萬塊錢,但大家又不可能同時取這五萬塊錢,所以銀行握著一大筆存款可以隨意支配,創造更大的價值。將內存集中到一起由操作系統統一調配更加高效,實現了進程調度和內存管理的解耦。
?總結
以上就是本文要講的全部內容。本文主要介紹了進程的相關概念,希望能給大家帶來幫助。在下一章我將介紹進程控制的相關知識,對進程進行進一步的講解。本篇文章就到這里啦,山高路遠,來日方長,期待下次見面~
總結
以上是生活随笔為你收集整理的Linux进程基本知识详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python qrcode生成彩色二维码
- 下一篇: linux stopped 进程,lin