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

OpenSUSE 42.3下如何修改C代码以sudo方式终止子进程?

问题分析与解决方案

版本差异原因

OpenSUSE 13.2与42.3的核心差异在于sudo版本的行为变化:

  • OpenSUSE 13.2使用的早期sudo版本中,会直接exec目标命令(如sleep),不会额外fork子进程,因此vfork返回的PID就是sleep进程的真实PID,此时kill该PID可以直接终止目标进程。
  • OpenSUSE 42.3使用的新版sudo引入了进程隔离机制,sudo会先fork一个子进程来执行目标命令,vfork返回的PID是sudo父进程的PID。当sudo完成权限切换并启动sleep后,sudo父进程会立即退出,此时你尝试kill的PID已经是已退出的进程,而sleep进程作为sudo子进程继续运行(父进程变为init/systemd),自然无法被终止。

为什么pkill -P <pid>不稳定

pkill -P是通过父进程PID查找子进程。在sudo父进程尚未退出的短暂窗口内,sleep的父进程还是sudo的PID,此时pkill能找到目标进程;但如果代码执行到system(pkill)时,sudo父进程已经退出,sleep的父进程变为init,pkill就无法匹配到目标,因此表现不稳定。

可行的代码改写方案

要稳定终止目标进程,需要先获取sleep的真实PID,再执行kill操作。以下是改写后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main (int argc, char *argv[]) 
{
    char execName[80] = "/usr/bin/sudo";
    char cmd[256];
    int pid = 0;
    int SIG = 15;
    if (argc > 1) SIG = atoi(argv[1]);

    printf("Sudo sleeping for 30 seconds.\n");
    printf("Sudo killing with signal %d.\n", SIG);
    printf("Use `ps aux | grep sleep` within 30 seconds to verify it was killed. Only this grep should show.\n");
    
    if ((pid = vfork()) == 0) {
        execl (execName, execName, "/bin/sleep", "30", (char *)NULL);
        perror(execName);
        exit (-1); 
    } else {
        if (pid == -1) perror("Bad PID for sleeper");
        fprintf( stderr, "sudo process PID = %d\n", pid );
    }

    // 短暂延迟,确保sudo已启动sleep进程
    usleep(100000); // 100ms,可根据实际情况调整

    // 获取sleep进程的真实PID(基于sudo进程的子进程)
    char get_pid_cmd[256];
    char sleep_pid[32] = {0};
    sprintf(get_pid_cmd, "/usr/bin/sudo ps -o pid= --ppid %d", pid);
    FILE *fp = popen(get_pid_cmd, "r");
    if (fp != NULL) {
        if (fgets(sleep_pid, sizeof(sleep_pid), fp) != NULL) {
            // 去除字符串中的换行符和空格
            sleep_pid[strcspn(sleep_pid, "\n\t ")] = '\0';
            if (strlen(sleep_pid) > 0) {
                sprintf(cmd, "/usr/bin/sudo kill -%d %s", SIG, sleep_pid);
                printf("Killing sleep process (PID: %s)...\n", sleep_pid);
                system(cmd);
            } else {
                fprintf(stderr, "Failed to get sleep process PID\n");
            }
        }
        pclose(fp);
    } else {
        perror("Failed to run ps command");
    }

    return 0;
}

关键改进点:

  1. 添加短暂延迟:确保sudo有足够时间启动sleep进程,避免在sudo父进程尚未fork出sleep时执行ps查询。
  2. 通过ps --ppid获取真实PID:精准匹配sudo父进程的子进程(即sleep),避免误杀其他进程。
  3. 处理PID字符串:去除多余的空白字符,确保kill命令能正确识别PID。

额外优化建议

如果sudoers配置允许,也可以考虑直接让当前进程以sudo权限运行,这样无需通过sudo启动子进程,直接fork+exec目标命令后,用kill()系统调用即可终止进程,避免PID匹配的问题。但这需要调整遗留程序的运行权限逻辑,需根据实际情况评估。

内容的提问来源于stack exchange,提问作者Hyreon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 00:32:49