为何执行'emacs &'会导致自制Shell程序崩溃及输入读取异常?
问题分析与修复方案
嘿,我来帮你拆解下为啥emacs &会搞崩你的Shell,还出现输入异常——你的后台进程处理代码里有几个关键问题,咱们一个个说:
1. waitpid参数错误与多余的等待逻辑
先看这行代码:
waitpid(pid1, &status, 1);
waitpid的第三个参数是选项标志位,1并不是合法的常量值!你大概率是想写WNOHANG(对应宏定义值为0x00000001),但直接写数字会触发未定义行为。更关键的是,这里你让Shell等待第一个子进程(pid1)结束,而pid1在fork出真正的命令进程(pid2)后就直接exit(0)了,这步等待完全没必要,还会打乱后台流程。
2. 末尾的wait(NULL)直接废掉了后台功能
你的函数最后调用了wait(NULL),这会强制Shell阻塞等待任意子进程退出——包括你想后台运行的emacs(pid2)!这等于把&的后台效果完全抵消,Shell会一直卡到emacs关闭才继续。更糟的是,emacs是交互式程序,即使后台运行也会尝试访问终端,这会和你的Shell抢终端控制权,直接导致输入读取异常甚至崩溃。
3. 后台进程未脱离终端会话
emacs这类交互式程序默认会绑定终端会话,你的代码没做任何终端隔离处理,后台运行的emacs会一直占用终端资源,干扰Shell的正常输入输出。
修复后的代码示例
我把你的代码调整了一下,解决这些核心问题:
#include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 假设Command结构体定义包含rstdin、rstdout字段 typedef struct { char *rstdin; char *rstdout; } Command; int execBgCmd(char **cmds, Command cmd) { pid_t pid; pid = fork(); if (pid < 0) { fprintf(stderr, "Fork Failure\n"); return -1; } else if (pid == 0) { // 创建新会话,彻底脱离当前终端 if (setsid() == -1) { perror("Failed to create new session"); exit(1); } // 处理标准输入重定向 if (cmd.rstdin != NULL) { int fd0 = open(cmd.rstdin, O_RDONLY); if (fd0 < 0) { perror("Failed to open stdin file"); exit(1); } dup2(fd0, STDIN_FILENO); close(fd0); } else { // 无重定向时,将标准输入指向/dev/null,避免读取终端 int fd_null = open("/dev/null", O_RDONLY); dup2(fd_null, STDIN_FILENO); close(fd_null); } // 处理标准输出重定向 if (cmd.rstdout != NULL) { int fd1 = open(cmd.rstdout, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd1 < 0) { perror("Failed to open stdout file"); exit(1); } dup2(fd1, STDOUT_FILENO); close(fd1); } else { // 无重定向时,将标准输出指向/dev/null,避免干扰Shell终端 int fd_null = open("/dev/null", O_WRONLY); dup2(fd_null, STDOUT_FILENO); close(fd_null); } // 执行目标命令 if (execvp(cmds[0], cmds) < 0) { perror("Could not execute command"); exit(1); } } else { // 父进程(Shell)不等待后台子进程,直接返回 printf("[%d] %s\n", pid, cmds[0]); // 可选:模仿bash打印后台进程PID return 0; } }
关键修复点说明
- 移除了多余的双重fork逻辑,改用
setsid()创建新会话,更直接地让后台进程脱离当前终端 - 删除所有阻塞等待子进程的代码,让Shell可以立即返回处理下一条命令
- 默认将后台进程的标准输入/输出重定向到
/dev/null,彻底避免它访问Shell的终端资源 - 增加了完整的错误处理(比如文件打开失败时用
perror输出具体原因),方便调试
这样修改后,emacs &就能正常后台运行,你的Shell也不会再出现崩溃或输入异常的问题啦!
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

