簡體   English   中英

當分叉的進程死亡時,該進程中的分離線程會如何處理?

[英]What happens to a detached thread inside a forked process when the process dies?

我的程序在需要處理任何事情時都會派生,並且在每個分叉的進程中,我都會分離一個線程,以便從該分叉的進程中記錄統計信息:該線程循環以收集數據,但沒有停止該循環的實際條件。

我讀過“當main()退出時,分離線程會發生什么?” 那:

如前所述,任何線程,無論是否分離,都將在大多數操作系統上隨其進程消失。

在我的程序中,我沒有為循環線程提供任何停止條件,因為當它產生的進程將死亡時,分離的線程將隨之死亡。 無論如何,我覺得自己理所當然地想做些什么,所以我編寫了以下代碼來簡化我的懷疑,並將多余的部分從我的原始程序中排除。

在此代碼中,每個分叉的進程都產生一個線程,該線程將打印一些數字。

#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

每個派生進程死亡時,都會出現相同的錯誤(警告?)消息。

最終輸出是關於main()進程PID 13533的:

==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)

我不知道如何讀取所有valgrind輸出,也不知道我處理分離線程的方式是否正確:我使用的是C ++ 11,並且由於不提供垃圾收集器,因此我不知道不知道那些possibly lost並且still reachable字節是否可能降低我的程序的性能; 我經常用fork()(即使每個分叉的進程都有幾秒鍾的生命期),每個分叉的進程也會產生一個記錄某些統計信息的分離線程。 當分叉的進程死亡時,線程也隨之死亡,但是我不知道從長遠來看我的程序是否會因為valgrind向我顯示的那些字節而變慢。

您認為我的擔憂合理嗎? 我是否在處理分支過程中分離線程的死亡而陷入僵局?

當您調用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/a/aa775a2960db09db

產量

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

由於我們自終止threadFn2,因此可以調用OnExit dtor,但threadFn1卻被殘酷地終止。

“當分叉的進程死亡時,該進程中的分離線程會發生什么?” -線蒸發。 線程存在於進程中。 當進程終止時,其中的線程也會終止。

線程死亡。 泄漏是由於以下事實造成的:線程沒有完成其運行,也沒有釋放其自己的資源。 如果您在主線程中的睡眠時間更長,則不會有任何泄漏。

閱讀有關pcap的評論后進行編輯:

正確的方法:不要分離。 使主線程可以訪問所有pcap句柄。 當main要停止時,它將關閉所有pcap,並加入所有線程。 當線程遇到pcap錯誤時,它們退出。 如果做對了,您將不會泄漏,並且干凈整潔。

進程是使您的線程保持活動的元素。 當過程進行時,線程跟隨。 唯一的問題是,無論如何,您的線程是否獲取了需要釋放的系統級資源。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM