在此之前和此处已经提出了与该问题类似的观点,并且我知道Google
coredump库(我已经评估并发现它缺乏,尽管如果我对问题有更好的了解,我可能会尝试解决该问题)
)。
我想获得一个正在运行的Linux进程的核心转储,而不会中断该进程。自然的做法是说:
if (!fork()) { abort(); }
由于分叉的进程获得了原始进程内存的固定快照副本,因此我应该获得完整的核心转储,并且由于该副本使用写时复制,因此它通常应该很便宜。但是,此方法的一个关键缺点是,fork()
仅派生了当前线程,而原始进程的所有其他线程将不存在于派生副本中。
我的问题是,是否有可能以某种方式获得其他原始线程的相关数据。我不确定如何解决这个问题,但是我提出了几个子问题:
包含所有线程堆栈的内存在分支过程中是否仍然可用并可以访问?
是否可以(快速)枚举原始进程中所有正在运行的线程并存储其堆栈基址的地址?据我了解,Linux上线程堆栈的基础包含一个指向内核线程簿记数据的指针,因此…
利用存储的线程基址,您是否可以在分支过程中读出每个原始线程的相关数据?
如果可能的话,也许只需要将其他线程的数据附加到核心转储即可。但是,如果该数据已经在分叉点丢失了,那么这种方法似乎就没有希望了。
您是否熟悉流程检查点重新启动?特别是CRIU?在我看来,它可能为您提供了一个简单的选择。
我想获得一个正在运行的Linux进程的核心转储,而不会中断该进程[并且]以某种方式获得其他原始线程的相关数据。
忘记不要中断该过程。如果考虑一下,核心转储 必须
在转储期间中断该进程;因此,您的真正目标必须是最大程度地减少这种中断的时间。您最初的使用想法fork()
确实会中断该过程,只是在很短的时间内就会中断。
- 包含所有线程堆栈的内存在分支过程中是否仍然可用并可以访问?
否。fork()
唯一保留执行实际调用的线程,其余线程的堆栈丢失。
假设CRIU不适合,这是我要使用的过程:
您可以使用来检测停止/继续事件waitpid(child,,WUNTRACED|WCONTINUED)
。
sched_setaffinity()
将进程限制为单个CPU,并且sched_setscheduler()
(也许sched_setparam()
)将进程优先级降低为IDLE
。您可以从父进程执行此操作,该父进程仅在有效组和允许组中都具有该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
/优先级操纵,我也可以获得足够的同步(线程停止之间的足够小的延迟)。
您是否需要一些示例代码来说明我的上述想法?
问题内容: 据我所知,操作系统创建线程时,每个线程都会获得一个不同的堆栈。我想知道每个线程是否也有与自己不同的堆吗? 问题答案: 否。所有线程共享一个公共堆。 每个线程都有一个专用堆栈,它可以快速添加和删除其中的项目。这样可以使基于堆栈的内存速度更快,但是,如果您使用过多的堆栈内存(如无限递归中所发生的那样),则会导致堆栈溢出。 由于所有线程共享同一个堆,因此必须同步对分配器/释放器的访问。有许多
问题内容: 可以使用fork()函数复制多线程进程。如果是这样,那么所有线程都将完全相同,如果不是,为什么不这样做。如果无法通过fork完成复制,是否还有其他功能可以帮助我? 问题答案: 进行分叉后,子进程中只有一个线程正在运行。这是POSIX标准要求。
我想举例说明一个关于铁路的项目。 我决定使用Swing。我在JPanel中有一个背景图,我画了在铁路上移动的小圆圈。如果我只有一列火车,它会非常完美,但是我想增加更多的火车。 这是我开始做的(和工作): “go”读取一个数组列表,其中包含我的圆应该指向的坐标。 我真的不知道如何创造几列火车。我应该创建几个JPanel还是只创建一个包含所有圆圈的JPanel? 如果我记得很清楚,我应该使用线程,但我
我有一段代码 如您所见,我首先将标志设置为false,这样其中一个线程就可以进入Sum2Elements方法并将其更改为true,从而让所有人都等待。 我知道在同步代码中,只有一个线程可以完成它的任务,这里我有两个同步方法,这是否意味着两个线程在每次通知之后都在尝试执行这个方法? 如果是这样,那么一个线程是否不可能输入Sum2Elements,在另一个线程进入InsertElement之前将标志更
问题内容: 在Java多线程中,术语和之间在语义上有区别吗? 问题答案: 每个线程都有自己的调用堆栈,“调用堆栈”和“线程堆栈”是同一件事。将其称为“线程堆栈”只是强调了调用堆栈特定于线程。 Bill Venners将此称为Java堆栈: 启动新线程时,Java虚拟机将为该线程创建一个新的Java堆栈。如前所述,Java堆栈将线程的状态存储在离散的帧中。Java虚拟机仅直接在Java堆栈上执行两项
问题内容: 我以前在应用程序中使用过线程,并且对线程的概念非常了解,但是最近在我的操作系统讲座中,我遇到了fork()。这类似于线程。 我用谷歌搜索了它们之间的区别,我知道: Fork只是一个看起来与旧流程或父流程完全相似的新流程,但它仍然是具有不同流程ID并拥有自己内存的不同流程。 线程是轻量级进程,具有较少的开销 但是,我仍然有一些疑问。 什么时候应该更喜欢fork()而不是线程? 如果我想以