如何解决在 mini-shell 中使用 setpgid 会破坏交互式命令
我正在尝试使用 this template 在 C 中编写一个迷你外壳。但是每当我尝试使用诸如 less
和 vi
之类的交互式命令时,shell 都会卡在 waitpid 上(启用 WUNTRACED
这些命令会立即返回,因为它们被作业控制信号停止由 ps
) 表示。其他不需要输入的命令(例如 ls
)都可以。根本原因是 setpgid
,它似乎将分叉的子进程(例如 less
和 vi
)放入不再共享终端的不同进程组中。因此子进程被作业控制信号停止。删除 setpgid
会使 mini-shell 重新工作,但不能删除,因为 mini-shell 需要将其前台进程作为一个组来控制(例如,如果前台进程 P 派生出额外的进程 P1 和 P2, shell 在收到用户发送的 SIGTSTP
后,应该停止 P、P1 和 P2。如果 P、P1、P2 在同一个进程组中,其 pgid 与 P 的 pid 相同,则可以方便地完成此操作。我们可以只向整个进程组发送 SIGTSTP)。
我尝试使用 tcsetpgrp
来修复我的 shell。虽然它会使 vi
等命令再次起作用,但 mini-shell 会在 fork 子进程完成后自动退出,大概是因为父 shell 错误地将 fork 子进程的完成也视为 mini 进程的完成-壳。
是否有修复程序仍然允许我保留 setpgid?
// full code is in the provided link
if (!builtin_command(argv)) {
if ((pid = fork()) == 0) { /* Child runs user job */
if (execve(argv[0],argv,environ) < 0) {
execvp(argv[0],argv);
printf("%s: Command not found.\n",argv[0]);
exit(0);
}
}
// call wrapper function for error handling
// set process group id of child to the pid of child
Setpgid(pid,0);
if (!bg) {
// foreground process,should wait for completion
// tcsetpgrp does make vi and less work,// but their completion also terminates the mini-shell
// tcsetpgrp(STDERR_FILENO,pid);
int status;
if (waitpid(pid,&status,0) < 0) {
unix_error("waitfg: waitpid error");
}
} else {
// background process
printf("%d %s",pid,cmdline);
}
}
解决方法
解决方案是使用 tcsetpgrp
将 tty 的控制权交给另一个进程组,当子进程完成后,再次使用 tcsetpgrp
收回对 tty 的控制权。请注意,tcsetpgrp
向其调用方 if the calling process belongs to a background process group 发送 SIGTTOU
,因此必须阻止 SIGTTOU
和 SIGTTIN
。
// error handling is omitted for brevity
// these two signal functions can be anywhere before tcsetpgrp
// alternatively,they can be masked during tcsetpgrp
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) {
if (execve(argv[0],argv,environ) < 0) {
execvp(argv[0],argv);
printf("%s: Command not found.\n",argv[0]);
exit(0);
}
}
if (!bg) {
setpgid(pid,0);
tcsetpgrp(STDERR_FILENO,pid);
int status;
if (waitpid(pid,&status,0) < 0) {
unix_error("waitfg: waitpid error");
}
tcsetpgrp(STDERR_FILENO,getpgrp());
} else {
printf("%d %s",pid,cmdline);
}
}
这是一个相当粗糙的实现。请参阅 Craig 对此问题的评论,了解在哪里可以找到 bash
的实现。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。