Mex函数在重新编译后未更新。

7

我有一个简单的Mex函数,它调用了一个库中的另一个C++函数。我使用以下命令编译源代码:

mex -cxx mymexfunction.cpp -I/some/include -L/some/lib -lmylib

mylib库是一个动态库(.so),它本身链接了一些其他库(boost、OpenCV等)。

我的问题是,一旦我调用了mymexfunction函数,当我重新编译源代码时它就不会被更新。我已经尝试过:

clear
clear all
clear mex
clear functions
clear fun('mymexfunction')
munlock('mymexfunction')
unloadlibrary('mymexfunction')

...但是没有任何帮助!我必须重新启动Matlab才能看到更新后的mexfunction。即使我删除已编译的mex文件并重新编译,我仍然得到旧版本的mex函数(不在磁盘上,而在内存中)。

如果我不与mylib连接,一切都很好,但我不知道是什么原因阻止了更新。这个库非常大且相互交织,无法逐个删除单个模块。

是否有一些已知的情况会导致这样的问题?

澄清:

我只更新mex函数的内容,而不是库。

更新:

它在Ubuntu 11.04下与Matlab R2011a一起工作! 我尝试在我的OpenSUSE机器上复制相同的环境(R2011a,Boost 1.42,OpenCV 2.2动态链接等),但仍然没有运气。 因此,我得出结论,我的库实际上没有任何问题(否则它在Ubuntu下不起作用),但它必须是依赖项和Matlab内部库的冲突。 我正式放弃。 Praetorian和Amro,谢谢你们的帮助!

2个回答

4
mex 命令会自动清除当前在内存中加载的 mex 函数。你确定你的 mex 函数正在关闭它所持有的与其他库的任何句柄吗?如果存在这样的句柄,则可能会阻止操作系统卸载 mex 文件。
我使用以下一组命令手动清除 mex 函数,根据我的经验,在调用 clear 时使用完整路径到 mex 文件可以起作用。因此,请尝试这个方法,如果它仍无法卸载,则可能需要开始查看加载和卸载其他库的代码。
[~,f] = inmem( '-completenames' );
result = strfind( f, ['mymexfile' '.' mexext] );
result = f(cellfun( @isempty, result, 'UniformOutput', true ) == 0);
clear( result{:} )

请尝试在上述操作后再次运行inmem命令,并查看您的mex文件是否仍在列表中。为确保卸载其他库,可以尝试使用std::shared_ptr来保存对该库的句柄。然后,在mexFunction()入口点的开头加载库并将句柄插入shared_ptr中。shared_ptr还需要使用自定义删除器以卸载库(在Windows上,自定义删除器会调用FreeLibrary)。当然,如果这是由于其他库中的错误导致的,无论如何都不会有帮助。

2
@Martin 但是 inmem 应该报告内存中是否有缓存版本!你不是在一个 MATLAB 实例中尝试运行 mex 文件并在另一个实例中编译它吧? - Praetorian
这种情况可能是什么样的错误呢?我不知道如何调试它,甚至不知道从哪里开始... - Martin
1
@Martin 开始查找由mex文件未关闭的第二个库的句柄。正如我在答案中提到的,如果发生这种情况,即使MATLAB尝试卸载mex文件,操作系统也可能不会卸载它。也许inmem尝试卸载该文件,但无法从操作系统获得确认已实际卸载该文件。您知道第二个库是否打开了其他共享库吗? - Praetorian
1
@Martin,现在你有一个开始寻找的方向了!祝你好运。 - Praetorian
1
@Martin:我发布了一个完整的示例来帮助解决问题,请看一下。 - Amro
显示剩余6条评论

4
尝试重现问题,我已经为您的情况编写了一个最小工作示例:一个MEX文件,链接到动态库并使用其公开的函数之一。我已经在WinXP 32位上使用MATLAB R2010b和VS2010编译器(DLL和MEX均使用)进行了测试。
该示例仅仅是将浮点数相加。MEX文件接受矩阵/向量,并循环遍历元素,在每对元素上调用库中的“add()”函数。

Adder.h

#ifndef ADDER_H
#define ADDER_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _WIN32
#   ifdef BUILDING_DLL
#       define DLL_IMPORT_EXPORT __declspec(dllexport)
#   else
#       define DLL_IMPORT_EXPORT __declspec(dllimport)
#   endif
#else
#   define DLL_IMPORT_EXPORT
#endif

