fork,vfork
#include <unistd.h>;
#include <sys/types.h>;
main ()
{
pid_t pid;
pid=fork();
if (pid < 0)
printf("error in fork!");
else if (pid == 0)
printf("i am the child process, my process id is %d\n",getpid());
else
printf("i am the parent process, my process id is %d\n",getpid());
}
要搞清楚fork的執行過程,就必須先講清楚操作系統中的“進程(process)”概念。一個進程,主要包含三個元素:
o. 一個可以執行的程序;
o. 和該進程相關聯的全部數據(包括變量,內存空間,緩沖區等等);
o. 程序的執行上下文(execution context)。
不妨簡單理解為,一個進程表示的,就是一個可執行程序的一次執行過程中的一個狀態。操作系統對進程的管理,典型的情況,是通過進程表完成的。進程表中的每 一個表項,記錄的是當前操作系統中一個進程的情況。對于單 CPU的情況而言,每一特定時刻只有一個進程占用 CPU,但是系統中可能同時存在多個活動的(等待執行或繼續執行的)進程。
一個稱為“程序計數器(program counter, pc)”的寄存器,指出當前占用 CPU的進程要執行的下一條指令的位置。
當分給某個進程的 CPU時間已經用完,操作系統將該進程相關的寄存器的值,保存到該進程在進程表中對應的表項里面;把將要接替這個進程占用 CPU的那個進程的上下文,從進程表中讀出,并更新相應的寄存器(這個過程稱為“上下文交換(process context switch)”,實際的上下文交換需要涉及到更多的數據,那和fork無關,不再多說,主要要記住程序寄存器pc指出程序當前已經執行到哪里,是進程上 下文的重要內容,換出 CPU的進程要保存這個寄存器的值,換入CPU的進程,也要根據進程表中保存的本進程執行上下文信息,更新這個寄存器)。
好了,有這些概念打底,可以說fork了。當你的程序執行到下面的語句:
pid=fork();
操作系統創建一個新的進程(子進程),并且在進程表中相應為它建立一個新的表項。新進程和原有進程的可執行程序是同一個程序;上下文和數據,絕大部分就是 原進程(父進程)的拷貝,但它們是兩個相互獨立的進程!此時程序寄存器pc,在父、子進程的上下文中都聲稱,這個進程目前執行到fork調用即將返回(此 時子進程不占有CPU,子進程的pc不是真正保存在寄存器中,而是作為進程上下文保存在進程表中的對應表項內)。問題是怎么返回,在父子進程中就分道揚 鑣。
父進程繼續執行,操作系統對fork的實現,使這個調用在父進程中返回剛剛創建的子進程的pid(一個正整數),所以下面的if語句中pid<0, pid==0的兩個分支都不會執行。所以輸出i am the parent process...
子進程在之后的某個時候得到調度,它的上下文被換入,占據 CPU,操作系統對fork的實現,使得子進程中fork調用返回0。所以在這個進程(注意這不是父進程了哦,雖然是同一個程序,但是這是同一個程序的另 外一次執行,在操作系統中這次執行是由另外一個進程表示的,從執行的角度說和父進程相互獨立)中pid=0。這個進程繼續執行的過程中,if語句中 pid<0不滿足,但是pid==0是true。所以輸出i am the child process...
我想你比較困惑的就是,為什么看上去程序中互斥的兩個分支都被執行了。在一個程序的一次執行中,這當然是不可能的;但是你看到的兩行輸出是來自兩個進程,這兩個進程來自同一個程序的兩次執行。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
globa=6;
int main(void)
{
var=88;
pid_t result;
result = fork();
//result = vfork();
if(result == -1)
{
perror("fork");
exit;
}
else if(result == 0)
{
globa++;
var++;
printf("The return value is %d\n In child process!!\n My PID is %d\n",result,getpid());
}
else
{
printf("The return value is %d\n In father process!!\n My PID is %d\n",result,getpid());
}
printf("PID=%d,globa=%d,var=%d",getpid(),globa,var);
}
程序運行結果如下:
(fork()運行結果)
The return value is 0
In child process!!
My PID is 3736
PID=3736,globa=7,var=89
The return value is 3736
In father process!!
My PID is 3735
PID=3735,globa=6,var=88
(vfork()運行結果)
The return value is 0
In child process!!
My PID is 3736
PID=3736,globa=7,var=89
The return value is 3736
In father process!!
My PID is 3735
PID=3735,globa=7,var=89
分析:首先分析fork與vfork函數的運行機制,拿fork為例,fork()并不是進程切換,而是復制一個當前進程。當使用pid= fork()時,其實是創建了兩個進程,這兩個進程有著相同的內容,例如變量的值,空間配,特別是正在執行的語句等等都相同,但這些內容卻在兩個獨立的內 存空間中。因此當執行上述代碼時,便相當于同一段代碼在兩個進程中執行,所有就出現了兩個結果,一個是子進程的信息,一個是父進程的信息。再看globa 和var這兩個變量,由于是兩個獨立的進程,因此當各自執行代碼時,變量也不會互相受到影響,所以在子進程中的globa和var均發生了變化,而在父進 程中卻沒有變化。
其次分析一下,fork()和vfork()的區別:vfork采用寫時拷貝技術(write-on-copy),父進程與子進程享用同一個內存空間,因 此,程序中的變量其實也就是父子進程的公共變量,所以,當其中一個進程中的變量值發生改變時,另一個進程中的變量值肯定也跟著發生變化,另外,兩個函數的 區別還在于vfork用于創建一個新進程,而該新進程的目的是exec一個新進程,vfork和fork一樣都創建一個子進程,但是它并不將父進程的地址 空間完全復制到子進程中,因為子進程會立即調用exec,于是也就不會存放該地址空間。不過在子進程中調用exec或exit之前,他在父進程的空間中運 行。vfork保證子進程先運行,在她調用exec exit之后父進程才可能被調度運行。如果在調用這兩個函數之前子進程依賴于父進程的進一步動作,則會導致死鎖。 用fork函數創建子進程后,子進程往往要調用一種exec函數以執行另一個程序,當進程調用一種exec函數時,該進程完全由新程序代換,而新程序則從 其main函數開始執行,因為調用exec并不創建新進程,所以前后的進程id 并未改變,exec只是用另一個新程序替換了當前進程的正文,數據,堆和棧段。
wait(等待子進程中斷或結束)
相關函數 waitpid,fork
表頭文件
#include
#include
定義函數 pid_t wait (int * status);
函數說明
wait()會暫時停止目前進程的執行,直到有信號來到或子進程結
束。如果在調用wait()時子進程已經結束,則wait()會立即返
回子進程結束狀態值。子進程的結束狀態值會由參數status 返回,
而子進程的進程識別碼也會一快返回。如果不在意結束狀態值,則
參數status 可以設成NULL。子進程的結束狀態值請參考waitpid()。
返回值
如果執行成功則返回子進程識別碼(PID),如果有錯誤發生則返回
-1。失敗原因存于errno 中。
附加說明
范例
#include
#include
#include
#include
main()
{
pid_t pid;
int status,i;
if(fork()= =0){
printf(“This is the child process .pid =%d\n”,getpid());
exit(5);
}else{
sleep(1);
printf(“This is the parent process ,wait for child...\n”;
pid=wait(&status);
i=WEXITSTATUS(status);
printf(“child’s pid =%d .exit status=^d\n”,pid,i);
}
}
執行
This is the child process.pid=1501
This is the parent process .wait for child...
child’s pid =1501,exit status =5
waitpid(等待子進程中斷或結束)
相關函數 wait,fork
表頭文件
#include
#include
定義函數 pid_t waitpid(pid_t pid,int * status,int options);
函數說明
waitpid()會暫時停止目前進程的執行,直到有信號來到或子進程
結束。如果在調用wait()時子進程已經結束,則wait()會立即
返回子進程結束狀態值。子進程的結束狀態值會由參數status 返回,
而子進程的進程識別碼也會一快返回。如果不在意結束狀態值,則
參數status 可以設成NULL。參數pid 為欲等待的子進程識別碼,
其他數值意義如下:
pid0 等待任何子進程識別碼為pid 的子進程。
參數option 可以為0 或下面的OR 組合:
WNOHANG 如果沒有任何已經結束的子進程則馬上返回,不予以
等待。
WUNTRACED 如果子進程進入暫停執行情況則馬上返回,但結束
狀態不予以理會。
子進程的結束狀態返回后存于status,底下有幾個宏可判別結束情
況:
WIFEXITED(status)如果子進程正常結束則為非0 值。
WEXITSTATUS(status)取得子進程exit()返回的結束代碼,一
般會先用WIFEXITED 來判斷是否正常結束才能使用此宏。
WIFSIGNALED(status)如果子進程是因為信號而結束則此宏值為
真
WTERMSIG(status) 取得子進程因信號而中止的信號代碼,一般
會先用WIFSIGNALED 來判斷后才使用此宏。
WIFSTOPPED(status) 如果子進程處于暫停執行情況則此宏值為
真。一般只有使用WUNTRACED 時才會有此情況。
WSTOPSIG(status) 取得引發子進程暫停的信號代碼,一般會先
用WIFSTOPPED 來判斷后才使用此宏。
返回值
如果執行成功則返回子進程識別碼(PID),如果有錯誤發生則返回
-1。失敗原因存于errno 中。
范例
參考wait()。
inux的system () 函數詳解
system(執行shell 命令)
相關函數
fork,execve,waitpid,popen
表頭文件
#i nclude<stdlib.h>
定義函數
int system(const char * string);
函數說明
system()會調用fork()產生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令,此命>令執行完后隨即返回原調用的進程。在調用system()期間SIGCHLD 信號會被暫時擱置,SIGINT和SIGQUIT 信號則會被忽略。
返回值
=-1:出現錯誤
=0:調用成功但是沒有出現子進程
>0:成功退出的子進程的id
如果system()在調用/bin/sh時失敗則返回127,其他失敗原因返回-1。若參數string為空指針(NULL),則返回非零值>。 如果system()調用成功則最后會返回執行shell命令后的返回值,但是此返回值也有可能為 system()調用/bin/sh失敗所返回的127,因此最好能再檢查errno 來確認執行成功。
附加說明
在編寫具有SUID/SGID權限的程序時請勿使用system(),system()會繼承環境變量,通過環境變量可能會造成系統安全的問題。
范例
#i nclude<stdlib.h>
main()
{
system(“ls -al /etc/passwd /etc/shadow”);
}
執行結果:
-rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
-r--------- 1 root root 572 Sep 2 15 :34 /etc/shado
例2:
char tmp[];
sprintf(tmp,"/bin/mount -t vfat %s /mnt/usb",dev);
system(tmp);
其中dev是/dev/sda1。
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/alentam/archive/2008/04/09/2270763.aspx