提升进程间和 valgrind

Posted

技术标签:

【中文标题】提升进程间和 valgrind【英文标题】:boost interprocess and valgrind 【发布时间】:2014-10-30 19:37:01 【问题描述】:

这是我用来在共享内存上分配映射的一段代码,我正在使用 boost::interprocess 和托管共享内存段,现在的问题是我遇到了内存泄漏。下面给出的是最高输出。

顶部输出:

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
1.  27594 tpmon     20   0 46132 2140 1664 S  0.0  0.0   0:00.00 test_stub
2.  27594 tpmon     20   0 46132 2176 1664 S  0.0  0.0   0:00.01 test_stub
3.  27594 tpmon     20   0 46264 2248 1664 S  0.0  0.0   0:00.01 test_stub
4.  27594 tpmon     20   0 46264 2280 1664 S  0.0  0.0   0:00.01 test_stub

从上面的输出可以看出,驻留内存在不断增加,在共享内存映射中,我只有下面列出的条目,作为三元组:

DEB0 0 150520 DEB1 1 150520 DEB11 11 150520 DEB12 12 150520 DEB13 150520 DEB14 14 150520 DEB1414 150520 DEB14 150520 DEB14 150520 DEB17 17 150520 DEB18 150520 DEB19 150520 DEB2 150520 DEB20 20 150520 DEB21 21 150520 DEB22 22 150520 DEB23 23 150520 DEB24 24 150520 DEB25 25 150520 DEB26 26 150520 DEB27 26 150520 DEB27 27 150520 DEB28 29 150520 DEB29 29 150520 DEB3 3 150520 DEB4 4 150520 DEB5 5 150520 DEB6 15 150520 DEB6 6 150520 DEB7 7 150520 DEB8 8 150520 DEB9 9 150520 P>

这些不会被进一步添加,它们只是得到更新。

我采取的下一步是按如下方式运行 valgrind:

sudo -u tpmon valgrind --tool=memcheck --leak-check=yes ./bin/test_stub

And below is the output:

==21404== Memcheck, a memory error detector
==21404== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21404== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21404== Command: ./bin/test_stub
==21404==
Finished initializing TickerInfo Manager
^C==21404==
==21404== HEAP SUMMARY:
==21404==     in use at exit: 60,627 bytes in 1,264 blocks
==21404==   total heap usage: **5,059 allocs, 3,795 frees**, 812,123 bytes allocated
==21404==
==21404== 29 bytes in 1 blocks are possibly lost in loss record 2 of 7
==21404==    at 0x4A075BC: operator new(unsigned long) (vg_replace_malloc.c:298)
==21404==    by 0x3A7149C3C8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.13)
==21404==    by 0x3A7149CDE4: ??? (in /usr/lib64/libstdc++.so.6.0.13)
==21404==    by 0x3A7149CF32: std::basic_string<char, std::char_traits<char>, > std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.13)
==21404==    by 0x40986F: main (test_stub.cxx:12)

从 valgrind 的输出可以看出,allocs 的数量大于 free,但这是否有任何实际意义,在共享内存的情况下,我们希望分配的内存在进程退出后出现。

test_stub.cxx

#include <stdio.h>
#include <iostream>
#include<string>
#include <sstream>

using namespace std;
int main() 

  TickerInfoManager * ticker_info_manager_inst = TickerInfoManager::get_instance("test");

  char_allocator ca(ticker_info_manager_inst->get_managed_memory_segment().get_allocator<char>());
  while(1) 
    for( int i=0; i < 30; i++ ) 
      basic_time now;
      stringstream convert;
      convert << i;
      int curr_time = now.fullTime();
      ticker_info_manager_inst->put_records( *(new tickerUpdateInfo(const_cast<char*>(("Deb"+convert.str()).c_str()), i, curr_time, ca ) ));
    
    sleep(1);
  
  //ticker_info_manager_inst->print_contents();
  return 0;

TickerInfoManager.cxx

#include <TickerInfoManager.h>
#include <TickerInfoMangerImplementation.h>

TickerInfoManager::TickerInfoManager( const sharedMemoryNameT & name) : pInfoMangerImpl( new tickerInfoMangerImplementation( name )) 



TickerInfoManager* TickerInfoManager::get_instance( const sharedMemoryNameT & name ) 
  return (new TickerInfoManager( name ) );


bool TickerInfoManager::put_records( const tickerUpdateInfoT & record ) 
    return pInfoMangerImpl->put_records( record );


void TickerInfoManager::print_contents() 
  return pInfoMangerImpl->print_contents();


