进程间的关系操作

原创
2016/11/28 16:54
阅读数 123

进程进程之间是有关系的,例如,父子进程、兄弟进程等。对于子进程而言,其退出时的状态可以由父进程得到。

一、等待进程退出

Linux内核为每个终止的子进程保存一定量的信息,这些信息包括进程ID、进程终止状态以及改进程的一些统计信息等。这些信息可以由父进程得到并做一些处理。

Linux下使用wait()函数得到子进程的结束信息,其原型如下:

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

调用wait()函数的进程会阻塞,直到该进程的任意一个子进程结束,wait()函数会得到结束的子进程的信息并返回该子进程的进程ID,结束信息保存在参数statloc所指向的内存空间中。如果改进程没有子进程,则立即出错返回,返回值为-1;如果在调用wait()函数时已经有若干个子进程结束运行了,则wait()函数立即返回,但是具体得到的是哪个子进程的信息则是不确定的,需要根据子进程的ID来判断。

    wait()函数的参数用来保存子进程的返回信息,内核会将取得的子进程结束信息保存在该指针所指向的空间。如果该指针为NULL,则表示用户堆返回信息不关心。

返回信息是一个整数,不同的位代表不同的信息,它们是进程正常结束状态、终止进程的信号编号和暂停进程的信号编号。Linux提供了专门的宏,来判断哪些状态有效并且取得相应的状态值:

进程终止信息和宏操作
状态 判断宏 取值宏
进程正常结束 WIFEXITED(status) WEXITSTATUS(status)
进程异常结束 WIFSIGNALED(status) WTERMSIG(status)
进程暂停 WIFSTOPPED(status) WSTOPSIG(status)

例如,当一个进程正常退出时,改进程的父进程得到其结束信息,需判断:如果WIFEXITED(status)值为真,那么说明该进程是正常退出的,WEXITSTATUS(status)使用返回信息中进程结束状态即可。

下面例子演示了如何使用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;
}

运行结果:

 

二、等待指定的进程

    wait()函数可以等待子进程的退出,并且获得其退出状态信息。但是wait()函数只能等待第一个结束的子进程,如果需要指定等待一个子进程,则需要使用如下代码实现:

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()函数支持作业控制

三、输出进程统计信息

    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;
}

运行结果:

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部