使用popen遭遇ENOMEM (Cannot allocate memory)的问题

本文介绍了在使用popen函数时遇到ENOMEM问题的原因及解决办法。通过strace跟踪发现是clone函数内存分配失败,最终通过自定义vpopen替代popen解决问题。
部署运行你感兴趣的模型镜像
遇到一个popen遭遇ENOMEM (Cannot allocate memory)的问题,记录一下


我需要在程序里获取标准输出的内容,于是在一个模块里使用了popen这个函数,本来一直运行着都没,但是最近这个模块老是出问题,最后定位到是popen调用出错。返回的errno是ENOMEM (Cannot allocate memory),查看popen的文档并没有ENOMEM 相关的说明,到网上搜索,有人说popen无非是pipe+fork+execel的一个函数,可以跟一下看看是哪个函数的问题。
使用
strace -o app.starce ./app
跟进去看程序的调用信息,找到了对应的出错点
pipe2([17, 18], O_CLOEXEC)              = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f084002aab0) = -1 ENOMEM (Cannot allocate memory)
close(18)                               = 0
close(17)  
可见,实际是clone函数出错了,man clone,确实有
ENOMEM Cannot  allocate  sufficient memory to allocate a task structure for the child, or to copy those parts of the caller's context that need to be copied.
写一个使用了fork的小程序,使用strace查看,
13097 getrlimit(RLIMIT_STACK, {rlim_cur=512*1024, rlim_max=512*1024}) = 0
13097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7feed3ccf9f0) = 13098
13097 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 10), ...}) = 0
13097 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
的确,fork函数调用了clone的。
我们都知道,linux使用fork创建子进程会copy父进程的堆,栈,静态存储区,文件描述符等等,那么就有可能父进程内存使用过多,导致子进程无法再从剩余的内存上分配内存。

同样使用system也有
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fffed9206f8) = 32710

