我创建了一个简单的Process
类来模仿std::thread
。它应该只能在 Linux 上运行。
struct Process
{
Process(/*...*/) { /* fork + join, initialize m_pid */ }
~Process() { assert(!joinable()); }
bool joinable() const { return m_pid != 0; }
void join()
{
assert (joinable());
int status;
waitpid(m_pid, &status, 0);
m_pid = 0;
}
private:
std::atomic<pid_t> m_pid;
};
我有一个额外的要求:我需要能够从另一个线程停止该进程。
例如:
Process p(/* ... */ );
Thread 1:
p.join();
Thread 2:
p.interrupt();
如何实现线程安全的Process:interrupt
?我的第一个想法是这样的:
void Process::interrupt()
{
if (m_pid)
kill(m_pid, SIGTERM);
}
不幸的是,我不确定这是否有效,因为如果在 join( 中的
那么我就杀死了一个不存在的进程,或者更糟糕的是,杀死了一个完全不相关的进程,但它恰好与旧进程具有相同的 pid。waitpid
和 m_pid = 0
之间调用 interrupt
)
交换join()
中的m_pid = 0
和waitpid
语句将使中断变得毫无用处,因为你可以预期第一个线程将花费大部分时间等待子进程终止。
所以我需要的是一个waitpid
,它等待子进程终止,但使子进程保持僵尸状态,以便同时不能生成具有相同pid的进程。
有这样的事情或任何其他解决方案吗?
最佳答案
你是对的,一个阻塞waitpid(m_pid, ...);一个控制线程中的 m_pid = 0
和另一个控制线程中的 kill(m_pid, ...)
将发生竞争。添加互斥体不会因为您指出的原因而有所帮助 - 服务员必须等待并更新一些全局信息,但必须允许 killer (间接)中断等待。
(我将忽略有关模仿 std::thread
的部分,例如,它不支持中断。)
相反,您可以依赖继承的 pipe
,并确保子进程继承管道的写端,而将读端留给父进程。当子进程终止时,读结束将指示 EOF,但外部资源(僵尸进程条目)将保留,直到显式收获。此方法并非万无一失,因为子进程可能会 fork 或 exec 或以其他方式意外地关闭或传递其写入端 fd。
顺便说一句,您可以设计更复杂的解决方案。例如,您可以生成一个 secret 线程来 waitpid
并kill
子进程,然后您的 interrupt
方法可以使用实时信号。您也可以接管整个父进程的 SIGCHLD 处理。不过,这些都是重量级的并且容易出错。
关于c++ - Waitpid和同步问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28898268/