ACE_Message_Block功能和实现浅析

2023-10-27

ACE_Message_Block实现浅析

1.     概述

ACE_Message_Block是ACE中很重要的一个类,和ACE框架中的重要模式的实现 如ACE_Reactor, ACE_Proactor, ACE_Stream, ACE_Task都有紧密的联系.

换个角度看,ACE_Message_Block实际上已经是这些实现中的重要组成部分.抛开和框架的配合不谈, ACE_Message_Block本身也相当有用,功能强大,用途广泛.ACE_Message_Block的实现中使用了很多技巧和模式,代表性的有GOF的composite模式.这让它在处理数据特别是网络数据时会很方便.而且, 在多线程开发中,它也可以充当线程之间传递的消息.

2.     ACE_Message_Block的特点

ACE_Message_Block有几个重要特性:

1.      ACE_Message_Block内部采用ACE_Data_Block来间接管理实际数据, 减轻了其它功能与实际内存管理之间的耦合.

2.      ACE_Message_Block采用了引用计数, 可以灵活高效的共享数据, 并降低了内存拷贝带来的额外开销.

实际上, ACE_Message_Block本身并没有reference count, 而是间接的由ACE_Data_Block来提供.

3.      允许多条消息连接起来,形成一个单向链表, 从而支持复合消息Composite模式).

由此,ACE_Message_Block提供了cont()方法.

4.      允许将多条消息连接起来,形成一个双链表. 为ACE_Message_Queue的实现提供了支持.

由此,ACE_Message_Block提供next()和prev()方法.

5.      集成将同步策略和内存管理策略, 使得无需修改底层代码就能改变ACE_Message_Block的运行特征.

 
下面是自己总结的, 一开始总是混淆和迷惑的地方, 需要注意:

6.      3)4)特性实际上是正交的, 不存在交叉和冲突. 单链表实现复合消息, 双链表实现消息队列; 前者重内部, 后者重外部. 换个角度来说, 就是消息队列中的消息可以是复合消息.

7.      ACE_Message_Block对内存空间的管理采用“谁申请谁释放”的策略.

在控制权转移时, 需要特别注意这一点.(空间的所有权可能会随size()方法的调用而转移)在使用外部的缓冲区构造ACE_Message_Block或者初始化时,需要特别注意.

3.     ACE_Message_Block的数据管理

前面已经说明, 处于减少耦合的原因,ACE_Message_Block并不直接管理实际数据,而是委托ACE_Data_Block进行管理.因此, 对ACE_Data_Block的很多函数都有包裹调用:如base(), end(), mark()等等.

虽然ACE_Data_Block的管理很高效,但是,总体来说ACE_Data_Block并不关注”数据的有效性”.ACE_Data_Block只是简单的对空间进行管理, 提供起始地址.就像一个普通buffer.

ACE_Message_Block弥补了ACE_Data_Block的不足, 提供了读指针和写指针(内部实现为游标). 这样,用户可以方便的进行连续读写,代表性的例子是ACE_Message_Block的copy()方法.也为多个ACE_Message_Block复用相同的ACE_Data_Block提供了良好的支持.

 
ACE_Data_Block有两种方式获取空间:

1)    自行申请空间

2)    使用用户提供的空间

如果没有设定DONT_DELETE标志, ACE_Data_Block还能自动替用户来释放所管理的空间.

当使用栈上的缓冲区初始ACE_Data_Block时,要特别注意保证缓冲区的有效性,防止ACE_Data_Block使用无效指针.

 4.     ACE_Message_Block的构造和初始化

ACE_Message_Block提供了7个构造函数和3个初始化函数, 在实现中, 这些函数都不过是内部函数

init_i()函数的一个包裹.

4.1     init_i()的参数

init_i的参数有11个,不过大部分构造函数中调用时都设定了默认值.

 

