当程序写得越来越大,进程占用的内存也就越来越多,调用popen时会返回空的FILE指针,网上说原因是system或popen这样的系统函数,其内部实现是调用fork函数创建子进程,创建过程中会复制父进程堆、栈等资源,这样就容易造成创建失败,返回NULL。
以下是我写的用vfork替换fork调用的Vpopen类。
Vpopen.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #pragma once #include <stdio.h> #include <sys/types.h> #include <unistd.h> class Vpopen { FILE *fp_; pid_t pid_; int pipeFd_[2]; public: Vpopen(); ~Vpopen(); FILE *open(const char *cmd, const char *flags); void close(); }; |
Vpopen.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/wait.h> #include "Vpopen.h" Vpopen::Vpopen() { fp_ = NULL; pid_ = -1; pipeFd_[0] = pipeFd_[1] = -1; } Vpopen::~Vpopen() { close(); } FILE *Vpopen::open(const char *cmd, const char *flags) { if (pipe(pipeFd_) != 0) goto failOut; if ((pid_ = vfork()) < 0) goto failOut; if (pid_ == 0) { if (strcmp(flags, "r") == 0) { ::close(pipeFd_[0]); if (pipeFd_[1] != STDOUT_FILENO) { dup2(pipeFd_[1], STDOUT_FILENO); ::close(pipeFd_[1]); } } else if (strcmp(flags, "w") == 0) { ::close(pipeFd_[1]); if (pipeFd_[0] != STDIN_FILENO) { dup2(pipeFd_[0], STDIN_FILENO); ::close(pipeFd_[0]); } } execl("/bin/sh", "sh", "-c", cmd, NULL); _exit(127); } if (strcmp(flags, "r") == 0) { ::close(pipeFd_[1]); pipeFd_[1] = -1; fp_ = fdopen(pipeFd_[0], flags); } else if (strcmp(flags, "w") == 0) { ::close(pipeFd_[0]); pipeFd_[0] = -1; fp_ = fdopen(pipeFd_[1], flags); } return fp_; failOut: close(); return NULL; } void Vpopen::close() { if (pid_ > 0) { int status; while (1) { if (waitpid(pid_, &status, 0) < 0) { perror("waitpid failed"); break; } if (WIFEXITED(status)) break; } pid_ = -1; } for (int i = 0; i < 2; i++) { if (pipeFd_[i] > 0) { ::close(pipeFd_[i]); pipeFd_[i] = -1; } } if (NULL != fp_) { fclose(fp_); fp_ = NULL; } } |
test.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdlib.h> #include "Vpopen.h" #define USING_VPOPEN 1 int main() { const char *cmd = "ls /etc"; #if USING_VPOPEN Vpopen vp; FILE *fp = vp.open(cmd, "r"); #else FILE *fp = popen(cmd, "r"); #endif char line[1024]; int i = 0; while (NULL != fgets(line, 1023, fp)) printf("%d: %s", i++, line); #if USING_VPOPEN vp.close(); #else pclose(fp); #endif return 1; } |
refer to:
1, bingqingsuimeng, https://www.it610.com/article/4096653.htm
2, litingli, https://blog.csdn.net/litingli/article/details/5891726