7

我正在编写一个在初始化期间需要 fork() 的 C 库。因此,我想 assert() 应用程序代码(不在我的控制范围内)从单线程上下文调用我的库初始化代码(以避免众所周知的“线程和分叉不混合”问题)。一旦我的库被初始化,它就是线程安全的(并且期望应用程序级代码可以创建线程)。我只关心支持 pthreads。

使用 pthreads 计算当前进程空间中的线程数似乎是不可能的。事实上,即使是 googletest 也只在 Mac OS 和 QNX 上实现GetThreadCount() 。

鉴于我无法计算线程数,是否有可能我可以断言一个单线程上下文?

澄清:如果可能,我想避免使用“/proc”(不可移植)、附加的库依赖项(如 libproc)和 LD_PRELOAD 样式的 pthread_create 包装器。

澄清 #2:在我的情况下,使用多个进程是必要的,因为我的库中的工作人员相对较重(使用 webkit)并且可能会崩溃。但是,我希望原始进程能够在工人崩溃中幸存下来。

4

3 回答 3

2

您可以将库初始化函数标记为在应用程序之前运行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 without /proc/)即使在 Linux 中该检查也会失败,因此-1返回值应始终被视为未知而不是错误(尽管errno会指示失败的实际原因)。

查看 FreeBSD 手册页,我想知道是否有相应的信息可用。

最后:

与其尝试检测有问题的情况,我强烈建议您fork()exec()(或)从属进程,在子进程(在 之前)中posix_spawn()仅使用异步信号安全函数(参见) ,从而避免 fork()-thread 并发症。您仍然可以在 forking() 之前创建任何共享内存段、套接字对等。我能看到的唯一缺点是您必须为奴隶工人使用单独的二进制文件。鉴于您对它们的描述,这对我来说听起来不是一个缺点。man 7 signalexec()

于 2012-12-21T16:34:21.610 回答
1

If you send a SIGINFO signal to the process' controlling tty, the process should describe the status of threads. From the description it should be possible to deduce whether any threads have been created.

You may have to develop a small utility that is invoked via popen to read the output back into your library.

Added sample code Fri Dec 21 14:45

Run a simple program that creates five threads. Threads basically sleep. Before the program exits, send a SIGINFO signal to get the status of the threads.

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  
于 2012-12-21T02:13:30.197 回答
-1

您可以使用 usepthread_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;
}

另一个例子可以在这里找到。

链接

于 2012-12-21T17:10:26.597 回答