Почему мой самоопределяемый обработчик сигнала заставит дочерний процесс получить SIGTTIN при сравнении pid - PullRequest
0 голосов
/ 13 октября 2018

Я пишу простую оболочку.Чтобы иметь дело с процессами зомби, я использую signal(SIGHLD, signal_handler) и хэдлер самоопределения.

Когда я каким-либо образом запускаю эту программу, GDB скажет мне, что ребенок получил 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 вы увидите, как дочерний процесс получает SIGTTIN в строке: 38.

Моя среда - Ubuntu 18.04 с bash & g ++

gdb image

1 Ответ

0 голосов
/ 15 октября 2018

Я не смог воспроизвести вашу ошибку 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 снова).Справочная страница для waitpid сообщает:

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

в случае успеха возвращает идентификатор процесса ребенка, состояние которого изменилось;если был указан WNOHANG и один или несколько дочерних элементов (ren), указанных в 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 в обработчике сигналов - плохая вещь (TM), но обычно он подходит для отладки.

...