bip::managed_shared_memory& TickerInfoManager::get_managed_memory_segment() 
  return pInfoMangerImpl->get_managed_memory_segment();

TickerInfoMangerImplementation.cxx

#include <TickerInfoMangerImplementation.h>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <iostream>
#include "basic_time.h"

using namespace boost::interprocess;

tickerInfoMangerImplementation::tickerInfoMangerImplementation( const sharedMemoryNameT & name ): m_name(name),
  m_managed_memory_segment( open_or_create, "test", 1000000 ),
  p_ticker_info_map( m_managed_memory_segment.find_or_construct<KeyTickerCountMap>("TickerInfoMap")(std::less<SharedString>(), m_managed_memory_segment.get_segment_manager() ) ) 
    std::cout<<"Finished initializing TickerInfo Manager" << std::endl;
  

bool tickerInfoMangerImplementation::put_records( const tickerUpdateInfoT & record ) 

  //If the key has not been inserted, insert the key and update the map

  KeyTickerCountMap::iterator iterator_to_map = p_ticker_info_map->find( record.m_id );

  if( iterator_to_map == p_ticker_info_map->end() ) 
    p_ticker_info_map->emplace( record.m_id, std::make_pair( record.m_total_ticker_count, record.m_active_ticker_count ) );
  
  else 
    p_ticker_info_map->at(record.m_id)  = std::make_pair( record.m_total_ticker_count, record.m_active_ticker_count) ;
  
  //record.m_ca.deallocate( const_cast<char*> ((record.m_id).c_str()), record.m_id.length() );
  return true;


int tickerInfoMangerImplementation::calculate_historical_time_using_threshold( const thresholdT seconds ) 

  basic_time::Secs_t secs( seconds );
  basic_time tick_time;
  tick_time -= secs;
  return ( tick_time.fullTime() );



void tickerInfoMangerImplementation::print_contents() 

  KeyTickerCountMap::iterator map_iter = (*p_ticker_info_map).begin();
  KeyTickerCountMap::iterator map_end  = (*p_ticker_info_map).end();

  for ( ; map_iter != map_end; ++map_iter ) 
    std::cout<< map_iter->first << " " << map_iter->second.first << " " << map_iter->second.second << std::endl;
  

TickerInfo.h

    #ifndef __TICKER_INFO__
    #define __TICKER_INFO__

    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <iostream>

    typedef boost::interprocess::managed_shared_memory::allocator<char>::type               char_allocator;
    typedef boost::interprocess::basic_string<char, std::char_traits<char>, char_allocator> shm_string;

    //Data to insert in shared memory
    typedef struct tickerUpdateInfo 

      shm_string     m_id;
      int            m_total_ticker_count;
      int            m_active_ticker_count;
      char_allocator m_ca;

        tickerUpdateInfo( char * id,
            int total_ticker_count,
            int active_ticker_count,
            const char_allocator &a )
        : m_id( id, a),
        m_total_ticker_count(total_ticker_count),
        m_active_ticker_count(active_ticker_count),
        m_ca(a) 
        

        ~tickerUpdateInfo() 
          std::cout<< "Calling destructor" <<std::endl;
        

      tickerUpdateInfo& operator=(const tickerUpdateInfo& other) 
        if (this != &other) 
          m_total_ticker_count  = other.m_total_ticker_count;
          m_active_ticker_count = other.m_active_ticker_count;
        
        return *this;
      
     tickerUpdateInfoT;

    #endif

**TickerInfoManager.h**

#ifndef __TICKER_INFO_MANAGER__
#define __TICKER_INFO_MANAGER__
#include <TickerInfoManagerConstants.h>
#include <TickerInfoMangerImplementation.h>

//class tickerInfoMangerImplementation;

class TickerInfoManager 

  public:
    static TickerInfoManager* get_instance( const sharedMemoryNameT & name );
    bool put_records( const tickerUpdateInfoT & record );
    TickerInfoManager( const sharedMemoryNameT & name);
    void print_contents();
    boost::interprocess::managed_shared_memory& get_managed_memory_segment();

  private:
    std::auto_ptr<tickerInfoMangerImplementation>  pInfoMangerImpl;
;

#endif

TickerInfoMangerImplementation.h

#ifndef __TICKER_INFO_MANAGER_IMPL__
#define __TICKER_INFO_MANAGER_IMPL__

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>
//#include <TickerInfoManagerConstants.h>
#include <TickerInfo.h>
#include <vector>
#include <fire/HashMap.h>
#include <string>

 typedef std::string sharedMemoryNameT;
 typedef int         thresholdT;


namespace bip = boost::interprocess;


