簡體   English   中英

pthreads:如何斷言代碼是在單線程上下文中運行的

[英]pthreads: how to assert code is run in a single threaded context

我正在編寫一個C庫,在初始化期間需要fork()。 因此,我想斷言()應用程序代碼(在我的控制范圍之外)從單個線程上下文調用我的庫初始化代碼(以避免眾所周知的“ 線程和fork不混合 ”問題)。 一旦我的庫被初始化,它就是線程安全的(並且期望應用程序級代碼可以創建線程)。 我只關心支持pthreads。

使用pthreads計算當前進程空間中的線程數似乎是不可能的。 實際上,即使googletest也只在Mac OS和QNX上實現了GetThreadCount()

鑒於我無法計算線程,我是否可以在某種程度上斷言單線程上下文?

澄清:如果可能的話,我想避免使用“/ proc”(非可移植),附加庫依賴(如libproc)和LD_PRELOAD樣式的pthread_create包裝。

澄清#2:在我的情況下,使用多個進程是必要的,因為我的庫中的工作人員相對較重(使用webkit)並且可能崩潰。 但是,我希望原始流程能夠在工作人員崩潰中幸存下來。

您可以將庫初始化函數標記為在應用程序main()之前運行。 例如,使用GCC,

static void my_lib_init(void) __attribute__((constructor));

static void my_lib_init(void)
{
    /* ... */
}

另一個選擇是使用posix_spawn()來分叉和執行工作進程作為單獨的從屬二進制文件。

編輯添加:

在我看來,如果你想確定進程是否已經創建(實際的,基於內核的)線程,你將不得不依賴於特定於操作系統的代碼。

在Linux的情況下,確定很簡單,並且在其他操作系統上也可以安全運行。 如果它無法確定當前進程使用的線程數,則該函數將返回-1:

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int count_threads_linux(void)
{
    DIR           *dir;
    struct dirent *ent;
    int            count = 0;

    dir = opendir("/proc/self/task/");
    if (!dir)
        return -1;

    while (1) {

        errno = 0;
        ent = readdir(dir);
        if (!ent)
            break;

        if (ent->d_name[0] != '.')
            count++;
    }

    if (errno) {
        const int saved_errno = errno;
        closedir(dir);
        errno = saved_errno;
        return -1;
    }

    if (closedir(dir))
        return -1;

    return count;
}

在某些情況下(如chroot沒有/proc/ ),即使在Linux中檢查也會失敗,因此-1返回值應始終被視為未知而不是錯誤 (盡管errno將指示失敗的實際原因)。

看一下FreeBSD手冊頁,我想知道相應的信息是否可用。

最后:

我沒有嘗試檢測有問題的情況,我認真推薦fork()exec() (或posix_spawn() )從屬進程,在子進程中只使用異步信號安全函數(參見man 7 signalexec() ),從而避免了fork() - 線程的並發症。 在forking()之前,您仍然可以創建任何共享內存段,套接字對等。 我能看到的唯一缺點就是你必須為奴隸工人使用單獨的二進制文件。 鑒於你對它們的描述,這對我來說聽起來不是一個缺點。

如果向進程'控制tty發送SIGINFO信號,則該進程應描述線程的狀態。 從描述中可以推斷出是否已經創建了任何線程。

您可能必須開發一個通過popen調用的小實用程序,以將輸出讀回到您的庫中。

添加了示例代碼Fri Dec 21 14:45

運行一個創建五個線程的簡單程序。 線程基本上睡覺了。 在程序退出之前,發送SIGINFO信號以獲取線程的狀態。

openbsd> cat a.c
#include <unistd.h>
#include <pthread.h> 

#define THREADS 5

void foo(void);

int
main()
{    
    pthread_t thr[THREADS];
    int j;

    for (j = 0; j < THREADS; j++) {
        pthread_create(&thr[j], NULL, (void *)foo, NULL);
    }   

    sleep(200);                             

    return(0);
}             

void
foo()
{   
    sleep(100);
}             
openbsd> gcc a.c -pthread
openbsd> a.out &
[1] 1234
openbsd> kill -SIGINFO 1234
 0x8bb0e000 sleep_wait  15 -c---W---f 0000          
 0x8bb0e800 sleep_wait  15 -c---W---f 0000          
 0x8bb0e400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d800 sleep_wait  15 -c---W---f 0000          
 0x7cd3d400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d000 sleep_wait  15 -c---W---f 0000 main  

你可以使用pthread_once()來確保沒有其他線程做同樣的事情:這樣你就不必關心多個線程正在調用你的初始化函數,只有一個線程會被真正執行。

使您的公共初始化函數通過pthread_once()運行私有初始化。

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT;

static void my_initialisation(void)
{
    /* do something */
}

int lib_initialisation(void)
{
    pthread_once(&my_initialisation_conce, my_initialisation);

    return 0;
}

另一個例子可以在這里找到。

鏈接

暫無
暫無

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

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