分叉后调用信号

发布于 2024-10-14 11:37:13 字数 584 浏览 2 评论 0原文

“代码清单1”和“代码清单2”有什么区别吗?因为在代码清单 1 中,子进程能够捕获 SIGTERM 信号并顺利退出。但是代码清单 2 在 SIGTERM 信号上突然终止。

我使用的是 Linux 和 C。

代码清单 1

if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}
pid = fork();

代码清单 2

pid = fork();
if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}

奇怪的是,在代码清单 2 中,子进程和父进程都设置了信号处理程序SIGTERM。所以,这应该有效。不是吗?

Is there any difference between "code listing 1" and "code listing 2"? Because in Code Listing 1, the child process is able to catch the SIGTERM signal and exit nicely. But code listng 2 is terminating abruptly on SIGTERM signal.

I am using Linux and C.

Code Listing 1

if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}
pid = fork();

Code Listing 2

pid = fork();
if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}

The strange part is that in Code Listing 2, both child and parent process sets the signal handler for SIGTERM. So, this is supposed to work. Isn't it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无悔心 2024-10-21 11:37:13

如果您从父进程发送 SIGTERM,则最终结果取决于进程的调度顺序。

如果首先调度子进程,则一切正常:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          |                                                           |
                          |                                                           |

但如果首先调度 paren,则子进程可能没有时间设置信号处理程序:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          |                                                           |
                          |                                                           |

这称为竞争条件,因为最终结果取决于就看谁先跑。

If you are sending the SIGTERM from the parent, the end result depends on the order on which processes get scheduled.

If the child gets scheduled first, everything works:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          |                                                           |
                          |                                                           |

But if the paren gets scheduled first, the child may have not had time to setup the signal handler:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          |                                                           |
                          |                                                           |

This is called a race condition, because the end result depends on who gets to run first.

萌化 2024-10-21 11:37:13

首先,signal()已被弃用,最好使用sigaction() 。我不认为 fork() 有完全消失的危险,因为很多东西都使用它,但 sigaction() 确实提供了一个更好的界面。

您遇到的行为通常是由从线程内调用 fork() 引起的。 POSIX具体解决了这个问题

应创建一个进程
单线程。如果是多线程
进程调用fork(),新进程
应包含调用的副本
线程及其整个地址空间,
可能包括以下州
互斥体和其他资源。
因此,为了避免错误,
子进程只能执行
异步信号安全操作直到
例如 exec 函数之一
被称为。 [THR] 叉车操作员可能会
是通过建立
pthread_atfork() 函数以便
保持应用程序不变性
fork() 调用。

当应用程序调用 fork() 时
信号处理程序和任何 fork
处理程序注册者
pthread_atfork() 调用一个函数
不是异步信号安全的,
行为未定义。

这意味着,您不是继承父级整个地址空间的副本,而是仅继承调用线程地址空间的副本,其中不包含您的处理程序。可以想象,您确实(甚至可能无意中)从线程内调用 fork() 。

子进程获取父进程地址空间的副本。与信号的唯一区别是待处理信号,子级将不会收到该信号,因为它获得了初始化为零的信号集。但是,是的,它确实获得了处理程序的副本。

First, signal() is deprecated, it's better to use sigaction(). I don't think fork() is in danger of vanishing altogether since so many things use it, but sigaction() does provide a much nicer interface.

The behavior you are experiencing is commonly caused by calling fork() from within a thread. POSIX addresses this specifically:

A process shall be created with a
single thread. If a multi-threaded
process calls fork(), the new process
shall contain a replica of the calling
thread and its entire address space,
possibly including the states of
mutexes and other resources.
Consequently, to avoid errors, the
child process may only execute
async-signal-safe operations until
such time as one of the exec functions
is called. [THR] Fork handlers may
be established by means of the
pthread_atfork() function in order to
maintain application invariants across
fork() calls.

When the application calls fork() from
a signal handler and any of the fork
handlers registered by
pthread_atfork() calls a function that
is not asynch-signal-safe, the
behavior is undefined.

This means, rather than inheriting a copy of the parent's entire address space, you inherit only a copy of the calling threads address space, which doesn't contain your handlers. It might be conceivable that you are, indeed (perhaps even unwittingly) calling fork() from within a thread.

A child process gets a carbon copy of the parent's address space. The only difference with signals would be pending signals, which the child will not receive as it gets a signal set initialized to zero. But yes, it does get a copy of the handlers.

燃情 2024-10-21 11:37:13

嗯,根据 man fork 的说法:

fork()、fork1() 和 forkall() 函数创建一个新进程。新进程(子进程)的地址空间是调用进程(父进程)地址空间的精确副本。子进程从父进程继承以下属性:

...

o 信号处理设置(即SIG_DFL、SIG_IGN、SIG_HOLD、函数地址)

在第一个示例中,信号处理程序将从父进程的上下文复制到分叉的孩子。但我无法解释为什么在第二个示例中在子级中设置信号处理程序会失败。

Well, according to man fork:

The fork(), fork1(), and forkall() functions create a new process. The address space of the new process (child processcess) is an exact copy of the address space of the calling process (parent process). The child process inherits the following attributes from the parent process:

...

o signal handling settings (that is, SIG_DFL, SIG_IGN, SIG_HOLD, function address)

In the first example the signal handler will be copied from the context of the parent to the forked child. But I can not explain why in the second example setting the signal handler in the child would fail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文