當前位置:學問君>學習教育>畢業論文>

論文:Linux進程和線程的基礎與管理

學問君 人氣:4.06K

一、進程的基本概念

論文:Linux進程和線程的基礎與管理

程序是爲了完成某種任務而設計的軟件,比如vi是程序。什麼是進程呢? 進程就是執行中的程序。一個執行着程序,可能有多個進程。比如Web服務器是Apache服務器,當管理員啓動服務後,可能會有好多人來訪問,也就是說許多用戶同時請求httpd,Apache服務器將會創建多個httpd進程來對其進行服務。

首先我們看看進程的定義。進程是一個具有獨立功能的程序關於某個數據集合的一次可以併發執行的執行活動,是處於活動狀態的計算機程序。進程作爲構成系統的基本細胞,不僅是系統內部獨立執行的實體,而且是獨立競爭資源的基本實體。瞭解進程的本質,對於理解、描述和設計操作系統有着極爲重要的意義。瞭解進程的活動、狀態,也有利於編制複雜程序。

二、進程的屬性

進程的定義:一個進程是一個程序的一次執行的過程;程序是靜態的,它是一些儲存在磁盤上的可執行的代碼和數據集合;進程是一個動態的概念,它是Linux系統的基本的調度單位。

一個進程由如下元素組成:

程序讀取的上下文,它表示程序讀取執行的狀態。 程序當前執行的目錄。 程序服務的檔案和目錄。 程序訪問的權限。 內存和其他分配給進程的系統資源。

Linux進程中最知名的屬性就是它的進程號(Process Idenity Number,PID)和它的父進程號(Parent Process ID,PPID)。PID、PPID都是非零正整數。一個PID唯一地標識一個進程。一個進程創建新進程稱爲創建了子進程(Child Process)。相反地,創建子進程的進程稱爲父進程。所有進程追溯其祖先最終都會落到進號爲1的進程身上,這個進程叫做init進程,是內核自舉後第一個啓動的進程。init進程扮演終結父進程的角色。因爲init進程永遠不會終止,所以系統總是可以確信它的存在,並在必要的時候以它爲參照。如果某個進程它在衍生出來的全部子進程結束之前被終止,就會出現必須以init爲參照的情況。此時那些失去了父進程的子進程就都會以init作爲它們的父進程。如果執行一下ps-af命令,可以列出許多父進程ID爲1的進程來。Linux提供了一條pstree命令,允許用戶檢視系統內正在執行的各個進程之間的繼承關係。直接在命令行中輸入pstree即可,程序會以樹狀結構方式列出系統中正在執行的各進程之間的繼承關係。

三、理解Linux下進程的結構

Linux中一個進程在內存裏有三部分數據,就是“數據段”、“堆棧段”、“代碼段”。基於I386相容的中央處理器,都有上述三種段寄存器,以方便操作系統的執行,如下圖所示。

代碼段

數據段

堆棧段

代碼段是存放了程序代碼的數據,假如機器中有數個進程執行相同的一個程序,那麼它們就可以使用同一個代碼段。而數據段則存放程序的全局變量、常數及動態數據分配的數據空間。堆棧段存放的就是子進程的返回地址、子程序的參數及程序的局部變量。堆棧段包含在進程控制塊PCB(Process Control Block)中。PCB處於進程核心堆棧的底部,不需要額外分配空間。

四、進程狀態

現在我們來看看,進程在生存週期中的各種狀態及狀態的轉換。下面是Linux系統的進程狀態模型的各種狀態。

用戶狀態:進程在用戶狀態下執行的狀態。 內核狀態:進程在內核狀態下執行的狀態。 內存中就緒:進程沒有執行,但處於就緒狀態,只要內核調度它,就可以執行。 內存中睡眠:進程正在睡眠並且進程存儲在內存中,沒有被交換到SWAP設備。 就緒且換出:進程處於就緒狀態,但是必須把它換入內存,內核才能再次調度它執行。 睡眠且換出:進程正在睡眠,且被換出內存。 被搶先:進程從內核狀態返回用戶狀態時,內核搶先於它做了上下文切換,調度了另一個進程。原先這個進程就處於被搶先狀態。 僵死狀態(zombie):進程調用exit結束,進程不再存在,但在進程表項中仍有記錄,該記錄可由父進程收集。

現在我們從進程的創建到退出來看看進程的狀態轉化。需要說明的是,進程在它的生命週期裏並不一定要經歷所有狀態。

五、Linux進程的創建

fork函數在Linux下產生新的進程的系統調用,這個函數名是英文中“分叉”的意思。爲什麼取這個名字呢? 因爲一個進程在執行中,如果使用了fork,就產生了另一個進程,於是進程就“分叉”了,所以這個名字取得很形象。fork的語法如下所示:

#include

#include

pid_t fork();

