__mt_alloc源码分析(7)

支持多线程的内存分配器__gnu_cxx::__mt_alloc __gnu_cxx::new_allocator Simply wraps ::operator new and ::operator delete. __gnu_cxx::malloc_allocator Simply wraps malloc and free. There is also a hook for an out-of-memory handler __gnu_cxx: 阅读详情

__pool<true>的初始化

__pool<true>的初始化工作同样包括2个部分,对象构造和初始化。

 

356        explicit __pool()

357        : _M_bin(NULL), _M_bin_size(1), _M_thread_freelist(NULL)

358        { }

359 

360        explicit __pool(const __pool_base::_Tune& __tune)

361        : __pool_base(__tune), _M_bin(NULL), _M_bin_size(1),

362        _M_thread_freelist(NULL)

363        { }

 

构造函数基本和__pool<false>一样,除了多出一个_M_thread_freelist成员变量。注意_M_bin_size仍是被初始化为1。

初始化函数_M_initialize是让我觉得头疼的函数之一,不仅仅是因为它很长(159行),而且因为它代码缩进混乱,变量命名有歧义等等。所以我不得不对代码进行一些“修整”工作,以便顺利的阅读,不过下面的示例代码我还是保持原样。

 

<mt_allocator.cc>

421    void

422    __pool<true>::_M_initialize()

 

函数_M_initialize的原型。前面有部分代码和__pool<false>::_M_initialize一样,所以不用详细解释。

 

