6

“代码清单 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 设置了信号处理程序。所以,这应该有效。不是吗?

4

3 回答 3

6

如果您从父级发送 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;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          |                                                           |
                          |                                                           |

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

于 2011-01-31T21:41:13.833 回答
3

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

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

应使用单个线程创建进程。如果多线程进程调用 fork(),则新进程应包含调用线程及其整个地址空间的副本,可能包括互斥锁和其他资源的状态。因此,为避免错误,子进程只能执行异步信号安全操作,直到调用其中一个 exec 函数。[THR] 可以通过 pthread_atfork() 函数建立 Fork 处理程序,以便跨 fork() 调用维护应用程序不变量。

当应用程序从信号处理程序调用 fork() 并且 pthread_atfork() 注册的任何 fork 处理程序调用非异步信号安全的函数时,行为未定义。

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

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

于 2011-01-28T13:54:13.047 回答
1

好吧,根据 man fork :

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

...

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

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

于 2011-01-28T13:20:47.147 回答