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

父进程追踪子进程内存占用问题求助

父进程追踪子进程内存使用异常问题

问题描述

通过fork创建子进程,子进程通过sh -c执行目标C++程序,在父进程中用waitpid轮询读取子进程内存时,得到的值固定在300KB左右,完全不随目标程序的内存分配变化。

问题代码

int get_memory_usage(pid_t pid) { 
    int fd, data, stack; 
    char buf[4096], status_child[100000]; 
    char *vm; 
 
    sprintf(status_child, "/proc/%d/status", pid); 
    if ((fd = open(status_child, O_RDONLY)) < 0) 
        return -1; 
 
    read(fd, buf, 4095); 
    buf[4095] = '\0'; 
    close(fd); 
 
    data = stack = 0; 
 
    vm = strstr(buf, "VmData:"); 
    if (vm) { 
        sscanf(vm, "%*s %d", &data); 
    } 
    vm = strstr(buf, "VmStk:"); 
    if (vm) { 
        sscanf(vm, "%*s %d", &stack); 
    } 
 
    return data + stack;     
} 

// void handler(int sig){
//     cout<<"Sig handler caught";
// }

int main() {
    // signal(SIGCHLD,handler);
    int pid = fork();
    if(pid == -1){
        perror("fork"); 
        exit(EXIT_FAILURE); 
    }
    else if(pid == 0){
        cout<<"I am the child process"<<endl;
        const char* command = "/bin/sh";
        const char* arg1 = "sh";
        const char* arg2 = "-c";
        const char* arg3 = "/user_code/myprogram < /user_code/input.txt > /user_code/output1.txt";

        execlp(command, arg1, arg2, arg3, (char*)NULL);

        perror("execl");
        exit(EXIT_FAILURE);
    }
    else{
        cout<<"I am the parent process";

        //What am I supposed to do here?

        //child pid
        // time_t t;
        // int status;
        // pid_t childpid = pid;
        
        // do {
        //     if ((pid = waitpid(pid, &status, WNOHANG)) == -1) perror("wait() error");
        //     else if (pid == 0) {
        //         time(&t);
        //         printf("child is still running at %s", ctime(&t));
        //         cout<<"Memory used: "<<get_memory_usage(childpid)<<endl;
        //     }
        //     else {
        //         if (WIFEXITED(status)){
        //             printf("child exited with status of %d\n", WEXITSTATUS(status));
        //         }
        //         else puts("child did not exit successfully");
        //     }
        // } while (pid == 0);
    }
}

问题原因

你当前追踪的pid是**sh进程的ID**,而非真正执行任务的myprogram进程ID。因为sh -c会创建新的子进程来运行目标程序,父进程拿到的只是sh的pid,读取的自然是sh的内存数据,所以数值固定不变。

解决方案

方案1:直接执行目标程序(推荐)

跳过sh,直接在子进程中执行myprogram,手动处理输入输出重定向:

else if(pid == 0){
    cout<<"I am the child process"<<endl;
    // 打开输入文件并重定向标准输入
    int in_fd = open("/user_code/input.txt", O_RDONLY);
    if(in_fd == -1) { 
        perror("open input file"); 
        exit(EXIT_FAILURE); 
    }
    dup2(in_fd, STDIN_FILENO);
    close(in_fd);

    // 打开输出文件并重定向标准输出
    int out_fd = open("/user_code/output1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if(out_fd == -1) { 
        perror("open output file"); 
        exit(EXIT_FAILURE); 
    }
    dup2(out_fd, STDOUT_FILENO);
    close(out_fd);

    // 直接执行目标程序
    execlp("/user_code/myprogram", "myprogram", (char*)NULL);

    perror("execlp failed");
    exit(EXIT_FAILURE);
}

此时父进程拿到的pid就是myprogram的进程ID,直接调用get_memory_usage就能得到正确的内存数据。

方案2:追踪sh的子进程(保留sh -c的场景)

如果必须通过sh -c执行,需要先获取sh进程的子进程(即myprogram)的pid:

// 获取父进程的直接子进程pid
pid_t get_child_pid(pid_t parent_pid) {
    char path[256];
    sprintf(path, "/proc/%d/task/%d/children", parent_pid, parent_pid);
    int fd = open(path, O_RDONLY);
    if(fd == -1) return -1;
    
    char buf[1024];
    ssize_t read_len = read(fd, buf, sizeof(buf)-1);
    close(fd);
    
    if(read_len <= 0) return -1;
    buf[read_len] = '\0';
    
    pid_t child_pid = -1;
    sscanf(buf, "%d", &child_pid);
    return child_pid;
}

然后在父进程的轮询逻辑中,先获取目标进程pid再进行内存读取:

else{
    cout<<"I am the parent process"<<endl;
    time_t t;
    int status;
    pid_t sh_pid = pid;
    pid_t target_pid = -1;

    do {
        if ((pid = waitpid(sh_pid, &status, WNOHANG)) == -1) {
            perror("waitpid error");
            sleep(1);
            continue;
        }
        else if (pid == 0) {
            // 首次循环时获取目标进程pid
            if(target_pid == -1){
                target_pid = get_child_pid(sh_pid);
                if(target_pid == -1){
                    printf("Failed to get target process pid\n");
                    sleep(1);
                    continue;
                }
            }
            time(&t);
            printf("target process is still running at %s", ctime(&t));
            cout<<"Memory used: "<<get_memory_usage(target_pid)<<endl;
            // 避免轮询过于频繁
            sleep(1);
        }
        else {
            if (WIFEXITED(status)){
                printf("sh process exited with status: %d\n", WEXITSTATUS(status));
            }
            else {
                puts("sh process exited abnormally");
            }
        }
    } while (pid == 0);
}

优化内存读取函数

原get_memory_usage的read可能无法读取完整的/proc/pid/status文件,改用fopen+fgets更可靠:

int get_memory_usage(pid_t pid) { 
    int data = 0, stack = 0; 
    char status_path[256];
    sprintf(status_path, "/proc/%d/status", pid);
    
    FILE* fp = fopen(status_path, "r");
    if(!fp) return -1;

    char line[256];
    while(fgets(line, sizeof(line), fp)){
        if(strncmp(line, "VmData:", 7) == 0){
            sscanf(line, "%*s %d", &data);
        }
        else if(strncmp(line, "VmStk:", 6) == 0){
            sscanf(line, "%*s %d", &stack);
        }
    }
    fclose(fp);
    return data + stack;     
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:43:22