来自popen()的ENOMEM用于system(),同时有足够的内存

时间:2017-10-04 21:56:27

标签: c linux memory fork

我在ARM上的嵌入式Linux中运行了两个相对较大的应用程序(进程),具有3个RAM库(在Linux cmdline中:mem = 128M mem = 256M @ 0x90000000 mem = 128M @ 0xA0000000)。 一个应用程序处理用户命令,在这些命令之间可能存在运行普通Linux shell命令的请求。这实现为:

if((fp=popen(UserCommand, "r")) == NULL) return(errno)); fgets(ReplyString, 128, fp); Res = pclose(fp);

第一行返回errno = 12 - ENOMEM,即使是最简单的命令,如" pwd",尽管有足够的内存:

root@dm814x-evm:~# free total used free shared buffers Mem: 461472 38576 422896 0 152 Swap: 0 0 0 Total: 461472 38576 422896

据我所知,有超过400MB的可用空间! 为了第一个测试目的,我也取消了第二个过程 - 操作!,错误已经消失!!!
对于第二个测试,我运行telnet并通过它执行命令(当两个进程都在运行时) - 没问题,工作正常。

那么,捕获的位置在哪里?

1 个答案:

答案 0 :(得分:4)

如果用fork()实现popen(因为它目前在glibc中)而不是posix_spawn()vfork(),那么你需要的内存与父进程使用的内存一样多成功。如果禁用系统上的过度使用,此分配可能会失败,因此要解决您的问题,您应该执行以下操作之一:

  1. 完全启用系统上的过度使用

    sudo sh -c 'echo 1>/proc/sys/vm/overcommit_memory'
    
  2. 使用未使用popen()实现fork()的libc库(muslexample

  3. 执行popen自己做的事情,但用pipe()posix_spawn()来表达,而不是经典的pipe()dup2(),{ {1}},fork()组合。

  4. execve() 1)创建一个管道2)将管道的一端包裹在popen中,然后3)创建一个过程并将管道的另一端连接到过程的{{1 }或FILE,将进程的stdin保存在stdout结构中

    经典pid + FILE进程创建方式的问题在于,如果禁用overcommit,fork()必须是悲观的,并假设子进程可能继续运行父进程,这意味着孩子需要父母记忆的每一页。启用过度使用后,内存将被借用,并且只有在访问借来的内存时才会出现问题(内存不足的杀手)(如果进程很快就会调用execve(),则不会出现问题)。禁用过度使用时,必须保留所有内存,这就是为什么如果fork()调用来自更大的进程,它可能会失败。