我基本上想在新的进程组中运行一个脚本(调用更多脚本),以便我可以向该脚本调用的所有进程发送信号。
在Linux中,我发现setsid可以帮助我做到这一点,但这在FreeBSD上不可用。
setsid 的语法(由 util-linux-ng 提供)。
setsid /path/to/myscript
然而,我了解到会话和进程组并不相同。但开始新的会话也解决了我的问题。
会议和小组不是一回事。让我们把事情清理干净:
一个会话由一个或多个进程组组成,并且可以有一个控制终端。当会话具有控制终端时,会话在任何时刻都恰好具有一个前台进程组和一个或多个后台进程组。在这种情况下,前台进程组中的每个进程都可以看到所有终端生成的信号和输入。
此外,当会话具有控制终端时,shell 进程通常是会话领导者,指示哪个进程组是前台进程组(隐式地使其他组成为后台进程组)。组中的进程通常通过线性管道放置在那里。例如,
ls -l | grep a | sort
通常会创建一个新的进程组,其中包含ls
、grep
和sort
。
支持作业控制的 shell(也需要内核和终端驱动程序的支持),就像 bash 一样,为每个调用的命令创建一个新的进程组——如果您调用它在后台运行(使用
&
表示法),该进程组没有获得终端的控制权,并且 shell 使其成为后台进程组(并且前台进程组仍然是 shell)。
因此,正如您所看到的,在这种情况下您几乎肯定不想创建会话。您想要创建会话的典型情况是如果您正在守护进程,但除此之外,创建新会话通常没有太大用处。
您可以将脚本作为后台作业运行,正如我提到的,这将创建一个新的进程组。由于
fork()
继承了进程组ID,因此脚本执行的每个进程都将位于同一组中。例如,考虑这个简单的脚本:
#!/bin/bash
ps -o pid,ppid,pgid,comm | grep ".*"
这会打印出类似的内容:
PID PPID PGID COMMAND
11888 11885 11888 bash
12343 11888 12343 execute.sh
12344 12343 12343 ps
12345 12343 12343 grep
如您所见,
execute.sh
、ps
和grep
都位于同一进程组(PGID
中的值)。
所以你想要的是:
/path/to/myscript &
然后可以用
myscript
查看ps -o pid,ppid,pgid,comm | grep myscript
的进程组ID。要向组发送信号,请使用 kill
和进程组 ID 的 负(PGID
是组领导者的 PID
)。发送到组的信号会传递到该组中的每个进程。在上面的示例中,要将 SIGTERM
发送到由 execute.sh
启动的每个进程(包括脚本本身),您可以使用 kill -- -12343
。 (请注意,向整个组发送信号与仅向组长发送信号不同:kill 12343
和 kill -- -12343
是不同的!)
使用 FreeBSD,您可以尝试使用
script
命令,该命令将在内部执行 setsid
命令。
stty -echo -onlcr # avoid added \r in output
script -q /dev/null /path/to/myscript
stty echo onlcr
# sync # ... if terminal prompt does not return
这并不完全是答案,而是一种基于名称的替代方法。
您可以为所有进程使用名称的公共部分。例如,我们有 my_proc_group_29387172 部分用于以下所有进程:
-rwxrwxr-x. my_proc_group_29387172_microservice_1
-rwxrwxr-x. my_proc_group_29387172_microservice_2
-rwxrwxr-x. my_proc_group_29387172_data_dumper
生成所有它们(只要你想要):
ADDR=1 ./my_proc_group_29387172_microservice_1
ADDR=2 ./my_proc_group_29387172_microservice_1
ADDR=3 ./my_proc_group_29387172_microservice_2
./my_proc_group_29387172_data_dumper
当您想终止所有进程时,可以使用 pkill 命令(模式杀死)或 killall 以及 --regexp 参数:
pkill my_proc_group_29387172
好处:) - 您可以在任何时间(或任何一天)从任何脚本启动任意数量的进程。
缺点 :( - 如果无辜的进程与你的模式有共同的名称部分,你可以杀死它们。
您可以使用 set -m
在 bash 中临时启用
作业控制。启用后,每个子进程都会在单独的进程组中创建。但作业控制还支持更多功能:
Ctrl+Z
、Ctrl+Y
快捷键可能还有其他东西,这在脚本中可能是不需要的。因此,您可以在子进程分叉后立即禁用作业控制:
#!/bin/bash
set -m
/path/to/myscript &
set +m
这也适用于复合命令,例如:
#!/bin/bash
set -m
{
while true; do
/bin/some_server
done
} &
set +m
server_pgid=$!
...
kill -- -$server_pgid
在此示例中,
/bin/some_server
进程以及包装进程将被终止。注意这里采用的是进程组查杀方式(PGID前缀为-
)。
您可以在 bash 手册页中阅读有关 job control 的更多信息。