有人知道Matlab MEX库的生命周期吗?具体来说,我对以下内容感兴趣:
- 是否有一种方法可以在调用之前强制加载库?
- 该库是单例还是加载多个实例?
- 是否有任何初始化挂钩可在调用之前使用?
- 是否有析构函数挂钩/信号可在卸载库以进行清理时拦截?
我在这里和在线上进行了广泛搜索,但找不到这些问题的答案。我的问题在初始化方面存在一些性能成本,如果可能的话,我想避免这种情况,而无需编写服务。
MEX文件会一直保持加载状态,直到您清除它(clear myMexFun
或clear mex
)或退出MATLAB。
对于预加载,我只能建议使用没有输入或等效无操作输入调用函数。我在我的mexFunction
中创建了简单的代码路径来处理这些调用而不出错,最简单的例子是if(!nrhs) return;
。随后的调用将不需要从磁盘加载mexFunction
(或任何由MEX函数调用的共享库中的其他函数),您也不需要担心此后的初始化成本。
mexFunction
is the only entry point),因此,正如 Amro 指出的那样,您可以在 Windows 中实现 DllMain
来定义模块和线程的附加/分离操作(请参见 他的回答 中的优秀示例)。我不知道还有其他与库交互的机制。mexFunction
中使用mexAtExit
向MATLAB注册一个函数,在MEX函数卸载(再次清除或MATLAB退出)时调用该函数。只需在全局命名空间中定义一个static
函数并将其注册到mexAtExit
即可。MATLAB提供的示例(mexatexit.c)演示了如何关闭在mexFunction
中打开但未关闭的文件流。您还可以释放持久内存、关闭流等。这是一个人为制造的例子:
mexDLLtext.cpp:
#include "mex.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
static FILE *fp=NULL;
static double *pDataC=NULL, *pDataCpp=NULL, *pMxData=NULL;
static char fName[L_tmpnam], counter = 0;
static void CleanUp(void)
{
fclose(fp); /* close file opened with fopen */
free(pDataC); /* deallocate buffer allocated with malloc/calloc */
delete[] pDataCpp; /* deallocate buffer allocated with new double[...] */
mxFree(pMxData); /* free data created with mx function like mxMalloc */
mexPrintf("Closing %s and freeing memory...\n",fName);
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (!fp) { tmpnam(fName); fp = fopen(fName,"w"); }
fprintf(fp,"%d ",++counter);
if (!pDataC) pDataC = (double*) malloc(sizeof(double)*16);
if (!pDataCpp) pDataCpp = new double[16];
if (!pMxData) {
pMxData = (double*) mxMalloc(sizeof(double)*16);
mexMakeMemoryPersistent(pMxData); mexPrintf("First!\n");
}
mexAtExit(CleanUp);
// Then use the persistent data...
}
>> mex -largeArrayDims mexDLLtest.cpp
>> for i=1:5, mexDLLtest; end
First!
>> clear mexDLLtest
Closing \s1rg.1 and freeing memory...
>> type E:\s1rg.1
1 2 3 4 5
你可以通过 mexLock
和 mexUnlock
来控制文件的卸载。
然而,当函数开始和返回到MATLAB时,参数(即prhs
,plhs
)的处理方式已经有非常详细的文档,所以我猜这不是你想问的问题。
version -modules
以查看已加载模块的列表,正如Amro所建议的那样。 MEX文件仍将存在,并且与Process Explorer可见的线程列表一样,我只看到某个MEX文件的一个实例。如我在评论中提到的,在Windows操作系统下,您可以实现DllMain
入口点。这是因为MEX文件只是具有不同扩展名的常规DLL文件。下面是一个最小示例:
#include "mex.h"
#include <windows.h>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
mexPrintf("DLL_PROCESS_ATTACH: hModule=0x%x\n", hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
mexPrintf("DLL_PROCESS_DETACH: hModule=0x%x\n", hModule);
break;
}
return TRUE;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mexPrintf("Inside MEX-function\n");
}
>> mex -largeArrayDims testDLL.cpp
>> testDLL
DLL_PROCESS_ATTACH: hModule=0xa0980000
Inside MEX-function
>> testDLL
Inside MEX-function
>> clear testDLL
DLL_PROCESS_DETACH: hModule=0xa0980000
#include <windows.h>
并将 NULL
替换为 (void*)
,我的示例应该可以在 Linux 上工作,尽管你在 MATLAB 中注册的 mexAtExit
函数不像 DllMain
或静态类构造函数和析构函数那样灵活。 - chappjc
DllMain
入口函数,并处理DLL_PROCESS_ATTACH
、DLL_THREAD_ATTACH
等通知。此外,MATLAB提供了一种记录退出函数的方法:mexAtExit
。顺便说一句,您可以使用version -modules
来查看所有加载的模块列表,而不是使用Sysinternals工具。 - Amroprivate
文件夹中并通过 MATLAB 面向对象类来公开其功能,从而使 MEX 函数“私有化”,这将为您提供适当的构造函数/析构函数语义,特别适用于 MEX 文件包装 C++ 类(请参见 mexopencv 项目以了解此模式的示例)。 - AmromexAtExit
。这很重要,所以我加了一点关于它的内容,并且根据您的评论,我总体上改进了我的答案。关于 DllMain 的好主意。我链接到了您的答案。 - chappjcmexAtExit
/mexMakeMemoryPersistent
的示例很好。顺便说一句,你是对的,任何时候只有一个MEX模块实例被加载(否则将会破坏共享库的整个目的)。更不用说整个MX/MEX API都不是线程安全的了(http://www.mathworks.com/matlabcentral/answers/101658)。即使可能的话(https://dev59.com/qEnSa4cB1Zd3GeqPRtys#1587451),在同时线程中加载同一个MEX函数的多个副本(在同一进程内)也会带来一系列新问题 :) - Amro