UNIX環境編程學習筆記(18)——進程管理之進程控制三部曲

lienhua34
2014-10-05html

1 進程控制三部曲概述

UNIX 系統提供了 fork、exec、exit 和 wait 等基本的進程控制原語。經過這些進程控制原語,咱們便可完成對進程建立、執行和終止等基本操做。進程的控制能夠劃分爲三部曲,函數

• 第一部:fork 建立新進程。學習

• 第二部:exec 執行新程序。spa

• 第三部:exit 和 wait 處理終止和等待終止。指針

2 第一部:fork 建立新進程

在一個現有的進程中,咱們能夠經過調用 fork 函數來建立一個新進程,code

#include <unistd.h>
pid_t fork(void);
返回值:子進程中返回0,父進程中返回子進程ID,出錯則返回-1htm

由 fork 建立的新進程被稱爲子進程。fork 函數調用一次,但返回兩次。兩次返回的惟一區別是:子進程返回值爲 0,而父進程的返回值是新子進程的進程 ID。由於 UNIX 系統沒有提供一個函數以獲取某個進程的全部子進程 ID,因此父進程能夠經過 fork 函數的返回值獲取其子進程的進程 ID,並進行後續的處理。而在子進程中,能夠經過調用 getppid 函數獲取其父進程的進程 ID。blog

fork 函數返回以後,子進程和父進程都各自繼續執行 fork 調用以後的指令。子進程是父進程的副本。例如,子進程得到了父進程數據空間、堆和棧的副本。可是,父子進程共享正文段。進程

例子:內存

下面程序調用 fork 建立一個新進程,在父子進程中都分別打印當前進程 ID 和父進程 ID。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int
main(void)
{
    pid_t pid;
    printf("before fork\n");
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
     } else if (pid == 0) {
        printf("in child process, process ID: %d, parent process ID: %d\n", getpid(),  getppid());
    } else {
        printf("in parent process, process ID: %d, child process ID: %d\n", getpid(), pid);
        sleep(1);
    }
    exit(0);
}

編譯該程序,生成 forkdemo 文件,而後執行該文件,

lienhua34:demo$ gcc -o forkdemo forkdemo.c
lienhua34:demo$ ./forkdemo
before fork
in parent process, process ID: 3499, child process ID: 3500
in child process, process ID: 3500, parent process ID: 3499

第二部:exec 執行新程序

用 fork 函數建立子進程後,子進程每每要調用一種 exec 函數以執行另外一個程序。當進程調用一種 exec 函數時,該進程執行的程序徹底替換爲新程序,而新程序則從其 main 函數開始執行。調用 exec 並無建立新進程,因此進程 ID 沒有改變,exec 只是用一個新的程序替換了當前進程的正文、數據、堆和棧段。

UNIX 提供了 6 種不一樣的 exec 函數可供使用。咱們在這裏只說明其中的一種,

#include <unistd.h>
int execv(const char *pathname, char *const argv[]);
返回值:若出錯則返回-1,若成功則沒有返回值

其中 pathname 是進程要執行的新程序文件的路徑,而 argv 參數則是要傳遞給新程序的參數列表。

例子:

咱們有一個 sayhello.c 的程序文件,其代碼以下,

#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
    printf("Hello World! process ID: %d\n", getpid());
    exit(0);
}

編譯 sayhello.c 程序,生成執行文件 sayhello,

lienhua34:demo$ gcc -o sayhello sayhello.c
lienhua34:demo$ pwd
/home/lienhua34/program/c/apue/ch08/demo
lienhua34:demo$ ./sayhello
Hello World! process ID: 3545

在 execdemo.c 程序文件中,咱們經過 fork 建立新進程,而後在子進程中調用 execv 函數執行 sayhello 文件,其代碼以下,

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
char sayhelloPath[] = "/home/lienhua34/program/c/apue/ch08/demo/sayhello";
int
main(void)
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
    } else if (pid == 0) {
        printf("in child process, process ID: %d, parent process ID: %d\n", getpid(), getppid());
        if (execv(sayhelloPath, NULL) < 0) {
            printf("execv error: %s\n", strerror(errno));
            exit(-1);
        }
    } else {
        printf("in parent process, process ID: %d, child process ID: %d\n", getpid(), pid);
        sleep(2);
    }
    exit(0);
}

編譯 execdemo.c 文件,生成並執行 execdemo 文件,

lienhua34:demo$ gcc -o execdemo execdemo.c
lienhua34:demo$ ./execdemo
in parent process, process ID: 3561, child process ID: 3562
in child process, process ID: 3562, parent process ID: 3561
Hello World! process ID: 3562

從上面的運行結果能夠看出,子進程(ID:3562)正常執行 sayhello 文件。

4 第三部:exit 和 wait 處理終止和等待終止

exit 函數提供了進程終止的功能,在「進程終止」一文中,咱們已經學習了 exit 函數的基本知識。咱們這裏補充一下關於進程的終止狀態。在學習 exit 函數時,咱們已經知道 exit 函數的參數做爲程序的退出狀態(exit status)。在進程終止時,內核會將程序的退出狀態做爲進程的終止狀態(termination status)。在進程異常終止的時候,內核會產生一個指示其異常終止緣由的終止狀態。不管進程是正常終止仍是異常終止,該終止進程的父進程均可以經過 wait 或 waitpid 函數獲取其終止狀態。

在前面兩節中的 forkdemo.c 和 execdemo.c 程序中,fork 調用以後的父進程都調用了 sleep 函數休眠一下。這是由於,在調用 fork 函數以後是父進程先執行仍是子進程先執行是不肯定的。因而,咱們在父進程中調用sleep 休眠一段時間,讓子進程先執行結束(注:這樣子也不能確保)。

咱們能夠在父進程中調用 wait 來等待子進程結束,

#include <sys/wait.h>
pid_t wait(int *statloc);
返回值:若成功則返回進程ID,若出錯則返回-1

statloc 參數是一個整型指針。若是 statloc 不是一個空指針,則終止進程的終止狀態就存儲在該指針指向的內存單元中。若是不關心終止狀態,則能夠將參數設置爲 NULL。

進程調用 wait 會致使該調用者阻塞,直到某個子進程終止。若是調用wait 函數時,調用進程沒有任何子進程則當即出錯返回。

例子:

下面程序在上一節的 execdemo.c 基礎上,將 fork 以後父進程的 sleep語句替換成 wait 來等待子進程的結束。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
char sayhelloPath[] = "/home/lienhua34/program/c/apue/ch08/demo/sayhello";
int
main(void)
{
    pid_t pid;
    int waitres;
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(-1);
    } else if (pid == 0) {
        printf("in child process, process ID: %d, parent process ID: %d\n", getpid(), getppid());
        if (execv(sayhelloPath, NULL) < 0) {
            printf("execv error: %s\n", strerror(errno));
            exit(-1);
       }
    } else {
        printf("in parent process, process ID: %d, child process ID: %d\n", getpid(), pid);
        if ((waitres = wait(NULL)) < 0) {
            printf("wait error: %s\n", strerror(errno));
            exit(-1);
        } else {
            printf("termination child process: %d\n", waitres);
        }
    }
    exit(0);
}

編譯該程序,生成並執行 execdemo 文件,

lienhua34:demo$ gcc -o execdemo execdemo.c
lienhua34:demo$ ./execdemo
in parent process, process ID: 3795, child process ID: 3796
in child process, process ID: 3796, parent process ID: 3795
Hello World! process ID: 3796
termination child process: 3796

(done)

相關文章
相關標籤/搜索