我正在编写一个在初始化期间需要fork()的C库。因此,我想断言()应用程序代码(在我的控制之外)从单个线程上下文调用我的库初始化代码(以避免众所周知的“threads and fork don't mix”问题)。一旦我的库被初始化,它就是线程安全的(并且期望应用程序级代码可以创建线程)。我只关心支持pthreads。
使用pthreads计算当前进程空间中的线程数似乎是不可能的。实际上,即使googletest只在Mac OS和QNX上实现GetThreadCount()。
鉴于我无法统计线程,我是否可以在某种程度上断言单线程上下文?
澄清:如果可能的话,我想避免使用“/ proc”(非可移植),一个额外的库依赖(如libproc)和LD_PRELOAD样式的pthread_create包装。
澄清#2:在我的情况下,使用多个进程是必要的,因为我的库中的工作人员相对较重(使用webkit)并且可能会崩溃。但是,我希望原始流程能够在工人崩溃中幸存下来。答案 0 :(得分: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;
}
在某些情况下(例如没有/proc/
的chroot),即使在Linux中该检查也会失败,因此-1
返回值应始终被视为 unknown 而不是错误(尽管errno
将指出失败的实际原因。)
查看FreeBSD手册页,我想知道相应的信息是否可用。
最后:
我不是尝试检测有问题的案例,而是强烈建议您使用fork()
和exec()
(或posix_spawn()
)从属进程,仅使用异步信号安全函数(请参阅{{ 3)})在子进程中(exec()
之前),从而避免fork() - 线程并发症。在forking()之前,您仍然可以创建任何共享内存段,套接字对等。我能看到的唯一缺点就是你必须为奴隶工人使用单独的二进制文件。鉴于你对它们的描述,这对我来说听起来不是一个缺点。
答案 1 :(得分:1)
如果向进程'控制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
答案 2 :(得分:-1)
你可以使用use 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;
}
可以找到另一个例子here。
<强>链接强>