bash 启动程序的流程

文章目录

  • [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 命令输出信息看到:

  • 程序 tPPID == 4598,也即 bashPID,所以 t 是由 bash 启动的;
  • 程序 tPID == 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 错误。glibcsetsid() 是对系统接口 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_testsetsid() 调用在上面代码标注 (1) 处返回,表示进程已经是一个Process Group Leader,而 Linux 规定已经是 Process Group Leader 不能成为新的 Session Leader。如何解决这个问题?方案一是通过 fork() 调用:fork() 后,首先退出父进程,以摆脱 bash 通过 setpgid() 设置的 leader 身份;然后再在子进程中调用 setsid() 将会成功,因为子进程不具备 leader 身份,这正是 glibc 库函数 daemon() 采用的方式;另一个解决方案是不通过 bash 启动程序,而是通过 /etc/rc.localSystemV 的 init script 启动程序。

那为什么不允许 Process Group Leader 成为 Session Leader ?因为一旦允许, 由于 setsid() 新成立了 SessionProcess Group,所以 Process Group Leader 将进入新的 Process GroupSession,这样在原本 Process Group 内进程将失去它们的 Process Group Leader, 这样会引发歧义,同时使得像 bashjob control 也将无法工作。如果你无法理解这些逻辑,就简单的理解为这是 Linux 内核的强制规定就可以了。

我们使用 glibcdaemon() 接口来创建 Sessiondaemon() 通过 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_testprintf() 输出的内容并没有看到,因为 setsid() 会断开与 tty 终端的连接;然后从 ps 命令输出也观察到,setsid_testPID == 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
相关推荐
wdfk_prog19 小时前
[Linux]学习笔记系列 -- hashtable
linux·笔记·学习
CheungChunChiu20 小时前
Linux 内核动态打印机制详解
android·linux·服务器·前端·ubuntu
BlueBirdssh21 小时前
linux 内核通过 dts 设备树 配置pcie 控制器 各种参数和中断等, 那freeRTOS 是通过直接设置PCIe寄存器吗
linux
小目标一个亿1 天前
Windows平台Nginx配置web账号密码验证
linux·前端·nginx
Aotman_1 天前
Element-UI Message Box弹窗 使用$confirm方法自定义模版内容,修改默认样式
linux·运维·前端
那些年的笔记1 天前
Linux屏幕旋转方法
linux·运维·服务器
XiaoHu02071 天前
Linux网络编程套接字
linux·服务器·网络·git
竹之却1 天前
CentOS 系列,防火墙相关指令
linux·运维·centos
一颗青果1 天前
进程组 | 会话 |终端 | 前台后台 | 守护进程
linux·运维·jvm
古城小栈1 天前
Rust 交叉编译:Windows ====> Linux (musl 静态编译)
linux·windows·rust