int  init_i (size_t size,
              ACE_Message_Type type,
              ACE_Message_Block 
* cont,
              
const   char   * data,
              ACE_Allocator 
* allocator_strategy,
              ACE_Lock 
* locking_strategy,
              Message_Flags flags,
              unsigned 
long  priority,
              
const  ACE_Time_Value  & execution_time,
              
const  ACE_Time_Value  & deadline_time,
              ACE_Data_Block 
* db,
              ACE_Allocator 
* data_block_allocator,
              ACE_Allocator 
* message_block_allocator);

参数

类型

用途说明

size

size_t

数据空间大小

type

ACE_Message_Type

消息类型

cont

ACE_Message_Block *

挂接其它MB以成为复合消息

data

const char *

data!=0 表示使用外部提供的空间
data==0 表示由ACE_Message_Block来分配所需空间

allocator_strategy

ACE_Allocator *

申请空间时, 使用的内存分配策略.

data==0时使用.

默认值ACE_Allocator::instance()

locking_strategy

ACE_Lock *

多线程下的安全策略

Message_Flags

flags

MB属性,用于判断是否要释放内部的ACE_Data_Block

当data!=0时,默认设置为DONT_DELETE

priority

unsigned long

优先级,默认为0

 excute_time

const ACE_Time_Value&

暂不使用

deadline_time

const ACE_Time_Value&

暂不使用

db

ACE_Data_Block *

使用外部提供的

data_block_allocator

ACE_Allocator *

ACE_Data_Block的分配策略. db==0时使用

默认值ACE_Allocator::instance()

message_block_allocator

ACE_Allocator *

ACE_Message_Block的分配策略.

默认值ACE_Allocator::instance()

 默认的ACE_Allocator::instance()返回ACE_New_Allocator类型的策略.

4.2     init_i()的实现

实现动作很简单, 主要是旧data_block的释放和再申请,至于用户要求的size大小的空间,则交由ACE_Data_Block去具体负责.

{
    
this->rd_ptr_ = 0;
    
this->wr_ptr_ = 0;
    
this->priority_ = priority;
    
this->cont_ = msg_cont;
    
this->next_ = 0;
    
this->prev_ = 0;
    
this->message_block_allocator_ = message_block_allocator;

    
if (this->data_block_ != 0)
    
{
        
this->data_block_->release ();
        
this->data_block_ = 0;
    }


    
if (db == 0)
    
{
        
if (data_block_allocator == 0)
        ACE_ALLOCATOR_RETURN (data_block_allocator,    ,  ACE_Allocator::instance (),    
-1);

        ACE_TIMEPROBE (ACE_MESSAGE_BLOCK_INIT_I_DB_ALLOC);

        ACE_NEW_MALLOC_RETURN (db,
            static_cast
<ACE_Data_Block *> (    data_block_allocator->malloc (sizeof (ACE_Data_Block))),
            ACE_Data_Block (size,
            msg_type,
            msg_data,
            allocator_strategy,
            locking_strategy,
            flags,
            data_block_allocator),
            
-1);

        ACE_TIMEPROBE (ACE_MESSAGE_BLOCK_INIT_I_DB_CTOR);

        
if (db != 0 && db->size () < size)
        
{
            db
->ACE_Data_Block::~ACE_Data_Block();
            data_block_allocator
->free (db);
            errno 
= ENOMEM;
            
return -1;
        }

    }


    
this->data_block (db);
    
return 0;
}

5.     ACE_Message_Block的析构和释放

ACE_Message_Block的在析构之外还单独具备了一个release()函数,各有用途,不能相互替代.

1) ACE_Message_Block的析构函数不关心单向链(复合消息)的处理,只是把自己本身清理干净.

2) release()是个递归函数, 它会通过cont()访问所有链接的ACE_Message_Block, 依次对其进行清理,然后最后清理自己本身.清理的方式是ACE_DES_FREE,类似于delete this,但不完全一样.

3) 还有一个静态的releas(ACE_Message_Block*)函数,功能是一样的

 由此也可以看出,在栈上生成的ACE_Message_Block,千万不能调用release(),否则会发生所谓的”fall off the stack”. 

