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