我有一个假设的情况:
假设我们有两个 strace 进程 S1 和 S2,它们只是互相监视。
这怎么可能?
好吧,在 strace 的命令行选项中,-p PID
是传递所需 PID 的方法,在我们发出 strace 命令时(在我们的例子中)还不知道。我们可以更改 strace 源代码,这意味着-P 0
向用户询问 PID。例如,从 STDIN read()。当我们可以在两个 shell 会话中运行两个 strace 进程并在第三个 shell 中找到 PID 时,我们可以将该输入提供给 S1 和 S2 并让它们互相监视。
S1和S2会卡住吗?或者,进入无限循环,或者立即崩溃或者......?再次,假设我们有另一个 strace 进程 S3,
-p -1
通过修改源代码,我们用它来告诉 S3 监视自身。例如,使用 getpid() 而不使用 STDIN。 S3会崩溃吗?或者,它会挂起而无法进行进一步处理吗?它是否会等待某个事件发生,但因为它正在等待,所以不会发生任何事件?
在 strace 手册页中,它说我们无法监视 init 进程。strace 或内核是否强制执行任何其他限制以避免循环依赖或循环?
一些特殊情况:
S4 监视 S5,S5 监视 S6,S6 监视 S4。
S7 和 S8 相互监视,其中 S7 是 S8 的父级。
更特殊的情况也是可能的。
编辑(@Ralph Rönnquist 和 @pfnuesel 发表评论后):
https://github.com/bnoordhuis/strace/blob/master/strace.c#L941
if (pid <= 0) {
error_msg_and_die("Invalid process id: '%s'", opt);
}
if (pid == strace_tracer_pid) {
error_msg_and_die("I'm sorry, I can't let you do that, Dave.");
}
strace.c
具体来说,如果不检查pid == strace_tracer_pid
或者有其他特殊情况会怎样?一个进程监控本身是否存在任何技术限制(在内核中)?一组 2 个(或 3 个或更多)进程进行自我监控怎么样?系统会崩溃或挂起吗?
答案1
% sh -c 'exec strace -p $$'
strace: I'm sorry, I can't let you do that, Dave.
:-)
答案2
我只回答Linux。
令人惊讶的是,在较新的内核中,为了实际执行跟踪ptrace
而使用的系统调用被允许跟踪 init 进程。strace
手册页说:
EPERM The specified process cannot be traced. This could be because
the tracer has insufficient privileges (the required capability
is CAP_SYS_PTRACE); unprivileged processes cannot trace pro‐
cesses that they cannot send signals to or those running set-
user-ID/set-group-ID programs, for obvious reasons. Alterna‐
tively, the process may already be being traced, or (on kernels
before 2.6.26) be init(8) (PID 1).
这意味着从版本 2.6.26 开始,您可以跟踪init
,当然您仍然必须是 root 才能执行此操作。我系统上的二进制文件strace
允许我跟踪init
,事实上我什至可以用来gdb
附加init
和杀死它。 (当我这样做时,系统立即停止了。)
ptrace
进程不能使用它来跟踪自身,因此如果strace
不检查,它仍然会在跟踪自身时失败。以下程序:
#include <sys/ptrace.h>
#include <stdio.h>
#include <unistd.h>
int main() {
if (ptrace(PTRACE_ATTACH, getpid(), 0, 0) == -1) {
perror(NULL);
}
}
印刷Operation not permitted
(IE,结果是EPERM
)。内核执行这次入住ptrace.c
:
retval = -EPERM;
if (unlikely(task->flags & PF_KTHREAD))
goto out;
if (same_thread_group(task, current)) // <-- this is the one
goto out;
现在,两个strace
进程可以互相跟踪;内核不会阻止这种情况,你可以自己观察结果。对我来说,第一个进程(PID = 5882)打印的最后一件事strace
是:
ptrace(PTRACE_SEIZE, 5882, 0, 0x11
而第二个strace
进程(PID = 5890)根本不打印任何内容。ps
显示两个进程都处于 state t
,根据proc(5)
手册页,这意味着跟踪已停止。
发生这种情况的原因是,只要被跟踪者进入或退出系统调用以及即将向其传递信号(除了SIGKILL
),它就会停止。
假设进程 5882 已经在跟踪进程 5890。那么,我们可以推断出以下事件序列:
- 进程5890进入
ptrace
系统调用,尝试跟踪进程5882。进程5890进入跟踪停止。 - 进程 5882 收到
SIGCHLD
通知,其被跟踪者进程 5890 已停止。 (跟踪停止的进程看起来好像收到了“SIGTRAP”信号。) - 进程 5882 看到它的被跟踪者已经进行了系统调用,就尽职尽责地打印出有关进程 5890 将要进行的系统调用的信息以及参数。这是您看到的最后一个输出。
- 进程 5882 调用
ptrace(PTRACE_SYSCALL, 5890, ...)
以允许进程 5890 继续。 - 进程 5890 离开跟踪停止并执行其
ptrace(PTRACE_SEIZE, 5882, ...)
.当后者返回时,进程5890进入跟踪停止。 - 进程 5882 被发送,
SIGCHLD
因为它的被跟踪者刚刚再次停止。由于正在跟踪,因此接收到信号会使其进入跟踪停止状态。
现在两个进程都停止了。结束。
从这个例子中可以看出,两个进程相互跟踪的情况不会给内核带来任何固有的逻辑困难,这可能就是为什么内核代码不包含检查来防止这种情况发生的原因。只是对于两个进程相互跟踪来说并不是很有用。
答案3
只是想向您展示一个可能导致系统冻结的循环依赖或循环的真实示例。
在您的 X 会话和图形终端模拟器中,运行以下命令来获取 Xorg.bin pid:
[xiaobai@xiaobai tmp]$ pgrep Xorg
1780
[xiaobai@xiaobai tmp]$
然后做:
[xiaobai@xiaobai tmp]$ sudo strace -p 1780
几秒钟(~5)后它会冻结整个桌面,至少在我的 Fedora 21 gnome 3.14.0 中,我必须关闭电源按钮。
但是,如果您尝试sudo strace -p 1780
通过 Ctrl-alt-F1|7 或 在另一个 tty 中运行sudo strace -p 1780 2>/tmp/strace.log
,则两者都不会冻结。
因此我们可以得出结论,strace 接收 Xorg 的输出,然后将其打印到 Xorg 并导致无限循环和冻结。