进程组
# 进程组
就是一个或是多个进程的组合(集合),一个进程都有一个标识组 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
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
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
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
2
3
4
5
6
可以看到bash
进程 id 为12087
,我们需要在另外一个新的bash进程里操作追踪
对这个进行追踪:
strace -f -s 65500 -o demo17.log -p 12087
# 按下回车进行监听
1
2
3
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
2
3
4
5
6
7
8
执行对应的命令,来观察bash
进程做了什么
[root@jb51 process]# php demo17.php
pid=24580, ppid=12087, gpid=24580, sid=12087
1
2
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
编辑 (opens new window)
上次更新: 2022/05/19, 23:17:44