cgicc 上传大文件失败问题

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.

扫描二维码关注公众号,回复: 10210943 查看本文章

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 实现的三种方式

  • eager copy (直接拷贝) // 现在几乎不用
  • COW (copy on write) // c++98 标准中使用
  • SSO (small string optimization) // c++11 标准中使用

详情可以看 《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

发布了63 篇原创文章 · 获赞 20 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/agave7/article/details/102895083