waitpid 和同步问题

Waitpid and synchronization issues

我创建了一个模仿 std::thread 的简单 Process class。它应该只适用于 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() 中的 waitpidm_pid = 0 之间调用 interrupt 那么我将杀死一个不存在的进程,或者更糟的是一个完全不相关的进程恰好与旧进程具有相同的 pid。

join() 中交换 m_pid = 0waitpid 语句会使中断变得毫无用处,因为您可以预期第一个线程将花费大部分时间等待子进程终止。

所以我需要的是 waitpid 等待子进程终止但保持子进程处于僵尸状态,以便同时不会产生具有相同 pid 的进程。

有这样的事情或者其他解决办法吗?

你是对的,一个控制线程中的阻塞 waitpid(m_pid, ...); m_pid = 0 和另一个控制线程中的 kill(m_pid, ...) 将竞争。由于您指出的原因,添加互斥锁无济于事 - 服务员必须等待 更新一些全局信息,但必须允许杀手(间接)中断等待。

(我将忽略关于模仿 std::thread 的部分,例如不支持中断。)

相反,您可以依赖继承的 pipe,并确保 child 进程继承管道的 write-end,留下 parent 和 read-end。 read-end 将在 child 终止时指示 EOF,但外部资源(僵尸进程条目)将保留,直到明确收割。此方法并非万无一失,因为 child 可能会分叉或执行或以其他方式关闭或意外传递其 write-end fd。

顺便说一句,您可以设计更复杂的解决方案。例如,您可以为 waitpidkill 进程生成一个秘密线程,然后您的 interrupt 方法可以使用 real-time 中断该线程信号。您也可以接管整个 parent 进程的 SIGCHLD 处理。不过,这些都是重量级的 error-prone。