ACE的注释:

    *  release()  is  designed to release the continuation chain; the
   
*  destructor  is  not.  If we make the destructor release the
   
*  continuation chain by calling release() or delete on the message
   
*  blocks  in  the continuation chain, the following code will not
   
*  work since the message block  in  the continuation chain  is  not off
   
*  the heap:
   
*
   
*   ACE_Message_Block mb1 ( 1024 );
   
*   ACE_Message_Block mb2 ( 1024 );
   
*
   
*   mb1.cont ( & mb2);
   
*
   
*  And hence, call release() on a dynamically allocated message
   
*  block. This will release all the message blocks  in  the
   
*  continuation chain.  If you call delete or let the message block
   
*  fall off the stack, cleanup of the message blocks  in  the
   
*  continuation chain becomes the responsibility of the user.
所以如果一定要在栈上生成 ACE_Message_Block, 那么只能自己手动的清理内部的单向链了 .

5.1     析构函数

析构函数很简单,主要是内部data_block的清理,注意是调用data_block()->release()

,因为ACE_Data_Block使用了引用计数.

 

ACE_Message_Block:: ~ ACE_Message_Block ( void )
{
    ACE_TRACE (
"ACE_Message_Block::~ACE_Message_Block");
    
if (ACE_BIT_DISABLED (this->flags_,  ACE_Message_Block::DONT_DELETE) &&  this->data_block ())
               
this->data_block ()->release ();

  
this->prev_ = 0;
  
this->next_ = 0;
}

5.2     release()函数

  

ACE_Message_Block::release ( void )
{
  destroy_dblock 
= this->release_i (0);
  
if (destroy_dblock != 0)
    
{
      ACE_Allocator 
*allocator = tmp->data_block_allocator ();
      ACE_DES_FREE (tmp,
                    allocator
->free,
                    ACE_Data_Block);
    }


  
return 0;
}


这里省略了大部分线程策略处理,只保留了关键代码,可以看出,核心是release_i()函数.

