[linux c] fork 等函數編寫執行命令實驗

相關知識點

一、exit程序退出

    exit(0);表示 非正常結束程序
    exit(0);表示在正常下結束程序 

    exit(1);表示強制結束程序

    _exit()終止程序時,不關閉任何文件,不清除任何緩衝器,也不調用任何終止函數  
     abort()程序終止運行,不清除文件,返回到調用過程,一般用在防止程序失控,

二、fork

fork會自己創建一個內存空間,複製 父進程的內存空間內容到這個內存空間

vfork 則是共享父進程的內存空間(共享數據段)


實驗過程

實驗一

使用 fork、exit 和 exec 系統調用編寫多進程程序
1.fork

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int global=22;
char buf[]="the test content!\n";
int main(void)
{
int test=0,stat;
pid_t pid;
if(write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf))
{ perror("write error!"); }
printf(" fork test!\n");
/* fork */
pid = fork();
 /*we should check the error*/
if (pid == -1)
{
perror("fork");
exit(0);
}
else if (pid == 0)
{
global++;
 test++;
printf("global=%d test=%d Child,my PID is %d\n",global,test,getpid());
exit(0);
}
/*else be the parent*/
global+=2;
test+=2;
printf("global=%d test=%d Parent,my PID is %d\n",global,test,getpid());
exit(0);
//printf("global=%d test=%d Parent,my PID is %d",global,test,getpid());
//_exit(0);
}


編譯執行,並分析結果:
[root@localhost root]# ./test
the test content!
fork test!
global=23 test=1 Child,my PID is 2751
global=24 test=2 Parent,my PID is 2750
可以看出父子進程打印出了各自的進程號和對應變量的值,
 顯然 global 和 test 在父子進程間是獨立的,
其各自的操作不會對對方的值有影響。將上述代碼最後的兩行代碼替換爲註釋掉的_exit(0)行,重新編譯,
查看結果,解釋原因:
[root@localhost root]# ./test


the test content!
fork test!
global=23 test=1 Child,my PID is 2771
父進程的信息沒有打印出來,其原因是:_exit()函數直接使進程停止運行,清除其使用的內存空間,
並銷燬其在內核中的各種數據結構;而 exit()函數則在這些基礎上作了一些包裝,在執行退出之前加了若干
道工序。exit()函數在調用 exit 系統調用之前要檢查文件的打開情況,把文件緩衝區中的內容寫回文件,即
會 "清理 I/O 緩衝"。若將上述_exit(0)改爲 exit(0),則肯定會有打印。另外,需要注意換行符\n 會引起 IO
的清理操作,若下面的語句 printf("global=%d test%d Parent,my PID is %d",global,test,getpid()); 加上\n,則調
用_exit(0)的結果和調用 exit(0)的結果是一樣的。

實驗二

2.vfork 的特點
將上述代碼的 pid = fork();
 改爲 pid = vfork();編譯後運行結果如下:
[root@localhost root]# ./test
the test content!
fork test!
global=23 test=1 Child,my PID is 2849
global=25 test=3 Parent,my PID is 2848
可以看出,vfork 與 fork 區別在於共享的資源不一樣,vfork 調用後,子進程先對 global 和 test 加 1,
父進程運行時,在其基礎之上再加 2,得到上述運行結果。即 vfork 的特點是:在調用 execv 或者 exit 前子
進程對變量的修改會影響到父進程,即他們是共享的;
特別注意:父進程等待子進程調用 execv 或 exit 才繼續執行。則若子進程依賴父進程的進一步動作時,
父進程又必須阻塞到子進程調用 execv 或者 exit 纔會往下執行,此時就會造成“死鎖”。讀者可自己設計
測試一下這種“死鎖”狀態。



 fork 等函數編寫執行命令實驗
10.1 execv 函數族的使用
注意點:調用 execv 後,程序不再返回!在上述代碼基礎上,在子進程的退出代碼前加入如下代碼:
printf("global=%d test%d Child,my PID is %d\n",global,test,getpid());
if(execl("/bin/ps","ps","-au",NULL)<0)
perror("execl error!");
printf("this message will never be printed!\n");
exit(0);

編譯運行後結果爲:
[root@localhost root]# ./test
the test content!
fork test!
global=23 test=1 Child,my PID is 2909
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2719 0.0 0.6 4360 1032 pts/1 S 23:14 0:00 /bin/bash
root 2908 0.0 0.1 1340 276 pts/1 R 23:38 0:00 ./test
root 2909 0.0 0.4 2684 736 pts/1 R 23:38 0:00 ps -au
global=25 test=3 Parent,my PID is 2908


10.2waitpid
waitpid 的作用是等待子進程退出並回收其資源,同時可以通過 WIFEXITED 等宏調用可以檢測
子進程退出的狀態。在第一個示例 fork 使用的代碼基礎上進行修改,添加檢測進程退出狀態的子函數,
參考代碼如下:
void exit_check(int stat)
{
if (WIFEXITED(stat))
{ printf("exit normally!the return code is: %d \n",WEXITSTATUS(stat)); }
else if (WIFSIGNALED(stat))
{ printf("exit abnormally!the signal code is: %d \n",WTERMSIG(stat)); }
}

在父進程處理 global 和 test 變量前加入如下代碼:
if (waitpid(pid,&stat,0) == pid)
{ exit_check(stat); } // the status of exit check

編譯運行後結果爲:
[root@localhost root]# ./test
the test content!
fork test!
global=23 test=1 Child,my PID is 2973
exit normally!the return code is: 0
global=24 test=2 Parent,my PID is 2972

可以看出父進程回收了退出的子進程的資源,檢測到了它的退出狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章