423    {

424      // _M_force_new must not change after the first allocate(),

425      // which in turn calls this method, so if it's false, it's false

426      // forever and we don't need to return here ever again.

427      if (_M_options._M_force_new)

428        {

429     _M_init = true;

430     return;

431        }

 

如果_M_force_new为true,则不需要额外的初始化工作,因为所有的内存操作都通过new和delete来完成。

 

433      // Create the bins.

434      // Calculate the number of bins required based on _M_max_bytes.

435      // _M_bin_size is statically-initialized to one.

436      size_t __bin_size = _M_options._M_min_bin;

437      while (_M_options._M_max_bytes > __bin_size)

438        {

439     __bin_size <<= 1;

440     ++_M_bin_size;

441        }

 

计算bin的个数,存放在_M_bin_size里。

 

443      // Setup the bin map for quick lookup of the relevant bin.

444      const size_t __j = (_M_options._M_max_bytes + 1) * sizeof(_Binmap_type);

445      _M_binmap = static_cast<_Binmap_type*>(::operator new(__j));

446      _Binmap_type* __bp = _M_binmap;

447      _Binmap_type __bin_max = _M_options._M_min_bin;

448      _Binmap_type __bint = 0;

449      for (_Binmap_type __ct = 0; __ct <= _M_options._M_max_bytes; ++__ct)

450        {

451     if (__ct > __bin_max)

452       {

453         __bin_max <<= 1;

454         ++__bint;

455       }

456     *__bp++ = __bint;

457        }

 

创建和初始化_M_binmap。

 

459      // Initialize _M_bin and its members.

460      void* __v = ::operator new(sizeof(_Bin_record) * _M_bin_size);

461      _M_bin = static_cast<_Bin_record*>(__v);

 

分配bin数组。

 

463      // If __gthread_active_p() create and initialize the list of

464      // free thread ids. Single threaded applications use thread id 0

465      // directly and have no need for this.

466      if (__gthread_active_p())

 

函数__gthread_active_p在以前详细讨论过,在研究__common_pool_base的时候。它通过检测库函数pthread_cancel是否存在,来判断是否链接了POSIX多线程库。

 

467        {

468     {

 

这个“{”括号的作用是限制下面的__gnu_cxx::lock的作用域。

 

469       __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);

 

freelist_mutex

这里又出现了几个新词汇,为了弄懂它们,我会穿插其他代码。我从freelist_mutex开始研究:

 

<mt_allocator.cc>

60     static __freelist freelist;

61     static __glibcxx_mutex_define_initialized(freelist_mutex);

 

__freelist是定义在__gnu_internal名字空间里的一个类,freelist_mutex是它的锁对象。在我的运行环境里,宏__glibcxx_mutex_define_initialized被定义为:

 

< libstdc++-v3/include/bits/concurrence.h>

46   #  define __glibcxx_mutex_define_initialized(NAME) /

47   __gthread_mutex_t NAME = __GTHREAD_MUTEX_INIT

 

__gthread_mutex_t的定义则是在如下位置:

 

<gthr-default.h>

46   typedef pthread_key_t __gthread_key_t;

47   typedef pthread_once_t __gthread_once_t;

48   typedef pthread_mutex_t __gthread_mutex_t;

49   typedef pthread_mutex_t __gthread_recursive_mutex_t;

 

总结起来就是,freelist_mutex是一个pthread_mutex_t对象,并且被初始化为__GTHREAD_MUTEX_INIT:

static pthread_mutex_t freelist_mutex = __GTHREAD_MUTEX_INIT;

 

freelist

那么freelist又是什么呢?答案是:它是真正的线程id链表!那么__pool<true>的成员变量_M_thread_freelist呢?什么作用也没有,一直保持着NULL的值。看到这里也许读者有些迷惑了,其实我也迷惑过,所以才会在开始的时候对函数_M_initialize那么“不满”。

现在我们忘记__pool<true>::_M_thread_freelist成员变量,只记得线程id链表是一个__freelist类型的全局静态对象freelist,看看它的实现吧。

 

<mt_allocator.cc>

40   #ifdef __GTHREADS

 

__freelist的定义也是被这个条件宏包裹起来的,和__pool<true>的一样。

 

41     struct __freelist

42     {

43       typedef __gnu_cxx::__pool<true>::_Thread_record _Thread_record;

 

存储在__freelist链表里的数据类型是_Thread_record,即线程id的节点。

 

44       _Thread_record*     _M_thread_freelist;

45       _Thread_record*     _M_thread_freelist_array;

46       size_t      _M_max_threads;

 

要弄清楚这3个成员变量的意义还真不容易,至少我想了很久才明白。

_M_thread_freelist是线程id的链表,每个节点表示1到_M_max_threads之间的一个id。

_M_thread_freelist_array是一个_Thread_record数组的首地址。可以想到,如果我事先知道线程id链表的长度,那么我不会一个一个分配每个节点,而是一次分配所有节点的内存,然后把它们“串联”成一个链表。于是这些节点本质上就是一个数组。把数组首地址存储在_M_thread_freelist_array里,我就可以通过下标访问到对应的节点。后面可以看到,通过把每个节点的线程id初始化为它的下标加1(0号id是全局的),__freelist可以通过给定的线程id访问到对应的节点,这在归还线程id的时候是很有用的。

_M_max_threads是当前线程id链表的长度。初次看到这个成员变量的时候,我想不通它的作用是什么。为了防止读者和我有同样的疑问,我可以提前告诉读者,它是__per_type_pool_policy下很重要的“功臣”。

 

47       __gthread_key_t     _M_key;

 

在介绍freelist_mutex的时候,我贴出了定义__gthread_key_t的代码,它实际上是pthread_key_t类型,用来操作线程私有数据,一个很好的指南位于:

http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/

简单的说,首先整个程序调用一次函数pthread_key_create,它会开辟出一块空间,并把一个给定的pthread_key_t类型对象初始化为这块空间的索引。然后每个线程都可以通过调用函数pthread_setspecific,以这个pthread_key_t类型对象为参数,向这个空间填写自己的私有数据(以void *的形式),而且各个线程间的数据互不干扰。如果要取出这些私有数据,各个线程只需调用函数pthread_getspecific,同样以这个pthread_key_t类型对象为参数。

在调用函数pthread_key_create的时候,还可以传一个函数指针,类型为:

void (*destructor)(void*)

这个destructor指针是一个“清理函数”,它在线程退出的时候被调用,清理存储在这个pthread_key_t类型对象下的线程私有数据。这个函数需要一个参数,就是线程的私有数据(以void *的形式)。如果线程退出时私有数据为NULL,则不调用清理函数。当然,如果没有设置destructor,也不会有清理函数被调用。

 

49       ~__freelist()

50       {

51         if (_M_thread_freelist_array)

52      {

53        __gthread_key_delete(_M_key);

 

整个程序退出的时候,销毁_M_key以及所有的线程私有数据。前面已经介绍过,类似于__gthread_xxx的符号都会变成对应函数pthread_xxx的弱引用,即如果函数pthread_xxx存在,那么链接的时候__gthread_xxx就是函数地址;如果不存在,那么__gthread_xxx就是0。

 

54        ::operator delete(static_cast<void*>(_M_thread_freelist_array));

 

释放所有的线程id节点,记住它们是一个数组。

 

55      }

56       }

57     };

58  

59     // Ensure freelist is constructed first.

60     static __freelist freelist;

61     static __glibcxx_mutex_define_initialized(freelist_mutex);

 

这2个全局静态变量供整个程序的内存池使用。freelist_mutex的类型前面已经介绍过了。还有一点需要说明,freelist对象的所有成员变量都会初始化为0,这是在链接的时候发生的。

 

63     static void

64     _M_destroy_thread_key(void* __id)

 

这个函数就是注册给_M_key的“清理函数”,而参数__id则是每个线程通过pthread_setspecific设置的自己的线程id。在线程退出时,只要__id不为0,都会调用_M_destroy_thread_key进行清理工作,于是freelist就可以在这个时候回收这个id。

 

65     {

66       // Return this thread id record to the front of thread_freelist.

67       __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);

 

锁定整个freelist。新单词__gnu_cxx::lock还没有研究到,不过它的意义很明显,就是锁住freelist对象。

 

68       size_t _M_id = reinterpret_cast<size_t>(__id);

 

真正得到线程的id。__id的类型是void *,此处进行强制类型转换,成为size_t。

 

70       using namespace __gnu_internal;

71       typedef __gnu_cxx::__pool<true>::_Thread_record _Thread_record;

72       _Thread_record* __tr = &freelist._M_thread_freelist_array[_M_id - 1];

 

通过线程id,找到节点的地址。正如我前面介绍_M_thread_freelist_array的作用时所说,整个线程id链表在物理上其实是一个数组,而每个节点的线程id又被设置成下标值加1,所以这里可以直接用_M_thread_freelist_array[_M_id - 1]得到节点的地址。

 

73       __tr->_M_next = freelist._M_thread_freelist;

74       freelist._M_thread_freelist = __tr;

 

把线程id节点加入到_M_thread_freelist链表里。

 

75     }

76   #endif

__mt_alloc源码分析(10) __pool::_M_reserve_block最后,当线程自己的空闲块没有的时候,__mt_alloc必须向内存池的全局空闲块链表求援,于是进入了_M_reserve_block函数。 312    char* 313    __pool::_M_reserve_block(size_t __bytes, const size_t __thread_id) _M_re 阅读详情

相关推荐

Matlab系列之GUI设计基础

Matlab系列之GUI设计基础简介编辑界面菜单设计控件设计控件描述个人理解Matlab转译控件属性【1】外观与行为【2】控件对象的信息【3】回调函数【4】状态信息结束结束更多精彩,等你发现~ 简介 GUI即图形用户界面(Graphical User Interface),人机交互的一种方式,可以简化用户的操作,比如只需要点点窗口上的按键或者菜单选择等等,就可以实现一个复杂的功能,有点类似黑盒的操作吧。 本篇将主要介绍GUI的使用,以及一些功能,后续的篇章再使用GUI来设计实例。 打开GUI可以在Matla

Smart_Devil的博客 5万+

__mt_alloc源码分析(11)

__pool的内存释放多线程内存池__pool的内存释放工作主要由函数_M_reclaim_block完成。 250    void251    __pool::_M_reclaim_block(char* __p, size_t __bytes) 函数原型,参数__p为用户释放的内存块地址,__bytes为内存块字节大小。 252    {253     

daidodo的专栏 1009

Mac微信聊天记录备份终极指南:用软链接轻松迁移到外接硬盘(附重签名教程)

本文详细介绍了如何通过软链接技术将Mac微信聊天记录备份迁移到外接硬盘,解决内置存储空间不足的问题。教程包括备份文件定位、软链接创建及微信重签名步骤,帮助用户高效管理微信数据,释放Mac存储空间。

uuu88的博客 1166

__mt_alloc源码分析(2)

struct __per_type_pool_policy 在继续深入到__common_pool_base和其他pool类里面之前,我好奇的看了下__per_type_pool_policy的定义,发现一些事情,于是决定先说说它。 545    /// @brief  Policy for individual __pool objects.546    template cla

daidodo的专栏 2256

看不懂分子动力学模拟的结果?一文读懂RMSD曲线

随后,将每一帧结构与初始模板进行叠合,计算对应原子的平均偏移量,即RMSD。总之,掌握 RMSD 的解读方法,就等于抓住了评估分子动力学模拟成败与质量的“总开关”。①在模拟初始阶段,RMSD会从0迅速上升,这是蛋白质从静态晶体或模型结构释放到溶液环境中并逐渐适应的正常过程。②经过一段时间后,曲线不再系统性上升或下降,而是围绕一个平均值,小幅度地上下波动,形成一个稳定的“平台”。这种情况下,RMSD 在初始快速上升后,并未进入平台,而是持续甚至加速上升,始终未见收敛。🔍 如何解读不同趋势的RMSD曲线。

weixin_46587777的博客 4894

Spring AI Alibaba实战训练营-16 NL2SQL (自然语言转 SQL)应用开发指南

本文介绍了一个基于AI的智能BI应用开发项目,主要实现自然语言转SQL(NL2SQL)功能,包含对话模块、MCP模块、向量数据管理模块。项目采用Spring AI Alibaba框架,集成通义千问大模型,支持内存向量库和AnalyticDB向量数据库两种存储方案。重点展示了chat模块的三个控制器实现:AnalyticNl2SqlController用于生产环境,SimpleChatController适用于开发测试,Nl2sqlForGraphController支持基于图的复杂对话流程。系统通过将业务知

program哲学 1589

C++11中静态局部变量初始化的线程安全性

大家都知道,在C++11标准中,要求局部静态变量初始化具有线程安全class Foopublic:private:Foo() {}在C++标准中,是这样描述的(在标准草案的6.7节中):看到了GCC如此复杂的实现,我的个人感想是还是不要自己造轮子来保证单例类的线程安全了,想要做到和GCC一样的高效还是比较难的,利用C++11标准的带来的便利就挺好。C++11中静态局部变量初始化的线程安全性-优快云博客。

木牛的博客 604

【腾讯小龙虾WorkBuddy专栏01】初识WorkBuddy!定位、核心优势、功能界面全解析

【腾讯小龙虾WorkBuddy专栏01】初识WorkBuddy!定位、核心优势、功能界面全解析

AI人人懂 3854

快速锁和递归锁,线上遇到锁问题分析

/*********************************************************************************************** created: 2020-05-20 author: chensong purpose: mutex 死锁问题 锁大致分为三种 1. fast 快速 又名悲观锁 -- 互斥锁 2. recursive 递归 又名乐观锁 3. err.

永远积极向上、永远热泪盈眶、永远豪情满怀、永远坦坦荡荡 494

YOLO11 改进 - C2PSA | C2PSA融合HEWL(高频增强小波层):小波逆变换+双通道注意力增强+高频显式增强 | TGRS 2026

本文介绍了高频增强小波层(HEWL),一种专为解决红外小目标检测中边缘退化与语义模糊问题设计的上采样模块。该方法结合小波逆变换与交替通道注意力(ALCA),通过显式增强高频细节并优化特征融合,实现精准的边缘重建与语义对齐。我们将 HEWL 融入 PSABlock 结构构建了 C2PSA_HEWL 模块,并成功集成进 YOLO11 的骨干网络,替代原有的 C2PSA 模块。实验证明,结合 HEWL 的 YOLO11 在检测任务中表现优异,有效提升了模型对细节特征的捕捉能力。

魔改工程师的博客 103

猫群优化算法(CSO)详解:从生物灵感到智能优化

猫群优化算法(CSO)是一种模拟猫科动物行为的群体智能优化算法。该算法基于猫的两种主要行为模式:搜寻状态(全局探索)和跟踪状态(局部开发)。CSO通过混合率(MR)动态调整两种模式的比例,实现探索与开发的平衡。算法核心包括搜寻模式的变异操作和跟踪模式的速度-位移模型,具有收敛性能好、全局搜索能力强等特点。相比传统优化算法,CSO在高维复杂问题中表现优异,已成功应用于函数优化、图像处理等多个领域。算法性能受混合率、变化域等参数影响,自适应参数策略可进一步提升优化效果。

qq_42212808的博客 620

__mt_alloc源码分析(1)

本文从源代码级别研究mt allocator的内部实现,使用GCC 4.1.2版本的源码,主要源文件为库文件和GCC源码中的“libstdc++-v3/src/ mt_allocator.cc”。假定读者对mt allocator的原理已有一定的了解。 class __mt_alloc为避免一开始就深入到复杂的内存分配机制里,我采用从上往下的研究方法。最顶层自然是提供给用户使用的_

daidodo的专栏 3130

直方图均衡化原理及matlab和opencv实现代码

详细分析了直方图均衡化的数学原理,配有matlab和opencv实现代码

qq_21603315的博客 4778

__mt_alloc源码分析(12)

__pool的销毁终于,我们来到了最后一站,本节完后,整个mt allocator源码分析也就结束了。和__pool一样,_M_destroy函数其实从未被调用过,但是为了保持设计的完整性,这个函数还是有意义的。 209    void210    __pool::_M_destroy() throw() _M_destroy函数原型。 211    {21

daidodo的专栏 985

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

__mt_alloc源码分析(9)

__pool的内存分配虽然我在前面已经介绍过__mt_alloc::allocate函数,但是结合这里的多线程环境,我建议读者还是回顾一下它的代码。其实__mt_alloc::allocate函数并不关心内存池是多线程还是单线程的,它在2种环境下都采用统一的操作步骤,只是在单线程的时候其中有些步骤什么事情都不用做,于是我们前面就忽略了。而到了多线程下,每个步骤都变得有用和必需了。 __

daidodo的专栏 1101

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

__mt_alloc源码分析(8)

__gnu_cxx::lockOK,现在是时候研究lock了。它的定义在GCC源码的“libstdc++-v3/include/bits/concurrence.h”文件里,以下简称concurrence.h。 79     /// @brief  Scoped lock idiom.80     // Acquire the mutex here with a constru

daidodo的专栏 1165

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

__mt_alloc源码分析(4)

class __pool终于,我们要开始研究mt allocator里最核心的东西了。__pool是实际上的内存池类,以前我们介绍的那么多类,都是在它的基础上建立起来的“上层建筑”。 188    template189      class __pool; 这是__pool的原型,模板参数_Thread表示是否支持多线程。接着分别对_Thread为false和true的情

daidodo的专栏 1053

__mt_alloc源码分析(3)

struct __common_pool__common_pool是__common_pool_base的基类,它作用就是定义pool_type和实现_S_get_pool函数。 382    template class _PoolTp, bool _Thread>383      struct __common_pool __common_pool的原型,模板参数_Po

daidodo的专栏 1062

__mt_alloc源码分析(5)

__pool的内存分配本节研究__pool在分配内存时做的事情。回忆一下__mt_alloc:: allocate函数的内容,一般情况下用户通过某个bin的_M_first[0]可以得到想要的内存,但是在_M_first[0]为0时,__mt_alloc:: allocate调用了: 701       // Null, reserve.702       __c = __pool

daidodo的专栏 798

__mt_alloc源码分析(6)

class __pool__pool是多线程下的内存池类型。在某些方面,它是__pool的扩展,但是在其他很多方面,它比__pool要复杂得多。__pool的基类也是__pool_base,所以它继承了__pool_base的所有成员变量,包括_M_options,_M_binmap和_M_init。同时__pool_base::_Tune和__pool_base::_Block_addres

daidodo的专栏 854

Linux内核DMA缓存处理的“真相“-实战

摘要:本文深入分析了dma_alloc_coherent和dma_map_single的核心差异。dma_alloc_coherent分配非缓存内存,保证一致性但性能较差,适合小数据频繁访问场景;dma_map_single使用缓存内存,性能更好但需要手动同步,适合大数据传输。关键点包括:1)内存属性差异(MT_NORMAL_NC vs MT_NORMAL);2)缓存对齐要求;3)所有权移交模型;4)同步时机控制。文章提供了调试方法(DMA_API_DEBUG、IOMMU、压力测试)和决策树,并指出常见错误

zhilin_tang的专栏 363

STL 源码分析六大组件-allocator

1. allocator 基本介绍 分配器(allocator))是C ++标准库的一个组件, 主要用来处理所有给定容器(vector,list,map等)内存的分配和释放。C ++标准库提供了默认使用的通用分配器std::allocator,但是,也可以由程序员自己提供自定义分配器。 2. allocator 标准库规范 我们去看std中的stl分配器实现,会发现无论你的实现思路怎么变,所有模...

weixin_33866037的博客 448

binutils 工具: strings 简单分析

binutils 工具: strings 简单分析 strings 是查看二进制代码中ascii 字符串的工具,默认4个字符连在一起即为字符串。 阅读源码,可以加深一下理解,理解其工作过程: 1. 下载源码, 配置,编译。 编译的时候倒是直接有编译log 输出,不过是-g -O2 形式。 $cp Makefile Makefile.bak 先备份一下Makefile,把 -g -O2

hjjdebug的专栏 991

Linux内核源码分析之setup_arch (三)

1. 前言 在 Linux内核源码分析之setup_arch (二) 中介绍了当前启动阶段的内存分配函数memblock_alloc,该内存分配函数在本篇将要介绍paging_init中用于页表和内存的分配,paging_init函数大致流程如下图所示。 2. paging_init 2.1 build_mem_type_table 该函数根据具体的CPU架构对静态定义的mem_types数组中定义的属性进行调整。 2.2 prepare_page_table 该函数的作用是把页目录项清零,源码大致如下。

公众号『机器感知』 647

Linux kernel workqueue机制分析【转】

转自:http://www.linuxsir.org/linuxjcjs/15346.html 在内核编程中,workqueue机制是最常用的异步处理方式。本文主要基于linux kernel 3.10.108的workqueue文档分析其基本原理和使用API。 概览 Workqueue(WQ)机制是Linux内核中最常用的异步处理机制。Workqueue机制的主要概念包括:wor...

weixin_30919571的博客 236

侯捷STL学习(三)--分配器测试

第七节:分配器测试 标准的分配器Allocator,#include&lt;ext/...&gt;都是拓展的 可以用不同的分配器测试同一容器 分配器allocate() &amp; deallocate()进行内存的分配和释放,这样操作太麻烦了 实际用到得new delete malloc free释放时并没有指定分配产生的字节 关于分配器实现需要后续的源码分析 测试 #include ...

weixin_34332905的博客 186
上一篇: __mt_alloc源码分析(6)
下一篇: __mt_alloc源码分析(8)
daidodo
博客等级 码龄22年 43粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值