int  ACE_Message_Block::release_i (ACE_Lock  * lock )
{
  
// Free up all the continuation messages.
  if (this->cont_)
    
{
      ACE_Message_Block 
*mb = this->cont_;
      ACE_Message_Block 
*tmp = 0;

      
do
        
{
          tmp 
= mb;
          mb 
= mb->cont_;
          tmp
->cont_ = 0;

          ACE_Data_Block 
*db = tmp->data_block ();
          
if (tmp->release_i (lock!= 0)
            
{
              ACE_Allocator 
*allocator = db->data_block_allocator ();
              ACE_DES_FREE (db,
                            allocator
->free,
                            ACE_Data_Block);
            }

        }

      
while (mb);

      
this->cont_ = 0;
    }


  
int result = 0;

  
if (ACE_BIT_DISABLED (this->flags_,
                        ACE_Message_Block::DONT_DELETE) 
&&
      
this->data_block ())
    
{
      
if (this->data_block ()->release_no_delete (lock== 0)
        result 
= 1;
      
this->data_block_ = 0;
    }


  
if (this->message_block_allocator_ == 0)
    delete 
this;
  
else
    
{
      ACE_Allocator 
*allocator = this->message_block_allocator_;
      ACE_DES_FREE (
this,
                    allocator
->free,
                    ACE_Message_Block);
    }


  
return result;
}


处理也很清晰, 基本递归处理完所有的单链MB  

6.     ACE_Message_Block中容易混淆的几个函数

ACE_Message_Block中有多个获取大小或者长度的函数,容易混淆.
下图是根据ACE_Message_Block(实际是ACE_Data_Block)空间的处理状况所绘,能比较清晰的反应出它们的异同.
需要注意,为了表现出多样性,下图是wr_ptr(),rd_ptr(),size()都调用过之后的情景.


 红色表示是ACE_Message_Block独有的函数, 其余则ACE_Message_Block和ACE_Data_Block均有.
矩形纸上函数的返回值均为指针类型,之下的返回值均为size_t类型.
 

函数

说明

length()

有效数据的长度

== wr_ptr() – rd_ptr()

size()

全部可用空间的长度,如果没有size()而变小,则等同capacity()

== mark() – base()

space()

剩余可用空间的长度

<= size() - length(),因为不含rd_ptr()移动过的空间

== mark() – wr_ptr()

capacity()

最大空间的长度(ACE_Message_Block构造或初始化时所用参数值)

== end() – base()

total_length()

复合消息(ACE_Message_Block内单向链 cont())的总长度

total_size()

复合消息(ACE_Message_Block内单向链 cont())的总大小

total_capacity()

复合消息(ACE_Message_Block内单向链 cont())的总空间大小

 

7.     ACE_Message_Block常用函数简介

7.1     duplicate()

duplicate()浅拷贝函数,公用一个内部的ACE_Data_Block
ACE_Message_Block::duplicate() 与 ACE_Data_Block.duplicate()的实现是不同的.
ACE_Data_Block::duplicate()简单的只是将自身的reference加+1, 然后返回自身(this)
ACE_Message_Block:duplicate()则将自身copy了一份, 然后将自身的状态值赋给拷贝,注意它们公用同一个data_block.而且ACE_Message_Block::duplicate()支持复合消息,它会检查内部单向链,来依次调用其duplicate().

 这里ACE_Data_Block::duplicate()的函数行为很怪异,以后就能看出它怪异行为的影响.说实话,这个地方如此设计我很不理解,因为ACE_Data_Block本身其实已经有reference了.

7.2     clone()

ACE_Message_Block::clone()深拷贝, 不但拷贝自身,内部的ACE_Data_Block也一并拷贝了,并且支持复合消息.

7.3     size()

ACE_Data_Block.size(size_t len)函数, 动态的变化ACE_Data_Block持有的空间.
ACE_Message_Block.size(size_t len)函数是ACE_Data_Block.size(size_t len)的简单包裹.
如果len比现有的尺寸小, 简单的cur_size_ = length;
如果len比现有的尺寸大, 会申请新的空间并拷贝原所有数据.

注意! 这里可能会发生空间控制权的转换! 即标志位DONT_DELETE的变化.若原ACE_Data_Block使用托管空间,则此时会更替为自己申请的空间,从而拥有了控制权, 所以此时要注意原有空间的管理.
ACE_Message_BlockACE_Data_Block, 除非主动调用size(), 否则它们不会自动申请和扩大空间.

7.4     其它函数

ACE_Message_Block::crunch() 将现有数据移动到现有的缓冲的开始.

ACE_Message_Block::reset()将现有读写指针赋为初始值(ACE_Data_Block.base())

ACE_Message_Block::base()是对ACE_Data_Block.base()的简单包裹

 8.     需要注意的地方

8.1     注意点1

1)ACE_Message_Block的构造函数中,如果data为NULL, 则ACE_Message_Block会为其自动分配空间. 但如data非NULL,则ACE_Message_Block会直接引用data指向的空间, 并不会进行新的空间分配和拷贝.

所以需要特别注意, 在ACE_Message_Block的实例没有销毁之前,不能释放data指向的空间.

 2)虽然ACE_Message_Block会根据size的值来更改自己的size(),但wr_ptr不会根据data的长度进行设置, 造成length()的返回为0.

需要特别注意, 当构造一个ACE_Message_Block实例后, 随之需要追加数据时,必须设置wr_ptr的值,否则原有数据将会被覆写.

 此时的含义是: ACE_Message_Block代管了data缓冲区,但不负责缓冲区的空间管理(因为也不是由它申请的).

8.2     注意点2

默认定义的flag: enum  { DONT_DELETE = 01, USER_FLAGS = 0x1000 }

1) set_flags()、clr_flags()是对ACE_Message_Block中的数据指针(ACE_Data_Block*)进行设置.

2) set_self_flags(),clr_self_flags()是对ACE_Message_Block本身进行设置.

8.3     注意点3

