我正在用C编写一个基本的shell,并且正在处理挂起一个子进程的问题。
我认为我的信号处理程序是正确的,我的子进程正在挂起,但在那之后,终端应该返回到父进程,但这并没有发生。
子进程被挂起,但是我的shell不再注册任何输入或输出。tcsetpgrp()似乎没有帮助。
下面是SIGTSTP shell代码中的信号处理程序:
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?我将如何做到这一点?
谢谢!
4条答案
按热度按时间py49o6xq1#
这是一个老问题,但我仍然认为我找到了答案。
你没有写你父母的代码,但我假设它看起来像这样:
问题出在wait()或waitpid()上,看起来像是如果你在Ubuntu这样的操作系统上运行你的程序,在使用Ctrl+Z之后,你的子进程正在获得SIGTSTP,但是父进程中的wait()函数仍然在等待!
正确的方法是用pause()替换父进程中的wait(),并创建另一个捕捉SIGCHLD的处理程序,例如:
在这种情况下,子进程接收Ctrl+Z后,父进程也接收SIGCHLD和pause()返回。
rn0zuynd2#
tcsetpgrp
是指定什么是前台作业。(没有&
),它应该创建一个新的进程组,并使其成为前台作业(控制终端的,而不是STDIN上的任何东西)。然后,按下CTRL-Z,该作业将获得TSTP。它是挂起作业的终端,而不是您的shell。您的shell不应该捕获TSTP或将TSTP发送给任何人。它应该只为它所派生的作业提供
wait()
,并检测它何时停止(收回前台组并将作业标记为内部挂起)。您的fg
命令将使作业的pgid再次成为前台进程组,并向它发送SIGCONT
,然后再次等待它,而bg
将只发送SIGCONT
k2fxgqgv3#
我用folk与信号为使进程暂停和恢复与ctrl + c
运行时的视频:link
代码:
我希望这有用。
如果有任何问题请告诉我
owfi6suc4#
在这里回答这个问题可能会晚一些,但是当我遇到同样的问题时,这是行之有效的方法。
函数tcsetpgrp()使进程组ID为pgrp的进程组成为fd关联的终端上的前台进程组,fd必须是调用进程的控制端,并且还与其会话关联,并且pgrp必须是与调用进程属于同一会话的(非空)进程组。
如果tcsetpgrp()被后台进程组的成员在其会话中调用,并且调用进程没有阻塞或忽略SIGTTOU,则SIGTTOU信号将发送到此后台进程组的所有成员。
所以,在我创建进程之前,忽略shell程序中的
SIGTTOU
信号,如果我不忽略这个信号,内核就会发送这个信号到shell程序并挂起它。