首页 文章

在C中的SIGCHLD信号上执行处理函数后会发生什么?

提问于
浏览
0

我想知道在C中的信号执行处理程序后到底发生了什么,更具体地说是SIGCHLD信号 .

我实际上正在构建一个shell,我需要这样做:

  • 用户输入一个命令"&"作为最后一个参数

  • 使用fork创建了一个新进程

  • 父进程返回main函数并等待用户的新输入

  • 子进程在后台执行

  • 子进程结束并触发SIGCHLD

  • 父进程处理他孩子的死亡

我的代码执行所有这些要点,但在处理程序函数返回时最终会以奇怪的方式运行 .

我的实施

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)

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;
}

问题

当我打电话时,我的问题出现了:

sudo apt-get update &
cd ../

然后当sudo命令终止时(调用处理程序并打印"Background job exited with code 0"),即使命令"cd ../"已经执行,它也会被执行 again .

输出看起来像:

$ /home/ubuntu/workspace$ sudo apt-get update &
$ /home/ubuntu/workspace$ cd ../
$ /home/ubuntu$
Background job exited with code 0
$ /home$

附加信息:

cd是一个内置函数,它只执行: chdir(path) 给出一个路径,并且在main中的每个循环开始时(在调用一个命令之后)只用 printf("%s$",path) 输出路径 .

这个问题

为了理解真正的问题是什么,我相信我应该理解在child_handler函数结束时会发生什么 .

如果我在主进程的代码中处于特定点,说等待用户输入,后台进程死掉,那么调用child_handler然后呢?它是否会改变主进程的任何内容,或者它仍然是代码中的相同点,等待输入?

谢谢你的帮助 .

2 回答

  • 1

    正如@ChrisDodd所说,问题在于系统调用(fgets)被后台进程的死亡打断并导致未定义的行为 .

    因此,我通过向我的处理程序添加以下标志来解决我的问题:

    sa.sa_flags = SA_RESTART;
    

    正如文档所述,它将:

    通过使某些系统调用可以跨信号重新启动,提供与BSD信号语义兼容的行为 .

    因此fgets不再存在问题 .

  • 0

    一个明确的问题是你在信号处理程序中调用 printf ,并且 printf 不是异步安全的,所以如果在任何库函数中发生SIGCHLD(例如 fgets 等待读取一行输入?),你会得到未定义的行为 . 也许破坏stdio文件缓冲区并使其像输入一样重复 .

    您需要非常小心如何在信号处理程序中编写代码,确保它不能调用(直接或间接)和非异步安全库函数,并确保所有代码本身对任何一方都是异步安全的效果在程序的其他部分 .

相关问题