一尘不染

带有线程的叉和堆芯

linux

在此之前和此处已经提出了与该问题类似的观点,并且我知道Google
coredump库(我已经评估并发现它缺乏,尽管如果我对问题有更好的了解,我可能会尝试解决该问题)
)。

我想获得一个正在运行的Linux进程的核心转储,而不会中断该进程。自然的做法是说:

if (!fork()) { abort(); }

由于分叉的进程获得了原始进程内存的固定快照副本,因此我应该获得完整的核心转储,并且由于该副本使用写时复制,因此它通常应该很便宜。但是,此方法的一个关键缺点是,fork()仅派生了当前线程,而原始进程的所有其他线程将不存在于派生副本中。

我的问题是,是否有可能以某种方式获得其他原始线程的相关数据。我不确定如何解决这个问题,但是我提出了几个子问题:

  1. 包含所有线程堆栈的内存在分支过程中是否仍然可用并可以访问?

  2. 是否可以(快速)枚举原始进程中所有正在运行的线程并存储其堆栈基址的地址?据我了解,Linux上线程堆栈的基础包含一个指向内核线程簿记数据的指针,因此…

  3. 利用存储的线程基址,您是否可以在分支过程中读出每个原始线程的相关数据?

如果可能的话,也许只需要将其他线程的数据附加到核心转储即可。但是,如果该数据已经在分叉点丢失了,那么这种方法似乎就没有希望了。


阅读 254

收藏
2020-06-07

共1个答案

一尘不染

您是否熟悉流程检查点重新启动?特别是CRIU?在我看来,它可能为您提供了一个简单的选择。

我想获得一个正在运行的Linux进程的核心转储,而不会中断该进程[并且]以某种方式获得其他原始线程的相关数据。

忘记不要中断该过程。如果考虑一下,核心转储 必须
在转储期间中断该进程;因此,您的真正目标必须是最大程度地减少这种中断的时间。您最初的使用想法fork()确实会中断该过程,只是在很短的时间内就会中断。

  1. 包含所有线程堆栈的内存在分支过程中是否仍然可用并可以访问?

否。fork()唯一保留执行实际调用的线程,其余线程的堆栈丢失。

假设CRIU不适合,这是我要使用的过程:

  • 有一个父进程,该进程会在子进程停止时生成子进程的核心转储。(请注意,可能会生成多个连续的停止事件;只有第一个停止事件才发生,直到下一个继续事件为止。)

您可以使用来检测停止/继续事件waitpid(child,,WUNTRACED|WCONTINUED)

您可以从父进程执行此操作,该父进程仅在有效组和允许组中都具有该CAP_SYS_NICE功能(setcap 'cap_sys_nice=pe' parent-binary如果您像大多数当前Linux发行版一样启用了文件系统功能,则可以将其提供给父二进制文件使用)。

目的是在一个线程确定要快照/转储的时间到所有线程停止之间的时间最小化其他线程的进度。我尚未测试过更改生效要花多长时间-
当然,更改最早最早会在当前时间片的结尾发生。因此,此步骤可能应该事先完成。

就个人而言,我不会打扰。在我的四核计算机上,SIGSTOP仅下面的语句在线程之间会产生与互斥量或信号量相似的延迟,因此我认为没有必要为更好的同步而努力。

  • 当子进程中的某个线程决定要对其自身进行快照时,它将向SIGSTOP自身发送一个(通过kill(getpid(), SIGSTOP))。这将停止进程中的所有线程。

父进程将收到有关子进程已停止的通知。它将首先检查/proc/PID/task/以获取子进程的每个线程的TID(以及/proc/PID/task/TID/其他信息的伪文件),然后使用附加到每个TID
ptrace(PTRACE_ATTACH, TID)。显然,ptrace(PTRACE_GETREGS, TID, ...)将获得每个线程的寄存器状态,这些状态可以与/proc/PID/task/TID/smaps和一起使用/proc/PID/task/TID/mem以获取每个线程的堆栈跟踪以及您感兴趣的任何其他信息。(例如,您可以创建一个与调试器兼容的内核每个线程的文件。)

当父进程完成转储操作时,它将让子进程继续。我相信您需要发送一个单独的SIGCONT信号来让整个子进程继续运行,而不是仅仅依靠ptrace(PTRACE_CONT, TID),但是我还没有检查这一点。请对此进行验证。

我确实相信上述内容将使进程停止中的线程之间的挂钟时间产生最小的延迟。在Xubuntu上的AMD Athlon II X4
640和3.8.0-29-Generic内核上进行的快速测试表明,紧紧的循环会增加其他线程中的volatile变量,只会使计数器增加几千,这取决于线程数(太多了)我做了一些更具体的测试后发现噪音)。

将进程限制为单个CPU,甚至限制为IDLE优先级,将大大减少该延迟。CAP_SYS_NICE功能使父级不仅可以降低子级进程的优先级,还可以将优先级提高到原始级别。文件系统功能意味着父进程甚至不必设置setuid,CAP_SYS_NICE就足够了。(我认为在父程序中进行一些良好的检查之后,将其安装在大学计算机中就足够安全了,在大学计算机中,学生非常积极地寻找有趣的方式来利用已安装的程序。)

可以创建提供增强功能的内核补丁(或模块),该补丁kill(getpid(), SIGSTOP)还尝试从运行中的CPU中启动其他线程,从而尝试使线程停止之间的延迟更小。就个人而言,我不会打扰。即使没有CPU
/优先级操纵,我也可以获得足够的同步(线程停止之间的足够小的延迟)。

您是否需要一些示例代码来说明我的上述想法?

2020-06-07