C 能否在Linux上实现正确的故障安全进程共享屏障?
在过去的一个问题中,我询问了如何在不破坏竞争的情况下实现pthread屏障: 并从Michael Burr处获得了一个完美的解决方案,用于处理局部障碍,但无法解决过程共享障碍。我们后来研究了一些想法,但从未得出令人满意的结论,甚至没有开始讨论资源故障案例 在Linux上是否可以制作一个满足以下条件的屏障:C 能否在Linux上实现正确的故障安全进程共享屏障?,c,linux,pthreads,posix,barrier,C,Linux,Pthreads,Posix,Barrier,在过去的一个问题中,我询问了如何在不破坏竞争的情况下实现pthread屏障: 并从Michael Burr处获得了一个完美的解决方案,用于处理局部障碍,但无法解决过程共享障碍。我们后来研究了一些想法,但从未得出令人满意的结论,甚至没有开始讨论资源故障案例 在Linux上是否可以制作一个满足以下条件的屏障: 进程共享(可以在任何共享内存中创建) 在barrier wait函数返回后,可以立即从任何线程取消映射或销毁屏障 由于资源分配失败,无法失败 Michael试图解决流程共享案例(参见链接
- 进程共享(可以在任何共享内存中创建)
- 在barrier wait函数返回后,可以立即从任何线程取消映射或销毁屏障
- 由于资源分配失败,无法失败
N-1
线程到达之前继续进行是不安全的
内核空间解决方案可能是唯一的方法,但即使这样也很困难,因为可能会有信号中断等待,而没有可靠的方法来恢复等待…好吧,我想我可以用一种笨拙的方法来完成 让“屏障”成为它自己监听套接字的过程。在等待时执行以下操作:
open connection to barrier process
send message telling barrier process I am waiting
block in read() waiting for reply
当N个线程等待时,屏障进程通知所有线程继续。然后,每个服务员关闭与屏障流程的连接并继续
按以下方式实施屏障破坏:
open connection to barrier process
send message telling barrier process to go away
close connection
一旦所有连接都关闭并且隔离过程被告知离开,隔离过程就会退出
[编辑:当然,作为等待和释放操作的一部分,这会分配和销毁套接字。但我认为您可以实现相同的协议,而不必这样做;请参见下文。]
第一个问题:这个协议真的有效吗?我想是的,但可能我不了解要求
第二个问题:如果它确实有效,那么它是否可以在没有额外进程开销的情况下进行模拟
我相信答案是肯定的。您可以让每个线程在适当的时间“扮演”屏障进程的角色。您只需要一个主互斥锁,由当前正在“扮演”屏障进程的线程持有。细节,细节。。。好的,那么屏障可能看起来像:
lock(master_mutex);
++waiter_count;
if (waiter_count < N)
cond_wait(master_condition_variable, master_mutex);
else
cond_broadcast(master_condition_variable);
--waiter_count;
bool do_release = time_to_die && waiter_count == 0;
unlock(master_mutex);
if (do_release)
release_resources();
不确定有关信号处理等的所有细节。。。但我认为“最后一次出门关灯”的基本思想是可行的。这在Linux futex API中是不可能的,我认为这也可以被证明 我们这里基本上有一个场景,其中N个进程必须由一个最终进程可靠地唤醒,并且在最终唤醒之后,任何进程都不能触及任何共享内存(因为它可能被异步销毁或重用)。虽然我们可以很容易地唤醒所有进程,但基本的竞争条件是在唤醒和等待之间;如果我们在等待之前发出唤醒命令,掉队者就永远不会醒来 对于这种情况,通常的解决方案是让掉队者用wait原子地检查状态变量;这样,如果已经唤醒,它就可以完全避免睡眠。然而,我们不能在这里这样做-一旦唤醒成为可能,触摸共享内存是不安全的 另一种方法是实际检查是否所有进程都已进入睡眠状态。然而,这在Linux futex API中是不可能的;服务员数量的唯一指示是FUTEX_WAKE的返回值;如果它返回的服务生数量少于您预期的数量,那么您知道有些人还没有睡着。然而,即使我们发现我们没有叫醒足够的服务员,做任何事情都为时已晚——其中一个醒来的过程可能已经破坏了屏障 因此,不幸的是,这种可立即销毁的原语不能用LinuxFutexAPI构建 请注意,在一名服务员、一名叫醒者的特定情况下,可以解决问题;如果FUTEX_WAKE返回零,我们知道实际上还没有人被唤醒,所以你有机会恢复。然而,将其转化为一个有效的算法是相当棘手的 在futex模型上添加一个健壮的扩展来解决这个问题是很棘手的。基本问题是,我们需要知道N个线程何时成功进入等待状态,并以原子方式将它们全部唤醒。然而,这些线程中的任何一个都可能在任何时候离开等待来运行信号处理程序——实际上,waker线程也可能离开等待信号处理程序 然而,一种可能的工作方式是在NT API中对模型进行扩展。对于键控事件,线程成对地从锁中释放;如果您有一个没有“等待”的“释放”,那么“释放”调用将阻止“等待”
由于信号处理程序的问题,这本身是不够的;但是,如果我们允许'release'调用指定要原子地唤醒的线程数量,这是可行的。您只需让屏障中的每个线程减少一个计数,然后“等待”该地址上的键控事件。最后一个线程“释放”N-1个线程。内核不允许处理任何唤醒事件,直到所有N-1线程都进入该键控事件状态;如果任何线程由于信号(包括释放线程)而离开futex调用,这将阻止任何唤醒,直到所有线程都返回。在与bdonlan就SO chat进行长时间讨论后,我想我有一个解决方案。基本上,我们将问题分解为两个自同步释放问题:销毁操作和取消映射 处理销毁很容易:只需让
pthread\u barrier\u destroy
函数等待所有等待者停止检查屏障即可。这可以由havi完成
lock(master_mutex);
time_to_die = true;
bool do_release = waiter_count == 0;
unlock(master_mutex);
if (do_release)
release_resources();