简易Shell后台执行命令后挂起无提示符问题求助
简易Shell后台执行命令挂起问题的解决方法
问题描述
开发简易Shell时,前台执行命令(调用wait()等待子进程)正常,但后台执行命令(带&)时,程序会挂起且命令提示符不再显示。需要修改代码让子进程后台运行的同时,父进程能继续接收新命令。
相关代码
lsh_launch函数
int lsh_launch(char **args) { pid = fork(); if (pid == 0) { // child process if (execvp(args[0], args) == -1) { perror("lsh"); } exit(0); } else if (pid < 0) { perror("lsh"); } else { // parent process if (!background) { wait(NULL); // works fine } else { printf("[PID]: %d\n", pid); // after printing the pid, the prompt symbol does not show again } } return 1; }
lsh_execute函数
int lsh_execute(char **args) { int i; if (args[0] == NULL) { // An empty command was entered, return return 1; } //if the command is in my predefined function list, execute for (i = 0; i < lsh_num_builtins(); i++) { if (strcmp(args[0], builtin_str[i]) == 0) { return (*builtin_func[i])(args); } } //if not, go to launch and fork a child and execute return lsh_launch(args); }
调用逻辑:执行lsh_execute后回到main函数的循环顶部打印新提示符。输入ls &时,程序输出文件列表后挂起。
问题原因
- 后台子进程结束时会成为僵尸进程,向父进程发送
SIGCHLD信号。若父进程未处理该信号,当父进程阻塞等待用户输入时,信号会打断输入系统调用,导致后续输入流程异常,提示符无法正常显示。 - 未及时回收僵尸进程,可能导致资源泄漏,同时影响Shell的正常交互流程。
解决方案
1. 注册SIGCHLD信号处理函数,回收僵尸进程
添加信号处理函数,在子进程结束时非阻塞地回收所有僵尸进程:
#include <signal.h> #include <sys/wait.h> // 处理SIGCHLD信号,循环回收所有已结束的子进程 void handle_sigchld(int sig) { int status; // 使用WNOHANG选项,非阻塞模式回收子进程 while (waitpid(-1, &status, WNOHANG) > 0); }
2. 在main函数初始化时注册信号处理函数
设置信号处理函数,并开启SA_RESTART标志,让被信号打断的系统调用自动重启:
int main() { // ... 其他初始化代码 ... struct sigaction sa; sa.sa_handler = handle_sigchld; sigemptyset(&sa.sa_mask); // SA_RESTART确保被SIGCHLD打断的read等系统调用自动重启 sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } // ... 主循环(读取输入、解析、执行命令) ... }
3. 确保命令解析阶段正确处理&符号
在命令解析时,若检测到最后一个参数是&,需将background设为1,并将该参数置为NULL(避免execvp将&当作命令参数):
// 示例解析逻辑(需根据你的解析代码调整) int parse_arguments(char **args) { int background = 0; int i = 0; while (args[i] != NULL) { if (strcmp(args[i], "&") == 0) { background = 1; args[i] = NULL; // 移除&参数 break; } i++; } return background; }
修改说明
handle_sigchld函数通过waitpid(-1, &status, WNOHANG)循环回收所有僵尸进程,避免遗漏。SA_RESTART标志保证父进程在等待用户输入时,被SIGCHLD信号打断的read等系统调用会自动重启,确保Shell的输入流程不中断,提示符能正常显示。- 正确处理
&符号,保证execvp执行的命令参数正确,后台进程能正常启动。
内容的提问来源于stack exchange,提问作者高造擎
相关产品推荐
相关产品推荐

