鍍金池/ 教程/ Linux/ 系統(tǒng)調(diào)用:進程控制
Linux 學(xué)習(xí)記錄--開機掛載錯誤
日志系統(tǒng)
數(shù)據(jù)流重定向
內(nèi)存交換空間的構(gòu)建
文件系統(tǒng)簡介
Linux 學(xué)習(xí)記錄--軟件安裝 RPM|SRPM|YUM
文件特殊權(quán)限
目錄配置 FHS
文件內(nèi)容查閱
Boot Loader
文件壓縮
Linux 學(xué)習(xí)記錄--文件權(quán)限
Linux 命令縮寫
命令與文件的查詢
文件|目錄的默認權(quán)限與隱藏權(quán)限
shell script
服務(wù)
Linux 學(xué)習(xí)記錄--程序編譯與函數(shù)庫
正則表達式與其應(yīng)用
關(guān)機相關(guān)指令
shell
vim 與 vi 常用命令
系統(tǒng)調(diào)用:進程控制
文件系統(tǒng)簡單操作
磁盤掛載與卸載
有名管道通訊
磁盤分區(qū),格式化與檢驗
工作管理與進程管理
匿名管道通訊
Linux 學(xué)習(xí)記錄--啟動流程
文件與目錄管理
管道命令
命名別名與歷史命令
文件備份|還原
shell變量
Linux 學(xué)習(xí)記錄--ACL 權(quán)限控制
內(nèi)核|內(nèi)核模塊編譯
文件管理相關(guān)系統(tǒng)編程

系統(tǒng)調(diào)用:進程控制

fork 系統(tǒng)調(diào)用

函數(shù)作用:創(chuàng)建一個子進程
形式:pid_tfork(void);
pid_t vfork(void);
說明:使用 vfork 創(chuàng)子進程時,不會進程父進程的上下文
返回值:[返回值=-1]子進程創(chuàng)建失敗
[返回值=0]子進程創(chuàng)建成功
[返回值>0]對父進程返回子進程 PID

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
    pid_t id = fork();
    if (id < 0) {
        perror("子進程創(chuàng)建失敗!");
    } else {
        if (id == 0) {
            printf("子進程工作:PID=%d,PPID=%d\n", getpid(), getppid());
        }else
        {
            printf("父進程工作:PID=%d,PPID=%d,子進程PID=%d\n", getpid(), getppid(),id);
            sleep(5)
        }
    }
}

控制臺輸出
父進程工作:PID=3173,PPID=2432,子進程 PID=3176
子進程工作:PID=3176,PPID=3173

exit 系統(tǒng)調(diào)用

函數(shù)作用:終止發(fā)出調(diào)用的進程
形式:voidexit(int status);
說明

  1. exit 返回信息可由 wait 系統(tǒng)函數(shù)獲得
  2. 如果父進程先退出子進程的關(guān)系被轉(zhuǎn)到 init 進程下
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
    pid_t id = fork();
    if (id < 0) {
        perror("子進程創(chuàng)建失敗!");
    } else {
        if (id == 0) {
            printf("子進程工作:PID=%d,PPID=%d\n", getpid(), getppid());
            sleep(20);
            printf("此時子進程:PID=%d,PPID=%d\n", getpid(), getppid());
        }else
        {
            printf("父進程工作:PID=%d,PPID=%d,子進程PID=%d\n", getpid(), getppid(),id);
            sleep(5);
            exit(3);
        }
    }
    return 0;
}

控制臺輸出
父進程工作:PID=3068,PPID=2432,子進程 PID=3071
子進程工作:PID=3071,PPID=3068
此時子進程:PID=3071,PPID=1

wait 系統(tǒng)調(diào)用

函數(shù)作用:父進程與子進程同步,父進程調(diào)用后。進入睡眠狀態(tài),直到子進程結(jié)束或者父進程在被其他進程終止,
形式:pid_twait(int status)
pid_t waitpid(pid_t pid ,int
status,int option)
參數(shù):statusè exit 是設(shè)置的代碼
pid è 進程號
option: WNOHANG|WUNTRACED

WNOHANG:,即使沒有子進程退出,它也會立即返回,不會像 wait 那樣永遠等下去.
WUNTRACED:子進程進入暫停則馬上返回,但結(jié)束狀態(tài)不予以理會.
返回值:如果成功等待子進程結(jié)束,則返回子進程 PID。后者為-1

用來檢查子進程返回狀態(tài)的宏
WIFEXITED 這個宏用來指出子進程是否為正常退出的,如果是,它會返回一個非零值.
WEXITSTATUS 當(dāng) WIFEXITED 返回非零值時,我們可以用這個宏來提取子進程的返回值

