pctl(PR_SET_PDEATHSIG) 竞争条件

2024-04-24

据我了解,当父进程死亡时终止子进程的最佳方法是通过prctl(PR_SET_PDEATHSIG)(至少在 Linux 上):父进程退出后如何让子进程终止? https://stackoverflow.com/questions/284325/how-to-make-child-process-die-after-parent-exits

对此有一个警告man prctl:

当执行 set-user-ID 或 set-group-ID 二进制文件或具有相关功能的二进制文件时(自 Linux 2.4.36 / 2.6.23 起),该值会被 fork(2) 的子进程清除(请参阅能力(7))。该值在 execve(2) 中保留。

因此,以下代码存在竞争条件:

父级.c:

#include <unistd.h>

int main(int argc, char **argv) {
  int f = fork();
  if (fork() == 0) {
    execl("./child", "child", NULL, NULL);
  }
  return 0;
}

child.c:

#include <sys/prctl.h>
#include <signal.h>

int main(int argc, char **argv) {
  prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
  // ...
  return 0;
}

即,父计数在之前死亡prctl()在子进程中执行(因此子进程不会收到SIGKILL)。解决这个问题的正确方法是prctl()在父级之前exec():

父级.c:

#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>

int main(int argc, char **argv) {
  int f = fork();
  if (fork() == 0) {
    prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
    execl("./child", "child", NULL, NULL);
  }
  return 0;
}

child.c:

int main(int argc, char **argv) {
  // ...
  return 0;
}

然而,如果./child是一个 setuid/setgid 二进制文件,那么这个避免竞争条件的技巧不起作用(exec()setuid/setgid 二进制文件会导致PDEATHSIG根据上面引用的手册页丢失),并且似乎您被迫采用第一个(活泼的)解决方案。

有没有办法如果child是一个 setuid/setgid 二进制文件prctl(PR_SET_PDEATH_SIG)以一种非活泼的方式?


让父进程设置管道更为常见。父进程保持写端打开(pipefd[1]),关闭读端(pipefd[0])。子进程关闭写端(pipefd[1]),并设置读取结束(pipefd[1])非阻塞。

这样子进程就可以使用read(pipefd[0], buffer, 1)检查父进程是否仍然存在。如果父进程仍在运行,它将返回-1 with errno == EAGAIN (or errno == EINTR).

现在,在Linux中,子进程还可以设置读取结束异步,在这种情况下,它将发送一个信号(SIGIO默认情况下)当父进程退出时:

fcntl(pipefd[0], F_SETSIG, desired_signal);
fcntl(pipefd[0], F_SETOWN, getpid());
fcntl(pipefd[0], F_SETFL, O_NONBLOCK | O_ASYNC);

使用 siginfo 处理程序desired_signal. If info->si_code == POLL_IN && info->si_fd == pipefd[0],父进程要么退出,要么向管道写入内容。因为read()异步信号安全,并且管道是非阻塞的,您可以使用read(pipefd[0], &buffer, sizeof buffer)在信号处理程序中,父级是否写入了某些内容,或者父级是否退出(关闭管道)。在后一种情况下,read()将返回0.

据我所知,这种方法没有竞争条件(如果您使用实时信号,则信号不会因为用户发送的信号已经挂起而丢失),尽管它非常特定于 Linux。设置信号处理程序后,在子进程生命周期的任何时刻,子进程始终可以显式检查父进程是否仍然存在,而不会影响信号生成。

因此,用伪代码回顾一下:

Construct pipe
Fork child process

Child process:
    Close write end of pipe
    Install pipe signal handler (say, SIGRTMIN+0)
    Set read end of pipe to generate pipe signal (F_SETSIG)
    Set own PID as read end owner (F_SETOWN)
    Set read end of pipe nonblocking and async (F_SETFL, O_NONBLOCK | O_ASYNC)
    If read(pipefd[0], buffer, sizeof buffer) == 0,
        the parent process has already exited.

    Continue with normal work.

Child process pipe signal handler:
    If siginfo->si_code == POLL_IN and siginfo->si_fd == pipefd[0],
        parent process has exited.
        To immediately die, use e.g. raise(SIGKILL).    

Parent process:
    Close read end of pipe

    Continue with normal work.

我不指望你相信我的话。

