当前位置 博文首页 > 想要去旅行:cgicc 上传大文件失败问题
上传小文件正常,上传文件大于10M 时失败
1,初步怀疑可用内存不够导致
说明:当前板子的 RAM 大小为128M
验证方法:更换大容量RAM 的板子
验证结果:使用 RAM 大小为1G 的板子,上传大文件一切正常,说明就是内存不够。
由此引出下面两个问题:
1,同样是128M内存,为什么mt7688 平台没有问题?
2,上传文件到底用了多少内存?
不妨先看看 google 上怎么说:
help-cgicc File upload memory usage
While upgrading compilers from GCC 4.7 to GCC7.2 on an embedded device, I noticed a substantial increase in memory usage while parsing a file upload request.
This seems to be caused by changed behavior in std::string. As GCC 4.7 was using copy-on-write and refcounting for std::string, a string copy did not cause a copy of the data. Now with GCC 7.2, a string copy also copies the data. For some large (+30MB) requests, this now makes our application go out-of-memory on a embedded device with only 128MB of RAM.
提取有效信息:
1,从 GCC4.7 升级到 GCC 7.2 后 ,才出了问题
2, std::string 在GCC 4.7 中使用 copy-on-write ,a string copy did not cause a copy of the data,
? GCC 7.2, a string copy also copies the data
我们H3 使用的GCC 版本是,GCC 7.3 , 而 MT7688 使用的 GCC 版本是GCC 4.8
因此
1,同样是128M内存,为什么mt7688 平台没有问题?
原因是:GCC 版本不一样。
继续了解:
什么是 copy-on-write ?
写时拷贝(copy-on-write) COW技术
摘要:
COW技术在C++中string实现上的应用
看如下代码:
string str1=“hello world”;
string srr2=str1;
此时str1和str2的存放地址其实是一样的之后执行
str1[1]=‘a’;
str2[1]=‘b’;执行修改后,此时str1的地址会发生变化,而str2的地址还是原来的。
? 即在复制对象时,并不真正为新对象开辟内存空间,而是在新对象的内存映射表中设立一个指针,指向源对象,这样在进行读操作时因为并不修改对象,并不会给源对象带来影响,当某一时刻要对某一对象进行修改时,即写操作时,再将对象复制到新的内存空间中去,在这上面执行修改,以避免相互之间的影响。这样做的一个好处也是尽可能提高效率。
? 当然,不同的编译器string的实现方式不一样,有些编译器并不使用COW技术去实现。
延伸阅读 std:string 实现的三种方式
详情可以看 《C++ 工程实践经验谈 by 陈硕 》第13章
SSO 只对短字符串(通常是长度<15)起优化作用,而长字符串仍是要开辟新的内存空间。
由此来看,对于小内存设备,不宜使用 SSO 实现方式。
2,上传文件到底用了多少内存?
采用SSO 方式,应该至少文件大小的 3 倍,
1./tmp +1
2.IO 和 CGICC 中的拷贝 +2
3.代码其它地方可能还有拷贝 +x
方法 1 :改GCC 版本为4.8
OpenWrt 中可选的 gcc 版本最低为 5.x ,手动添加 4.8 版本过于麻烦,因此放弃。
方法 2 :想办法禁用 c++11 特性,使std::tring 用回 COW
google 到 一篇: c++ - gcc using c++11 standard even though 98 explicitly specified - Stack Overflow
https://stackoverflow.com/questions/42514202/gcc-using-c11-standard-even-though-98-explicitly-specified
有效信息
I added -D_GLIBCXX_USE_CXX11_ABI=0
to the Makefile’s g++ command. That’s definitely the way to do it.
在Makefile 中编译选项中加入 -D_GLIBCXX_USE_CXX11_ABI=0 。
实测此方法可行!另外我们的文件上传本身就不是多线程设计,因此不必考虑线程不安全问题。
对于小内存设备,使用 c++ 的 sting 类不宜使用 SSO 方式,应使用COW 方式节省内存开销。
gcc 5.x 版本之前使用 c++ 98 标准, 使用 COW 方式
gcc 5.x 版本之后使用 c++ 11 标准,默认使用 SSO 方式 ,要使用 COW 方式需要编译时指定
-D_GLIBCXX_USE_CXX11_ABI=0
cs