簡體   English   中英

在C中對SIGCHLD信號執行處理函數后會發生什么?

[英]What happens after the execution of a handler function on the SIGCHLD signal in C?

我想知道在對C中的信號(尤其是SIGCHLD信號)執行處理程序之后究竟發生了什么。

我實際上是在構建外殼,我需要這樣做:

  1. 用戶輸入帶有“&”作為最后一個參數的命令
  2. 用fork創建一個新過程
  3. 父進程返回到主函數並等待用戶的新輸入
  4. 子進程在后台執行
  5. 子進程結束並觸發SIGCHLD
  6. 父進程處理孩子的死亡

我的代碼完成了所有這些要點,但是在處理程序函數返回時,其行為最終卻以奇怪的方式出現。

我的實施

主循環

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命令終止時(調用處理程序並打印“以代碼0退出的后台作業”),即使命令“ cd ../”已經執行,它也會再次執行。

輸出看起來像:

$ /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)printf("%s$",path)進行輸出。被稱為)。

問題

為了了解真正的問題是什么,我相信我應該了解在child_handler函數末尾會發生什么。

如果我在主流程的代碼中處於特定位置,例如等待用戶輸入,並且后台流程死了,則調用child_handler,然后呢? 它會更改主進程的任何內容還是在代碼中等待輸入的同一點?

感謝您的幫助。

一個明確的問題是,您正在信號處理程序中調用printf ,而printf也不是異步安全的,因此,如果SIGCHLD發生在任何庫函數中(例如fgets正在等待讀取一行輸入?),您將無法定義行為。 可能損壞了stdio文件緩沖區,並使它的行為就像輸入被重復一樣。

您需要非常小心如何在信號處理程序中編寫代碼,確保它不能(直接或間接)調用非異步安全的庫函數,並確保所有代碼本身在任何方面都是異步安全的程序其他部分的效果。

就像@ChrisDodd所說的那樣,問題在於系統調用(fgets)被后台進程的中斷而中斷,並導致不確定的行為。

因此,我通過向處理程序中添加以下標志解決了我的問題:

sa.sa_flags = SA_RESTART;

如文檔所述,它將:

通過使某些系統調用可跨信號重新啟動來提供與BSD信號語義兼容的行為。

因此,fgets不再存在問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM