文章目录
- [1. 启动流程概要](#1. 启动流程概要)
- [2. 实践](#2. 实践)
-
- [2.1 新进程的 Process Group 观察](#2.1 新进程的 Process Group 观察)
- [2.2 在 bash 启动的程序里创建新 session](#2.2 在 bash 启动的程序里创建新 session)
- [3. 番外](#3. 番外)
1. 启动流程概要
bash 启动程序的 3 个主要步骤:fork() + setpgid() + exec*():

为什么 bash 要对新进程调用 setpgid()、将其放入一个独立的 Process Group 呢?主要原因如下:
-
bash job 控制。如果不将新程序放入独立的Process Group,则新程序进程将继承父进程 bash 的Process Group,这样fg/bg/jobs程序将无法Process Group唯一标识来控制新程序(这些程序按 PGID 进行操作); -
独立处理信号。如果 bash 不将新启动的程序放入独立Process Group,则所有启动的进程都将位于和 bash 同一Process Group内。发给某个 (bash 启动的)程序的 Ctrl+C 等信号,将会传递给 bash 和其启动所有程序所在的同一Process Group,从而使 bash 及其启动的程序都终止(自身配置了信号处理的程序除外); -
bash 管道处理。像cmd1 | cmd2 | cmd3管道命令,如果不放入独立Process Group,则无法当都当作一个 job 来控制处理;相反,则可以通过独立的 PGID 来 job 控制。
2. 实践
2.1 新进程的 Process Group 观察
bash
$ cat t.c
#include <unistd.h>
void main(void)
{
while (1) sleep(8);
}
$ ./t &
[1] 4612
$ ps -eo pid,ppid,pgid,sid,comm
PID PPID PGID SID COMMAND
1 0 1 1 systemd
[......]
4598 4580 4598 4598 bash
4612 4598 4612 4598 t
[......]
从上面的 ps 命令输出信息看到:
- 程序
t的PPID == 4598,也即bash的PID,所以t是由bash启动的; - 程序
t的PID == PGID == 4612,所以t位于独立的Process Group 4612中;
2.2 在 bash 启动的程序里创建新 session
Linux 通过 setsid() 新建一个 Session。下面的 setsid_test 程序,试图通过 setsid() 新建立一个 session:
c
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t sid;
sid = setsid();
if (sid == -1) {
perror("setsid");
return -1;
}
printf("new session: %d\n", sid);
while (1) sleep(8);
return 0;
}
编译,然后从 bash 终端启动:
bash
$ ./setsid_test
setsid: Operation not permitted
结果失败了,报 EPERM 错误。glibc 的 setsid() 是对系统接口 sys_setsid() 的直接调用,看下 sys_setsid() 的内核代码:
c
SYSCALL_DEFINE0(setsid)
{
struct task_struct *group_leader = current->group_leader; // group_leader == current,指向 setsid_test
struct pid *sid = task_pid(group_leader);
pid_t session = pid_vnr(sid);
int err = -EPERM;
write_lock_irq(&tasklist_lock);
/* Fail if I am already a session leader */
if (group_leader->signal->leader)
goto out;
/* Fail if a process group id already exists that equals the
* proposed session id.
*/
/* 如果进程已经是 Process Group Leader,则返回错误码 EPERM */
if (pid_task(sid, PIDTYPE_PGID))
goto out; /* (1) */
/* 进程成为新的 Process Group/Session Leader */
group_leader->signal->leader = 1;
set_special_pids(sid);
/* 断开与 tty 终端的连接 */
proc_clear_tty(group_leader);
err = session;
out:
write_unlock_irq(&tasklist_lock);
if (err > 0) {
proc_sid_connector(group_leader);
sched_autogroup_create_attach(group_leader);
}
return err;
}
测试程序 setsid_test 的 setsid() 调用在上面代码标注 (1) 处返回,表示进程已经是一个Process Group Leader,而 Linux 规定已经是 Process Group Leader 不能成为新的 Session Leader。如何解决这个问题?方案一是通过 fork() 调用:fork() 后,首先退出父进程,以摆脱 bash 通过 setpgid() 设置的 leader 身份;然后再在子进程中调用 setsid() 将会成功,因为子进程不具备 leader 身份,这正是 glibc 库函数 daemon() 采用的方式;另一个解决方案是不通过 bash 启动程序,而是通过 /etc/rc.local 或 SystemV 的 init script 启动程序。
那为什么不允许 Process Group Leader 成为 Session Leader ?因为一旦允许, 由于 setsid() 新成立了 Session 和 Process Group,所以 Process Group Leader 将进入新的 Process Group 和 Session,这样在原本 Process Group 内进程将失去它们的 Process Group Leader, 这样会引发歧义,同时使得像 bash 的 job control 也将无法工作。如果你无法理解这些逻辑,就简单的理解为这是 Linux 内核的强制规定就可以了。
我们使用 glibc 的 daemon() 接口来创建 Session,daemon() 通过 fork() 之后的子进程调用 setsid(),而 父进程则退出以摆脱 bash 赋予的 Process Group Leader 角色。修改后的代码如下:
c
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t sid;
#if 1
if (daemon(0, 0) != 0) {
perror("daemon");
return -1;
}
sid = getsid(0);
#else
sid = setsid();
if (sid == -1) {
perror("setsid");
return -1;
}
#endif
printf("new session: %d\n", sid);
while (1) sleep(8);
return 0;
}
bash
$ ./setsid_test
[2] 4864
$ ps -eo pid,ppid,pgid,sid,comm
PID PPID PGID SID COMMAND
1 0 1 1 systemd
[......]
2173 1 2173 2173 systemd
[......]
4598 4580 4598 4598 bash
[......]
4865 2173 4865 4865 setsid_test
[......]
首先观察到,setsid_test 的 printf() 输出的内容并没有看到,因为 setsid() 会断开与 tty 终端的连接;然后从 ps 命令输出也观察到,setsid_test 的 PID == PGID == SID == 4865,且 PPID == 2173,指向 systemd,而不是 bash,因为 fork() 的父进程退出了,保留了子进程,被托孤给了 systemd。
3. 番外
笔者在内核代码进程创建、程序加载执行、进程 SID 设置处加入一些,可以帮助理解本文内容(修改源码就不贴了):
bash
[ 2.769850] sys_setsid()-2#: current@ffff800035330000,systemd,1,1 group_leader@ffff800035330000,systemd, pgrp@ (null),(null), session@ (null),(null)
[ 5.475602] sys_setsid()-2#: current@ffff800034afab80,(journald),1357,1357 group_leader@ffff800034afab80,(journald), pgrp@ (null),(null), session@ (null),(null)
[ 5.533479] sys_setsid()-2#: current@ffff800034afba00,(modprobe),1358,1358 group_leader@ffff800034afba00,(modprobe), pgrp@ (null),(null), session@ (null),(null)
[ 5.605649] sys_setsid()-2#: current@ffff800034afc880,(modprobe),1359,1359 group_leader@ffff800034afc880,(modprobe), pgrp@ (null),(null), session@ (null),(null)
[ 5.668971] sys_setsid()-2#: current@ffff800034afd700,(modprobe),1360,1360 group_leader@ffff800034afd700,(modprobe), pgrp@ (null),(null), session@ (null),(null)
[ 5.708364] sys_setsid()-2#: current@ffff800034488000,(modprobe),1361,1361 group_leader@ffff800034488000,(modprobe), pgrp@ (null),(null), session@ (null),(null)
[ 5.763098] sys_setsid()-2#: current@ffff800034488e80,(les-load),1362,1362 group_leader@ffff800034488e80,(les-load), pgrp@ (null),(null), session@ (null),(null)
[ 5.808880] sys_setsid()-2#: current@ffff800034489d00,(mount-fs),1363,1363 group_leader@ffff800034489d00,(mount-fs), pgrp@ (null),(null), session@ (null),(null)
[ 6.372598] sys_setsid()-2#: current@ffff80003448ba00,(dom-seed),1366,1366 group_leader@ffff80003448ba00,(dom-seed), pgrp@ (null),(null), session@ (null),(null)
[ 6.508326] sys_setsid()-2#: current@ffff80003448c880,(d-sysctl),1367,1367 group_leader@ffff80003448c880,(d-sysctl), pgrp@ (null),(null), session@ (null),(null)
[ 6.695839] sys_setsid()-2#: current@ffff800034afe580,(sysusers),1368,1368 group_leader@ffff800034afe580,(sysusers), pgrp@ (null),(null), session@ (null),(null)
[ 7.067327] sys_setsid()-2#: current@ffff80003448d700,(urnalctl),1371,1371 group_leader@ffff80003448d700,(urnalctl), pgrp@ (null),(null), session@ (null),(null)
[ 7.285510] sys_setsid()-2#: current@ffff80003448e580,(tmpfiles),1372,1372 group_leader@ffff80003448e580,(tmpfiles), pgrp@ (null),(null), session@ (null),(null)
[ 7.788486] sys_setsid()-2#: current@ffff800034448000,(tmpfiles),1374,1374 group_leader@ffff800034448000,(tmpfiles), pgrp@ (null),(null), session@ (null),(null)
[ 8.395642] sys_setsid()-2#: current@ffff800034afd700,(resolved),1375,1375 group_leader@ffff800034afd700,(resolved), pgrp@ (null),(null), session@ (null),(null)
[ 8.460479] sys_setsid()-2#: current@ffff800034afc880,(imesyncd),1376,1376 group_leader@ffff800034afc880,(imesyncd), pgrp@ (null),(null), session@ (null),(null)
[ 8.508925] sys_setsid()-2#: current@ffff800034afba00,(ate-utmp),1377,1377 group_leader@ffff800034afba00,(ate-utmp), pgrp@ (null),(null), session@ (null),(null)
[ 10.588649] sys_setsid()-2#: current@ffff800034afba00,(s-daemon),1379,1379 group_leader@ffff800034afba00,(s-daemon), pgrp@ (null),(null), session@ (null),(null)
[ 10.731695] sys_setsid()-2#: current@ffff800034afe580,(crub_all),1380,1380 group_leader@ffff800034afe580,(crub_all), pgrp@ (null),(null), session@ (null),(null)
[ 10.868722] sys_setsid()-2#: current@ffff800034af9d00,(spatcher),1383,1383 group_leader@ffff800034af9d00,(spatcher), pgrp@ (null),(null), session@ (null),(null)
[ 11.041570] sys_setsid()-2#: current@ffff800034af8e80,(d-logind),1384,1384 group_leader@ffff800034af8e80,(d-logind), pgrp@ (null),(null), session@ (null),(null)
[ 11.139285] sys_setsid()-2#: current@ffff800034af8000,(sessions),1385,1385 group_leader@ffff800034af8000,(sessions), pgrp@ (null),(null), session@ (null),(null)
[ 16.689700] sys_setsid()-2#: current@ffff800034449d00,(agetty),1387,1387 group_leader@ffff800034449d00,(agetty), pgrp@ (null),(null), session@ (null),(null)
[ 16.694360] sys_setsid()-2#: current@ffff800034448e80,(agetty),1386,1386 group_leader@ffff800034448e80,(agetty), pgrp@ (null),(null), session@ (null),(null)
[ 95.171292] sys_setsid()-2#: current@ffff800034af8000,(ate-utmp),1402,1402 group_leader@ffff800034af8000,(ate-utmp), pgrp@ (null),(null), session@ (null),(null)
[ 180.544365] sys_setsid()-2#: current@ffff80003444c880,(time-dir),1412,1412 group_leader@ffff80003444c880,(time-dir), pgrp@ (null),(null), session@ (null),(null)
[ 180.821462] sys_setsid()-2#: current@ffff80003444d700,(systemd),1413,1413 group_leader@ffff80003444d700,(systemd), pgrp@ (null),(null), session@ (null),(null)
[ 181.911442] SYSC_setpgid(): p@ffff8000321be580,bash,1419,1419 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 pgrp@ffff8000320f4600
[ 181.959156] copy_process(): init_struct_pid@ffff200009b56520
[ 181.959307] copy_process()-1#: p@ffff800031610000,bash,1420,1420 p->group_leader@ffff800031610000,bash,1420,1420 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 181.972394] SYSC_execve(): /usr/bin/groups current@ffff800031610000,bash,1420,1420
[ 182.028751] copy_process()-1#: p@ffff800031610e80,bash,1421,1421 p->group_leader@ffff800031610e80,bash,1421,1421 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 182.080254] copy_process()-1#: p@ffff800031611d00,bash,1422,1422 p->group_leader@ffff800031611d00,bash,1422,1422 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 182.134983] copy_process()-1#: p@ffff800031612b80,bash,1423,1423 p->group_leader@ffff800031612b80,bash,1423,1423 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 192.925429] copy_process()-1#: p@ffff800031613a00,bash,1424,1424 p->group_leader@ffff800031613a00,bash,1424,1424 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 192.928675] SYSC_setpgid(): p@ffff800031613a00,bash,1424,1424 current@ffff800031613a00,bash,1424,1424 current_group@ffff800031613a00,bash,1424,1424 pgrp@ffff80002fc70200
[ 192.939927] SYSC_execve(): ./setsid_test current@ffff800031613a00,bash,1424,1424
[ 192.965327] sys_setsid()-1#: current@ffff800031613a00,setsid_test,1424,1424 group_leader@ffff800031613a00,setsid_test,1424,1424 pgrp@ffff800031613a00,setsid_test,1424,1424 session@ (null),(null)
[ 213.538658] copy_process()-1#: p@ffff800031614880,bash,1425,1425 p->group_leader@ffff800031614880,bash,1425,1425 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 213.539338] SYSC_setpgid(): p@ffff800031614880,bash,1425,1425 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 pgrp@ffff80002fc70280
[ 213.546247] copy_process()-1#: p@ffff800031418000,bash,1426,1426 p->group_leader@ffff800031418000,bash,1426,1426 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 task_pgrp(current)=ffff8000320f4600, task_session(current)=ffff800033f1f480
[ 213.546731] SYSC_setpgid(): p@ffff800031418000,bash,1426,1426 current@ffff8000321be580,bash,1419,1419 current_group@ffff8000321be580,bash,1419,1419 pgrp@ffff80002fc70280
[ 213.558916] SYSC_execve(): /usr/bin/dmesg current@ffff800031614880,bash,1425,1425