wxvirus wxvirus
首页
  • Go文章

    • Go语言学习
  • Rust

    • Rust学习
  • Java

    • 《Java》
  • Python文章

    • Python
  • PHP文章

    • PHP设计模式
  • 学习笔记

    • 《Git》
  • HTML
  • CSS
  • JS
  • 技术文档
  • GitHub技巧
  • 刷题
  • 博客搭建
  • 算法学习
  • 架构设计
  • 设计模式
  • 学习
  • 面试
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

无解的lifecycle

let today = new Beginning()
首页
  • Go文章

    • Go语言学习
  • Rust

    • Rust学习
  • Java

    • 《Java》
  • Python文章

    • Python
  • PHP文章

    • PHP设计模式
  • 学习笔记

    • 《Git》
  • HTML
  • CSS
  • JS
  • 技术文档
  • GitHub技巧
  • 刷题
  • 博客搭建
  • 算法学习
  • 架构设计
  • 设计模式
  • 学习
  • 面试
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • C&C++

  • PHP

    • PHP基础或常见方法

    • Laravel

    • ThinkPHP

    • PHP多进程编程

      • 程序与进程
      • PHP解释器的执行过程
      • 进程环境
      • 解释器文件
      • 进程标识与fork
      • 进程退出
      • 进程exec
      • 进程调度
      • 多进程编写
      • SUID特权进程
      • 进程查看
      • 中断信号
      • 进程关系与守护进程
      • 进程组
        • 进程组
      • 会话
      • 守护进程
      • 作业控制
      • 进程间通信
    • swoole

  • Python

  • Go

  • microservice

  • rust

  • Java

  • 学习笔记

  • 后端
  • PHP
  • PHP多进程编程
wxvirus
2022-05-19

进程组

# 进程组

就是一个或是多个进程的组合(集合),一个进程都有一个标识组 ID,表示该进程属于哪个进程组。

bash进程启动之后,它会把自己setsid把自己设置为会话首进程,也会设置自己为组长进程。

<?php
$pid = posix_getpid();
fprintf(STDOUT, "pid=%d, ppid=%d, gpid=%d, sid=%d\n",
    $pid, posix_getppid(), posix_getpgid($pid), posix_getsid($pid));
1
2
3
4

进程:是正在执行的进程。

所以上述的代码执行的进程,肯定是在 bin/bash [它是有伪终端的]进程启动的,启动之后[通过execve启动],它会继承一些数据,比如组 ID,会话 ID,同时也会进程父进程已经打开的文件描述符

0,1,2就是伪终端里的标准输入、标准输出和标准错误,是通过pts和ptmx来模拟出来的。

当我们调用php demo17.php的时候:

bin/bash
-----php demo17.php [tcp/ip linux它有伪终端设备驱动程序模拟出一个终端]
1
2

bin/bash调用setsid把自己设置为会话首进程,也会把自己设置成为组长进程。

[root@jb51 process]# php demo17.php
pid=20955, ppid=12087, gpid=20955, sid=12087
[root@jb51 process]# echo $$
12087
1
2
3
4

可以看的出来12087不仅是一个会话首进程,也是一个组长进程,同时它打开了伪终端从设备文件pts[它有一个伪终端设备驱动程序],bin/bash它也是一个连接控制终端的。

我们可以使用追踪命令来查看是如何将自己设置为组长进程的

  pstree -ap

  ├─sshd,1590 -D
  │   ├─sshd,12085
  │   │   └─bash,12087
  │   │       └─pstree,22719 -ap
1
2
3
4
5
6

可以看到bash进程 id 为12087,我们需要在另外一个新的bash进程里操作追踪对这个进行追踪:

strace -f -s 65500 -o demo17.log -p 12087

# 按下回车进行监听
1
2
3

我们再打开一个终端,此时的进程树为:

  ├─sshd,1590 -D
  │   ├─sshd,12085
  │   │   └─bash,12087
  │   ├─sshd,21140
  │   │   └─sftp-server,21147
  │   └─sshd,23134
  │       └─bash,23138
  │           └─pstree,23249 -ap
1
2
3
4
5
6
7
8

执行对应的命令,来观察bash进程做了什么

[root@jb51 process]# php demo17.php
pid=24580, ppid=12087, gpid=24580, sid=12087
1
2

这里我演示反了

因为要监听12087的,竟然还在bash进程为12087的上面进行追踪自己。这里应该和下面的反过来操作一遍。

可以看到,上述执行过后,组长进程为24580,然后我们去生成的日志文件查找是否有setpgid函数

// 设置组长进程
12087 setpgid(24580, 24580 <unfinished ...>

// 执行我们的程序
24580 execve("/usr/bin/php", ["php", "demo17.php"], 0x15f9e50 /* 25 vars */) = 0
1
2
3
4
5

<?php

function showPID()
{
    $pid = posix_getpid();
    fprintf(STDOUT, "pid=%d, ppid=%d, gpid=%d, sid=%d\n",
        $pid, posix_getppid(), posix_getpgid($pid), posix_getsid($pid));
}

// 一个父进程
showPID(); // ppid和下面2个子进程是不一样的

// 创建一个子进程
$pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的

if ($pid > 0) {
    // 第二个子进程
    $pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的
}

// 父进程执行一次,两个子进程再执行一次
showPID();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@jb51 process]# php demo18.php
pid=27793, ppid=12087, gpid=27793, sid=12087
pid=27794, ppid=27793, gpid=27793, sid=12087
pid=27793, ppid=12087, gpid=27793, sid=12087
[root@jb51 process]# pid=27795, ppid=1, gpid=27793, sid=12087
1
2
3
4
5

