我在做嵌入式项目的时候发现了一个问题:当使用内存比较大时,调用系统函数system或者popen打开新进程时,系统会提示错误,errno=12,表示内存不够。于是做了一些分析。
我的硬件的总内存为128M,系统启动后,加载文件系统后还剩余大约110M的空间。当我的程序运行后,大约还剩余2M左右的内存(通过free或者top查看)
这个程序malloc了一个60M,另外还有一些malloc,但不会超过10M。不过它是基于QTSDK写的,所以在调用sdk时候可能也会申请一些内存。但不管如何,最后剩余2M的时候,system或者popen就会失败。(尽管只是一些cp和rm命令)
于是我就想查查到底为什么。
对于sytem(先不说popen)函数调用,它能够执行shell命令,我查到的system实现如下:
int system(const char *cmd)
{
pid_t pid;
int status;
if(cmd == NULL)
{
return 1;
}
if((pid = fork()) < 0)
{
status = -1;
}
else if(pid == 0)
{
execl("/bin/sh", "sh", "-c",cmd, (char*)0);
_exit(127);
}
else
{
while(waitpid(pid, &status, 0) < 0)
{
if(errno != EINTR)
{
status = -1;
break;
}
}
}
return status;
}
可以看到fork函数,fork函数执行时会拷贝父进程的数据段和代码段,不过它使用了写时拷贝技术,为了减少内存的使用。说到fork还有一个vfork,它会共享父进程的数据段(代码段内?),因此占用内存会更少。(当然还有其他区别,例如子进程会先运行,结束后才运行父进程)
fork函数什么情况下会失败,并且原因是缺少内存呢?
有个链接提供了线索:http://bbs.chinaunix.net/thread-1965115-1-1.html
帖子了说了,fork的实现中需要检查内存是否够用,只要父进程某块连续数据区大于现系统可用物理内存时会报错。如下:
/* copy all the process information */
retval = copy_semundo(clone_flags, p);
if (retval)
goto bad_fork_cleanup_audit;
retval = copy_files(clone_flags, p);
if (retval)
goto bad_fork_cleanup_semundo;
retval = copy_fs(clone_flags, p);
if (retval)
goto bad_fork_cleanup_files;
retval = copy_sighand(clone_flags, p);
if (retval)
goto bad_fork_cleanup_fs;
retval = copy_signal(clone_flags, p);
if (retval)
goto bad_fork_cleanup_sighand;
retval = copy_mm(clone_flags, p);
if (retval)
goto bad_fork_cleanup_signal;
retval = copy_namespaces(clone_flags, p);
不过可以通过/proc/sys/vm/overcommit_memory调整检查策略:
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
* We currently support three overcommit policies, which are set via the
* vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting
*
* Strict overcommit modes added 2002 Feb 26 by Alan Cox.
* Additional code 2002 Jul 20 by Robert Love.
*
* cap_sys_admin is 1 if the process has admin privileges, 0 otherwise.
*
* Note this is a helper function intended to be used by LSMs which
* wish to use this logic.
*/
int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
{
unsigned long free, allowed;
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
return 0;
因此,当我将/proc/sys/vm/overcommit_memory写入1时,system调用就没有报错了。
还是有点不安呐。又查了一些资料,
关于/proc/meminfo的
http://blog.sina.com.cn/s/blog_7106477c0100qj9d.html
让我看到了Cache的占用了稍多的内存
http://www.it165.net/os/html/201403/7603.html
让我知道Active和InActive代表最近使用过的内存和没有使用过的内存
http://www.newsmth.net/nForum/#!article/KernelTech/46857
让我知道Cache缓存的文件内容,Buffered缓存的是文件系统相关的Meta数据(文件节点?文件夹节点?)
http://www.linuxidc.com/Linux/2007-08/6995p2.htm
让我知道/proc/sys/vm/还有其他相关设置可以进行优化。