为什么我的自定义信号处理程序在比较pid

时间:2018-10-13 19:52:31

标签: c++ c multiprocessing signals

我写了一个简单的外壳。 为了处理僵尸进程,我使用signal(SIGHLD, signal_handler)和自定义的hadler。

当我以某种方式运行该程序时,gdb会告诉我孩子收到了SIGTTIN并且程序停止了。 但是,如果我将处理程序更改为while(waitpid(-1, &status, WNOHANG)>0);,一切都会顺利进行。

我想知道为什么此更改会导致错误。经过一些谷歌,它似乎与“子进程”信号处理程序有关。 但是,此处理程序永远不会尝试从stdin或类似的东西读取。

有人可以告诉我为什么会发生这种情况以及它如何工作吗? 或给我一些关键词进行搜索。 谢谢您的帮助!

#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>

using namespace std;

int parse_argv(stringstream &in_command, char** &argv);   //get argv list
void sig_handle(int sig);

int main()
{

    while(true){
        string input, output;
        stringstream ss;

        char **myArgv;
        int myArgc;
        bool back_ground = false;
        cout << ">";

        getline(cin, input);
        ss << input;

        myArgc = parse_argv(ss, myArgv);    //get argument!
        if(strcmp(myArgv[myArgc-1], "&") == 0){        //check if & exit
            back_ground = true;
            myArgv[myArgc-1] = NULL;
        }
        pid_t pid = fork();


        if(pid < 0){
            cout << "Fork process error!" << endl;
        }
        else if(pid == 0){ //child process
            execvp(myArgv[0], myArgv);

            exit(0);
        }
        else{    //parent process
            if(back_ground == false)
                wait(&pid);
            else
                signal(SIGCHLD, sig_handle);
                                             //Free memory!!
            for(int i=0; i<=myArgc; i++)     //size of myArgv is myArgc+1
                delete[] myArgv[i];
            delete[] myArgv;
        }
    }
    return 0;
}

int parse_argv(stringstream &in_command, char** &argv){
    vector<string> tmp;
    string tmp_s;

    tmp.reserve(10);

    while(in_command >> tmp_s){
        tmp.push_back(tmp_s);
    }

    argv = new char* [tmp.size()+1];        //dynamic allocate true arugments array
    for(int i=0; i<tmp.size(); i++){
        argv[i] = new char[strlen(tmp[i].c_str())];
        strcpy(argv[i], tmp[i].c_str());
    }
    argv[tmp.size()] = NULL;                //argv should terminated by NULL
    return tmp.size();
}

void sig_handle(int sig){
    int status;
    while(waitpid(-1, &status, WNOHANG));
}

如何重复错误:

  1. 编译并运行该程序
  2. 键入ls &[Enter]
  3. 键入ls[Enter]
  4. 然后,您将发现第二个ls不返回任何内容。在gdb中,您会看到子进程在第38行中得到SIGTTIN。

我的环境是具有bash和g ++的Ubuntu 18.04

gdb image

1 个答案:

答案 0 :(得分:0)

我无法使用您提供的代码重现SIGTTIN错误,这可能是由于所使用的gdb命令的确切顺序所致,但是在我输入以下命令后,您的程序却没有响应:

ls &
ls

gdb显示此while语句处于无限循环中:

void sig_handle(int sig){
    int status;
    while(waitpid(-1, &status, WNOHANG));
}

添加printf以获得更仔细的观察 1

void sig_handle(int sig){
    int status, i, r;

    for (i=0; i<5; i++) {
        errno = 0;
        r = waitpid(-1, &status, WNOHANG);
        printf("waitpid returns %d errno %d\n", r, errno);
        sleep(1);
    }
}

,输出为:

waitpid returns 3672 errno 0
waitpid returns -1 errno 10
waitpid returns -1 errno 10
waitpid returns -1 errno 10
waitpid returns -1 errno 10

因此应该更改逻辑,以便在等待进程或没有进程等待时(或仅通过调用waitpid而消失的其他错误时,循环终止再次)。 man page for waitpid 说:

  

返回值

     

成功时,返回状态已更改的子进程的ID。如果指定了WNOHANG,并且存在由pid指定的一个或多个孩子,但尚未更改状态,则返回0。出错时,返回-1。

     

错误

     

ECHILD由pid指定的进程不存在或不是调用进程的子进程。

     

未设置EINTR WNOHANG,并且捕获到畅通的信号或SIGCHLD。

     

EINVAL options参数无效。

所以我将代码更改为:

void sig_handle(int sig){
    int status;

    while (waitpid(-1, &status, WNOHANG) == 0); // more precisely, break if r > 0 || r == -1
}

1 :信号处理程序中的printf是Bad Thing(TM),但通常可以调试。