進程間的關係操做

進程進程之間是有關係的,例如,父子進程、兄弟進程等。對於子進程而言,其退出時的狀態能夠由父進程獲得。函數

1、等待進程退出

Linux內核爲每一個終止的子進程保存必定量的信息,這些信息包括進程ID、進程終止狀態以及改進程的一些統計信息等。這些信息能夠由父進程獲得並作一些處理。性能

Linux下使用wait()函數獲得子進程的結束信息,其原型以下:優化

#include <sys/wait.h>
pid_t wait(int *statloc);

調用wait()函數的進程會阻塞,直到該進程的任意一個子進程結束,wait()函數會獲得結束的子進程的信息並返回該子進程的進程ID,結束信息保存在參數statloc所指向的內存空間中。若是改進程沒有子進程,則當即出錯返回,返回值爲-1;若是在調用wait()函數時已經有若干個子進程結束運行了,則wait()函數當即返回,可是具體獲得的是哪一個子進程的信息則是不肯定的,須要根據子進程的ID來判斷。spa

    wait()函數的參數用來保存子進程的返回信息,內核會將取得的子進程結束信息保存在該指針所指向的空間。若是該指針爲NULL,則表示用戶堆返回信息不關心。指針

返回信息是一個整數,不一樣的位表明不一樣的信息,它們是進程正常結束狀態、終止進程的信號編號和暫停進程的信號編號。Linux提供了專門的宏,來判斷哪些狀態有效而且取得相應的狀態值:code

進程終止信息和宏操做
狀態 判斷宏 取值宏
進程正常結束 WIFEXITED(status) WEXITSTATUS(status)
進程異常結束 WIFSIGNALED(status) WTERMSIG(status)
進程暫停 WIFSTOPPED(status) WSTOPSIG(status)

例如,當一個進程正常退出時,改進程的父進程獲得其結束信息,需判斷:若是WIFEXITED(status)值爲真,那麼說明該進程是正常退出的,WEXITSTATUS(status)使用返回信息中進程結束狀態便可。orm

下面例子演示瞭如何使用wait函數取得子進程的結束狀態:進程

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

int main(void)
{
  pid_t pid;
  int num, status;
  pid = fork();
  if(pid < 0){
    perror("fail to fork\n");
    exit(1);
  }else if(pid == 0){  //第一個子進程
    printf("the first, exit normally\n");
    exit(0);
  }else {
    if(wait(&status) == -1){  //父進程等待子進程的退出
      perror("fail to wait\n");
      exit(1);
    }
    if(WIFEXITED(status) == 1) {  //獲得正常退出的退出狀態,exit()函數的參數
      printf("the status of first is : %d\n", WEXITSTATUS(status));
    }
  }
  pid = fork();
  if(pid < 0){
    perror("fail to fork\n");
    exit(1);
  }else if(pid == 0){  //第二個子進程
    printf("the second, exit abnormally\n");
    num = 1/0;  //除以0,會產生SIGFPE異常信號
  }else{
    if(wait(&status) == -1){  //父進程等待子進程退出
      perror("fail to wait\n");
      exit(1);
    }
    if(WIFSIGNALED(status) == 1){  //獲得終止子進程的信號的值
      printf("the terminated signal is : %d\n", WTERMSIG(status));
    }
  }
  return 0;
}

運行結果:內存

 

2、等待指定的進程

    wait()函數能夠等待子進程的退出,而且得到其退出狀態信息。可是wait()函數只能等待第一個結束的子進程,若是須要指定等待一個子進程,則須要使用以下代碼實現:ci

int status;  //保存進程的狀態信息
//pid中保存的是須要的獲得的結束信息的進程ID
//若是獲得結束狀態信息的進程不是所須要的進程,則循環取子進程的結束狀態
while(pid != wait(&status));

Linux下提供waitpid()函數,用以等待一個指定的子進程:

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);

waitpid()函數的第一個參數指定要等待的子進程的進程ID,pid參數的做用以下:

pid參數的做用
pid值 等待的做用
-1 等待任意子進程
>0 等待進程ID和pid相等的子進程
0 等待組ID和pid相等的子進程
<-1 等待組ID等於pid絕對值的組內任意子進程

