如何只在前台杀死子进程?
How to only kill the child process in the foreground?
我正在尝试构建一个 shell,并且我已经设法编写了大部分功能,但是我有一个小问题。
假设我输入 firefox &
。 Firefox 将作为后台进程打开。 &
激活一个 BG 标志,使父进程不等待子进程。
然后我输入 gedit
。 Gedit 将作为前台进程打开。这意味着当前父级正在等待进程关闭。
此时,父进程有两个进程 - firefox
和 gedit
。 Firefox 尚未等待,目前处于后台,而我们目前正在等待 Gedit 完成。到目前为止一切顺利。
但是,如果我决定通过按 ctrl-c
发送 SIGINT 信号,firefox
和 gedit
都会关闭。不好,只有 gedit
应该关闭。
这是我的信号处理函数:
pid_t suspended_process[10];
int last_suspended = -1;
void signal_handler(int signo){
pid_t process = currentpid();
// Catches interrupt signal
if(signo == SIGINT){
int success = kill(process, SIGINT);
}
// Catches suspend signal
else if(signo == SIGTSTP){
int success = kill(process, SIGTSTP);
resuspended = 1;
suspended_process[last_suspended+1] = process;
last_suspended++;
}
}
这是 fork-exec 代码中等待进程或继续运行的部分。
else if(pid > 0){ //Parent
current_pid = pid;
// Waits if background flag not activated.
if(BG == 0){
// WUNTRACED used to stop waiting when suspended
waitpid(current_pid, &status, WUNTRACED);
if(WIFEXITED(status)){
setExitcode(WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
printf("Process received SIGNAL %d\n", WTERMSIG(status));
}
}
}
如果我事先挂起进程,也会发生这种情况。比如我运行firefox
然后按ctrl-z
就暂停了。然后我运行gedit
然后按ctrl-c
关闭它。紧接着,如果我按 fg
恢复挂起的 firefox
,它会立即关闭。
我找不到只将 SIGINT 信号发送到前台进程的方法,它总是将信号发送给父进程以外的所有子进程,无论它们是在后台还是挂起。
以防万一,这是初始化信号处理程序的函数:
void init_handler(){
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
// If conditions for signal handling.
// Also creates 2 signal handlers in memory for the SIGINT and SIGTSTP
if(sigaction(SIGINT, &sa, NULL) == -1)
printf("Couldn't catch SIGINT - Interrupt Signal\n");
if(sigaction(SIGTSTP, &sa, NULL) == -1)
printf("Couldn't catch SIGTSTP - Suspension Signal\n");
}
这很简单,但是 signals 并没有完成。相反,您必须使用名为 进程组 的功能。每个 作业 (executable 或管道等)将是一个单独的 进程组 。您可以使用 setpgid
(或在某些系统上使用 setpgrp
)创建进程组。可以简单的在fork
之后exec
之前设置子进程的进程组,然后将这个job的进程组id存入jobtable.
现在,前台的进程组被设置为终端的活动进程组(shell 的 /dev/tty
)和 tcsetpgrp
- 这是将接收 CTRL+C 的进程组。那些属于同一会话但不属于使用 tcsetpgrp
设置为前台的组的进程组将完全忘记 CTRL+C.
在Antti的帮助下,我终于找到了问题所在。我在 fork-exec 代码中添加了一行:
else if(pid > 0){ //Parent
current_pid = pid;
if(setpgid(pid, pid) == 0) perror("setpid");
// Waits if background flag not activated.
if(BG == 0){
// WUNTRACED used to stop waiting when suspended
waitpid(current_pid, &status, WUNTRACED);
if(WIFEXITED(status)){
setExitcode(WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
printf("Process received SIGNAL %d\n", WTERMSIG(status));
}
}
}
if(setpgid(pid, pid) == 0) perror("setpid");
据我所知,setpgid
设置进程的进程组 ID。这意味着在上面的行中,我将 pid 为 pid
的进程的 pgid 设置为 pid
。
我可能是错的,我仍然没有完全理解这个过程,但之所以有效,是因为 SIGINT
信号只发送到 pgid
pid
。之前的意思是,由于我没有设置每个进程的 pgid
,它们都具有相同的 pgid
,因此它们都会收到信号。但是,一旦我为每个进程设置 pgid
,如果我在前台进程中间按 CTRL-C
,它只会退出 that 运行宁进程.
至少这是我能收集到的信息。 tcsetpgrp
我还是不太明白,尤其是第一个参数可以设置什么,也就是文件描述符。在 setpgid
之后添加此行:
tcsetpgrp(STDIN_FILENO, pid)
每当我 exec
命令时,只需在后台启动整个程序。而不是 运行ning firefox
它出现了,我 运行 firefox
整个程序得到 stopped
(根据终端的内容至少说)。我不知道为什么会这样。
还是感谢Antti!
我正在尝试构建一个 shell,并且我已经设法编写了大部分功能,但是我有一个小问题。
假设我输入 firefox &
。 Firefox 将作为后台进程打开。 &
激活一个 BG 标志,使父进程不等待子进程。
然后我输入 gedit
。 Gedit 将作为前台进程打开。这意味着当前父级正在等待进程关闭。
此时,父进程有两个进程 - firefox
和 gedit
。 Firefox 尚未等待,目前处于后台,而我们目前正在等待 Gedit 完成。到目前为止一切顺利。
但是,如果我决定通过按 ctrl-c
发送 SIGINT 信号,firefox
和 gedit
都会关闭。不好,只有 gedit
应该关闭。
这是我的信号处理函数:
pid_t suspended_process[10];
int last_suspended = -1;
void signal_handler(int signo){
pid_t process = currentpid();
// Catches interrupt signal
if(signo == SIGINT){
int success = kill(process, SIGINT);
}
// Catches suspend signal
else if(signo == SIGTSTP){
int success = kill(process, SIGTSTP);
resuspended = 1;
suspended_process[last_suspended+1] = process;
last_suspended++;
}
}
这是 fork-exec 代码中等待进程或继续运行的部分。
else if(pid > 0){ //Parent
current_pid = pid;
// Waits if background flag not activated.
if(BG == 0){
// WUNTRACED used to stop waiting when suspended
waitpid(current_pid, &status, WUNTRACED);
if(WIFEXITED(status)){
setExitcode(WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
printf("Process received SIGNAL %d\n", WTERMSIG(status));
}
}
}
如果我事先挂起进程,也会发生这种情况。比如我运行firefox
然后按ctrl-z
就暂停了。然后我运行gedit
然后按ctrl-c
关闭它。紧接着,如果我按 fg
恢复挂起的 firefox
,它会立即关闭。
我找不到只将 SIGINT 信号发送到前台进程的方法,它总是将信号发送给父进程以外的所有子进程,无论它们是在后台还是挂起。
以防万一,这是初始化信号处理程序的函数:
void init_handler(){
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
// If conditions for signal handling.
// Also creates 2 signal handlers in memory for the SIGINT and SIGTSTP
if(sigaction(SIGINT, &sa, NULL) == -1)
printf("Couldn't catch SIGINT - Interrupt Signal\n");
if(sigaction(SIGTSTP, &sa, NULL) == -1)
printf("Couldn't catch SIGTSTP - Suspension Signal\n");
}
这很简单,但是 signals 并没有完成。相反,您必须使用名为 进程组 的功能。每个 作业 (executable 或管道等)将是一个单独的 进程组 。您可以使用 setpgid
(或在某些系统上使用 setpgrp
)创建进程组。可以简单的在fork
之后exec
之前设置子进程的进程组,然后将这个job的进程组id存入jobtable.
现在,前台的进程组被设置为终端的活动进程组(shell 的 /dev/tty
)和 tcsetpgrp
- 这是将接收 CTRL+C 的进程组。那些属于同一会话但不属于使用 tcsetpgrp
设置为前台的组的进程组将完全忘记 CTRL+C.
在Antti的帮助下,我终于找到了问题所在。我在 fork-exec 代码中添加了一行:
else if(pid > 0){ //Parent
current_pid = pid;
if(setpgid(pid, pid) == 0) perror("setpid");
// Waits if background flag not activated.
if(BG == 0){
// WUNTRACED used to stop waiting when suspended
waitpid(current_pid, &status, WUNTRACED);
if(WIFEXITED(status)){
setExitcode(WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)){
printf("Process received SIGNAL %d\n", WTERMSIG(status));
}
}
}
if(setpgid(pid, pid) == 0) perror("setpid");
据我所知,setpgid
设置进程的进程组 ID。这意味着在上面的行中,我将 pid 为 pid
的进程的 pgid 设置为 pid
。
我可能是错的,我仍然没有完全理解这个过程,但之所以有效,是因为 SIGINT
信号只发送到 pgid
pid
。之前的意思是,由于我没有设置每个进程的 pgid
,它们都具有相同的 pgid
,因此它们都会收到信号。但是,一旦我为每个进程设置 pgid
,如果我在前台进程中间按 CTRL-C
,它只会退出 that 运行宁进程.
至少这是我能收集到的信息。 tcsetpgrp
我还是不太明白,尤其是第一个参数可以设置什么,也就是文件描述符。在 setpgid
之后添加此行:
tcsetpgrp(STDIN_FILENO, pid)
每当我 exec
命令时,只需在后台启动整个程序。而不是 运行ning firefox
它出现了,我 运行 firefox
整个程序得到 stopped
(根据终端的内容至少说)。我不知道为什么会这样。
还是感谢Antti!