父进程追踪子进程内存占用问题求助
父进程追踪子进程内存使用异常问题
问题描述
通过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
相关产品推荐
相关产品推荐

