我正在尝试编写一个程序,
因此,我尝试执行此操作的方法是,为父进程添加了SIGINT处理程序,我的猜测是,当调用execvp()函数时,子进程将使用默认处理程序执行SIGINT进程。
void sigint_handler(int signum){
printf("");
}
int main(int argc, char **argv){
pid_t pid = fork();
if(pid<0)
{
printf("fork: error");
exit(-1);
}
else if(pid>0){
int status;
signal(SIGINT,sigint_handler);
pid_t child_pid= waitpid(pid, &status, 0);
printf("");
}
else if(pid==0){
printf("");
if(execvp(argv[1],&argv[1]) == -1) {
printf(" ");
exit(-1);
}
}
}
此代码足以处理相同的代码吗?另外,如果我的子进程被SIGINT或SIGSEGV等终止,我将如何查找我的子进程是在完成后还是由于信号以及使用了什么信号而终止。
答案 0 :(得分:2)
来自wait()
或好友的状态值会告诉您-使用WIFEXITED()
和WIFEXITSTATUS()
宏进行常规退出,或使用WIFSIGNALED()
和WIFTERMSIG()
进行退出信号(IF
宏会测试它如何退出)。还有WIFSTOPPED()
,WIFCONTINUED()
和WSTOPSIG()
用于作业控制。
请参阅POSIX waitpid()
(也记录了wait()
)。而且大多数基于Unix的系统也提供WCOREDUMP()
,但是POSIX并没有要求它。您也可以用十六进制(4位数字)查看退出状态,并不需要很长时间就能发现模式,但是您应该使用宏。
对于我的问题的第一部分,我上面提到的代码是否仍在流程的父部分中而忽略了
SIGINT
信号?
当心:avoid using printf() in signal handlers。
目前尚不清楚信号处理代码的工作方式;您从问题中砍掉了太多代码。但是,您现在已经添加了足够多的缺少的代码,并且,正如我猜想的那样,您的代码不是防弹的,因为显示的signal()
调用位于fork()
之后且在父进程中。您应该在调用fork()
之前禁用该信号;那么孩子应该重新启用它。这样可以正确地保护进程。
例如(文件sig11.c
—使用sig11
编译以创建gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes sig11.c -o sig11
):
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
return 1;
}
bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork: error\n");
return 1;
}
else if (pid > 0)
{
int status;
pid_t child_pid = waitpid(pid, &status, 0);
printf("child %d exited with status 0x%.4X\n", (int)child_pid, status);
}
else
{
assert(pid == 0);
if (handle_signals)
signal(SIGINT, SIG_DFL);
execvp(argv[1], &argv[1]);
fprintf(stderr, "failed to exec %s\n", argv[1]);
exit(-1);
}
return 0;
}
示例输出:
$ ./sig11 sleep 40
^Cchild 19721 exited with status 0x0002
$
我们可以讨论您是否应该改用sigaction()
而不是signal()
。
检查handle_signals
意味着,如果在忽略SIGINT的情况下运行程序,则不会突然启用该信号。这是一种标准的防御技术。请注意,错误是根据标准错误而不是标准输出报告的。无需检查execvp()
的返回值;如果成功则返回不成功,如果返回则肯定失败。使用errno
和strerror()
可能会报告更好的错误,但是显示的内容并非完全不合理。检查是否有足够的论据是另一种防御措施。报告正确的用法是很好的举止。我还将if
/ else if
/ else if
转换为if
/ else if
/ else
+断言-不需要第三项测试。>
使用<sys/wait.h>
中的宏:
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
return 1;
}
bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork: error\n");
return 1;
}
else if (pid > 0)
{
int status;
int corpse = waitpid(pid, &status, 0);
printf("child %d exited with status 0x%.4X\n", corpse, status);
if (WIFEXITED(status))
printf("child %d exited normally with exit status %d\n", corpse, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child %d exited because of signal %d\n", corpse, WTERMSIG(status));
else
printf("child %d neither exited normally nor because of a signal\n", corpse);
}
else
{
assert(pid == 0);
if (handle_signals)
signal(SIGINT, SIG_DFL);
execvp(argv[1], &argv[1]);
fprintf(stderr, "failed to exec %s\n", argv[1]);
exit(-1);
}
return 0;
}
示例输出:
$ ./sig11 sleep 4
child 19839 exited with status 0x0000
child 19839 exited normally with exit status 0
$ ./sig11 sleep 10
^Cchild 19842 exited with status 0x0002
child 19842 exited because of signal 2
$