wait 函數(shù)使用

#include <sys/types.h>
#include <sys/uio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    pid_t cid;
    cid = fork();
    if (cid < 0) {
        perror("子進程創(chuàng)建失?。?);
    } else {
        if (cid == 0) {
            printf("子進程工作\n");
            printf("子進程PID=%d,PPID=%d\n", getpid(),getppid());
            //sleep(20); //1
        } else {
            //wait(NULL);//2
            //sleep(20);//3
            printf("父進程工作\n");
            printf("父進程PID=%d,PPID=%d\n", getpid(),getppid());
        }
    }
    return 0;
}

針對上述代碼作以下分析:
1. 當(dāng)子進程退出時,如果父進程沒有 wait 進行回收資源,子進程就會一直變?yōu)榻┦M程(Z)直到父進程退出
作法:
打開3處注釋后執(zhí)行程序,查看進程狀態(tài),如下
[root@localhostDebug]# ps -C Process -o pid,ppid,stat,cmd
PID PPID STAT CMD
12233 11563S /root/workspace/Process/Debug/Process
12238 12233Z [Process]
=>可以看到子進程此時的狀態(tài)時 Z(僵尸進程)

控制臺輸出如下
子進程工作
子進程 PID=12238,PPID=12233
(20S后…..)
父進程工作
父進程 PID=12233,PPID=11563

2. 使用 wait 進行進程同步,父進程直到子進程退出,wait 才會結(jié)束等待
作法:
打開1,2處注釋后執(zhí)行程序,查看進程狀態(tài),如下
[root@ Debug8$] ps -C Process -o pid,ppid,stat,cmd
PID PPID STAT CMD
3425 2432 S /root/workspace/Process/Debug/Process
3430 3425 S /root/workspace/Process/Debug/Process
=>父進程與子進程都處于 sleep 狀態(tài)

控制臺輸出如下
子進程工作
子進程 PID=3430,PPID=3425
(20S后…..)
父進程工作
父進程 PID=3425,PPID=2432

3. 使用 wait 進行進程同步,子進程退出后,父進程結(jié)束 wait 等待,同時清空子進程信息,此時子進程不再是僵尸進程
作法:
打開2,3處注釋后執(zhí)行程序,查看進程狀態(tài),如下