孤儿进程:就是指父进程已经先结束,但是子进程晚结束,这个时候子进程就是孤儿进程,它会被1号进程接管。

代码优化

<?php

function showPID()
{
    $pid = posix_getpid();
    fprintf(STDOUT, "pid=%d, ppid=%d, gpid=%d, sid=%d\n",
        $pid, posix_getppid(), posix_getpgid($pid), posix_getsid($pid));
}

// 一个父进程
showPID(); // ppid和下面2个子进程是不一样的
$pidMap = [];
// 创建一个子进程
$pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的

if ($pid > 0) {
    // 第二个子进程
    $pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的
    if ($pid > 0) {
        $pidMap[$pid] = $pid;
    }
}

// 父进程执行一次,两个子进程再执行一次
showPID();

if ($pid > 0) {
    $i = 0;
    // 回收

    while (1) {
        $pid = pcntl_waitpid(-1, $status);
        if ($pid > 0) {
            $i++;
            echo "子进程 $pid 结束了\n";
        }
        unset($pidMap[$pid]);
        if (empty($pidMap)) {
            break;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

运行:

[root@jb51 process]# php demo18.php
pid=31195, ppid=12087, gpid=31195, sid=12087
pid=31196, ppid=31195, gpid=31195, sid=12087
pid=31195, ppid=12087, gpid=31195, sid=12087
pid=31197, ppid=31195, gpid=31195, sid=12087
子进程 31196 结束了
子进程 31197 结束了
1
2
3
4
5
6
7

调整一个子进程设置为组长

<?php

function showPID()
{
    $pid = posix_getpid();
    fprintf(STDOUT, "pid=%d, ppid=%d, gpid=%d, sid=%d\n",
        $pid, posix_getppid(), posix_getpgid($pid), posix_getsid($pid));
}

// 一个父进程
showPID(); // ppid和下面2个子进程是不一样的
$pidMap = [];
// 创建一个子进程
$pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的

if ($pid > 0) {
    $pidMap[$pid] = $pid;
    // 第二个子进程
    $pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的
    if ($pid > 0) {
        $pidMap[$pid] = $pid;
    } else {
        $pid = posix_getpid();
        // 把自己设置为组长进程
        posix_setpgid($pid, $pid);
    }
}

// 父进程执行一次,两个子进程再执行一次
showPID();

if ($pid > 0) {
    $i = 0;
    // 回收

    while (1) {
        $pid = pcntl_waitpid(-1, $status);
        if ($pid > 0) {
            $i++;
            echo "子进程 $pid 结束了\n";
        }
        unset($pidMap[$pid]);
        if (empty($pidMap)) {
            break;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@jb51 process]# php demo18.php
pid=32135, ppid=12087, gpid=32135, sid=12087
pid=32136, ppid=32135, gpid=32135, sid=12087
pid=32135, ppid=12087, gpid=32135, sid=12087
pid=32137, ppid=32135, gpid=32137, sid=12087
子进程 32136 结束了
1
2
3
4
5
6

第二个子进程就把自己设置为组长了。

<?php

function showPID()
{
    $pid = posix_getpid();
    fprintf(STDOUT, "pid=%d, ppid=%d, gpid=%d, sid=%d\n",
        $pid, posix_getppid(), posix_getpgid($pid), posix_getsid($pid));
}

// 一个父进程
showPID(); // ppid和下面2个子进程是不一样的
$pidMap = [];
// 创建一个子进程
$pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的

if ($pid > 0) {
    $pidMap[$pid] = $pid;
    // 第二个子进程
    $pid = pcntl_fork(); // 该子进程的ppd和pgid和sid也是一样的
    if ($pid > 0) {
        $pidMap[$pid] = $pid;
    } else {
        $pid = posix_getpid();
        // 把自己设置为组长进程
        posix_setpgid($pid, $pid);

        $pid = pcntl_fork();
        if ($pid > 0) {
            $pidMap[$pid] = $pid;
        }
    }
}

// 父进程执行一次,两个子进程再执行一次
showPID();

if ($pid > 0) {
    $i = 0;
    // 回收

    while (1) {
        $pid = pcntl_waitpid(-1, $status);
        if ($pid > 0) {
            $i++;
            echo "子进程 $pid 结束了\n";
        }
        unset($pidMap[$pid]);
        if (empty($pidMap)) {
            break;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[root@jb51 process]# php demo18.php
pid=32366, ppid=12087, gpid=32366, sid=12087
pid=32366, ppid=12087, gpid=32366, sid=12087
pid=32367, ppid=32366, gpid=32366, sid=12087
子进程 32367 结束了
pid=32368, ppid=32366, gpid=32368, sid=12087
pid=32369, ppid=32368, gpid=32368, sid=12087
子进程 32369 结束了
1
2
3
4
5
6
7
8
编辑 (opens new window)
上次更新: 2022/05/19, 23:17:44
进程关系与守护进程
会话

← 进程关系与守护进程 会话→

最近更新
01
vue3配合vite初始化项目的一些配置
07-26
02
网盘系统开发学习
07-24
03
linux多进程
06-19
更多文章>
Theme by Vdoing | Copyright © 2021-2024 wxvirus 苏ICP备2021007210号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式