关于阿里云ESC上go语言项目编译6l: running gcc failed: Cannot allocate memory
Tag golang, 编译, 内存不够, swap, on by view 4308

前段时间将自己的阿里云服务器上的系统由centos 6.5换为了ubuntu 14,其他的硬件配置都没有发生改变,将服务器上的数据恢复并且重新安装了golang的编译环境后,发现使用go build编译稍微大一点的golang项目就会报错:

/usr/local/go/pkg/tool/linux_amd64/6l: running gcc failed: Cannot allocate memory

一直想不通为啥换了个系统就会报这个错,字面意思是gcc分配内存失败,应该是内存不够用,机器配置是1G内存,free -m 发现尚有400M的内存未使用,难道剩余400M的内存还不够go build命令编译代码使用?好吧,既然如此我就给它释放内存,kill掉众多的进程之后再进行go build编译,发现又可以编译了。之后发现偶尔能编译偶尔又不能编译,看样子确实是内存不够,可是为啥之前的centos系统上没有出现这种状况呢,一直不相信简单的“内存不够”就可以解释这一问题,因为之前的centos系统上是正常的,我甚至觉得可能是gcc版本的问题,猜测只有较高版本的gcc才会报这个错误。后来也曾在“golang天朝”论坛上发过帖子,并表达自己的猜测,认为不是内存不够这么简单,结果被别人鄙视不看英文……

不想花钱升级机器硬件,难道我只有装回centos?今天执行free -m偶然间注意到了swap的数值貌似一直是空的,我思考若是我添加swap交换空间是否能解决这一问题呢,毕竟swap其实就是用硬盘空间虚拟出的内存,一个内存的缓冲区。于是就给它加了个1G的文件作为swap,居然直接就可以用go build,再也不用担心gcc对我说Cannot allocate memory了。简单的记录一下添加文件作为swap的步骤:

  • 创建1个1GB的file

sudo dd if=/dev/zero of=/mnt/1GB.swap bs=1M count=1024
  • 格式化为Swap file

sudo mkswap /mnt/1GB.swap
  • 把swap file加入到系统中

sudo swapon /mnt/1GB.swap
  • 将swap永久添加
    在/ect/fstab中加入新的Swap分区

sudo gedit /etc/fstab
  • 在最后加入下列内容

/mnt/1GB.swap none swap sw 0 0

最后,free -m 命令可以看到swap的数据如下

             total       used       free     shared    buffers     cached
Mem:           992        903         88          0         57        188
-/+ buffers/cache:        656        335
Swap:         1023          0       1023

1G的内存交换区文件已经创建。


内存越界的灾难
Tag C语言, 内存, 越界, on by view 3437

今天为了修复一个bug折腾了将近一天的时间,原本是计划在沙箱子项目里面添加命令行指定配置文件路径功能,结果被我发现之前留下的一个坑,但是整个发现过程花了一整下午的时间。

调试过程中发现明明传入到函数中的路径printf是正确的,可是当执行到fopen时却直接异常退出,于是gdb跟踪调试

filename_interrupt.png

调试显示传入的文件名是正常的,但是当执行到#93步时文件名很明显被截断了,这是一件非常奇怪的事情,我甚至怀疑fopen函数是不是对传入文件名长度有限制,可事实上不是这样的。因为我后来专门写了个测试代码,同样的文件名路径,fopen却是正常的。我又怀疑是不是因为文件名字符串是malloc分配的原因,测试后却也是正常的。

非常纠结,之后决定采用大段大段的砍掉无关代码的方法来更清晰的定位错误可能的位置,因为代码带多确实看着眼花缭乱。当我砍到只剩下调用config_read的时候,惊奇的发现上面内存初始化是这样的:

// alloc memory for path string    
executable = (char*)malloc(sizeof(EXE_LEN));    
memset(executable, 0, sizeof(EXE_LEN));

shit! 这儿的EXE_LEN是int型指定文件名长度的啊,也就是说sizeof(EXE_LEN)是4byte,坑爹啊。但是我后面给这个内存strncpy了73个字符组成的字符串,嗯,编译器没给我报错,然后这内存几经传递传到了fopen里面,却被莫名的修改了,呵呵,修改了活该啊,谁让我把剩下的69个字符强行放入了它不该放的地方呢。其实应该是这个字符串越界到了fopen函数所使用的内存去,被修改也是太正常不过了。

C语言编程确实需要小心谨慎,因为编译器给你的提示实在是太少,逻辑错了就会按照错的运行,结果便会是谬之千里。