[英]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.