一、背景
1. 讲故事
今年准备多写一点 Linux平台上的东西,这篇从 C# 调用 C 这个例子开始。在 windows 平台上,我们常常在 C++ 代码中用 extern "C" 导出 C风格 的函数,然后在 C# 中用 DllImport 的方式引入,那在 Linux 上怎么玩的?毕竟这对研究 Linux 上的 C# 程序非托管内存泄露有非常大的价值,接下来我们就来看下。
二、一个简单的非托管内存泄露
1. 构建 so 文件
在 Windows 平台上我们会通过 MSVC 编译器将 C代码编译出一个成品 .dll,在 Linux 上通常会借助 gcc 将 c 编译成 .so 文件,这个.so 全称 Shared Object,为了方便讲解,先上一段简单的代码:
接下来使用 gcc 编译,参考如下:
- -shared: 编译成共享库
- -fPIC: 指定共享库可以在内存任意位置被加载(地址无关性)
命令执行完之后,就可以看到一个 .so 文件了,截图如下:
图片
最后可以用 nm 命令验证下 libmyleak.so 中是否有 Text 段下的 heapmalloc 导出函数。
2. C# 代码调用
so构建好了之后,后面就比较好说了,使用 dotnet new console -n CSharpApplication --use-program-main true 新建一个CS项目。
编译下 C# 项目,然后将 libmyleak.so 放到 C#项目的 bin目录,修改 C# 代码如下:
最后用 dotnet CSharpApplication.dll 运行:
程序是跑起来了,那真的是吃了1G呢? 可以先用 htop 观察程序,从截图看没毛病。
图片
那这 1G 真的在 heap 上吗? 可以用 maps 观察。
根据 linux 进程的内存布局,可执行image之后是 heap 堆,可以看到 [heap] 约等于1G (614e9b8a8000 - 614e5b5d9000),即 pmap 中的 1051452K。
三、总结
部署在 Linux上的.NET程序同样存在 非托管内存泄露的问题,这篇文章的例子虽然很简单,希望能给大家带来一些思考和观测途径吧。