//the strings also need to be assigned from the shared memory
typedef bip::allocator<void, bip::managed_shared_memory::segment_manager> VoidAllocator;
typedef bip::allocator<char, bip::managed_shared_memory::segment_manager> CharAllocator;
typedef bip::basic_string<char, std::char_traits<char>, CharAllocator> SharedString;

//Note that map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>,
//so the allocator must allocate that pair.
typedef bip::allocator<std::pair<const SharedString, std::pair<int,int> >, bip::managed_shared_memory::segment_manager> MapValueTypeAllocator;
typedef bip::map<SharedString, std::pair<int,int>, std::less<SharedString>, MapValueTypeAllocator>  KeyTickerCountMap;

//allocator for the string
typedef bip::allocator<SharedString, bip::managed_shared_memory::segment_manager> StringAllocator;

class tickerInfoMangerImplementation 

  public:
    tickerInfoMangerImplementation( const sharedMemoryNameT & name );

    bool put_records( const tickerUpdateInfoT & record );

    void print_contents();

    bip::managed_shared_memory& get_managed_memory_segment() 
      return m_managed_memory_segment;
    


  private:
    const sharedMemoryNameT    m_name;
    bip::managed_shared_memory m_managed_memory_segment;
    bip::offset_ptr<KeyTickerCountMap> p_ticker_info_map;

    int calculate_historical_time_using_threshold( const thresholdT seconds );
;
#endif

所以基本上代码流程是这样的:

从 test_stub.cxx 我们调用 TickerInfoManager 中的 put_records ------> 调用 put_records(in tickerInfoManagerImplementation)---> tickerInfoManagerImplementation 中的 put_records 将数据插入到位于共享内存中的映射中。

如果有人想重现这种情况,我已经添加了完整的代码。

我的问题是如何调试这个问题,我是不是没有正确理解 valgrind 的输出?

谢谢, 黛比!

【问题讨论】:

【参考方案1】:

警告:前方咆哮。我确实有建设性,并在最后提供了一个建设性的固定版本。

我知道这听起来不太好,但也许你应该停止在 C++ 中这样做。

至少停止尝试模仿 Java。停止编写线程不安全的代码。停止写入内存泄漏。

怎么样? 从简单开始

你的目标太高了。

我已经帮助了你大约 4 次了

Keeping fixed size symbols in shared memory Program crashes when trying to access the inner map Using boost multi index like relational DB

在最后两个答案中,我基本上重写了整个内容,并建议您重新考虑设计。

然而,您设法返回已经 [...] 代码的 /further/ 复杂版本。从最重要的开始:

您在 main() 中使用了内存泄漏运算符 (*new)¹:

*(new tickerUpdateInfo(const_cast<char *>(("Deb" + convert.str()).c_str()), i, curr_time, ca)));

恭喜,因为您还在同一行中滥用了const_cast。就拿std::strings 吧,不要作恶。

您的所有共享内存访问都没有受到锁的适当保护。这意味着您的流程的所有行为都是未定义的。

除此之外,代码还有很多问题:

tickerUpdateInfo::operator= 不分配 id(和分配器)违反了值语义。 (未使用此运算符)

1234563 ,见第一个项目符号)。

typedef struct X ... X_t; 是 C 主义。在 C++ 中无需标记结构

tickerUpdateInfo 不需要持有分配器(它是字符串的属性)。

您的TickerUpdateManagerget_instance。那么,它是单例吗?哦不,不可能,因为每次调用它都会返回一个新实例。哦,经理也被泄露了。

经理什么都不做。它所做的只是委托给一个实现。 Pimpl 习惯用法很好,但如果它实际上将接口与实现隔离,则只能用于任何目的。但是,您的 Manager 直接公开接口中的实现类型,甚至直接将非常量引用公开到实现中 (get_segment_manager)。那是界面设计的禁忌,违反了德米特法则。

这种管理器/实现的区别只不过是噪音并增加了复杂性

TicketUpdateManagerImplementation 的构造不会保护共享内存或带有锁的TickerInfoMap 的构造。

auto_ptr 已弃用。使用std::unique_ptr(或boost::scoped_ptr,如果必须的话。)。当然,这是假设您甚至需要那里的 pimpl。

m_name 从未使用过(甚至没有用于打开共享内存段),sharedMemoryNameT 是蛇油。

std::less&lt;SharedString&gt; 已经是默认比较器

put_records 是一种极其迂回的做法:

(*p_ticker_info_map)[record.m_id] =  record.m_total_ticker_count, record.m_active_ticker_count ;

更喜欢简洁的代码而不是带有流控制逻辑的重复。还有,为什么会有true返回值?