DLL_IMPORT_EXPORT double add(double x, double y);

#ifdef __cplusplus
}
#endif

#endif

Adder.c

#include "Adder.h"

double add(double x, double y)
{
    return x+y;
}

mymexfunction.c

#include "mex.h"
#include "Adder.h"

#define X_IN    input[0]
#define Y_IN    input[1]
#define Z_OUT   output[0]

void mexFunction(int output_size, mxArray *output[], int input_size, const mxArray *input[])
{
    double *inX, *inY, *outZ;
    mwSize m,n;
    int i;

    /* check for proper number of arguments */
    if (input_size != 2) {
        mexErrMsgTxt("Two input arguments required.");
    }
    if (output_size > 1) {
        mexErrMsgTxt("Too many output arguments.");
    }

    /* check input argument sizes */
    m = mxGetM(X_IN);
    n = mxGetN(X_IN);
    if ( !mxIsDouble(X_IN) || !mxIsDouble(Y_IN) ) {
        mexErrMsgTxt("Input arguments must be matrices/vectors of doubles.");
    }
    if ( mxGetM(Y_IN)!=m || mxGetN(Y_IN)!=n ) {
        mexErrMsgTxt("X and Y must be of same size.");
    }

    /* Create a matrix for the return argument */
    Z_OUT = mxCreateDoubleMatrix(m, n, mxREAL);

    // get pointers to data
    inX =  (double *) mxGetPr(X_IN);
    inY =  (double *) mxGetPr(Y_IN);
    outZ = (double *) mxGetPr(Z_OUT);

    // compute and store result
    for(i=0; i<m*n; ++i) {
        outZ[i] = add(inX[i], inY[i]);
    }

    return;
}

首先,我们需要构建动态库。正如我所提到的,我使用VC++来完成这项工作。对于基于GCC的Unix系统,我认为这一步应该是这样的(如果我错了,请纠正):

gcc -c -DBUILDING_DLL Adder.c -o Adder.o -I.
gcc -shared -o libAdder.so Adder.o -Wl,--out-implib,libAdder.a

然后在MATLAB中,我们编译MEX文件:
>> mex mymexfunction.c -I. -L. -lAdder

(注意:我把所有文件都放在同一个文件夹中,以避免处理路径问题。)
接下来,我们可以在MATLAB中测试该函数:
>> mymexfunction([1 2;3 4], [5 6; 7 8])
ans =
     6     8
    10    12

使用Sysinternals的Process Explorer工具,我们可以查看MATLAB进程、MEX函数和自定义动态库加载的DLL。

sysinternals

如果我们发出命令clear mex,那么两个模块都会按预期卸载(这一点可以使用Process Explorer进行验证)。INMEM也证实了这一点,正如@Praetorian所展示的那样:
clear mex
[~,m] = inmem('-completenames');
any( ismember(m,fullfile(pwd,['mymexfunction.' mexext])) )

最后,如果我们对mymexfunction.c进行一些更改:
// add 10 to all results
outZ[i] = add(inX[i], inY[i]) + 10.0;

重新编译MEX文件,并在同一个会话中进行测试(无需重新启动)。结果将反映所做的更改,如下所示:
>> mymexfunction([1 2;3 4], [5 6; 7 8])
ans =
    16    18
    20    22

请尝试在您的Mac/Linux机器上重复上述操作。如果仍然收到旧的总和,则必须是非Windows平台特定的错误,并应向MathWorks报告...否则,我怀疑在您的代码中,可能存在一些未释放的资源导致该模块仍留在内存中?

感谢提供详细的示例!我已经尝试了,当然它可以工作。我还怀疑我的库或者它所依赖的某个库中存在一些问题。但是,我仍然无法找出导致实际问题的原因... - Martin
这个问题还没有简单的解决方案吗? - Alec Jacobson
@mangledor:正如我在上面的示例中所展示的,我一开始就无法重现这个问题。当时的结论是,OP的外部库中存在未释放的资源,导致其仍然留在内存中。如果您有具体的反例,请考虑创建一个新的问题。 - Amro
那么,如何调试这样的问题呢? - Alec Jacobson
@mangledor:没有办法重现问题,很难说清楚。尝试在调用clear mex后运行version -modules以查看哪些模块仍在内存中加载。否则,我只能建议您使用调试器逐步执行代码,特别注意分配的资源。 - Amro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接