在Linux網絡編程中經常用到fork()系統調用。例如在一個客戶機/Web服務器構建的網絡環境中,Web服務器往往可以滿足許多客戶端的請求。如果一個客戶機要訪問Web服務器,需要發送一個請求,此時由服務器生成一個父進程,然後父進程透過fork()系統調用產生一個子進程,此時客戶機的請求由子進程完成。父進程可以再度回到等待狀態不斷服務其他客戶端。原理如下圖所示。

有一個更簡單的執行其他程序的函數system,參數string傳遞給一個命令解釋器(一般爲sh)執行,即string被解釋爲一條命令,由sh執行該命令。若參數string爲一個空指針,則檢查命令解釋器是否存在。該命令可以和同命令行下的命令形式相同,但由於命令作爲一個參數放在系統調用中,應注意編譯時對特殊意義字元的處理。命令的查找是按PATH環境變量的定義執行的。命令所生成的後果一般不會對父進程編程造成影響。返回值:當參數爲空指針時,只有當命令解釋器有效時返回值爲非零。若參數不爲空指針,返回值爲該命令的返回狀態(同waitpid())的返回值。命令無效或語法錯誤則返回非零值,所執行的命令被終止。其他情況則返回-1.它是一個較高層的函數,實際上相當於在shell下執行一條命令,除了system之外,系統調用exec來執行一個可執行檔案,來代替當前進程的執行映像。系統調用exit的功能是終止發出調用的進程。sleep函數調用用來指定進程掛起的秒數。wait函數族用來等待和控制進程。poppen函數和system函數類似,區別是它用管道方式處理輸出。

父進程和子進程的關係是管理和被管理的關係,當父進程終止時,子進程也隨之而終止。但子進程終止時,父進程並不一定終止。比如httpd服務器執行時,我們可以殺掉其子進程,父進程並不會因爲子進程的終止而終止。

六、進程的管理

1、啓動進程

輸入需要執行的程序的程序名,執行一個程序,其實也就是啓動了一個進程。在Linux系統中,每個進程都具有一個進程號(PID),用於系統識別和調度進程。啓動一個進程有兩個主要途徑:手工啓動和調度啓動,後者是事先進行設定,根據用戶要求自動啓動。由用戶輸入命令,直接啓動一個進程便是手工啓動進程。但手工啓動進程又可以分爲很多種,根據啓動的進程類型不同;性質不同,實際結果也不一樣。

(1)前臺啓動

前臺啓動是手工啓動一個進程的最常用的方式。用戶鍵入一個命令“df”,就已經啓動了一個進程,而且是一個前臺的進程。這時候系統其實已經處於多進程狀態。有許多執行在後臺的、系統啓動時就已經自動啓動的進程正在悄悄執行着。有的用戶在鍵入“df”命令以後趕緊使用“ps -x”檢視,卻沒有看到df進程,會覺得很奇怪。其實這裏因爲df這個進程結束太快,使用ps檢視時該進程已經執行結束了。如果啓動一個比較耗時的進程,例如在根命令下執行:find,然後使用ps aux檢視,就會看到在裏面有一個find進程。

(2)後臺啓動

直接從後臺手工啓動一個進程用得比較小一些,除非是該進程甚爲耗時,且用戶也不急着需要結果。假設用戶要啓動一個需要長時間執行的格式化文字檔案的進程,爲了不使整個shell在格式化過程中都處於“癱瘓”狀態,從後臺啓動這個進程是明智的選擇。

2、進程調度

當需要中斷一個前臺進程的時候,通常使用Ctrl+C組合鍵。但是對於一個後臺進程,就不是一個組合鍵所能解決的了,這時就必須使用kill命令。該命令可以終止後臺進程。至於終止後臺進程的原因有很多,或許是該進程佔用的CPU時間過多;或許是該進程已經掛死。這種情況是經常發生的。kill命令的工作原理是:向Linux系統的內核發送一個系統操作信號和某個程序的進程標識號,然後系統內核就可以對進程標識號指定的進程進行操作。

七、Linux的第一個進程:init

init是Linux系統執行的第一個進程,進程ID爲1,是系統所有進程的起點,主要用來執行一些開機初始化腳本和監視進程。Linux系統在完成核內引導以後就開始執行init程序,init程序需要讀取配置檔案/etc/inittab。Inittab是一個不可執行的文字檔案,它由若干行命令所組成。

在RHEL 4系統中,inittab配置檔案的內容如下所示:

#

#inittab

#

#

#author

#

#Default runlevels used by rhs are:

#0 - halt (do not set initdefault to this)

#1 - single user mode

#2 - multiuser,without nfs (the same as 3, if you do not haver networking)

#3 - full multiuser mode

#4 - unused

#5 - X11

#6 - reboot (do not set initdefault to this)

#

//表示當前缺省執行級別爲5,啓動系統進入圖形化介面

