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
相关推荐
大志若愚YYZ2 小时前
嵌入式Linux——Shell脚本编程
linux
大志若愚YYZ2 小时前
嵌入式Linux学习——环境变量与配置文件的关系(⭐难理解)
linux·学习
香吧香2 小时前
SNMP 请求响应报文传输分片定位
linux·网络与传输协议
Bowen_CV2 小时前
Linux 系统安装与环境配置实践
linux·运维·服务器
无泊里3 小时前
linux:系统用户命令
linux·运维·服务器
Fcy6484 小时前
Linux下的项目自动化构建-make\makefile详解
linux·运维·自动化·makefile·make
chde2Wang4 小时前
Linux中bash: ls: 未找到命令… 相似命令是: ‘lz‘
linux·运维·bug·bash
楼田莉子5 小时前
Linux学习:进程的控制
linux·运维·服务器·c语言·后端·学习
JiMoKuangXiangQu5 小时前
Linux:文件 mmap 读写流程简析
linux·内存管理·file mmap