ACE_Message_Block::copy(const char* buf) 函数将字符串copy到ACE_Message_Block, 如果内在空间不足, 将会返回-1.

需要特别注意, copy的数据将包括末尾的0, 也就是copy的数据长度为strlen(buf)+1.

而且, 会自动进行wr_ptr()的设置

9.     ACE_Data_Block的思考

 ACE_Data_Block的析构函数是释放持有空间base_的惟一路径(size()的情况不讨论).

ACE_Data_Block中通过duplicate()递增引用计数. ACE_Data_Block中通过release()递减引用计数, 当引用计数为0时,先调用ACE_Data_Block析构函数,然后释放ACE_Data_Block自身.
注意, ACE_Data_Block的构造和析构函数都不知道引用计数的存在. 在构造函数中, 只是设置了初始值1.

 ACE_Data_Block一个很奇怪的地方就是ACE_Data_Block::duplicate()的实现, 并没有创建新的拷贝, 而仅仅是返回了自身(return this). 这中实现方式带来了很多奇怪的问题.如下面的2,3.
 release()-> release_no_delete()->release_i()->~ACE_Data_Block()

 如果在Stack上构造ACE_Data_Block,那么不能使用release()函数, 因为release()函数会试图删除this

 如果在stack上构造ACE_Data_Block, 那么不能使用duplicate()函数, 因为duplicate()返回的是this指针, 栈中的ACE_Data_Block析构后会导致问题.

 如果在heap上构造ACE_Data_Block,那么尽量使用release()来替代delete, 如果存在因为析构并不处理reference count, delete时不考虑其它会导致指针悬空

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ACE_Message_Block功能和实现浅析 的相关文章