[root@localhostDebug]# ps -C Process -o pid,ppid,stat,cmd
PID PPID STAT CMD
1250611563 S /root/workspace/Process/Debug/**Process**
=>可以看到此時只有父進程信息

控制臺輸出如下
子進程工作
子進程 PID=12511,PPID=12506
(20S后…..)
父進程工作
父進程 PID=12506,PPID=11563

WEXITSTATUS 與 WIFEXITED 宏的使用

#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    pid_t cid;
    int pr, status;
    cid = fork();
    if (cid < 0) {
        perror("子進程創(chuàng)建失?。?);
    } else {
        if (cid == 0) {
            printf("子進程工作 PID=%d,父進程 PID=%d\n", getpid(),getppid());
            sleep(20);
            exit(3);
        } else {
            pr = wait(&status);
            if (WIFEXITED(status)) {
                printf("父進程工作 PID=%d\n", getpid());
                printf("WAIT 返回值=%d\n", pr);
                printf("子進程正常退出 PID=%d\n", getpid());
                printf("WIFEXITED(status)=%d\n", WIFEXITED(status));
                printf("WEXITSTATUS(status)=%d\n", WEXITSTATUS(status));
            } else {
                printf("子進程異常退出 PID=%d,信號=%d\n", getpid(), status);
                printf("WAIT 返回值=%d\n", pr);
            }
        }
    }
    return 0;
}

基于上面代碼做出分析:

1. 子進程正常退出
控制臺輸出信息如下:
子進程工作 PID=12070,父進程 PID=12069
(20S后…..)
父進程工作 PID=12069
WAIT 返回值=12070
子進程正常退出 PID=12069
WIFEXITED(status)=1
WEXITSTATUS(status)=3

2. 子進程異常退出
作法:
運行程序,在子進程 SLEEP 期間,殺死子進程
[root@localhost Debug]# kill -9 11990

控制臺臺輸出如下
子進程工作 PID=11990,父進程 PID=11985
(kill -9 PID 殺死子進程)
子進程異常退出 PID=11985,信號=9
可以看出子進程正常退出時,status 返回值是 exit 的退出值,子進程異常退出時 status 返回值信號值

waitpid 函數(shù)使用

waitpid 的參數(shù)說明

參數(shù) pid 的值有以下幾種類型:
pid>0時,只等待進程 ID 等于 pid 的子進程,不管其它已經(jīng)有多少子進程運行結(jié)束退出了,只要指定的子進程還沒有結(jié)束,waitpid 就會一直等下去.
pid=-1時,等待任何一個子進程退出,沒有任何限制,此時 waitpid 和 wait 的作用一模一樣.
pid=0時,等待同一個進程組中的任何子進程,如果子進程已經(jīng)加入了別的進程組,waitpid 不會對它做任何理睬.
pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的 ID 等于 pid 的絕對值.

參數(shù) options 的值有以下幾種類型:
如果使用了 WNOHANG 參數(shù),即使沒有子進程退出,它也會立即返回,不會像 wait 那樣永遠等下去.
如果使用了 WUNTRACED 參數(shù),則子進程進入暫停則馬上返回,但結(jié)束狀態(tài)不予以理會.
如果我們不想使用它們,也可以把 options 設(shè)為0,如:ret=waitpid(-1,NULL,0);

WNOHANG 使用

#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    pid_t cid;
    int pr, status;
    cid = fork();
    if (cid < 0) {
        perror("子進程創(chuàng)建失敗!");
    } else {
        if (cid == 0) {
            printf("子進程工作 PID=%d\n", getpid());
            sleep(5);
            exit(3);
        } else {
            do{
                pr = waitpid(0,&status, WNOHANG);
                if(pr==0)
                {
                 printf("沒有子進程退出,繼續(xù)執(zhí)行..\n");
                 sleep(1);
                }
            }while(pr==0);
            printf("子進程正常退出 PID=%d\n", pr);
        }
    }
    return 0;
}

控制臺輸出:

沒有子進程退出,繼續(xù)執(zhí)行..
子進程工作 PID=3632
沒有子進程退出,繼續(xù)執(zhí)行..
沒有子進程退出,繼續(xù)執(zhí)行..
沒有子進程退出,繼續(xù)執(zhí)行..
沒有子進程退出,繼續(xù)執(zhí)行..
子進程正常退出 PID=3632

針對某一進程進行等待

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    pid_t cid;
    int pr, status;
    cid = fork();
    if (cid < 0) {
        perror("子進程創(chuàng)建失?。?);
    } else {
        if (cid == 0) {
            printf("子進程工作 PID=%d,PPID=%d\n", getpid(), getppid());
            sleep(20);
            exit(3);
        } else {
            pr = waitpid(cid, &status, 0);
            printf("父進程正常退出 PID=%d\n", pr);
        }
    }
    return 0;
}

控制臺輸出
子進程工作 PID=4257,PPID=4252
父進程正常退出 PID=4257

WUNTRACED 使用

#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
    pid_t cid;
    int pr, status;
    cid = fork();
    if (cid < 0) {
        perror("子進程創(chuàng)建失??!");
    } else {
        if (cid == 0) {
            printf("子進程工作 PID=%d,PPID=%d\n", getpid(), getppid());
            sleep(30);
            exit(3);
        } else {
            pr = waitpid(cid, &status, WUNTRACED);
            printf("父進程正常退出 PID=%d,status=%d\n", pr,status);
        }
    }
    return 0;
}

作法:在子進程 SLEEP 時,通過 SHELL 命令停止子進程
[root@ ~ 6$] kill -STOP PID

控制臺輸出
子進程工作 PID=4110,PPID=4108
(SLEEP 期間,停止子進程)
父進程正常退出 PID=4110,status=4991
在查看進程狀態(tài),發(fā)現(xiàn)此時父進程子進程都已經(jīng)退出
[root@ Debug 13$] ps -C Process -opid,ppid,stat,cmd
PID PPID STAT CMD

exec 系統(tǒng)調(diào)用

函數(shù)作用:以新進程代替原有進程,但 PID 保持不變
形式:
int execl(const char path, const chararg, ...);
int execlp(const char file, const chararg, ...);
int execle(const char path, const chararg, ..., char const envp[]);
int execv(const char
path, char constargv[]);
int execvp(const char
file, char constargv[]);
int execve(const char
path, char constargv[], char const envp[]);

舉例:

exec1.c
#include <stdio.h>
#include <unistd.h>
int main()
{
  printf("這是第一個進程 PID=%d\n",getpid());
  execv("e2",NULL);
  printf("asa");
  return 0;
}
exec2.c
#include <stdio.h>
#include <unistd.h>
int main()
{
  printf("這是第二個進程 PID=%d\n",getpid());
}

運行結(jié)果:
[root@ Process 9$] gcc -o e1 exec1.c
[root@ Process 10$] gcc -o e2 exec2.c
[root@ Process 11$] ./e1
這是第一個進程 PID=3051
這是第二個進程 PID=3051