当前位置: 首页 > 知识库问答 >
问题:

当进程死亡时,分叉进程中分离的线程会发生什么?

林波鸿
2023-03-14

我的程序每次要处理某件事情时都会分叉,在每个分叉进程中,我都分离一个线程,以便从分叉进程中记录统计数据:这个线程循环收集数据,但它没有停止这个循环的实际条件。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <thread>

void threadFoo(int id) {
    int i=0;

    // this loop will simulate some stas collecting
    while (i<1000000) {
        printf("[%d]%d \t", id, i);
        ++i;
    }
    printf("\n\n\n");
    return;
}

void forkFoo(int id) {
    std::thread t(threadFoo, id);
    t.detach();
    printf("PID %d detached thread\n", getpid());
    return;
}


int main(void) {

    int i;
    pid_t pid;

    for (i=0; i<3; i++) {
        pid = fork();
        if (pid == 0) {
            forkFoo(i);
            // this sleep will simulate some work
            sleep(1);
            printf("Proc %d about to terminate...even its detached thread?\n");
            _exit(EXIT_SUCCESS);
        }
        else if(pid > 0) {
            // wait for all children to terminate
            wait(NULL);
        }
    }

    printf("main() about to terminate...\n");
}

程序的输出证实了每一个线程都随其工艺而死

PID 13476 detached thread
[0]0    [0]1    [0]2    [0]3  ...
... [0]48940    [0]48941    Proc 13476 about to terminate...even its detached thread?
PID 13478 detached thread
[1]0    [1]1    [1]2    [1]3 ... [1]42395   [1]42396    Proc 13478 about to terminate...even its detached thread?
PID 13480 detached thread
[2]0    [2]1    [2]2    [2]3 ...
... [2]41664    [2]41665    Proc 13480 about to terminate...even its detached thread?
main() about to terminate...

当我使用valgrind--leak-check=full--show-leak-kinds=all运行这个程序时,引起了一些疑问:当每个分叉进程死亡时,valgrind会显示一些令人毛骨悚然的输出(13534是分叉进程PID):

==13534== HEAP SUMMARY:
==13534==     in use at exit: 352 bytes in 2 blocks
==13534==   total heap usage: 2 allocs, 0 frees, 352 bytes allocated
==13534== 
==13534== 64 bytes in 1 blocks are still reachable in loss record 1 of 2
==13534==    at 0x4C2B145: operator new(unsigned long) (vg_replace_malloc.c:333)
==13534==    by 0x401DB5: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (new_allocator.h:104)
==13534==    by 0x401CE1: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) (alloc_traits.h:351)
==13534==    by 0x401B41: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >*, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr_base.h:499)
==13534==    by 0x401A8B: std::__shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr_base.h:957)
==13534==    by 0x401A35: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >::shared_ptr<std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::_Sp_make_shared_tag, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:316)
==13534==    by 0x4019A9: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::allocate_shared<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > >, std::_Bind_simple<void (*(int))(int)> >(std::allocator<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > const&, std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:598)
==13534==    by 0x401847: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::make_shared<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >, std::_Bind_simple<void (*(int))(int)> >(std::_Bind_simple<void (*(int))(int)>&&) (shared_ptr.h:614)
==13534==    by 0x401621: std::shared_ptr<std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> > > std::thread::_M_make_routine<std::_Bind_simple<void (*(int))(int)> >(std::_Bind_simple<void (*(int))(int)>&&) (thread:193)
==13534==    by 0x4012AB: std::thread::thread<void (&)(int), int&>(void (&)(int), int&) (thread:135)
==13534==    by 0x400F42: forkFoo(int) (funwiththreadinsidefork.cpp:21)
==13534==    by 0x400FBD: main (funwiththreadinsidefork.cpp:36)
==13534== 
==13534== 288 bytes in 1 blocks are possibly lost in loss record 2 of 2
==13534==    at 0x4C2C9B4: calloc (vg_replace_malloc.c:711)
==13534==    by 0x4012E14: allocate_dtv (dl-tls.c:296)
==13534==    by 0x4012E14: _dl_allocate_tls (dl-tls.c:460)
==13534==    by 0x5359D92: allocate_stack (allocatestack.c:589)
==13534==    by 0x5359D92: pthread_create@@GLIBC_2.2.5 (pthread_create.c:500)
==13534==    by 0x4EE8CAE: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==13534==    by 0x4012D1: std::thread::thread<void (&)(int), int&>(void (&)(int), int&) (thread:135)
==13534==    by 0x400F42: forkFoo(int) (funwiththreadinsidefork.cpp:21)
==13534==    by 0x400FBD: main (funwiththreadinsidefork.cpp:36)
==13534== 
==13534== LEAK SUMMARY:
==13534==    definitely lost: 0 bytes in 0 blocks
==13534==    indirectly lost: 0 bytes in 0 blocks
==13534==      possibly lost: 288 bytes in 1 blocks
==13534==    still reachable: 64 bytes in 1 blocks
==13534==         suppressed: 0 bytes in 0 blocks

