Подтвердить что ты не робот

Waitpid блокирование, когда это не должно

Я пишу мини-оболочку (нет, не для школы: P; для собственного удовольствия), и теперь большинство основных функций выполняются, но я застреваю при попытке обработать SIGTSTP.

Предположительно, когда пользователь нажимает Ctrl+Z, SIGTSTP должен быть отправлен в процесс Foreground оболочки, если он существует, и Shell должна продолжать нормально.

После создания каждого процесса (если это процесс Foreground), следующий код ждет:

if(waitpid(pid, &processReturnStatus, WUNTRACED)>0){//wait stopped too
    if(WIFEXITED(processReturnStatus) || WIFSIGNALED(processReturnStatus))
        removeFromJobList(pid);
}

И я обрабатываю сигнал следующим образом:

void sigtstpHandler(int signum)
{
    signum++;//Just to remove gcc warning
    pid_t pid = findForegroundProcessID();
    if(pid > -1){
        kill(-pid, SIGTSTP);//Sending to the whole group
    }
}

Что происходит, когда я нажимаю Ctrl+Z, дочерний процесс действительно приостанавливается (используя ps -all для просмотра состояния процессов), но моя оболочка зависает в waitpid, она никогда не возвращается, хотя я прошел WUNTRACED, который, насколько я понял, должен возвращать waitpid, когда процесс также остановлен.
Так что я мог сделать неправильно? или я неправильно понял поведение waitpid?

Примечания:
-findForegroundProcessID() возвращает правый pid; Я дважды проверил это.
- Я меняю каждую группу процессов, когда сразу после я fork
-Handling Ctrl+C работает отлично! -Если я использую другой терминал для отправки SIGCONT после того, как моя оболочка зависает, дочерний процесс возобновляет свою работу, а оболочка пожинает его в конце концов.
-Я улавливаю SIGTSTP, который, насколько я читал (и тестировал), можно поймать. -Я попытался использовать waitid вместо waitpid на всякий случай, проблема сохранилась. EDIT:

void sigchldHandler(int signum)
{
    signum++;//Just to remove the warning
    pid_t pid;
    while((pid = waitpid(-1, &processReturnStatus, 0)) > 0){    
        removeFromJobList(pid);
    }
    if(errno != ECHILD)
        unixError("kill error");
}

Мой обработчик SIGCHLD.

4b9b3361

Ответ 1

SIGCHLD поставляется для остановленных детей. Вызов waitpid() в обработчике сигнала - который не указывает WUNTRACED - блокирует навсегда.

Вероятно, вы не должны иметь обработку removeFromJobList() в двух разных местах. Если бы я должен был догадаться, это звучит так, как будто оно касается глобальных структур данных и не относится к обработчику сигналов.

Ответ 2

Waitpid не возвращается, потому что вы не устанавливаете обработчик sigchld (который я отправил вам ранее). У вас есть дочерняя процессия, которая не получает жатву. Кроме того, waitpid должен быть в цикле while, а не if (также отправлен вам).

Единственным сигналом, который вы должны поймать, является SIGCHLD. Причина в том, что если ваши процессы раздвоены правильно, ядро ​​отправит этот сигнал в процесс переднего плана, и он прекратит его или остановит его или сделает все, что будет правильно.

Если группы процессов не установлены правильно, сигналы будут отправляться в неправильный процесс. Один из способов проверить это - запустить процесс переднего плана и нажать Ctrl-Z. Если вся ваша оболочка существует, тогда сигнал Ctrl-Z отправляется во всю оболочку. Это означает, что вы не установили новый процесс в новой группе процессов и не дали ему терминал.

Теперь, что вам нужно сделать, если ваш сигнал Ctrl-Z останавливает всю вашу оболочку. Как только вы переделаете процесс, в дочернем процессе: - Задайте процесс в своей собственной группе с помощью setpgid. - Дайте ему разумный терминал, заблокировав SIGTTOU, а затем предоставив ему терминал с помощью tcsetpgrp.

В родительском: - Также установите его дочерний процесс с помощью setpgid. Это связано с тем, что вы не знаете, будет ли сначала выполняться ребенок или родитель, поэтому это позволит избежать состояния гонки. Это не помешает установить его дважды.