如何在*每个*进程开始时将PID打印到终端?

如何在*每个*进程开始时将PID打印到终端?

如果我开始一个异步(“后台”)进程,一些信息,包括新进程的 PID,在进程运行之前被打印到终端;例如

$ sleep 3 &
[1] 8217
$ 
[1]  + done       sleep 3
$ 

有没有办法在开始时打印这样的信息(特别是PID)每一个进程,而不仅仅是那些异步启动的进程?


背景

想要这样做的原因是,由于我日常工作设置的特殊性,经常会发生这样的情况:同步长时间运行的进程无法响应Ctrl-C。 (总是,使这些进程“长时间运行”的原因是它们产生的输出比我预期的要多得多。)停止此类进程的最可靠方法是kill -9从不同的窗口访问它,并且最好有它的为此,PID 随时可用。

更新:在我原来的帖子中,我忽略了这Ctrl-Z不是一个选择。 (我正在开发在 Emacs 下运行的 shell,因此Ctrl-Z只需暂停 Emacs。)

答案1

作为史蒂芬·基特解释说,让 zsh 打印 PID 会相当困难。但您可以通过其他方式获取信息。

您可以按Ctrl+Z暂停进程,然后 zsh 显示其 PID。如果你想杀死它,请尝试先按Ctrl+ C,直接杀死它。如果Ctrl+C失败,请尝试Ctrl+\进行“更难”的终止(Ctrl+C发送 SIGINT,通常告诉程序停止当前操作并将控制权交还给用户;Ctrl+\发送 SIGQUIT,通常告诉程序严重崩溃)。您甚至可以在 Emacs 中执行此操作:在 Shell 模式下,按C-c C-z传递C-z到终端、C-c C-c传递C-cC-c C-\传递C-\。在 Term 模式下,C-zC-\是直接传递的,但需要C-c C-c传递单个C-c.

如果进程更改了终端设置或阻止了信号,则通过终端定位它并进行查杀的便捷方法是通过终端。找出终端是什么;您可以使用tty终端内的命令来执行此操作。您可以将这部分作为提示符或终端标题的一部分(我将其放在终端标题中)。 Emacs 不显示终端标题,但它允许您通过计算以下表达式来访问信息:

(process-tty-name (get-buffer-process (current-buffer)))

要在大多数模式(包括 Shell 模式)下计算表达式,请键入M-:然后输入表达式。在术语模式下,键入C-c M-x eval-expression RET然后输入表达式。如果您经常使用此功能,请将以下命令绑定到相关模式下的按键:

(defun buffer-process-tty-name ()
  (interactive)
  (let ((tty (process-tty-name (get-buffer-process (current-buffer)))))
    (if (interactivep) (message "%s" tty))
    tty))

一旦您知道了终端名称,您就可以使用例如ps -t pts/42pgrep -t pts/42来列出连接到该终端的进程。

答案2

似乎没有一种方法可以记录这些信息,并更详细地研究它,很难正确设计(我最初认为这也很难实现,但是伊尔卡丘修复了这个问题)。

我认为实施问题是将信息记录在正确的位置。当 shell 启动一个子进程时,它会分叉,然后执行该子进程(除非它可以用子进程替换自身,在这种情况下它不会分叉)。当它分叉时,该过程会重复;其中一个副本从fork()调用中获取返回码 0,这表明它是子进程,而另一个副本则获取子进程的标识符,这表明它是父进程。子进程继承父进程的文件描述符,因此它可以在进行设置之前记录自己的 pid exec()

设计问题是选择记录什么。如果我们记录 shell 启动的所有进程,我们最终会得到大量无关的日志,例如对于构建提示所涉及的过程!即使对于“真实”命令,我们也需要决定为管道和其他复合命令记录哪些内容...然后为了保持一致性,我们需要为内置命令或 shell 替换的情况提供一些记录内容本身与孩子(尽管我认为交互式 shell 不会发生这种情况,除非您手动exec)。

如果您想进一步探索这一点,请查看addproc()printjobs()in jobs.c,以及zexecve()in exec.c

相关内容