進程控制原語


#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>                                                           
#include<stdlib.h>

int main(void)
{
    pid_t fpid; //fpid表示fork函數返回的值
    int count=0;
    fpid=fork(); 
    if (fpid < 0) 
        printf("error in fork!\n"); 
    else if (fpid == 0) {
        printf("i am the child process, my process id is %d\n",getpid()); 
        printf(" my parent process id is %d\n",getppid()); 
        count++;
    }
    else {
        printf("i am the parent process, my process id is %d\n",getpid()); 
        count++;
        

    }
    printf("count 統計結果是: %d\n",count);
    while(1);//無限阻塞
    return 0;
}

 

fork 函數,創建子進程。

函數原型:

 

 

關於其返回值:

 

 

fork函數一次調用,兩次返回。子進程中返回0,父進程中,返回子進程的ID。如果fork失敗,返回-1.並且不會創建子進程,同時錯誤代碼errno會被設置。

fork的讀時共享,寫時復制機制。子進程擁有和父進程一樣的0-3G用戶空間,但是3-4G內核空間中PCB(進程控制塊)的進程ID號並不相同。子進程和父進程有如此多相同的地方,如果僅僅是讀取0-3G用戶空間的數據,則沒有必要復制一份父進程的數據到內存,但如果需要寫入數據,就必須開辟新的空間了。

——————————————————》》》分割線

但是,子進程創建出來如果所做的任務和父進程完全一致,那么也就沒有必要創建子進程的意義了。通常而言,子進程被創建,是要做與父進程不一樣的事情。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>                                                           
#include<stdlib.h>

int main(void)
{
    pid_t fpid; 
    int count=0;
    fpid=fork(); 
    if (fpid < 0) 
        printf("error in fork!\n"); 
    else if (fpid == 0) {
        char *const argv[]={"ls","-l",NULL};
        printf("i am a child process\n");
        execvp("ls",argv);
        printf("where is my process?\n");
    }
    else {
        printf("i am a parent process\n"); 

    }
    while(1);
    return 0;
}

execvp:屬於exec族中的一個函數。

 

當進程調用了exec族的某一函數之后,該進程執行的程序被替換成全新的程序,新程序從其main函數開始執行。由於調用exec函數不會創建新的進程,所以替換前后的進程ID並不會改變。exec只是用磁盤上的一個新程序替換了當前進程的正文段、數據段、堆段和棧段。

在上面的程序中,使用了ls –l這個指令,可以發現該指令確實正確執行了,但是顯示的列表中,a.out沒有顏色信息。其次,在char *const argv[]={"ls","-l",NULL};中,第一個“ls”字符串只有一個占位符的作用,該字符串無論是什么都不會影響程序的運行結果,因為在execvp("ls",argv);中已經指定了ls程序,在argv指針數組中的argv[0]只用作占位符。哪怕將其改為”hehe”:

程序依然正常運行,exec函數會從argv[1]開始,直到NULL結束。

現在新建一個upper.c文件:

/* upper.c */
#include<stdio.h>
#include <ctype.h>

int main(void)
{
    in ch;
    while((ch=getchar())!=EOF)
        putchar(toupper(ch));//小寫轉大寫
    return 0;
}

把之前的test.c改成:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>                                                           
#include<stdlib.h>

int main(void)
{
    pid_t fpid; 
    int count=0;
    fpid=fork(); 
    if (fpid < 0) 
        printf("error in fork!\n"); 
    else if (fpid == 0) {
        char *const argv[]={"upper",NULL};
        printf("i am a child process\n");
        execv("./app",argv);
        printf("where is my process?\n");
    }
    else {
        printf("i am a parent process\n"); 

    }
    while(1);
    return 0;
}

先編譯upper.c生成app,然后編譯test.c生成a.out,運行a.out:

exec函數運行之后,只有出錯才會返回,這也就意味着,exec函數后面的語句不會被執行。同樣,這里的char *const argv[]={"upper",NULL};中的“upper”字符串也可以是其他任何字符串(即使為NULL也行,但通常使用能代表其含義的名稱)。

wait和waitpid函數:

僵屍進程: 子進程退出,父進程沒有回收子進程資源(PCB),則子進程變成僵屍進程。

孤兒進程: 父進程先於子進程結束,則子進程成為孤兒進程,子進程的父進程成為1號進程init進程,稱為init進程領養孤兒進程。

一個進程在終止時會關閉所有文件描述符,釋放在用戶空間分配的內存,但它的PCB還保留着,內核在其中保存了一些信息:如果是正常終止則保存着退出狀態,如果是異常終止則保存着導致該進程終止的信號是哪個。這個進程的父進程可以調用wait或waitpid獲取這些信息,並徹底清除掉這個進程。我們知道一個進程的退出狀態可以在Shell中用特殊變量$?查看(echo $?),因為Shell是它的父進程,當它終止時Shell調用wait或waitpid得到它的退出狀態同時徹底清除掉這個進程。如果一個進程已經終止,但是它的父進程尚未調用wait或waitpid對它進行清理,這時的進程狀態稱為僵屍(Zombie)進程。任何進程在剛終止時都是僵屍進程,正常情況下,僵屍進程都立刻被父進程清理了。

目前,只用知道,wait和waitpid的基本用法即可,至於到底怎么應用在程序中,可以暫時不理會。

如果使用了WNOHANG參數調用waitpid,即使沒有子進程退出,它也會立即返回,不會像wait那樣永遠等下去。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM