【进程】进程组

一、进程组

1. 进程组

(1)进程组,也称之为作业,BSD与1980年前后向UNIX中增加的一个新特性,代表一个或多个进程的集合。每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾经使用到,操作系统设计的进程组的概念,是为了简化对多个进程的管理。

当父进程创建子进程的时候,默认子进程与父进程属于同一个进程组,进程组ID等于进程组第一个进程ID(组长进程)。所以,组长进程标识:其进程组ID等于其进程ID.

组长进程可以创建一个进程组,创建该进程组的进程,然后终止,只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

(2)kill发送给进程组

使用 kill -n -pgid 可以将信号 n 发送到进程组 pgid 中的所有进程。例如命令 kill -9 -4115 表示杀死进程组 4115 中的所有进程。


2. getpgid、getpgrp函数原型:

pid_t getpgrp(void);
pid_t getpgid(pid_t pid);

分析:

  • 函数1:获取当前进程的进程组ID
  • 函数2:如果pid = 0,那么该函数作用和getpgrp一样。

 

3. setpgid函数函数原型:改变进程默认所属的进程组,通常可用来加入一个现有的进程组或新进程组。

int setpgid(pid_t pid, pid_t pgid);

分析:将参数1对应的进程,加入参数2对应的进程组中。

注意:

  • 如改变子进程为新进程组,用fork后,exec前。
  • 权级问题:非root进程只能改变自己创建的子进程,或有权限操作的进程。

4. 测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main()
{
    pid_t pid;
    if ((pid = fork()) < 0) 
    {
        perror("fork");
        exit(1);
    }
    else if (pid == 0) //子进程
    {
        printf("child PID = %d\n", getpid());
        printf("child Group ID = %d\n", getpgid(0)); //返回组id
        sleep(7);
        printf("-------Group ID of child  id change to %d\n", getpgid(0));
        exit(0);
    }
    else if (pid > 0) //父进程
    {
        sleep(1);
        setpgid(pid, pid); //让子进程自立门户,成为进程组组长,以它的pid为进程组 id 
 
        sleep(13);
        printf("\n");
        printf("parent PID = %d\n", getpid());
        printf("parent's parent PID = %d\n", getppid());
        printf(" parent Group ID = %d\n", getpgid(0));
 
        sleep(5);
        setpgid(getpid(), getppid());  //改变父进程组id为父进程的父进程
        printf("\n-------Group ID of parent is change to %d\n", getpgid(0));
 
        while (1);
    }
    return 0;
}

输出结果:

 

二、进程组的应用

1. 实验一:

题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。   
 

1. 测试代码:

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main() 
{
    int pid, i;
    int group1, group2;

    // 设置父进程(进程 0)为组长 
    setpgid(getpid(), getpid());
    group1 = getpgid(getpid());

    for (i = 1; i <= 3; ++i) 
    {
        pid = fork();
        if (pid == 0)   child
        {
            
            if (i == 1) 
            {
                // 如果 group1 根本不存在,就会出问题。
                // 比如进程 0 已经运行结束。
                setpgid(getpid(), group1);
            }
            else if (i == 2) 
            {
                setpgid(getpid(), getpid());
                group2 = getpgid(getpid());
            }
            else if (i == 3) 
            {
                // 试想如果进程 2 还没运行,进程 3 先运行了,
                // 这时候 group2 还未进行设置,这里就会有问题。
                // 或者进程 2 已经结束,那进程 3 的设置也会失败
                setpgid(getpid(), group2);
            }
            break;
        }
        else if (pid < 0) 
        {
            perror("fork");
            return -1;
        }
    }
    printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d], (%s)\n", i % 4, getpid(), getppid(), getpgid(getpid()), strerror(errno));
    while (1) sleep(1);
    return 0;
}

输出结果: 

测试代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
 
int main(void)
{
    setpgid(getpid(), getpid());
    pid_t group1 = getpgid(getpid());
    pid_t group2;
 
    int i = 0;
    for(; i < 3; ++i)
    {
        pid_t pid = fork();
        if(pid < 0)
        {
            perror("fork error");
            exit(1);
        }
        else if(pid > 0)
        {
            // parent process
            if(i == 0)
                setpgid(pid, group1);
            if(i == 1)
            {
                setpgid(pid, pid);
                group2 = getpgid(pid);
            }
            if(i == 2)
                setpgid(pid, group2);
        }
        else
        {
            // child process
            if(i == 0)
                setpgid(getpid(), group1);
            if(i == 1)
            {
                setpgid(getpid(), getpid());
                group2 = getpgid(getpid());
            }
            if(i == 2)
                setpgid(getpid(), group2);
            break;
        }
    }
    printf("pid:%d, ppid:%d, pgid:%d\n", getpid(), getppid(), getpgid(getpid()));
 
    for(int i = 0; i < 3; ++i)
        wait(0);
 
    return 0;
}

输出结果:

2. 实验二:

题目:利用进程扇完成一个小实验。该进程扇有 1 个父进程和 3 个子进程,我们希望达到图 1 中的效果,即将进程 0 (父进程)和进程 1 设置成一组,假设为组 1,将进程 2 和 进程 3 设置成另一个组,假设为组 2. 另外,我们希望进程 0 和进程 2 分别是这两个组的组长。

测试代码:

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
 
int main() 
{
  int pid, i;
  int group1, group2;
 
  setpgid(getpid(), getpid());
  group1 = getpgid(getpid());
 
  for (i = 0; i < 3; ++i) 
  {
    pid = fork();
    if (pid > 0) //父进程
    {
      if (i == 0) 
      {
        setpgid(pid, pid);
        group2 = getpgid(pid);
      }   
      else if (i == 1) 
      {
        setpgid(pid, group1);
      }   
      else if (i == 2)
      {
        setpgid(pid, group2);
      }   
      break;
    }   
    else if (pid == 0)  //子进程
    {
      if (i == 0) 
      {
        setpgid(getpid(), getpid());
        group2 = getpgid(getpid());
      }   
      else if (i == 1) 
      {
        setpgid(getpid(), group1);
      }   
      else if (i == 2) 
      {
        setpgid(getpid(), group2);
      }   
    }   
    else if (pid < 0) 
    {
      perror("fork");
      return -1; 
    }   
  }
 
  printf("进程 %d, pid: %d -> ppid: %d, pgid: [%d]\n", i, getpid(), getppid(), getpgid(getpid()));
  while(1) sleep(1);
  return 0;
}

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值