進程控制原語

#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 函數,建立子進程。數組

函數原型:app

 

 

關於其返回值:函數

 

 

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

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

——————————————————》》》分割線指針

可是,子進程建立出來若是所作的任務和父進程徹底一致,那麼也就沒有必要建立子進程的意義了。一般而言,子進程被建立,是要作與父進程不同的事情。code

#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族中的一個函數。blog

 

當進程調用了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那樣永遠等下去。

相關文章
相關標籤/搜索