随机推荐

  • JS获取系统时间

    网页通过JavaScript获取系统日期 时间的操作是比较常规的操作 我这里将自己使用过的方法小结一下 方便自己回顾或参考 目录 一 实现非常简单 第一步实例化 第二步获取值 二 参考实例 1 无格式要求简单获取 2 自定义格式获取 3 闰
  • 实现vue自定义指令v-loading

    1 创建文件 在 loading文件夹下创建3个文件 index js loading vue 和 loading js index js文件用来暴露安装插件接口 这个下面会有说明 有了 Vue directive这个方法就可以在页面中使用
  • Springboot获取IOC容器的方式

    1 前言 大家好 我是孤焰 最近我在制作日志审计功能时发现不知道怎样获取到Springboot项目中的IOC容器 经过摸索 最终解决了这个问题 现在把解决方式和大家分享一下 2 Springboot项目中获取IOC容器的方式 在Spring
  • go中如何处理error

    0 前言 go 中的异常处理和其他语言大不相同 像 Java C python 等语言都是通过抛出 Exception 来处理异常 而 go 是通过返回 error 来判定异常 并进行处理 在 go 中有 panic 的机制 但 panic
  • IDEA中使用vue 报 Namespace 'xxx' is not bound的解决办法

    IDE 问题编辑页中v on指令标红并提示 Namespace v on is not bound 解决办法 file Settings下 点ok后标红提示的问题解决 此方法是 Namespace xxx is not bound 通用解决
  • 阿里云RDS如何进行账号及数据库管理?

    本文说说如何管理RDS实例 进入控制台 选中RDS 在列表中选择我们需要管理的地域 并且找到我们需要管理的实例 首先建立一个数据库 点击数据库管理 点击创建数据库 输入数据库名 确定
  • [工具使用]WhatWeb

    网站指纹识别工具WhatWeb WhatWeb简介 用法 10小点 因为想要面对一个新的开始 一个人必须有梦想 有希望 有对未来的憧憬 如果没有这些 就不叫新的开始 而叫逃亡 玛丽亚 杜埃尼亚斯 WhatWeb简介 web指纹扫描主要用于对
  • modbus tcp报文解析_Modbus协议详解

    Modbus概述 Modbus通信协议由Modicon公司 现已经为施耐德公司并购 成为其旗下的子品牌 于1979年发明的 是全球最早用于工业现场的总线规约 由于其免费公开发行 使用该协议的厂家无需缴纳任何费用 Modbus通信协议采用的是
  • 如何共享服务器主机文件,服务器主机如何做文件共享

    服务器主机如何做文件共享 内容精选 换一换 云堡垒机支持文件传输功能 以及审计传输的文件 Linux主机和Windows主机的文件传输方式有所区别 Linux主机上传 下载文件 可选择Web运维和FTP SFTP客户端运维两种方式 Web运
  • C#学习笔记-C#的类及其成员

    类 是面向对象语言的的一个基本特征 它是一种数据结构 类可以理解为将现实生活中的食物进行抽象的结果 比如创建一个person的类 人有身高体重年龄的基本属性 而类的实例化是指将类具体化 比如刚刚创建的一个person的类 将他实例化之后 就
  • 【Vue】关于CSS样式绑定整理

    因突发奇想设计一款组件 需要根据属性动态绑定样式 故而整理一些Vue的动态绑定样式方法 传参绑定类似 不做过多叙述 仅供参考 方式一 直接在元素上绑定具体样式 方式二 定义属性对象 绑定到style 可以在style中使用定义的变量 方式二
  • Linux格式化命令

    如果我们购买一个新的虚拟主机空间或者要更改操作程序 会经常使用到磁盘格式化来清空以前的数据 磁盘格式化的方法很多 我们常用的是NTFS和linux格式化命令两种方法来格式化磁盘 我们讲解一下linux格式化命令的详情 linux格式化磁盘命
  • 自学中走出的大三学生面临就业选择

    来信 贺老师您好 这是我第二次向您请教问题了 非常感谢您上次给我的建议 注 上次来信见http blog csdn net sxhelijian article details 7760011 如邮件主题所述 我是一个即将大四的学生 我学的
  • Java实体映射工具MapStruct使用详解

    1 序 通常在后端开发中经常不直接返回实体Entity类 经过处理转换返回前端 前端提交过来的对象也需要经过转换Entity实体才做存储 通常使用的BeanUtils copyProperties方法也比较粗暴 不仅效率低下 使用反射 而且
  • 图解线性表顺序存储结构(附完整代码)

    目录 一 前言 二 常用接口的实现 三 完整代码 一 前言 顺序表是线性表采用顺序存储结构在计算机内存中的存储方式 它由多个连续的存储单元构成 每个存储单元存放线性表的一个元素 逻辑上相邻的数据元素在内存中也是相邻的 不需要额外的内存空间来
  • html中下划虚线dash定义长度,【CSS】有没有办法自定义 border 为 dashed 时的虚线间距?...

    CSS 的 border 1px dashed 样式太难看了 虚线非常的宽 dotted 也是一样 间距太窄 有没有办法控制虚线或者点的长度或者间距呢 回答 可以的 用渐变 width 100 height 1px background i
  • xml文件Unable to resolve table ‘xxx‘解决

    xml文件Unable to resolve table xxx 解决 问题 解决 打开IDEA数据库设置 输入所用的数据库名 问题解决
  • 导入数据_导入外部数据的三个技巧

    使用Excel编辑外部数据 如 工作表 文本文件 网页 等 时 为了方便可不必将其复制 粘贴到当前工作表 只需要使用 导入 功能 就能实现外部数据的输入 导入Excel表格数据 如图所示为设计好的 一季度业绩快报 如果想要在其他工作表中引用
  • Fiddler配置显示响应时间和服务器IP

    1 Fiddler如何显示响应时间 1 在Tool bar上面找到Rules gt Customize Rules 或Ctrl R快捷键 2 在class Handlers 里面添加 function BeginRequestTime oS
  • ACE_Message_Block功能和实现浅析

    ACE Message Block实现浅析 1 概述 ACE Message Block是ACE中很重要的一个类 和ACE框架中的重要模式的实现 如ACE Reactor ACE Proactor ACE Stream ACE Task都有