如何利用fork()生成的终端进程PID关闭对应的vi编辑器进程?
解决终端启动vi后无法通过终端PID关闭vi的问题
核心原因是gnome-terminal启动后会fork出独立子进程运行vi,若未让终端等待vi退出,终端进程可能提前终止,此时vi的父进程会切换为系统进程(如systemd),杀死原终端PID无法影响vi。以下是三种可行解决方案:
方案1:让终端等待vi退出,直接终止终端即可关闭vi
修改openFile中的execlp调用,添加--wait参数,使gnome-terminal保持运行直到vi退出。此时杀死终端PID会向vi发送SIGHUP信号,默认情况下vi会响应并退出:
void openFile (char *fileName) { int pid=fork(); if (pid==0){ // 添加--wait参数,让终端等待vi进程结束 execlp("gnome-terminal", "gnome-terminal", "--wait", "--", "vi", fileName, NULL) ; } insert(fileName, pid); return; }
如果担心vi忽略SIGHUP,可以在关闭时先发送SIGTERM(优雅退出)再发送SIGKILL(强制杀死):
void closeProcess (int pid){ printf("killing terminal %d\n", pid); kill(pid, SIGTERM); sleep(1); // 给进程处理退出的缓冲时间 kill(pid, SIGKILL); return; }
方案2:直接记录vi进程的PID
通过让终端执行shell命令,启动vi的同时将其PID写入临时文件,后续读取该文件获取vi的PID进行杀死:
修改openFile函数:
#include <stdio.h> #include <string.h> void openFile (char *fileName) { int pid=fork(); if (pid==0){ // 构造命令:启动vi并将PID写入关联的临时文件 char cmd[256]; // 注意:如果fileName包含特殊字符,需要做转义处理避免shell注入 snprintf(cmd, sizeof(cmd), "vi \"%s\" & echo $! > /tmp/vi_%s.pid", fileName, fileName); // 让终端执行该shell命令 execlp("gnome-terminal", "gnome-terminal", "--", "sh", "-c", cmd, NULL) ; } // 调整insert逻辑,存储临时PID文件路径而非终端PID char pidFilePath[256]; snprintf(pidFilePath, sizeof(pidFilePath), "/tmp/vi_%s.pid", fileName); insert(fileName, pidFilePath); return; }
对应的关闭函数:
#include <stdio.h> #include <unistd.h> void closeProcess (char *pidFilePath){ FILE *fp = fopen(pidFilePath, "r"); if (!fp) { perror("Failed to open PID file"); return; } int viPid; if (fscanf(fp, "%d", &viPid) == 1) { printf("killing vi process %d\n", viPid); kill(viPid, SIGKILL); } fclose(fp); // 删除临时文件 unlink(pidFilePath); return; }
方案3:通过进程树查找vi的PID
如果无法修改启动逻辑,可以通过终端PID查找其子进程vi,或者直接通过文件名查找vi进程:
依赖系统工具pgrep实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> void closeProcess (int terminalPid, char *fileName){ // 先尝试通过父PID查找vi char cmd[256]; snprintf(cmd, sizeof(cmd), "pgrep -P %d", terminalPid); FILE *fp = popen(cmd, "r"); int viPid = -1; if (fp) { fscanf(fp, "%d", &viPid); pclose(fp); } if (viPid != -1) { printf("killing vi process %d\n", viPid); kill(viPid, SIGKILL); return; } // 父PID找不到时,尝试通过文件名查找vi进程 snprintf(cmd, sizeof(cmd), "pgrep -f 'vi \"%s\"'", fileName); fp = popen(cmd, "r"); if (fp) { if (fscanf(fp, "%d", &viPid) == 1) { printf("killing vi process %d\n", viPid); kill(viPid, SIGKILL); } pclose(fp); } }
直接读取/proc文件系统(不依赖外部工具)
通过遍历/proc目录下的进程文件,检查进程的父PID和名称来找到vi:
#include <dirent.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <ctype.h> int findViPid(int parentPid, const char *fileName) { DIR *dir = opendir("/proc"); if (!dir) return -1; struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (!isdigit(entry->d_name[0])) continue; int pid = atoi(entry->d_name); char statusPath[256]; snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid); FILE *fp = fopen(statusPath, "r"); if (!fp) continue; char line[256]; int ppid = -1; char comm[256] = {0}; while (fgets(line, sizeof(line), fp)) { if (strncmp(line, "PPid:", 5) == 0) { sscanf(line + 5, "%d", &ppid); } else if (strncmp(line, "Name:", 5) == 0) { sscanf(line + 5, "%s", comm); } } fclose(fp); // 先检查父PID和进程名 if (ppid == parentPid && strcmp(comm, "vi") == 0) { closedir(dir); return pid; } // 父PID不匹配时,检查命令行参数是否包含目标文件 char cmdlinePath[256]; snprintf(cmdlinePath, sizeof(cmdlinePath), "/proc/%d/cmdline", pid); fp = fopen(cmdlinePath, "r"); if (fp) { char cmdline[512]; fread(cmdline, 1, sizeof(cmdline)-1, fp); fclose(fp); // cmdline中各参数用\0分隔,替换为空格方便查找 for (int i=0; cmdline[i]; i++) { if (cmdline[i] == '\0') cmdline[i] = ' '; } if (strcmp(comm, "vi") == 0 && strstr(cmdline, fileName) != NULL) { closedir(dir); return pid; } } } closedir(dir); return -1; } void closeProcess (int terminalPid, char *fileName){ int viPid = findViPid(terminalPid, fileName); if (viPid != -1) { printf("killing vi process %d\n", viPid); kill(viPid, SIGKILL); } else { printf("vi process for file %s not found\n", fileName); } }
内容的提问来源于stack exchange,提问作者Biplab Roy
相关产品推荐
相关产品推荐

