You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 03:50:26