wait4(32710, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 32710


那么使用vfork呢
vfork()                           = 13123
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 10), ...}) = 0
fstat(1,  <unfinished ...>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
可将,用vfork就并没有调用clone


于是自己写了一个函数来代替popen

[cpp]  view plain copy
  1. <pre name="code" class="cpp">//#ifdef  OPEN_MAX  
  2. //static long openmax = OPEN_MAX;  
  3. //#else  
  4. static long openmax = 0;  
  5. //#endif  
  6.    
  7. /* 
  8.  * If OPEN_MAX is indeterminate, we're not 
  9.  * guaranteed that this is adequate. 
  10.  */  
  11. #define OPEN_MAX_GUESS 1024  
  12.    
  13. long open_max(void)  
  14. {  
  15.     if (openmax == 0) {      /* first time through */  
  16.         errno = 0;  
  17.         if ((openmax = sysconf(_SC_OPEN_MAX)) < 0) {  
  18.            if (errno == 0)  
  19.                openmax = OPEN_MAX_GUESS;    /* it's indeterminate */  
  20.            else  
  21.                printf("sysconf error for _SC_OPEN_MAX");  
  22.         }  
  23.     }  
  24.    
  25.     return(openmax);  
  26. }  
  27.   
  28. static pid_t    *childpid = NULL;  /* ptr to array allocated at run-time */  
  29. static int      maxfd;  /* from our open_max(), {Prog openmax} */  
  30.   
  31. FILE *vpopen(const char* cmdstring, const char *type)  
  32. {  
  33.     int pfd[2];  
  34.     FILE *fp;  
  35.     pid_t   pid;  
  36.   
  37.     if((type[0]!='r' && type[0]!='w')||type[1]!=0)  
  38.     {  
  39.         errno = EINVAL;  
  40.         return(NULL);  
  41.     }  
  42.   
  43.     if (childpid == NULL) {     /* first time through */    
  44.                 /* allocate zeroed out array for child pids */    
  45.         maxfd = open_max();    
  46.         if ( (childpid = (pid_t *)calloc(maxfd, sizeof(pid_t))) == NULL)    
  47.             return(NULL);    
  48.     }  
  49.   
  50.     if(pipe(pfd)!=0)  
  51.     {  
  52.         return NULL;  
  53.     }  
  54.       
  55.     if((pid = vfork())<0)  
  56.     {  
  57.         return(NULL);   /* errno set by fork() */    
  58.     }  
  59.     else if (pid == 0) {    /* child */  
  60.         if (*type == 'r')  
  61.         {  
  62.             close(pfd[0]);    
  63.             if (pfd[1] != STDOUT_FILENO) {    
  64.                 dup2(pfd[1], STDOUT_FILENO);    
  65.                 close(pfd[1]);    
  66.             }             
  67.         }  
  68.         else  
  69.         {  
  70.             close(pfd[1]);    
  71.             if (pfd[0] != STDIN_FILENO) {    
  72.                 dup2(pfd[0], STDIN_FILENO);    
  73.                 close(pfd[0]);    
  74.             }             
  75.         }  
  76.   
  77.         /* close all descriptors in childpid[] */    
  78.         for (int i = 0; i < maxfd; i++)    
  79.         if (childpid[ i ] > 0)     
  80.             close(i);    
  81.   
  82.         execl("/bin/sh""sh""-c", cmdstring, (char *) 0);    
  83.         _exit(127);       
  84.     }  
  85.   
  86.     if (*type == 'r') {    
  87.         close(pfd[1]);    
  88.         if ( (fp = fdopen(pfd[0], type)) == NULL)    
  89.             return(NULL);    
  90.     } else {    
  91.         close(pfd[0]);    
  92.         if ( (fp = fdopen(pfd[1], type)) == NULL)    
  93.             return(NULL);    
  94.     }  
  95.   
  96.     childpid[fileno(fp)] = pid; /* remember child pid for this fd */    
  97.     return(fp);       
  98. }  
  99.   
  100.   
  101. int vpclose(FILE *fp)  
  102. {  
  103.     int     fd, stat;    
  104.     pid_t   pid;    
  105.     
  106.     if (childpid == NULL)    
  107.         return(-1);     /* popen() has never been called */    
  108.     
  109.     fd = fileno(fp);    
  110.     if ( (pid = childpid[fd]) == 0)    
  111.         return(-1);     /* fp wasn't opened by popen() */    
  112.     
  113.     childpid[fd] = 0;    
  114.     if (fclose(fp) == EOF)    
  115.         return(-1);    
  116.     
  117.     while (waitpid(pid, &stat, 0) < 0)    
  118.         if (errno != EINTR)    
  119.             return(-1); /* error other than EINTR from waitpid() */    
  120.     
  121.     return(stat);   /* return child's termination status */    
  122.   
  123. }</pre><pre name="code" class="cpp"></pre><pre name="code" class="cpp"><pre name="code" class="cpp"><p>//我一度怀疑是程序使用的内存多,导致fork时无法给子进程分配内存</p><p>//但是文档上都说,fork时并非马上分配,只有实际使用到时才真正分配。  
  124.   
  125. //如果这样的话,那出现ENOMEM 的错误是为什么呢?不解</p></pre>  
  126. <pre></pre>  
  127. <pre name="code" class="cpp"></pre>  
  128. <pre></pre>  
  129.   
  130. </pre>  

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>好的,我需要解决用户遇到的popen失败和PostgreSQL程序未找到的问题。首先,用户提到两个错误:“popen failure: Cannot allocate memory”和“initdb: error: The program "postgres" is not found in the same directory”。我需要结合提供的引用信息来分析可能的原因和解决方案。 首先,关于“popen failure: Cannot allocate memory”。根据引用[3],当进程的内存不足时,popen可能会失败。例如,主线程占用了大量堆内存,导致其他线程无法分配内存。这时候需要检查系统的内存使用情况,看看是否有内存泄漏或者过度分配的情况。可以通过free -h命令查看系统内存,或者使用top/htop监控进程的内存消耗。另外,调整内存限制,比如ulimit的配置,可能也有帮助。 接下来是PostgreSQL的问题:“postgres”程序未找到在initdb的同一目录下。根据引用[1],这可能是因为PostgreSQL安装不完整或路径配置错误。用户可能安装的是不同版本的PostgreSQL,比如安装的是13版本,但路径指向了14版本的目录。需要检查initdb所在的路径,确认是否存在postgres可执行文件。如果路径不对,可能需要重新安装PostgreSQL 13,或者修复安装路径的符号链接。此外,环境变量如PATH的设置也需要检查,确保能够正确找到postgres的位置。 另外,引用[2]提到共享内存分配问题,但用户当前的问题似乎更多是路径问题,不过内存不足也可能影响PostgreSQL的启动。需要综合检查内存和路径两方面。 可能的解决方案包括: 1. 检查并确保系统有足够的内存,处理内存泄漏问题。 2. 验证PostgreSQL的安装路径是否正确,特别是版本号是否匹配。 3. 重新安装或修复PostgreSQL 13的安装包,确保所有必要文件存在。 4. 设置正确的环境变量或符号链接,确保initdb能找到postgres。 还需要用户提供更多信息,比如PostgreSQL的安装方式、使用的命令,以及系统日志,以便更准确地诊断问题。例如,用户是否通过包管理器安装,还是手动编译安装?安装过程中是否有错误提示?这些信息有助于进一步缩小问题范围。 总结来说,需要分步骤处理内存不足和路径错误这两个问题,可能需要同时调整系统内存配置和修复PostgreSQL的安装路径。</think>根据您遇到的$popen$内存分配错误和PostgreSQL程序路径问题,以下是分步解决方案: ### 一、popen内存分配问题分析 1. **内存不足导致失败**: - 使用`free -h`检查系统可用内存 - 通过`top`命令查看内存占用前10的进程 - 验证是否存在内存泄漏:使用`valgrind --leak-check=full`检测应用程序 2. **系统级解决方案**: ```bash # 临时扩大内存限制 ulimit -v unlimited # 永久修改内存限制(需root权限) echo "* soft memlock unlimited" >> /etc/security/limits.conf echo "* hard memlock unlimited" >> /etc/security/limits.conf ``` ### 二、PostgreSQL路径问题解决 1. **验证安装完整性**: ```bash # 检查postgresql-13实际安装路径 dpkg -L postgresql-13 | grep 'bin/postgres$' # 示例输出: # /usr/lib/postgresql/13/bin/postgres ``` 2. **修复符号链接**(当存在版本错配时): ```bash sudo ln -sf /usr/lib/postgresql/13/bin/* /usr/lib/postgresql/14/bin/ ``` 3. **环境变量配置**: ```bash # 在~/.bashrc中添加 export PATH="/usr/lib/postgresql/13/bin:$PATH" export LD_LIBRARY_PATH="/usr/lib/postgresql/13/lib:$LD_LIBRARY_PATH" ``` ### 三、复合问题处理流程 1. 同时出现内存与路径错误时建议执行: ```bash # 清理旧版本残留 sudo apt purge postgresql-14* # 完整安装v13 sudo apt install postgresql-13 postgresql-client-13 # 验证二进制文件位置 which initdb && which postgres ``` ### 四、进阶调试方法 对popen失败场景添加预分配检查: ```c #include <sys/sysinfo.h> void check_memory() { struct sysinfo info; sysinfo(&info); if (info.freeram < 100 * 1024 * 1024) { // 剩余内存<100MB时报警 fprintf(stderr, "内存不足警告:可用内存仅剩%.2f MB\n", (float)info.freeram / 1024 / 1024); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值