linux多線程編程----相關概念

學習Linux多進程編程

一、進程的定義:程序執行和資源管理的最小單位。

二、進程控制:

(1)進程標識: 進程標識  子進程號  父進程號
 頭文件  #include<unistd.h>  #include<unistd.h>
 函數功能  取得當前進程的進程號  取得當前進程的父進程號
 函數原型  Pid_t getpid(void) Pid_t getppid(void) 
 函數返回值  成功返回進程的進程標識符   成功返回父進程的進程標識符


注:Pid_t其實是一個typedef類型,相當於unsigned int.

例:

(2)進程的創建:

    1)exec族函數:

 頭文件  #include<unistd.h>
原型
int execl(const chat *path,const char *args,...)

int execv(const char *path,char const *argv[])

int execle(const cahr *path,const char *arg,...,char *const envp[])

int execve(const char *path,char *const argv[],char *const envp[])

int execlp(const char *file,char *arg,...)

int execvp(const cahr *file,char *const argv[])
 
返回  返回-1表示出錯


由於比較多,在此只舉例execve函數:

注:在用execve函數創建新進程的後,會以新的程序取代原來的進程,然後系統會從新的進程運行,但是新的進程的PID值會與原來進程的PID值相同.

2)system()函數

 頭文件  #include<stdlib.h>
 功能  在進程中開始另一個進程
 原型  int system(const char *string)
 傳入值  系統變量
 返回值
 成功則返回執行shell命令後的返回值,調用/bin/sh數百返回127,其他

失敗返回-1,三叔string爲空返回非零值
 


例:

注:執行system函數是,會調用fork,execve,waitpid函數,其中任意一個調用失敗將導致system函數調用失敗。

3)fork函數:

 頭文件  #include<unistd.h>
 功能  建立一個新進程(複製進程)
 原型 pid_t fork(void); 
 返回值
 執行成功則在子進程中返回0,在父進程中返回新建進程的PID,

 失敗返回-1
 


例:

注:使用fork要小心,特別是當把它放在if else或循環裏,如:

int main()

{

    for(;;) fork();

}

可能造成系統死機,因爲由fork創建的進程如果比父進程結束的晚,就有可能形成殭屍進程,佔用系統資源又沒有用,造成資源殆盡。

 功能  等待子進程中斷或結束
 原型  Pid_t wait(int *status)
 傳入值  status子進程狀態
 返回值  成功返回子進程的進程號,否則返回-1
 注  wait()會暫停目前進程的執行,直到有信號來到或子進程終止

注:wait()函數會阻塞父進程運行直到子進程正常結束。

5)waitpid()函數

頭文件
#include<sys/types.h>

#include<sys/wait.h>
 
功能 等待子進程中斷或結束
原型 pid_t waitpid(pid_t pid,int *status,int options)
傳入值
pid爲子進程號    

status爲子進程狀態  

option可以爲0、WNOHANG(如果沒有任何已終止的子進程則馬上返回,不予等待)或WUNTRACED(如果子進程進入暫停執行則馬上返回,但終止狀態不予理會)
 
返回值 成功返回子進程號,否則返回-1
注 此函數會暫停目前進程的執行,直到有信號來到或子進程終止


例:略(同wait()例差不多)。


進程終止:

6)exit()函數

 頭文件  #include<stdlib.h>  #include<stdlib.h>
 功能  正常終止進程 終止進程執行 
 原型  void exit(int status) void _exit(int status) 
 傳入數值  整數 status  整數 status
 注
 exit()用來正常終止目前進程的

 執行,並把參數status返回給父

 進程,而進程所有的緩衝數據會

 自動寫回並關閉未關閉的文件
 
_exit()喲年來立刻終止目前進程

的執行,並把參數status返回給父

進程,並關閉未關閉的文件,但不處

理標準I/O緩衝區

(一) 理解Linux下進程的結構
  Linux下一個進程在內存裏有三部份的數據,就是“數據段”,“堆棧段”和“代碼段”,其實學過彙編
語言的人一定知道,一般的CPU象I386,都有上述三種段寄存器,以方便操作系統的運行。“代碼段”,顧名
思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那麼它們就可以使用同一
個代碼段。
  堆棧段存放的就是子程序的返回地址、子程序的參數以及程序的局部變量。而數據段則存放程序的全局
變量,常數以及動態數據分配的數據空間(比如用malloc之類的函數取得的空間)。這其中有許多細節問題,
這裏限於篇幅就不多介紹了。系統如果同時運行數個相同的程序,它們之間就不能使用同一個堆棧段和數據
段。

(二) 如何使用fork
  在Linux下產生新的進程的系統調用就是fork函數,這個函數名是英文中“分叉”的意思。爲什麼取這個
名字呢?因爲一個進程在運行中,如果使用了fork,就產生了另一個進程,於是進程就“分叉”了,所以這
個名字取得很形象。下面就看看如何具體使用fork,這段程序演示了使用fork的基本框架:

此程序從終端讀入命令並執行之,執行完成後,父進程繼續等待從終端讀入命令。熟悉DOS和WINDOWS系統
調用的朋友一定知道DOS/WINDOWS也有exec類函數,其使用方法是類似的,但DOS/WINDOWS還有spawn類函數,
因爲DOS是單任務的系統,它只能將“父進程”駐留在機器內再執行“子進程”,這就是spawn類的函數。
WIN32已經是多任務的系統了,但還保留了spawn類函數,WIN32中實現spawn函數的方法同前述UNIX中的方法
差不多,開設子進程後父進程等待子進程結束後才繼續運行。UNIX在其一開始就是多任務的系統,所以從核
心角度上講不需要spawn類函數。
  另外,有一個更簡單的執行其它程序的函數system,它是一個較高層的函數,實際上相當於在SHELL環境
