标记的"欺骗"以上不回答我的问题,因为它涉及使用线程分叉的安全性。我没有在我的代码中生成线程,并且更关心pthread_mutex_t
结构及其在fork()
发生fork()
时在操作系统中注册的内部结构的有效性,即:是在子进程中的pthread_mutex_t
上重新创建互斥锁,或者孩子只是拥有父虚拟内部的有效(或无效)浅表副本?
我有一个音频/硬件处理库,它使用递归static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
void read() {
pthread_mutex_lock(&mutex);
// ...
pthread_mutex_unlock(&mutex);
}
void write() {
pthread_mutex_lock(&mutex);
// ...
pthread_mutex_unlock(&mutex);
}
void toggle() {
pthread_mutex_lock(&mutex);
read();
// ...
write();
pthread_mutex_unlock(&mutex);
}
通过简单的API包装一些DSP函数。它是递归互斥的原因是因为一些API函数依次调用其他API函数,我想确保只有一个线程进入每个库实例的临界区。所以,代码看起来像这样:
fork()
如果用户应用程序使用我的库,并且应用程序发出mmap
调用,那么子进程的库实例是否需要重新初始化其互斥锁的实例?我知道子进程不会继承线程,如果我希望这两个进程真正共享互斥锁(不能回想起标志是什么)或者我必须要做的话,需要使用特定的互斥初始化标志使用fork()
IIRC。但是,子项使用的互斥锁实例是否有效(即:fork()
复制内部值,但它们不再有效,或者是否是使用操作系统初始化的新互斥锁)? 我不希望子进程和父进程在发生fork()
时共享互斥锁,但我想确保客户端使用有效的互斥锁句柄。
注意:我可以保证在发出var links1 = link2 = link3 = link4 = link5 = ["a.html","b.html","c.html","d.html","e.html","f.html","g.html"]
function myLinkJS(){
document.write( '<a href=\"'+link1+'\" ></a>\n' );
document.write( '<a href=\"'+link2+'\" ></a>\n' );
document.write( '<a href=\"'+link3+'\" ></a>\n' );
document.write( '<a href=\"'+link4+'\" ></a>\n' );
}
来电时,互联网不会被锁定。
谢谢。
答案 0 :(得分:2)
WRT。 POSIX,我同意caf's answer,但幸运的是在Linux中,语义是定义的。
来自Linux man 2 fork
手册页:
使用单个线程创建子进程 - 调用fork()的线程。父节点的整个虚拟地址空间在子节点中复制,包括互斥锁,条件变量和其他pthreads对象的状态;使用pthread_atfork(3)可能有助于处理这可能导致的问题。
因此,在Linux中,如果互斥锁已解锁,则在fork()
之后无需重新初始化。
更重要的是,POSIX为信号量定义了相同的内容;该
在父进程中打开的任何信号量也应在子进程中打开。
这意味着您可以用信号量替换递归的互斥锁;只需用
替换你的代码#include <semaphore.h>
static sem_t my_lock;
static void my_lock_init(void) __attribute__((constructor));
static void my_lock_init(void) {
sem_init(&my_lock, 0, 1U);
}
void my_read() {
sem_wait(&my_lock);
// ...
sem_post(&my_lock);
}
void my_write() {
sem_wait(&my_lock);
// ...
sem_post(&my_lock);
}
void toggle() {
sem_wait(&my_lock);
// ...
my_read();
// ...
my_write();
// ...
sem_post(&my_lock);
}
重新初始化已初始化的信号量会导致未定义的行为。这意味着上述工作,如果您以某种方式证明fork()
永远不会发生在执行第一个sem_wait(&my_lock)
和最后一个sem_post(&my_lock)
的代码之间。
问题在于,通常无法证明在多线程程序中,当另一个线程执行fork()
时,其他任何线程都没有执行任何上述函数。
在Linux中,内核2.6或更高版本以及GNU C库版本2.5或更高版本,pthreads基于NPTL,而pthread锁定原语在futex()
syscall之上实现。
当线程被阻塞或等待它们时,内核只知道一个futex。其余时间,futex只是一个普通的数据结构。 (内核使用futex的地址来区分它们;共享内存的地址是专门处理的。)
这意味着当使用futex时,只要您自己的代码在保持互斥锁的同时不进行分叉,就可以在fork()
之后安全地重新启动互斥锁。
作为一个例子 - 请记住,仅适用于Linux 2.6或更高版本,以及GNU C库2.5或更高版本:
#define _GNU_SOURCE
#include <pthread.h>
static pthread_mutex_t my_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
static void my_reinit(void)
{
my_lock = (pthread_mutex_t)PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
}
static void my_init(void) __attribute__((constructor (65535)));
static void my_init(void)
{
pthread_atfork(NULL, NULL, my_reinit);
}
void my_read()
{
pthread_mutex_lock(&my_lock);
// ...
pthread_mutex_unlock(&my_lock);
}
void my_write()
{
pthread_mutex_lock(&my_lock);
// ...
pthread_mutex_unlock(&my_lock);
}
void toggle()
{
pthread_mutex_lock(&my_lock);
// ...
my_read();
// ...
my_write();
// ...
pthread_mutex_unlock(&my_lock);
}
答案 1 :(得分:1)
仔细阅读POSIX表明它未定义:
fork()
表示会创建该过程的精确副本; 如果您同意此读数,那么最简单的解决方案是避免在单线程情况下使用互斥锁。您可以要求库用户链接到库的多线程版本或者具有#ifdef&#39; d out同步的单线程版本,具体取决于它们的用例。或者,不要自己调用pthread_mutex_lock()
/ pthread_mutex_unlock()
,而是允许用户提供库调用的锁定/解锁回调 - 单线程代码只提供空回调。
答案 2 :(得分:1)
注意:我可以保证在发出
fork()
来电时,互联网不会被锁定。
为了保证这一点,这意味着你[正如你所说] 不使用线程。否则,您无法保证。所以,我同意Sam的观点。您不需要使用互斥锁。
如果 使用线程,您可能希望pthread_create
而不是fork
,以便互斥锁被释放&#34;自然&#34; [通过持有线程]。
当您fork
时,pthread互斥锁具有 no 有效性/含义。它需要一个共享的地址空间,在你分叉后,你不再拥有。因此,安全的赌注是重新启动孩子的互斥锁。但是,这个互斥锁不再与父进程或其兄弟进程一起运行。如果分支线程不是互斥锁所有者,则init只是为了防止子进程阻塞。
要锁定线程,请使用pthread_mutex_*
。要锁定进程,请使用SysV信号量或posix信号量。
根据您的代码示例中的pthread互斥锁用法,我怀疑您希望在给定API调用期间拥有对音频设备的独占访问权。
如果您不使用线程,那么您已经拥有了而没有互斥锁。当您添加分叉时,互斥锁不起作用,因此您需要[命名]信号量[而且您必须实现自己的包装器,因为,IIRC,sem_wait
等。人。不做递归]