waitpid()函數的第二個參數的意義和wait()函數相同,第三個是控制選項,該選項有3中狀況,其中2種和做業控制有關:

waitpid函數的選項
選項 選項說明
WCONTINUED 當子進程在暫停後繼續執行,且其狀態還沒有報告,則返回其狀態
WNOHANG 當所等待進程還沒有結束運行時不阻塞,waitpid()函數直接返回
WUNTRACED 當子進程暫停時,而且其狀態自暫停以來還未報告過,則返回其狀態

該參數能夠是0,也能夠由上面所述的3種選項「或」獲得。

下面的實例演示了使用WNOHANG選項非阻塞等待一個子進程:

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

int main(int argc, char *argv[])
{
  pid_t pid;
  pid = fork();
  if(pid < 0){
    printf("fail to fork\n");
    exit(1);
  } else if(pid == 0) {  //子進程
    printf("the child\n");
    sleep(3);
    exit(0);
  } else {
    printf("the parent\n");
    if(waitpid(pid, NULL, WNOHANG) == 0)  //非阻塞等待子進程
      printf("the child is not available now\n");
  }
  printf("no waiting, parent done\n");
  return 0;
}

運行結果:

從上面的程序中能夠看出父進程並無阻塞在waitpid()函數上。

waitpid()函數和wait()函數的區別有一下3點:

  • waitpid()函數能夠指定一個子進程
  • waitpid()函數能夠不阻塞等待一個子進程
  • waitpid()函數支持做業控制

3、輸出進程統計信息

    wait3()函數和wait4()函數基本等同於wait()函數和waitpid()函數,不一樣的是wait3()和wait4()函數除了wait()和waitpid()函數的功能外,還可以獲得更詳細的信息。這些信息CPU使用時間等,其保存在內核中,病經過參數rusage結構返回給用戶,函數原型以下:

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/source.h>
pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);

參數rusage是一個統計資源結構的指針,其結構聲明以下:

struct rusage
{
    struct timeval ru_utime;    //用戶CPU時間
    struct timeval ru_stime;    //系統CPU時間
    long int ru_maxrss;         //最大rss數量
    long int ru_ixrss;          //與其餘進程共用代碼段數量
    long int ru_idrss;          //數據段大小
    long int ru_isrss;          //棧大小
    long int ru_minflt;         //軟頁面錯誤
    long int ru_majflt;         //硬頁面錯誤
    long int ru_nswap;          //換頁次數
    long int ru_inblock;        //從文件系統讀次數
    long int ru_oublock;        //向文件系統寫次數
    long int ru_msgsnd;         //發送消息數
    long int ru_msgrcv;         //接受消息數
    long int ru_nsignals;       //收到信號數
    long int ru_nvcsw;          //主動換頁次數
    long int ru_nivcsw;         //被動換頁次數
};

讀者使用上面結構得出須要的進程信息,例如用戶CPU時間加上系統CPU時間能夠獲得程序佔用CPU的時間,再用程序運行總時間減去該時間,就能夠獲得程序運行時的I/O時間。這樣就能夠知道程序的性能瓶頸在自己的計算流程上仍是在I/O上,由此能夠優化程序。

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

int main(int argc, char *argv[])
{
  pid_t pid;
  struct rusage rusage;
  pid = fork();
  if(pid < 0){
    printf("fail to fork\n");
    exit(0);
  } else if(pid == 0){
    printf("the child\n");
    exit(0);
  } else {
    printf("the parent\n");
  }
  if(wait3(NULL, 0, &rusage) == -1) {  //獲得改進程的詳細信息
    perror("fail to wait");
    exit(1);
  }
  printf("utime is %d\n", rusage.ru_utime);    //打印用戶CPU時間
  printf("stime is %d\n", rusage.ru_stime);    //打印系統CPU時間
  printf("maxrss is %ld\n", rusage.ru_maxrss); //打印最大rss數量
  printf("ixrss is %ld\n", rusage.ru_ixrss);   //打印與其餘進程共用代碼段的數量
  return 0;
}

運行結果:

相關文章
相關標籤/搜索