下執行一條命令,而exec類函數則是低層的系統調用。

(四) Linux的進程與Win32的進程/線程有何區別
  熟悉WIN32編程的人一定知道,WIN32的進程管理方式與UNIX上有着很大區別,在UNIX裏,只有進程的概念
,但在WIN32裏卻還有一個“線程”的概念,那麼UNIX和WIN32在這裏究竟有着什麼區別呢?
  UNIX裏的fork是七十年代UNIX早期的開發者經過長期在理論和實踐上的艱苦探索後取得的成果,一方面,
它使操作系統在進程管理上付出了最小的代價,另一方面,又爲程序員提供了一個簡潔明瞭的多進程方法。
  WIN32裏的進程/線程是繼承自OS/2的。在WIN32裏,“進程”是指一個程序,而“線程”是一個“進程”
裏的一個執行“線索”。從核心上講,WIN32的多進程與UNIX並無多大的區別,在WIN32裏的線程才相當於UNIX
的進程,是一個實際正在執行的代碼。但是,WIN32裏同一個進程裏各個線程之間是共享數據段的。這纔是與
UNIX的進程最大的不同。
  下面這段程序顯示了WIN32下一個進程如何啓動一個線程:(請注意,這是個終端方式程序,沒有圖形界面)

在WIN32下,使用CreateThread函數創建線程,與UNIX不同,線程不是從創建處開始運行的,而是由
CreateThread指定一個函數,線程就從那個函數處開始運行。此程序同前面的UNIX程序一樣,由兩個線程各打
印1000條信息。threadID是子線程的線程號,另外,全局變量g是子線程與父線程共享的,這就是與UNIX最大
的不同之處。大家可以看出,WIN32的進程/線程要比UNIX複雜,在UNIX裏要實現類似WIN32的線程並不難,只
要fork以後,讓子進程調用ThreadProc函數,並且爲全局變量開設共享數據區就行了,但在WIN32下就無法實
現類似fork的功能了。所以現在WIN32下的C語言編譯器所提供的庫函數雖然已經能兼容大多數UNIX的庫函數,
但卻仍無法實現fork。
  對於多任務系統,共享數據區是必要的,但也是一個容易引起混亂的問題,在WIN32下,一個程序員很容
易忘記線程之間的數據是共享的這一情況,一個線程修改過一個變量後,另一個線程卻又修改了它,結果引
起程序出問題。但在UNIX下,由於變量本來並不共享,而由程序員來顯式地指定要共享的數據,使程序變得
更清晰與安全。
  Linux還有自己的一個函數叫clone,這個函數是其它UNIX所沒有的,而且通常的Linux也並不提供此函數
(要使用此函數需自己重新編譯內核,並設置CLONE_ACTUALLY_WORKS_OK選項),clone函數提供了更多的創建
新進程的功能,包括象完全共享數據段這樣的功能。
  至於WIN32的“進程”概念,其含義則是“應用程序”,也就是相當於UNIX下的exec了。
---------------------------------------------------------------------------
linux多進程編程 
 在linux中,運行的一個進程,會佔去linux的三個地方,代碼區,堆棧區和數據區.如果同時運行多個相同的程序,他們就會使用相同的代碼區,代碼區中存放的就程序的代碼,但是數據區和堆棧區分別存放的是程序的數據,全局變量和局部變量,因此即使是相同的程序,也不可同時使用相同的數據和堆棧區.

可以關注下程序的運行結果,以及輸出的順序,並理解爲什麼是這種輸出結果.

fork()函數:

程序調用fork()函數,系統就爲新的進程準備了堆棧區,數據區和代碼區.系統先讓fork()出的進程和原先的進程使用同一個代碼區.那麼數據區和堆棧區就不能共享了,因此係統會複製一份完全一抹一樣的給fork()出的那個進程.這樣父進程的所有數據就給了子進程.這樣子進程開始運行時,雖然複製了父進程的數據和堆棧,但是數據和堆棧已經分開了,相互之間已經沒有影響了.對於父進程,fork()函數返回了子進程的進程號,對於子進程,fork()函數則返回0,因此根據fork()函數的返回值就能知道,程序現在處的位置是在子進程中還是在父進程中.

也就是傳說中的,一個函數具有兩個返回值,就是指fork()函數.

那麼引來一個問題...如何在一個進程中讓另外一個進程啓動呢..?

在linux中,基本上使用exec類的函數,但是exec類函數的使用有一個特點就是:一旦你使用exec類的函數,原先的進程就廢掉了,因爲代碼段會被新的進程佔據,數據區和堆棧區也會被廢掉,併產生新的數據區和堆棧區供新的進程使用.唯一沒有變的就是進程號,實際上,只有PID是一樣的,其他的東西已經物是人非了.對系統而言,還是同一個進程,因爲系統只認進程號,而對編程而言,已經完全是一個新的進程了.

那麼...又來了...

如果你想繼續原先的進程運行,並且同時啓動新的進程..要怎麼辦..?

看上面我給的代碼...對了..就是利用fork()函數和exec類的函數搭配使用.

fork()出一個和父進程完全一抹一樣的進程,然後再使用exec類函數來啓動新的進程,這樣,原先的進程也在運行,新的進程也在運行了.只不過,區別是,現在的關係變成了父進程和子進程的關係了,而不是原來的同一個進程的關係.

發佈了9 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章