内存映射文件 std::allocator 实现冻结 WM6 设备

Posted

技术标签:

【中文标题】内存映射文件 std::allocator 实现冻结 WM6 设备【英文标题】:Memory mapped file std::allocator implementation freezes WM6 device 【发布时间】:2011-08-18 22:27:17 【问题描述】:

我有一个适用于 Windows Mobile 6.x 的 Visual Studio 2008 C++ 项目,我需要比 32MB 进程槽中可用的内存更多的内存。所以,我正在考虑使用内存映射文件。我创建了一个标准的分配器实现,用CreateFileMapping 和MapViewOfFile 替换new/delete。

预期用途是这样的:

struct Foo

    char a[ 1024 ];
;

int _tmain( int argc, _TCHAR* argv[] )

    std::vector< boost::shared_ptr< Foo > > v;
    for( int i = 0; i < 40000; ++i )
    
        v.push_back( boost::allocate_shared< Foo >( MappedFileAllocator< Foo >() ) );
    
    return 0;

使用std::allocator,在我得到std::bad_alloc 异常之前,我可以在该示例中获得28197 次迭代。使用MappedFileAllocator,在设备完全冻结并且必须重新启动之前,我得到了 32371 次迭代。由于我的设备有 512MB 的 RAM,我希望能够从该循环中获得更多的迭代。

我的MappedFileAllocator 实现是:

template< class T >
class MappedFileAllocator

public:
    typedef T         value_type;
    typedef size_t    size_type;
    typedef ptrdiff_t difference_type;
    typedef T*        pointer;
    typedef const T*  const_pointer;
    typedef T&        reference;
    typedef const T&  const_reference;

    pointer address( reference r ) const  return &r; ;
    const_pointer address( const_reference r ) const  return &r; ;

    /// convert a MappedFileAllocator<T> to a MappedFileAllocator<U>
    template< class U >
    struct rebind  typedef MappedFileAllocator< U > other; ;

    MappedFileAllocator() throw() : mapped_file_( INVALID_HANDLE_VALUE )  ;

    template< class U >
    explicit MappedFileAllocator( const MappedFileAllocator< U >& other ) throw()
        : mapped_file_( INVALID_HANDLE_VALUE )
    
        if( other.mapped_file_ != this->mapped_file_ )
        
            ::DuplicateHandle( GetCurrentProcess(), 
                other.mapped_file_,
                GetCurrentProcess(),
                &this->mapped_file_,
                0,
                FALSE,
                DUPLICATE_SAME_ACCESS );
        
    ;

    pointer allocate( size_type n, const void* /*hint*/ = 0 )
    
        if( n > max_size() )
           throw std::bad_alloc();

        if( n > 0 )
        
            size_type buf_size = n * sizeof( value_type );
            mapped_file_ = ::CreateFileMapping( INVALID_HANDLE_VALUE, 
                NULL,
                PAGE_READWRITE,
                0,
                buf_size,
                L"45E4FA7B-7B1E-4939-8CBB-811276B5D4DE" );

            if( NULL == mapped_file_ )
                throw std::bad_alloc();

            LPVOID f = ::MapViewOfFile( mapped_file_, 
                FILE_MAP_READ | FILE_MAP_WRITE, 
                0, 
                0, 
                buf_size );

            if( NULL == f )
            
                ::CloseHandle( mapped_file_ );
                mapped_file_ = INVALID_HANDLE_VALUE;
                throw std::bad_alloc();
            
            return reinterpret_cast< T* >( f );
        

        return 0;
    ;

    void deallocate( pointer p, size_type n )
    
        if( NULL != p )
        
            ::FlushViewOfFile( p, n * sizeof( T ) );
            ::UnmapViewOfFile( p );
        
        if( INVALID_HANDLE_VALUE != mapped_file_ )
        
            ::CloseHandle( mapped_file_ );
            mapped_file_ = INVALID_HANDLE_VALUE;
        
    ;

    size_type max_size() const throw() 
     
        return std::numeric_limits< size_type >::max() / sizeof( T );
    ;

    /// handle to the memory-mapped file
    HANDLE mapped_file_;