id:5:initdefault:

//啓動時自動執行/etc/rc.d/nit腳本

#system initialization.

si::sysinit:/etc/rc.d/nit

10:0:wait:/etc/rc.d/rc 0

11:1:wait:/etc/rc.d/rc 1

12:2:wait:/etc/rc.d/rc 2

13:3:wait:/etc/rc.d/rc 3

14:4:wait:/etc/rc.d/rc 4

//當執行級別爲5時,以5爲參數執行/etc/rc.d/rc腳本,init將等待其返回

15:5:wait:/etc/rc.d/rc 5

16:6:wait:/etc/rc.d/rc 6

//在啓動過程中允許按[ctrl-alt-]重啓系統

#trap ctrl-alt-

ca::ctrlaltdel:/sbin/shutdown -t3 -r now

#

..................................

#

//在執行級別2、3、4、5以上ttyX爲參數執行/sbin/mingetty程序,開啟ttyX終端用於用戶登入,如果進程退出則再次執行mingetty程序

#run gettys in standard runlevels

1:2345:respawn:/sbin/mingetty tty1

2:2345:respawn:/sbin/mingetty tty2

3:2345:respawn:/sbin/mingetty tty3

4:2345:respawn:/sbin/mingetty tty4

5:2345:respawn:/sbin/mingetty tty5

6:2345:respawn:/sbin/mingetty tty6

//在級別5上執行xdm程序,提供xdm圖形方式登入介面,並在退出時重新執行

x:5:respawn:/etc/x11/prefdm -nodaemon

#run xdm in runleverl 5

Inittab配置檔案每行的基本格式如下。

id:runlevels:action:procees

其中某些部分可以爲空,下面我們逐一介紹。

1、id

1~2個字元,配置行的惟一標識,在配置檔案中不能重複。

2、runlevels

配置行適用的執行級別,在這裏可填入多個執行級別,比如12345或者35等。

Linux有7個執行級別:

0:關機

1:單用戶字元介面

2:不具備網絡檔案系統(NFS)功能的多用戶字元介面

3:具有網絡功能的多用戶字元介面

4:保留不用

5:具有網絡功能的圖形用戶介面

6:重新啓動系統

3、action

init有如下幾種行爲,如下表所示。

init行爲

行爲

描述

respawn

啓動並監視第4項指定的process,若process終止則重啓它

wait

執行第4項指定的process,並等待它執行完備

once

執行第4項指定的process

boot

不論在哪個執行等級,系統啓動時都會執行第4項指定的process

bootwait

不論在哪個執行等級,系統啓動時都會執行第4項指定的process,且一直等它執行完備

off

關閉任何動作,相當於忽略該配置行

ondemand

進入ondemand執行等級時,執行第4項指定的process

initdefault

系統啓動後進入的執行等級,該行不需要指定process

sysinit

不論在哪個執行等級,系統會在執行boot及bootwait之前執行第4項指定的'process

powerwait

當系統的供電不足時執行第4項指定的process,且一直等它執行完備

powerfailnow

當系統的供電嚴重不足時執行第4項指定的process

ctrlaltdel

當用戶按下ctrl+alt+del 時執行的操作

kbrequest

當用戶按下特殊的組合鍵時執行第4項指定的process,此組合鍵需在keymaps檔案定義

4、process

Process爲init執行的進程,這些進程都儲存在目錄/etc/rc.d/rcX中,其中的X代表執行級別,rc程序接收X參數,然後執行/etc/rc.d/rc.X下面的程序。使用如下命令可以檢視/etc/rc.d目錄內容。

#ls –l /etc/rc.d/

total 112

drwxr-xr-x 2 root root 4096 3/15 14:44 init.d

-rxwr-xr-x 1 root root 2352 2004-3-17 rc

drwxr-xr-x 2 root root 4096 3/15 14:44 rc0.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc1.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc2.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc3.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc4.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc5.d

drwxr-xr-x 2 root root 4096 3/15 14:44 rc6.d

-rxwr-xr-x 1 root root 2200 2004-3-17 l

-rxwr-xr-x 1 root root 2352 2004-3-17 nit

…………

使用如下命令檢視/etc/rc.d/rc5.d的內容。

#ls –l /etc/rc.d/rc5.d

這些檔案都是符號連結,以S打頭的標識啓動該程序,而以K打頭的標識終止該程序,後面的數字標識執行順序,越小越先執行,剩下的標識程序名。系統啓動或者切換到該執行級別時會執行以S打頭的程序,系統切換到該執行級別時會執行以K打頭的程序。

這個目錄下的程序可透過chkconfig程序進行管理,當然這個目錄下的程序需要符合一定規範,如果瞭解shell編程,可以檢視這些符號連結所指向的程序的源碼。