calculate_historical_time_using_threshold 是一个有趣的名字。该函数不属于此类,并且不计算历史时间。哦,它不使用任何阈值。 (AFAICT 它返回全职seconds 之前。)

shm_stringSharedString 的类型定义存在竞争(以及它们各自的分配器)。

无需将所有实现细节(typedef)放入全局命名空间和头文件中。

清理

这是对经理的建议清理,pimpl 做得正确。看看我们如何在标头中使用标准库类型:

#pragma once

#include <string>
#include <memory>

class TickerInfoManager 

  public:
    TickerInfoManager(const char* name);
    ~TickerInfoManager();

    void put(std::string const& id, int total, int active);
    void print_contents() const;

  private:
    struct Impl;
    std::unique_ptr<Impl> pimpl_;
;

现在主程序同样简单多了:

#include <iostream>
#include <string>
#include "TickerInfoManager.hxx"
#include "basic_time.h"
#include <thread>

int main() 
    TickerInfoManager tickerinfo("test");

    while (1) 
        for (int i = 0; i < 30; i++) 
            tickerinfo.put("Deb" + std::to_string(i), i, basic_time().fullTime());
        

        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // sleep(1)
        std::cout << "." << std::flush;
    

该实现通过 IPC 同步实现了线程安全,并集中了所有与 Boost Interprocess 相关的内容:

#include "TickerInfoManager.hxx"
#include <iostream>

#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/thread/lock_guard.hpp>

namespace /*anon*/ 
    namespace bip = boost::interprocess;

    typedef bip::allocator<char, bip::managed_shared_memory::segment_manager> CharAllocator;
    typedef bip::basic_string<char, std::char_traits<char>, CharAllocator> SharedString;
    typedef bip::allocator<std::pair<SharedString const, std::pair<int, int> >, bip::managed_shared_memory::segment_manager> MapValueTypeAllocator;
    typedef bip::map<SharedString, std::pair<int, int>, std::less<SharedString>, MapValueTypeAllocator> KeyTickerCountMap;
    typedef boost::lock_guard<bip::interprocess_mutex> lock_guard;

    struct LockableMap 
        LockableMap(MapValueTypeAllocator alloc) : map(alloc) 

        KeyTickerCountMap       map;
        bip::interprocess_mutex mutex;
    ;


struct TickerInfoManager::Impl 
    Impl(const char* name)
        : m_segment(bip::open_or_create, name, 1000000),
          m_alloc(m_segment.get_segment_manager()),
          p_data(m_segment.find_or_construct<LockableMap>("TickerInfoMap")(m_segment.get_segment_manager())) 
    

    bip::managed_shared_memory   m_segment; // order is relevant
    CharAllocator                m_alloc;
    bip::offset_ptr<LockableMap> p_data;

    KeyTickerCountMap&       map()    return p_data->map;   
    bip::interprocess_mutex& mutex()  return p_data->mutex; 
;

TickerInfoManager::TickerInfoManager(const char* name) : pimpl_(new Impl(name))  

TickerInfoManager::~TickerInfoManager()  

void TickerInfoManager::put(std::string const& id, int total, int active) 
    SharedString shid(id.begin(), id.end(), pimpl_->m_alloc);

    lock_guard lock(pimpl_->mutex());
    pimpl_->map()[shid] =  total, active ;


void TickerInfoManager::print_contents() const 
    lock_guard lock(pimpl_->mutex());
    for (auto const& e : pimpl_->map())
        std::cout << e.first << " " << e.second.first << " " << e.second.second << std::endl;

总而言之,它的代码量不到一半,而确实如此

适当隐藏实现细节 正确同步对共享内存的访问 妥善管理内存

查看代码Compiling On Coliru


¹C++ program crashes after creating 2000 objects

【讨论】:

哇,但在这 4 种情况下,您都没有得到任何感谢或明显的任何赞成票(op 的唯一回复是对其他事情的间接思考,任何当前的 +1 都是我的)。有没有想过你为什么要打扰?感谢有耐心解释 - 为了其他可能真正倾听的人的利益 - 为什么他们不应该这样编码。 ;-) @underscore_d 老实说,我主要是为了自学而回答。改进(设计)现有代码是一门艺术。让其他东西在其他地方停止工作是一个挑战。我不是天生的天才。我必须努力在这些事情上做得更好:)

以上是关于提升进程间和 valgrind的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 的进程间和服务器间事件发射器/侦听器?

Linux进程内存泄露分析

Linux进程内存泄露分析

进程 线程通信方式(转载)

多线程知识点总结

32线性空间06——行空间和左零空间