“代码清单1”和“代码清单2”之间有什么区别吗?因为在代码清单1中,子进程能够捕获SIGTERM信号并很好地退出。但是代码listng 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的信号处理程序。所以,这应该是有效的。不是吗?
答案 0 :(得分:5)
如果您从父母发送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; |
| | } |
| +-------------------------+--------------------------+
| |
| |
| |
这称为竞争条件,因为最终结果取决于谁先运行。
答案 1 :(得分:3)
首先,不推荐使用signal(),最好使用sigaction()。我不认为fork()完全消失的危险,因为很多东西都使用它,但sigaction()确实提供了一个更好的界面。
您遇到的行为通常是由线程内部调用fork()引起的。 POSIX解决了这个specifically:
应使用a创建流程 单线程。如果是多线程的 进程调用fork(),新进程 应包含呼叫的复制品 线程及其整个地址空间, 可能包括的状态 互斥和其他资源。 因此,为了避免错误, 子进程可能只执行 异步信号安全操作直到 这样的时间作为执行功能之一 叫做。 [THR]叉子处理器可能 通过建立 pthread_atfork()函数为了 维护应用程序不变量 fork()调用。
当应用程序从中调用fork()时 一个信号处理程序和任何一个fork 由...注册的处理程序 pthread_atfork()调用一个函数 不是异步信号安全的 行为未定义。
这意味着,您只继承调用线程地址空间的副本,而不是继承父项的整个地址空间的副本,该地址空间不包含您的处理程序。可能可以想象你确实(甚至可能是在不知不觉中)从一个线程中调用fork()。
子进程获取父级地址空间的副本。与信号的唯一区别是待定信号,当孩子将信号集初始化为零时,孩子将不会收到这些信号。但是,它确实得到了处理程序的副本。
答案 2 :(得分:0)
嗯,根据男子叉子:
fork(),fork1()和forkall()函数创建一个新进程。新进程(子进程)的地址空间是调用进程(父进程)的地址空间的精确副本。子进程从父进程继承以下属性:
...
o信号处理设置(即SIG_DFL,SIG_IGN,SIG_HOLD,函数地址)
在第一个示例中,信号处理程序将从父级的上下文复制到分叉的子级。但是我无法解释为什么在第二个例子中设置孩子中的信号处理程序会失败。