init也是一個進程,和普通的進程具有一樣的屬性。比如修改了/etc/inittab,想讓修改馬上生效,可透過執行“kill-SIGHUP 1”來實現,也可透過執行“init q”來實現。

八、Linux的線程簡介

1、Linux線程的定義

線程(thread)是在共享內存空間中併發的多道執行路徑,它們共享一個進程的資源,如檔案描述和信號處理。在兩個普通進程(非線程)間進行切換時,內核準備從一個進程的上下文切換到另一個進程的上下文要花費很大的開銷。這裏上下文切換的主要任務是儲存老進程CPU狀態並加載新進程的儲存狀態,用新進程的內存映像替換進程的內存映像。線程允許你的進程在幾個正在執行的任務之間進行切換,而不必執行前面提到的完整的上下文。另外本文介紹的線程是針對POSIX線程的,也就是Pthread。也因爲Linux對它的支援最好,相對進程而言,線程是一個更加接近於執行體的概念,它可以與同進程中的其他線程共享數據,但擁有自己的棧空間,擁有獨立的執行序列。在串行程序基礎上引入線程和進程是爲了提高程序的併發度,從而提高程序執行效率和響應時間。也可以將線程和輕量級進程(LWP)視爲等同的,但其實在不同的系統/實現中有不同的解釋,LWP更恰當的解釋爲一個虛擬CPU或內核的線程。它可以幫助用戶態線程實現一些特殊的功能。Pthread是一種標準化模型,它用來把一個程序分成一組能夠同時執行的任務。

2、什麼場合使用Pthread,即線程

(1)在返回前阻塞的I/O任務能夠使用一個線程處理I/O,同時繼續執行其他處理任務。

(2)在有一個或多個任務受不確定性事件,比如網絡通信的可獲得性影響的場合,能夠使用線程處理這些異步事件,同時繼續執行正常的處理。

(3)如果某些程序功能比其他的功能更重要,可以使用線程以保證所有功能都出現,但那些時間密集型的功能具有更高的優先級。

以上三點可以歸納爲:在檢查程序中潛在的並行性時,也就是說在要找出能夠同時執行任務時使用Pthread。上面已經介紹了,Linux進程模型提供了執行多個進程的能力,已經可以進行並行或併發編程,可是純種能夠讓你對多個任務的控制程序更好、使用資源更少,因爲一個單一的資源,如全局變量,可以由多個線程共享。而且,在擁有多個處理器的系統上,多線程應用會比用多個進程實現的應用執行速度更快。

3、Linux進程和線程的發展

1999年1月發佈的Linux 2.2內核中,進程是透過系統調用fork創建的,新的進程是原來進程的子進程。需要說明的是,在2.2.x版本中,不存在真正意義上的線程(thread)。Linux中常用的線程Pthread實際上是透過進程來模擬的。也就是說Linux中的線程也是透過fork創建的,是“輕”進程。Linux 2.2只默認允許4096個進程/線程同時執行。高端系統同時要服務上千個用戶,所以這顯然是一個問題,它一度是阻礙Linux進入企業級市場的一大因素。

2001年1月發佈的Linux 2.4內核消除了這個限制,並且允許在系統執行中動態調整進程數上限。因此,進程數現在只受制於物理內存的多少。在高端服務器上,即使安裝了512MB內存,現在也能輕而易舉地同時支援1萬6千個進程。

2003年12月發佈的2.6內核,進程調度經過重新編寫,去掉了以前版本中效率不高的算法。以前,爲了決定下一步要執行哪一個任務,進程調度程序要檢視每一個準備好的任務,並且經過計算機來決定哪一個任務相對來更爲重要。進程標識號(PID)的數目也從32000升到10億。內核內部的大改變之一就是Linux的線程框架被重寫,以使NPTL(Native POSIX Thread Library)可以執行於其上。對於執行負荷繁重的線程應用的Pentium Pro及更先進的處理器而言,這是一個主要的性能提升,也是企業級應用中的很多高端系統一直以來所期待的。線程框架的改變包含Linux線程空間中的許多新的概念,包括線程組、線程各自的本地存儲區、POSIX風格信號,以及其他改變。改進後的多線程和內存管理技術有助於更好地執行大型多媒體應用軟件。

4、總結

線程和進程在使用上各有優缺點:線程執行開銷小,但不利於資源的管理和保護;而進程正相反。同時,線程適合於在對稱處理器的計算機上執行,而進程則可以跨機器遷移。另外,進程可以擁有資源,線程共享進程擁有的資源。進程間的切換必須儲存在進程控制塊PCB(Process Control Block)中。同一個進程的多個線程間的切換不用那麼麻煩。最後一個實例來作爲本文的結束:當你在一臺Linux PC上開啟兩個OICQ,每一個OICQ是一個進程;而當你在一個OICQ上和多人聊天時,每一個聊天視窗就是一個線程。