private:

    /// disallow assignment
    void operator=( const MappedFileAllocator& );

; // class MappedFileAllocator

任何人都可以建议我的MappedFileAllocator 实现可能出问题的地方吗?

谢谢, 保罗H

【问题讨论】:

检查从 allocate() 返回的每个指针是否在某个边界上对齐;每次您尝试映射文件时,似乎 MapViewOfFile 可能会消耗一个页面。 @vividos - 它们在 4 字节边界上与 ARM 对齐。 WM 版本的 MVOF 不需要页面对齐。 msdn.microsoft.com/en-us/library/aa914405.aspx 那我不知道是什么问题。接下来我要尝试的是由 VirtualAlloc() 分配的 LMA 中的内存池,而不是使用匿名文件映射。本文档可能会有所帮助:davidfindlay.org/weblog/files/ce_lma.php @vividos - 太美了。我要试一试。谢谢! 【参考方案1】:

检查 boost::interprocess 是否支持 Windows Mobile。它们具有创建内存映射文件和使用内存分配您想要的任何数据的功能:

http://www.boost.org/doc/libs/1_47_0/doc/html/interprocess/sharedmemorybetweenprocesses.html#interprocess.sharedmemorybetweenprocesses.mapped_file

【讨论】:

【参考方案2】:

您正在使用匿名文件映射(一个没有实际文件支持它的文件)。执行此操作时,映射由系统页面文件支持。移动操作系统很可能实际上没有页面文件,因为“硬盘”可能是闪存设备。对闪存设备进行虚拟内存分页通常不是一个好主意,因为虚拟内存的性质意味着大量写入会很快烧毁闪存(尤其是旧类型的闪存)。

这似乎得到了您获得的迭代次数的支持。看起来您最多可以映射设备上大约 60% 的总内存。

如果您打开一个真实文件(使用OpenFile)并映射它而不是在CreateFileMapping 中使用INVALID_FILE_HANDLE,您可能可以让它与更大的部分一起使用。但是,如果您要大量写入文件映射,请注意您的闪存(和性能!)。

【讨论】:

Windows Mobile 大内存区接近 1GB。充其量,我只能在崩溃前消耗 31MB。 31MB 远不及设备总量的 60%,即使我只有 512MB - msdn.microsoft.com/en-us/library/bb331824.aspx 切换到文件支持的实现,我能够在设备冻结之前分配 26283 次 (25MB)。如有必要,我可以发布该实现。 呃,对不起,我错了一个数量级,我以为是 310 MB。无论如何,如果您无法从 32MB 进程槽中获得超过 31MB,那么答案可能是……这就是他们称之为 32MB 进程槽的原因? 这就是为什么我搬到MappedFileAllocator 的原因,它应该在LMA 中分配32MB 进程槽之外的内存。我可以验证进程槽内存没有被填满,而 std::allocator 确实如此。【参考方案3】:

我刚刚得到了这个问题的“热门问题”徽章,所以我想我会(迟来的)发布答案。我的可用手柄用完了。 Windows Mobile 内核中有一个与溢出的句柄分配相关的无符号短计数器。一旦发生这种情况,设备就会冻结。

【讨论】:

以上是关于内存映射文件 std::allocator 实现冻结 WM6 设备的主要内容,如果未能解决你的问题,请参考以下文章

内存映射文件是不是为大缓冲区提供优势?

如何强制库使用自定义 std::allocator?

为啥不从 std::allocator 继承

std :: list对其allocator参数做了什么?

g++ 编译错误:std::ctype<char>、std::__detail、std::allocator<Taquart

对“std::vector<int, std::allocator<int>>”类型空指针的引用绑定