我想知道在C中的信号执行处理程序后到底发生了什么,更具体地说是SIGCHLD信号。
我实际构建了一个shell,我需要这样做:
我的代码完成了所有这些要点,但在处理函数返回时最终会以奇怪的方式运行。
主循环
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;
}
用户的输入处理
// max 100000 characters
char input[MAX_INPUT];
// read keyboard inputs
fgets(input,MAX_INPUT,stdin);
内置功能(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);
}
信号和处理程序之间的链接:
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = child_handler;
sigaction(SIGCHLD, &sa, NULL);
处理程序:
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;
}
当我打电话时会出现问题:
sudo apt-get update &
cd ../
然后当sudo命令终止时(处理程序被调用并打印&#34;后台作业退出,代码为0&#34;),即使命令&#34; cd ../"已执行再次执行。
输出看起来像:
$ /home/ubuntu/workspace$ sudo apt-get update &
$ /home/ubuntu/workspace$ cd ../
$ /home/ubuntu$
Background job exited with code 0
$ /home$
其他信息:
cd是一个内置函数,它只执行:chdir(path)
给出一个路径,并且在主要的每个循环开始时只用printf("%s$",path)
输出路径(在命令发出之后)叫)。
为了理解真正的问题是什么,我相信我应该理解在child_handler函数结束时会发生什么。
如果我在主进程的代码中的某一点,说等待用户输入,并且后台进程死掉,那么调用child_handler然后呢?它是否会改变主进程的任何内容,或者它仍然是代码中的相同点,等待输入?
谢谢你的帮助。
答案 0 :(得分:1)
一个明确的问题是你在信号处理程序中调用printf
,并且printf
不是异步安全的,所以如果SIGCHLD在任何库函数中发生(例如{{1})等待读取一行输入?),你得到未定义的行为。也许破坏stdio文件缓冲区并使其像输入一样重复。
你需要非常小心如何在信号处理程序中编写代码,确保它不能调用(直接或间接)和非异步安全库函数,并确保所有代码对于程序其他部分的任何副作用,它本身都是异步安全的。
答案 1 :(得分:0)
正如@ChrisDodd所说,问题在于系统调用(fgets)被后台进程的死亡打断并导致未定义的行为。
因此,我通过向我的处理程序添加以下标志来解决我的问题:
sa.sa_flags = SA_RESTART;
正如文件所述:
通过使某些系统调用可以跨信号重新启动,提供与BSD信号语义兼容的行为。
因此fgets不再存在问题。