我从下面2个来源果断得出结论:Shell的进程组ID=前台作业进程组ID。当选择后台作业在前台运行时,是shell的进程组ID更改为前台作业的进程组ID还是相反?
1.
为了便于实现作业控制的用户界面,操作系统维护当前终端进程组ID的概念。该进程组的成员(进程组 ID 等于当前终端进程组 ID 的进程)接收键盘生成的信号,例如 SIGINT。据说这些进程位于前台。后台进程是指进程组ID与终端进程组ID不同的进程;这些进程不受键盘生成的信号的影响。(来源)
2.
添加:
$sleep 3000 &
$sleep 2000 &
$ps xao pid,ppid,pgid,sid,tty,comm | grep tty
PID PPID PGID SID TTY COMMAND
1153 1135 1153 1153 tty1 bash
1173 1153 1173 1153 tty1 sleep
1189 1153 1189 1153 tty1 sleep
1219 1153 1219 1153 tty1 ps
1220 1153 1219 1153 tty1 grep
答案1
我从下面2个来源果断得出结论:Shell的进程组ID=前台作业进程组ID。
您不应该这样做,因为您引用的两个来源都没有声称 shell 的 PGID 等于前台作业 PGID。
shell 的 PGID 不会更改。在终端中运行交互式 shell 的正常情况下,shell 位于其自己的进程组中,并且 shell 的 PGID 等于 shell 的 PID。
每个进程都有一个 PGID。该终端有一个关联的前台进程组 ID。根据定义,前台进程组是具有该 PGID 的进程组。
请注意,如果终端由终端仿真器提供,则这些与终端仿真器进程的 PID 之间没有任何关系。如果您考虑完全由内核提供的硬件终端,这一点应该是显而易见的:在这种情况下没有终端仿真器进程。
终端关联的PGID可以通过tcsetpgrp
函数,当 shell 在前台启动外部程序或使用 . 将作业移动到前台时,会调用该函数fg
。
答案2
这个想法来自 Bill Joy 1979-1980 年左右的 csh。Bill Joy 添加了相关的内核支持以启用此功能。
因为当时原始的 csh 已经使用 vfork() 支持编写,所以很难理解 csh 中的代码,因为 vfork() 需要从子级中的操作恢复父级中的影响,而子级中的操作是子级之间共享内存的结果vfork() 中的父级和子级。
如果您想了解有效的实现,我建议您阅读 Bourne Shell 源代码。
shell 在启动时检查它是否是进程组领导者,如果需要,则使自己成为进程组领导者。
当然,shell 在其生命周期内会保留此 ID,除非它在启动时不是进程组领导者并且喜欢自行挂起。请注意,bash 并不完全支持这一点...
为了使已启动的作业易于管理,每个作业都在单独的新且自己的进程组 ID 中运行。