c++ - 为什么我的自定义信号处理程序会让子进程在比较 pid 时得到 SIGTTIN

标签 c++ c multiprocessing signals

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

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

我想知道为什么这个改变会导致错误。经过一番谷歌后,它似乎与“子进程”信号处理程序有关。 然而,这个处理程序从不尝试从标准输入或类似的东西读取。

谁能告诉我为什么会发生这种情况以及它是如何工作的? 或者给我一些关键词来搜索。 感谢您的帮助!

#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。

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

gdb image

最佳答案

我无法使用您提供的代码重现您的 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< 不会消失的一些其他错误)/code> 再次)。 man page for waitpid 说:

RETURN VALUE

on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.

ERRORS

ECHILD The process specified by pid does not exist or is not a child of the calling process.

EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was caught.

EINVAL The options argument was invalid.

所以我将代码更改为:

void sig_handle(int sig){
    int status;

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

1:信号处理程序中的 printf 是一件坏事(TM),但通常可以用于调试。

关于c++ - 为什么我的自定义信号处理程序会让子进程在比较 pid 时得到 SIGTTIN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52796755/

相关文章:

c - 链表包含其他链表 & 免费

在简单的宏扩展中混淆 gcc 错误消息

c - C中的重新定义

if __name__ == '__main__' 之后未定义 Python 变量

c++ - 强制转换为 &(void*&)variable 与 (void**)variable 的区别

c++ - 如何在多维数组中组织文本文件中的数据并从中乘以列?

python - 僵尸进程,我们又来了

python - Twitter、多进程和数据库

c++ - 什么是简单、可测试、跨平台的 C++ GUI?

c++ - 按下 X 时 QDialog 没有立即关闭,如何让它不在顶部?