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