我正在用C编写一个基本的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?我该怎么做?
谢谢!
答案 0 :(得分:4)
这是一个老问题,但我认为我找到了答案 你没有写你父母的代码,但我假设它看起来像:
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()函数仍在等待!
正确的方法是使用pause()替换父级中的wait(),并创建另一个捕获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()返回。
答案 1 :(得分:1)
我用带信号的伙计让进程暂停并使用ctrl + c恢复
正在运行的视频:link
代码:
#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;
}
}
我希望这会有用。
如有任何疑问,请评论我
答案 2 :(得分:0)
tcsetpgrp
用于指定前台作业。当你的shell在前台生成一个作业(没有&
)时,它应该创建一个新的进程组并将其作为前台作业(控制终端的作业,而不是STDIN上的任何内容)。然后,按CTRL-Z,该作业将获得TSTP。这是暂停工作的终端,而不是你的shell。您的shell不应该捕获TSTP或将TSTP发送给任何人。
对于它已生成的作业,它应该只有wait()
并检测它何时被停止(并声明返回前景组并将作业标记为内部暂停)。您的fg
命令会使作业的pgid再次成为前台进程组并向其发送SIGCONT
并再次等待它,而bg
只会发送SIGCONT
答案 3 :(得分:0)
我在这里回答这个问题可能会迟到,但是当我遇到同样的问题时,这是有用的。根据tcsetpgrp()
的手册页函数tcsetpgrp()使进程组具有进程组ID pgrp与fd关联的终端上的前台进程组, 它必须是调用进程的控制终端,并且 仍然与其会话相关联。而且,pgrp必须是a (非空)进程组属于与调用相同的会话 过程
如果tcsetpgrp()由后台进程组的成员调用 它的会话,调用进程没有阻塞或忽略 SIGTTOU,SIGTTOU信号发送给此背景的所有成员 过程组。
所以,在我创建进入前台的进程之前,对我有用的是忽略shell程序中的信号SIGTTOU
。如果我不忽略这个信号,那么内核会将这个信号发送到我的shell程序并暂停它。