C 叉子漏水?花越来越长的时间来完成一个简单的过程
我有一个系统,其中运行两个相同的进程(让我们称它们为副本)。当发出信号时,副本将使用C 叉子漏水?花越来越长的时间来完成一个简单的过程,c,linux,linux-kernel,fork,C,Linux,Linux Kernel,Fork,我有一个系统,其中运行两个相同的进程(让我们称它们为副本)。当发出信号时,副本将使用fork()调用复制自身。第三个进程选择其中一个进程随机终止,然后向另一个进程发出信号以创建替换进程。在功能上,系统运行良好;它可以全天杀死/恢复副本,但性能问题除外 fork()调用花费的时间越来越长。以下是仍然显示问题的最简单设置。正时将显示在下图中: 复制副本的代码如下所示: void restartHandler(int signo) { // fork timestamp_t last = gene
fork()
调用复制自身。第三个进程选择其中一个进程随机终止,然后向另一个进程发出信号以创建替换进程。在功能上,系统运行良好;它可以全天杀死/恢复副本,但性能问题除外
fork()
调用花费的时间越来越长。以下是仍然显示问题的最简单设置。正时将显示在下图中:
复制副本的代码如下所示:
void restartHandler(int signo) {
// fork
timestamp_t last = generate_timestamp();
pid_t currentPID = fork();
if (currentPID >= 0) { // Successful fork
if (currentPID == 0) { // Child process
timestamp_t current = generate_timestamp();
printf("%lld\n", current - last);
// unblock the signal
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
return;
} else { // Parent just returns
waitpid(-1, NULL, WNOHANG);
return;
}
} else {
printf("Fork error!\n");
return;
}
}
int main(int argc, const char **argv) {
if (signal(SIGUSR1, restartHandler) == SIG_ERR) {
perror("Failed to register the restart handler");
return -1;
}
while(1) {
sleep(1);
}
return 0;
}
系统运行的时间越长,情况就越糟
很抱歉,缺少一个具体的问题,但是有人对正在发生的事情有任何想法/线索吗?在我看来,内核中似乎存在资源泄漏(因此linux内核标签),但我不知道从哪里开始查找
我所尝试的:
- 试过了,什么也没钓到。这意味着,如果存在一些内存“泄漏”,那么它仍然是可访问的
没有增长/proc//maps
- 目前正在运行带有RT补丁的3.14内核(注意,这种情况发生在非RT和RT进程上),并且已经在3.2上进行了尝试
- 僵尸进程不是问题。我试过一个版本,在这个版本中,我使用
- 我首先在一个系统中注意到这种减速,在这个系统中,定时测量值在重新启动的进程之外下降;同样的行为
有什么提示吗?有什么我能帮忙的吗?谢谢 只是一个想法:可能与MMU或缓存有关?据我所知,在fork()上,内核用对相同物理RAM页面的引用填充适当的表条目。您编写了,您正在进行虚拟写入,但您是否正在对可执行segmen执行这些操作(如果是,如何执行,因为这些应该是写保护的)?从图中可以看出,性能在某些点上有所提高(512?512*3?512*4?)。这让我怀疑系统(内核?、硬件?)意识到了这个问题,并使用了一些解决方法(在MMU上为同一物理页复制antries?一些数据结构被拆分?。也许您可以尝试使用通用的wait()调用,而不是waitpid()?这只是一个猜测,但我从一个大学教授那里听说更好。还有,你试过使用吗 此外,您还可以使用GDB调试子进程(如果您还没有尝试过的话)。您可以使用follow fork模式:
set follow-fork-mode child
但这只能调试父级。通过获取子进程的pid,在分叉后调用sleep(),可以调试这两个进程:
attach <child process pid>
这很有用,因为您可以将内存泄漏转储到valgrind中。打电话给valgrind
valgrind --vgdb-error=0...<executable>
然后:
监视器块列表
减速是由匿名VMA的累积造成的,这是一个已知的问题。当有大量的fork()
调用并且父类在子类之前退出时,问题就很明显了。以下代码重新创建了问题():
#包括
int main(int argc,char*argv[])
{
pid_t pid;
而(1){
pid=fork();
如果(pid==-1){
/*错误*/
返回1;
}
如果(pid){
/*母公司*/
睡眠(2);
打破
}
否则{
/*孩子*/
睡眠(1);
}
}
返回0;
}
可以通过在/proc/slabinfo
中检查anon_vma
来确认该行为
有一个补丁()将复制的anon_vma_链的长度限制为5。我可以确认修补程序修复了问题
至于我最终是如何发现问题的,我最终开始在fork
代码中放置printk
调用,检查dmesg
中显示的时间。最后,我发现调用anon_vma_fork花费的时间越来越长。然后是谷歌搜索的快速问题
这花了相当长的时间,所以我仍然希望有人能提出更好的方法来追踪这个问题。对于所有已经花时间试图帮助我的人,谢谢。在新进程开始执行之前,或者更确切地说,在第一条存储指令之前,子进程只是一个精确的副本。在整个系统中,我通过使用mlockall,然后遍历/proc/pid/maps进行虚拟写入来解决这个问题。在完整的系统和我在这里提供的代码中,fork的性能问题仍然存在。您是否使用ps命令查看正在运行的内容(通常“top”实用程序会重复执行此操作)以及在每个运行过程中花费了多少时间?在您的代码中,我看不到任何user1信号的传递,因此怀疑是否有任何子进程被终止。您是否可以尝试使用vWork或clone而不是Fork?您发布的图是众所周知的posix Fork行为(即,Fork计数越大,性能越慢)这是因为每次分叉时,它都会复制页表。请参阅优秀书籍《Linux编程接口》@user3629249中的此表。有一个单独的进程将kill-9
发送到其中一个副本,并将kill-s USR1
发送到另一个副本。
valgrind --vgdb-error=0...<executable>
monitor leak_check full reachable any
monitor block_list <loss_record_nr>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
while (1) {
pid = fork();
if (pid == -1) {
/* error */
return 1;
}
if (pid) {
/* parent */
sleep(2);
break;
}
else {
/* child */
sleep(1);
}
}
return 0;
}