簡體   English   中英

使用SIGTSTP掛起子進程后,shell沒有響應

[英]After suspending child process with SIGTSTP, shell not responding

我正在用C編寫一個基本的Shell,現在正在暫停子進程。

我認為我的信號處理程序是正確的,並且我的子進程正在掛起,但是在那之后,終端應該返回到父進程,並且這沒有發生。

這個孩子被暫停了,但是我的外殼不再注冊任何輸入或輸出了。 tcsetpgrp()似乎沒有幫助。

這是我的SIGTSTP外殼代碼中的信號處理程序:

void suspend(int sig) {
    pid_t pid;
    sigset_t mask;
    //mpid is the pgid of this shell.
    tcsetpgrp(STDIN_FILENO, mpid);
    tcsetpgrp(STDOUT_FILENO, mpid);
    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
    signal(SIGTSTP, SIG_DFL);
    //active.pid is the pid of the child currently in the fg.
    if (active.pid != 0) {
        kill(active.pid, SIGTSTP);
    }
    else{
        //if this code is being run in the child, child calls SIGTSTP on itself.
        pid = getpid();
        if (pid != 0 && pid != mpid){
            kill(pid, SIGTSTP);
        }
    }
    signal(SIGTSTP, suspend);
}

誰能告訴我我在做什么錯?

我是否要與孩子一起暫停我的shell,是否需要以某種方式將stdin和stdout返回到shell? 我該怎么做?

謝謝!

這是一個老問題,但我仍然認為我找到了答案。
您沒有編寫父母的代碼,但我假設它看起來像:

int main(){ 
     pid_t pid = fork();
     if(pid == 0){ //child process
        //call some program
     else //parent process
        wait(&status); //or waitpid(pid, &status, 0)
        //continue with the program
}

問題出在wait()或waitpid()上,好像您在使用Ctrl + Z后在像Ubuntu這樣的操作系統上運行程序時,您的子進程正在獲取SIGTSTP,但父進程中的wait()函數仍在等待!

正確的方法是將父級中的wait()替換為pause(),並制作另一個捕獲SIGCHLD的處理程序。 例如:

void sigHandler(int signum){
     switch(signum){
        case SIGCHLD:
             // note that the last argument is important for the wait to work
             waitpid(-1, &status, WNOHANG);
             break;
     }
}

在這種情況下,子進程收到Ctrl + Z之后 ,父進程也會收到SIGCHLD並返回pause()。

我使用帶有信號的民謠使進程暫停並使用ctrl + c恢復

正在運行的視頻: 鏈接

碼:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void reverse_handler(int sig);
_Bool isPause=0;
_Bool isRunning=1;

int main()
{
    int ppid;
    int counter=0;
    //make parent respond for ctrl+c (pause,resume).
    signal(SIGINT,reverse_handler);
    while(isRunning){
        while(isPause==0)
        {
            /*code exec while process is resuming */
            printf("\nc:%d",counter++);
            fflush(stdout);
            sleep(1);
        }
        //close parent after child is alive.
        if((ppid=fork())==0){   exit(0);    }
        //make child respond for ctrl+c (pause,resume).
        signal(SIGINT,reverse_handler);
        //keep child alive and listening.
        while(isPause==1){ /*code exec while process is pausing */  sleep(1);   }
    }
    return 0;
}
//if process is pause made it resume and vice versa.
void reverse_handler(int sig){
    if(isPause==0){
        printf("\nPaused");
        fflush(stdout);
        isPause=1;
    }
    else if(isPause==1){
        printf("\nresuming");
        fflush(stdout);
        isPause=0;
    }
}

我希望這會有所幫助。

如有任何疑問,請評論我

tcsetpgrp用於指定什么是前台作業。 當您的shell在前台生成一個作業(不帶& )時,它應該創建一個新的進程組並使其成為前台作業(控制終端的,而不是STDIN上的任何東西)。 然后,在按CTRL-Z時,該作業將獲得TSTP。 掛起作業的是終端,而不是外殼。 您的外殼程序不應捕獲TSTP或將TSTP發送給任何人。

它應該只wait()它產生的作業,並檢測何時停止(並收回前台組並將該作業標記為內部暫停)。 您的fg命令將使作業的pgid再次成為前台進程組,並向其發送SIGCONT並再次等待它,而bg只會發送SIGCONT

我在這里回答這個問題可能太遲了,但是當我遇到同樣的問題時,這才起作用。 根據tcsetpgrp()的手冊頁

函數tcsetpgrp()使具有進程組ID pgrp的進程組成為與fd關聯的終端上的前台進程組,fd必須是調用進程的控制終端,並且仍與其會話關聯。 而且,pgrp必須是與調用進程屬於同一會話的(非空)進程組。

如果tcsetpgrp()在其會話中由后台進程組的成員調用,並且該調用進程沒有阻塞或忽略SIGTTOU,則SIGTTOU信號將發送到該后台進程組的所有成員。

因此,在我創建將要出現的進程之前,對我SIGTTOU是忽略shell程序中的信號SIGTTOU 如果我不忽略此信號,則內核會將此信號發送到我的Shell程序並將其掛起。

暫無
暫無

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

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