[英]What happens after the execution of a handler function on the SIGCHLD signal in C?
I was wondering what exactly happens after the execution of a handler on a signal in C, more specifically on the SIGCHLD signal. 我想知道在对C中的信号(尤其是SIGCHLD信号)执行处理程序之后究竟发生了什么。
I'm actually building a shell and I need to do this: 我实际上是在构建外壳,我需要这样做:
My code do all of these points, but behaves in a strange way at the very end when the handler function returns. 我的代码完成了所有这些要点,但是在处理程序函数返回时,其行为最终却以奇怪的方式出现。
Main loop 主循环
int
main()
{
// SIGNALS HANDLING :
sigaction(SIGTERM, NULL, NULL);
sigaction(SIGQUIT, NULL, NULL);
// Current location
char* path = malloc(PATHMAX);
int argc;
char** argv;
// main loop
while(1)
{
getcwd(path, PATHMAX);
printf("%s$ ",path);
argc = 0;
// Parsing
argv = malloc(MAX_INPUT); // user's input is limited to 100000 characters
getParameters(argv, &argc);
// First we check if the user wants to execute the task in background
if ( strcmp(argv[argc-1],"&") == 0 ) {
// builtin functions can't be executed in background:
int isExit = (strcmp(argv[0],"exit") == 0) ? 1 : 0;
int isCd = (strcmp(argv[0],"cd") == 0) ? 1 : 0;
if(isExit || isCd) {
printf("Built-in functions can't be executed in background. \n");
} else {
// Execute the job in background
// First we delete the last arg
argv[argc-1] = NULL;
argc--;
execBackground(argv,argc);
}
} else {
// Execute built-in functions
if(!(exeBuiltin(argv, argc))) {
// Execute jobs if no builtin functions were executed
exeJob(argv,argc);
}
}
free(argv);
}
return 0;
}
User's input processing 用户的输入处理
// max 100000 characters
char input[MAX_INPUT];
// read keyboard inputs
fgets(input,MAX_INPUT,stdin);
Built-in functions (cd and exit) 内置功能(cd和退出)
int exeBuiltin(char** argv, int argc) {
// execute builtin functions if it exists
char* builtin[2];
builtin[0] = "exit";
builtin[1] = "cd";
// run through the arguments
for(int i = 0; i < argc; i++) {
// exit
if (strcmp(argv[i],builtin[0]) == 0) {
exitBuiltin(EXIT_SUCCESS);
} else if (strcmp(argv[i],builtin[1]) == 0) {
// cd
if (argc >= 2) {
cdBuiltin(argv[i+1]);
return 1;
}
}
}
return 0;
}
static void cdBuiltin(char* path) {
if(chdir(path) == -1) {
printf("%s\n",strerror(errno));
};
}
static void exitBuiltin(int signal) {
exit(signal);
}
Link between signal and handler: 信号和处理程序之间的链接:
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = child_handler;
sigaction(SIGCHLD, &sa, NULL);
Handler: 处理程序:
static void child_handler(int sig)
{
int status;
/* kills process
pid is a global variable that is set when I call fork() */
if(waitpid(pid, &status, 0) != -1) {
if (status == 0) // Verify child process terminated without error.
{
// success
printf("\nBackground job exited with code 0\n");
}
if (status == 1)
{
// error
printf("\nBackground job exited\n");
}
}
return;
}
My problem occurs when I call something like: 当我调用类似的东西时,会发生我的问题:
sudo apt-get update &
cd ../
Then when the sudo command terminates (the handler is called and prints "Background job exited with code 0"), even though the command "cd ../" is already executed it gets executed again . 然后,当sudo命令终止时(调用处理程序并打印“以代码0退出的后台作业”),即使命令“ cd ../”已经执行,它也会再次执行。
The ouput looks like: 输出看起来像:
$ /home/ubuntu/workspace$ sudo apt-get update &
$ /home/ubuntu/workspace$ cd ../
$ /home/ubuntu$
Background job exited with code 0
$ /home$
Additional information: 附加信息:
cd is a builtin function which only does: chdir(path)
with a path given, and the path is simply output with a printf("%s$",path)
at the beginning of every loop in the main (after a command has been called). cd是一个内置函数,它仅执行以下操作:
chdir(path)
具有给定的路径,并且该路径仅在main中每个循环的开头(在命令执行完后printf("%s$",path)
以printf("%s$",path)
进行输出。被称为)。
In order to understand what the real problem is, I believe that I should understand what happens at the end of my child_handler function. 为了了解真正的问题是什么,我相信我应该了解在child_handler函数末尾会发生什么。
If I'm at a certain point in the code with the main process, say waiting on the user input, and the background process dies, child_handler is called and then what? 如果我在主流程的代码中处于特定位置,例如等待用户输入,并且后台流程死了,则调用child_handler,然后呢? Does it change anything for the main process or is it still a the same point in the code, waiting for an input?
它会更改主进程的任何内容还是在代码中等待输入的同一点?
Thanks for you help. 感谢您的帮助。
One definite problem is that you are calling printf
in your signal handler, and printf
is not async-safe, so if the SIGCHLD happens while in any library function (such as fgets
waiting to read a line of input perhaps?), you get undefined behavior. 一个明确的问题是,您正在信号处理程序中调用
printf
,而printf
也不是异步安全的,因此,如果SIGCHLD发生在任何库函数中(例如fgets
正在等待读取一行输入?),您将无法定义行为。 Perhaps corrupting the stdio file buffers and causing it to act as if the input was duplicated. 可能损坏了stdio文件缓冲区,并使它的行为就像输入被重复一样。
You need to very careful how you write the code in your signal handler, making sure it cannot call (directly or indirectly) and non-async-safe library function, and ensuring that all the code is itself async-safe with respect to any side effects in other parts of your program. 您需要非常小心如何在信号处理程序中编写代码,确保它不能(直接或间接)调用非异步安全的库函数,并确保所有代码本身在任何方面都是异步安全的程序其他部分的效果。
As @ChrisDodd said, the problem was that a system call (fgets), was interrupted by the death of the background process and caused undefined behaviour. 就像@ChrisDodd所说的那样,问题在于系统调用(fgets)被后台进程的中断而中断,并导致不确定的行为。
Therefore, I solved my problem by adding the following flag to my handler: 因此,我通过向处理程序中添加以下标志解决了我的问题:
sa.sa_flags = SA_RESTART;
As the documentation says it will: 如文档所述,它将:
Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals.
通过使某些系统调用可跨信号重新启动来提供与BSD信号语义兼容的行为。
Thus no more issue with fgets. 因此,fgets不再存在问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.