相同的错误(警告?)每个分叉进程死亡时的消息。

==13533== HEAP SUMMARY:
==13533==     in use at exit: 0 bytes in 0 blocks
==13533==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==13533== 
==13533== All heap blocks were freed -- no leaks are possible
==13533== 
==13533== For counts of detected and suppressed errors, rerun with: -v
==13533== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

共有1个答案

白高逸
2023-03-14

当您调用std::thread::Detach时,它不会将线程与进程解耦,它只是将std::thread实例与线程解耦。它的堆栈是从进程的内存中分配的,它仍然与进程共享内存和资源:当进程停止时,它将线程带出去。

而且它没有优雅地完成,没有调用任何析构函数,甚至没有释放堆栈(这就是为什么您会看到漏洞)。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <atomic>

struct OnExit
{
    const char* id = "none";
    ~OnExit()
    {
        std::cout << "Exiting " << id << std::endl;
    }
};

thread_local OnExit onExit;

void threadFn1()
{
    onExit.id = "threadFn1";
    for (size_t i = 0; i < 100000; ++i) {
        std::cout << onExit.id << std::endl;
        std::this_thread::sleep_for(std::chrono::microseconds(50));
    }
}

std::atomic<bool> g_running { true };

void threadFn2()
{
    onExit.id = "threadFn2";
    while (g_running) {
        std::cout << onExit.id << std::endl;
        std::this_thread::sleep_for(std::chrono::microseconds(50));
    }
}

int main()
{
    std::thread t1(threadFn1);
    std::cout << "started t1\n";
    t1.detach();
    std::cout << "detached t1\n";

    std::thread t2(threadFn2);
    std::cout << "started t2\n";
    std::this_thread::sleep_for(std::chrono::microseconds(500));

    std::cout << "ending\n";
    g_running = false;
    t2.join();
}

现场演示:http://coliru.stacked-crooked.com/aa775a2960db09db

输出

started t1
detached t1
started t2
threadFn2
threadFn1
threadFn2
threadFn2
ending
Exiting threadFn2

因为我们自终止threadFn2,所以它可以调用onexitdtor,但是threadFn1被残忍地终止了。

 类似资料:
  • 问题内容: 我知道从线程调用是一个坏主意。但是,如果线程使用创建新进程,将会发生什么? 新进程将是创建线程的主线程的子进程。我认为。 如果其父级先完成,则新进程将附加到init进程中。它的父级是主线程,而不是创建它的线程。 如果我错了,请纠正我。 问题答案: 新进程将是创建线程的主线程的子进程。我认为。 创建一个新的过程。一个进程的父进程是另一个进程,而不是线程。因此,新流程的父级是旧流程。 请注

  • 使用Ubuntu 13.10和运行节点v0.10.0。我正在使用pty.jsv0.2.4生成一个程序(需要在交互式环境中运行)。该程序是用C编写的,并分叉子进程本身。 我已经编写了一个非常精简的C程序版本(我称之为“forktest”),它具有产生此问题所需的最小值,并包含以下内容: 我还整理了一个最小节点脚本,用coffeescript(test.coffee)编写,它运行程序,如下所示: 运行

  • 在命令行中运行时,会出现以下失败 在肯定的情况下-报告说 null 谢谢你的帮助

  • 群集环境: Storm0.9.2 Zookeeper 3.4.6 Ubuntu 13.10

  • 问题内容: 在不让父进程等待子进程被杀死的情况下,我如何跟踪子进程的死亡? 我正在尝试一个客户端-服务器方案,其中服务器从客户端接受连接,并为它接受的每个连接派生一个新进程。 我忽略了SIGCHLD信号以防止僵尸创建。 上述情况下的问题是,如果子进程在函数中被杀死,则全局变量不会递减。 注意: 我正在寻找不使用SIGCHLD信号的解决方案…如果可能 问题答案: 通常,您编写一个调用pid 的处理程

  • 问题内容: 我有一个程序生成并与CPU繁重,不稳定的进程通信,而不是由我创建的。如果我的应用程序崩溃或被杀死,我也希望子进程也被杀死,因此用户不必跟踪它们并手动杀死它们。 我知道以前已经讨论过该主题,但是我已经尝试了所有描述的方法,但似乎没有一种方法能够经受住测试的考验。 我知道这是有可能的,因为终端一直在这样做。如果我在终端中运行某些程序并杀死该终端,则这些东西总是会死掉。 我试过了,双叉和。不