下面是一个粗略的示例程序,您可以使用它自己检查此行为。它很长,但只是因为我希望很容易看到运行时发生的情况。要在普通程序中实现这一点,您只需要几十行代码。示例.c:

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static volatile sig_atomic_t done = 0;

static void handle_done(int signum)
{
    if (!done)
        done = signum;
}

static int install_done(const int signum)
{
    struct sigaction act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

static int  deathfd = -1;

static void death(int signum, siginfo_t *info, void *context)
{
    if (info->si_code == POLL_IN && info->si_fd == deathfd)
        raise(SIGTERM);
}

static int install_death(const int signum)
{
    struct sigaction act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = death;
    act.sa_flags = SA_SIGINFO;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

int main(void)
{
    pid_t  child, p;
    int    pipefd[2], status;
    char   buffer[8];

    if (install_done(SIGINT)) {
        fprintf(stderr, "Cannot set SIGINT handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    if (pipe(pipefd) == -1) {
        fprintf(stderr, "Cannot create control pipe: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    child = fork();
    if (child == (pid_t)-1) {
        fprintf(stderr, "Cannot fork child process: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    if (!child) {
        /*
         * Child process.
        */

        /* Close write end of pipe. */
        deathfd = pipefd[0];
        close(pipefd[1]);

        /* Set a SIGHUP signal handler. */
        if (install_death(SIGHUP)) {
            fprintf(stderr, "Child process: cannot set SIGHUP handler: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        /* Set SIGTERM signal handler. */
        if (install_done(SIGTERM)) {
            fprintf(stderr, "Child process: cannot set SIGTERM handler: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        /* We want a SIGHUP instead of SIGIO. */
        fcntl(deathfd, F_SETSIG, SIGHUP);

        /* We want the SIGHUP delivered when deathfd closes. */
        fcntl(deathfd, F_SETOWN, getpid());

        /* Make the deathfd (read end of pipe) nonblocking and async. */
        fcntl(deathfd, F_SETFL, O_NONBLOCK | O_ASYNC);

        /* Check if the parent process is dead. */
        if (read(deathfd, buffer, sizeof buffer) == 0) {
            printf("Child process (%ld): Parent process is already dead.\n", (long)getpid());
            return EXIT_FAILURE;
        }

        while (1) {
            status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
            if (status == SIGINT)
                printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
            else
            if (status)
                break;
            printf("Child process (%ld): Tick.\n", (long)getpid());
            fflush(stdout);
            sleep(1);

            status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
            if (status == SIGINT)
                printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
            else
            if (status)
                break;
            printf("Child process (%ld): Tock.\n", (long)getpid());
            fflush(stdout);
            sleep(1);
        }

        printf("Child process (%ld): Exited due to %s.\n", (long)getpid(),
               (status == SIGINT) ? "SIGINT" :
               (status == SIGHUP) ? "SIGHUP" :
               (status == SIGTERM) ? "SIGTERM" : "Unknown signal.\n");
        fflush(stdout);

        return EXIT_SUCCESS;
    }

    /*
     * Parent process.
    */

    /* Close read end of pipe. */
    close(pipefd[0]);

    while (!done) {
        fprintf(stderr, "Parent process (%ld): Tick.\n", (long)getpid());
        fflush(stderr);
        sleep(1);
        fprintf(stderr, "Parent process (%ld): Tock.\n", (long)getpid());
        fflush(stderr);
        sleep(1);

        /* Try reaping the child process. */
        p = waitpid(child, &status, WNOHANG);
        if (p == child || (p == (pid_t)-1 && errno == ECHILD)) {
            if (p == child && WIFSIGNALED(status))
                fprintf(stderr, "Child process died from %s. Parent will now exit, too.\n",
                        (WTERMSIG(status) == SIGINT) ? "SIGINT" :
                        (WTERMSIG(status) == SIGHUP) ? "SIGHUP" :
                        (WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "an unknown signal");
            else
                fprintf(stderr, "Child process has exited, so the parent will too.\n");
            fflush(stderr);
            break;
        }
    }

    if (done) {
        fprintf(stderr, "Parent process (%ld): Exited due to %s.\n", (long)getpid(),
                   (done == SIGINT) ? "SIGINT" :
                   (done == SIGHUP) ? "SIGHUP" : "Unknown signal.\n");
        fflush(stderr);
    }

    /* Never reached! */
    return EXIT_SUCCESS;
}

使用例如编译并运行上面的内容

gcc -Wall -O2 example.c -o example
./example

The parent process will print to standard output, and the child process to standard error. The parent process will exit if you press Ctrl+C; the child process will ignore that signal. The child process uses SIGHUP instead of SIGIO (although a realtime signal, say SIGRTMIN+0, would be safer); if generated by the parent process exiting, the SIGHUP signal handler will raise SIGTERM in the child.

为了使终止原因易于看到,孩子抓住SIGTERM,并退出下一次迭代(一秒钟后)。如果需要,处理程序可以使用例如raise(SIGKILL)立即终止自身。

父进程和子进程都会显示其进程 ID,因此您可以轻松地发送SIGINT/SIGHUP/SIGTERM来自另一个终端窗口的信号。 (子进程忽略SIGINT and SIGHUP从进程外部发送。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pctl(PR_SET_PDEATHSIG) 竞争条件 的相关文章

  • 读取进程的进程内存不会返回所有内容

    我正在尝试扫描第三方应用程序的内存 我已经查到地址了 现在是在0x0643FB78 问题是 从那以后我就再也爬不上去LPMODULEENTRY32 gt modBaseAddr is 0x00400000 and LPMODULEENTRY
  • Boost.Intrusive 和 unordered_map

    我希望使用侵入性的 unordered map 由于某种原因 库中只有一个 unordered set 还有一个侵入式哈希表 但我不确定它是否具有相同的功能 而且它没有相同的接口 我错了吗 我错过了 unordered map 链接吗 如果
  • 使 C++ 在模板函数的特定实例化上编译失败

    我正在开发一个具有模板功能的项目 如下所示 template
  • fgetc,检查 EOF

    在书里Linux系统编程我读过一些这样的内容 fgetc返回读取为的字符unsigned char投射到int or EOF在文件末尾或错误 使用时的一个常见错误fgetc is char c if c fgetc EOF 该代码的正确版本
  • LogicalOperationStack 与 .Net 4.5 中的异步不兼容吗

    Trace CorrelationManager LogicalOperationStack允许具有嵌套逻辑操作标识符 其中最常见的情况是日志记录 NDC 它是否仍然可以使用async await 这是一个简单的例子 使用LogicalFl
  • 了解左值到右值转换的示例

    我很难理解这段代码 来自 C 14 草案标准的示例 转换拉瓦尔 调用未定义的行为g false 为什么constexpr使程序有效 另外 不访问 是什么意思 y n 在两次通话中g 我们正在返回n数据成员那么为什么最后一行说它不能访问它呢
  • 为什么 Resources.Load 返回 null?

    我的项目有多个精灵 位于 Assets Sprites 中 我想使用 C 脚本加载它们 我已经测试过这个 Sprite myFruit Resources Load
  • Xamarin 中 QR 扫描后的处理对话框

    我在Xamarin应用程序中使用QR码扫描仪 当它扫描QR码时 它会执行一些操作 大约需要一分钟 而在执行操作时 我想在屏幕上显示一个加载对话框 但是 它没有显示在屏幕上 并且在应用程序的其他地方 它运行得很好 Code var expec
  • Oracle ODP.Net 与实体框架 6 - 从表视图中选择时出现 ORA-00955

    我创建了两个应用程序 第一个使用 ODP Net 另一个没有实体 效果很好 static void Main string args OracleConnection con new OracleConnection using conne
  • 将 XML 转换为 JSON 时保留 json:Array 属性

    我有一段 XML 看起来像
  • 与智能指针的返回类型协方差

    在 C 中我们可以这样做 struct Base virtual Base Clone const virtual Base struct Derived Base virtual Derived Clone const overrides
  • 如何查看某个函数以 3 秒的间隔被调用了多少次?

    我想检查我的函数在 3 秒内可以运行多少次 我写了这段代码 include
  • 使用箭头键滚动可滚动控件

    我正在使用一个ScrollableControl在我的 C 项目中 我想知道如何将箭头键映射到垂直 水平滚动 编辑 我的图片框获得焦点 并且我设法映射滚动键 这里的问题是 当我按下箭头键时 它会滚动一次 然后失去焦点 将其交给滚动查看器旁边
  • 消息在事务处理时未到达 MSMQ

    我在本地计算机中创建了一个私有 MSMQ 我使用以下 C 代码将消息发送到队列 当我将队列更改为事务性队列时 消息未到达 MSMQ 但是 Send 方法中没有抛出异常 我需要做出什么改变才能使其发挥作用 using System using
  • 如何取消 NetworkStream.ReadAsync 而不关闭流

    我正在尝试使用 NetworkStream ReadAsync 读取数据 但我找不到如何取消调用后的 ReadAsync 作为背景 NetworkStream 由连接的 BluetoothClient 对象 来自 32Feet NET 蓝牙
  • ASP.NET MVC C#:将多个表/查询中的数据引入视图中

    好吧 我仍在掌握 ASP NET 和 MVC 框架的窍门 并将我的知识从经典的 ASP 和 VB 转换过来 所以请保持温柔 我的第一个视图 home details X 运行良好感谢之前的帮助为我指明了正确的方向 https stackov
  • 即使使用前向声明也会出现未定义的类型错误

    我正在阅读循环引用和前向声明 我确实知道在头文件中实现并不是一个好的设计实践 然而我正在尝试并且无法理解这种行为 使用以下代码 包含前向声明 我期望它能够构建 但是我收到此错误 Error 1 error C2027 use of unde
  • 图像处理编程

    我想知道是否有任何方法可以使用某种编程语言检测图像中对象的位置 例如 如果我有一个球的图像 每 100 毫秒更新一次 是否可以通过某些程序使用某些东西来获取球的坐标 看一下OpenCV http opencv willowgarage co
  • Qt 对象的生命周期

    Qt 对象的生命周期是多少 Such as QTcpSocket socket new QTcpSocket 套接字什么时候会被销毁 我应该使用 delete socket 有什么区别吗 QTcpSocket socket 我找不到有关此的
  • 通过网络共享的 SQL CE

    我之前见过这个问题 但找不到关于什么是可能 不可能以及什么解决方法可能可用的明确解释 我有一个现有的 C 应用程序 它使用 SQL CE 来存储本地信息 该数据库只能由单个应用程序访问 并存储在用户的 appdata 文件夹中 某些环境将

随机推荐

  • 如何在 IIS 7.5 中启用 GZIP 压缩

    我想使用 GZIP 压缩我的文件 您能分享一下使用 GZIP 压缩文件的 web config 代码吗 上传 web config 文件后还需要做什么吗 GZip 压缩可以直接通过 IIS 启用 首先 打开IIS 转到您希望调整的网站并点击
  • 在 Docker 中使用私有模块构建 Go 应用程序

    我正在尝试在依赖于私有子模块的 docker 容器中构建一个 go 项目 我本来希望 mount type ssh会将我的 ssh 凭据传递给容器并且它会起作用 目前我可以在本地构建 只需制作GOPRIVATE变量集和git config
  • 从代码中获取全局色调颜色

    有没有办法通过代码从我的项目中获取全局色调颜色 为了避免误解 我指的是全局色调 我可以在文件检查器中设置它 Easy 目标C UIColor tintColor self view tintColor Swift let tintColor
  • 根据其他数据框的函数创建新的数据框

    我是 R 的新手 所以我可能很难提出我的问题 请多多包涵 我有两个数据框 为了解释起见 我们假设 df1 柱形代表收益类型 玉米 燕麦 小麦等 行代表一年中的月份 一月 二月等 Elements 代表在该特定月份购买的增益类型的每吨价格 d
  • 从Sql服务器中选择表并将数据插入到Mysql表中

    我有一个正在运行的 ms sql 服务器 并且希望将一些数据复制到 mysql 数据库 我已经可以连接到它们 所以我做了类似的事情 pdo new PDO SQLSERVER user password sql SELECT id name
  • 在 swift 中使用 UUID() 和 json

    我在网上找到了在 json 文件中使用硬编码 uuid 的示例 并且这些示例非常适合我 但是当我在应用程序中添加从 json 数组中删除项目的功能时 我需要动态创建这些 uuid 这是我的json文件 list json 它曾经有硬编码的i
  • Objective-C 中的二叉树

    我正在学习算法和数据结构 并尝试使用 Objective C 设计和实现二叉树进行训练 到目前为止 我有以下课程 main 供测试用 Node 树的节点 BinaryTree 对于与树相关的所有方法 最早的方法之一BinaryTree我实现
  • 创建一个过程来检索表上的所有索引并重建

    我想创建一个过程来检索表上的所有索引并重建 我用这个查询检索所有索引 select index name from user indexes where table name your table name 我用这个查询重建 alter i
  • 以编程方式获取导航栏的高度

    我知道更多视图控制器 导航栏 的存在将 UIView 的高度推低 我也知道这个高度 44px 我还发现这种下推可以维持 self view frame origin y 0 那么 除了将其设置为常量之外 如何确定该导航栏的高度呢 或者 更短
  • Java 堆转储是否包括线程堆栈

    我一直在使用 Eclipse 内存分析工具来检查堆转储 我还没有看到任何对象通过线程堆栈中的局部变量保持活动状态的情况 java线程堆栈是否保留在堆转储中 如果不是 这些对象是否会被视为转储中无法访问的对象 如果是这样 是否有任何方法可以保
  • C# 位图/图形内存不足

    我正在尝试拍摄整个屏幕的快照以读取像素值 事实上我这样做没有任何问题 但在 214 个快照之后 我出现了内存不足的异常 Bitmap ScreenShot new Bitmap Screen PrimaryScreen Bounds Wid
  • 从 C# Windows 窗体在 MS Word 中打开 MS Word 文档

    我希望能够通过单击表单上的按钮 从 C 表单中打开 MS Word 中已制作的 Word 文档 但不知道如何操作 请帮忙 Thanks 上次我使用 Excel 时 我使用以下代码打开它 Process Start FileLocation
  • 使用ExternalContext.redirect()将面孔消息添加到重定向页面

    我在用ExternalContext redirect String 将用户重定向到另一个页面的方法 FacesContext getCurrentInstance addMessage new FacesMessage Bla bla b
  • java.lang.NoClassDefFoundError:无法解析:Landroid/webkit/PacProcessor

    实在找不到言语 我的项目只是一天天都无法编译 这就是我所做的 我下载并显示一个 PDF 然后将其作为保存的 PDF 打印到设备上 然后崩溃了 这是崩溃的情况 at java lang Class java lang Class classF
  • 将预处理器宏添加到 xcode 6 中的目标

    也许这非常简单 但我找不到在 Xcode 6 中为目标定义预处理器宏的方法 我已经做了一个屏幕截图来显示它在 Xcode 中的位置 因为它更容易 选择项目文件 选择您想要的目标 转到构建设置 搜索 预处理器 添加用于调试 发布或两者的预处理
  • Apache Spark:在分区上并行应用 sklearn 的函数

    我是大数据和 Apache Spark 的新手 也是一名在导师指导下工作的本科生 是否可以将函数 即样条曲线 仅应用于 RDD 的分区 我正在尝试实现论文中的一些工作here http sites stat psu edu rli rese
  • onBackPressed 功能在 IMMERSIVE STICKY 模式下不起作用

    我正在使用沉浸模式进行带有 videoView 的活动 我的目标是当触摸屏幕时 媒体控制器和系统控制栏一起显示或消失 现在一切正常 问题是我无法正常离开活动 当我按一次后退按钮时 系统栏再次隐藏 什么也没有发生 我必须按两次才能退出活动 我
  • Cython 类执行同类 cython 类列表

    我定义了一个泛型类Rule在 Cython 中 它期望cdef方法initialize 我创建了一个类 它本身定义为Rule 但它执行initialize规则列表的方法 cython cdef class Rule cdef initial
  • cells().paste VS cells.pastespecial?

    下面的代码是如何工作的 Range D4 F4 copy cells 1 1 PasteSpecial 虽然下面不起作用 Range D4 F4 copy cells 1 1 Paste 我意识到正确的语法是 Range D4 F4 cop
  • pctl(PR_SET_PDEATHSIG) 竞争条件

    据我了解 当父进程死亡时终止子进程的最佳方法是通过prctl PR SET PDEATHSIG 至少在 Linux 上 父进程退出后如何让子进程终止 https stackoverflow com questions 284325 how