Magic Number 在编程中的使用

MAGIC_NO,好方法!

调试无锁线程池代码过程中,采用了一种很有意思的技术,和大家share一下。她的名字叫:MAGIC NUMBER。

假设我们有了一大块缓冲区(以下称buffer),say,1M,这一块缓冲区将用来满足很多小的顺序到来的内存请求,并且,其释放先后次序和申请次序一致。我们可以考虑采用环形队列来解决这个问题。为了记录分配出去的内存块(以下称buflet)的大小、状态等信息,需要对内存区进行管理,采用如下策略:
    (1)每一小块内存头部划分出一小片来记录这块内存的大小、状态,剩余部分供给用户使用。
 (2)对于新的请求,首先查看当前空闲池是否能够满足需求,如果不能满足,就往前查看,合并可回收的内存区到空闲池(适用于回环了一周的内存使用状况,首次分配不存在该问题),然后进行分配。分配时,新区域可能比用户需要的区域大,此时需要执行split操作,仅仅将用户需要的内存切割出去,剩余的放入空闲池。
 (3) 用户归还内存时将内存块标记为free
如下图所示:


Magic Number 在编程中的使用_第1张图片为了实现上述管理,我们需要编写较为复杂的代码(以上是简化模型,实际应用中有更复杂的需求),这里面有个很麻烦的问题就是:指针偏移!为了通过当前块计算出下一个块的首地址,通过 next_addr = current_addr + current_size计算即可,在复杂应用中,情况往往比较复杂,计算可能出错,此时得到的next_addr是个错误的值,用这个错误的next_addr去得到下一块的大小,显然也是错的,就这样,一步算错了,将一路错下去,无日无夜……
如何及时发现这种错误?不要说仔细地写代码,因为,在程序员心中,有个概念必须牢记:软件错误是无法避免的。在一块内存区上,任意位置都可以是一个buflet的起始,当我们得到一个错误的指针时,它表面上看也可以是一个buflet地址,实际上确实错误的。这个错误无法直接检测!这个问题麻烦了我三四个小时后,终于不堪忍受了,想出使用MAGIC_NO的方法:每生成一个buflet的时候,在其头结构中加入一个magic number。每次通过现有指针计算分配一个buflet时,我们就检查magic number域,如果magic number等于某个确定值MAGIC_NO,则表明指针偏移正确,否则报错,让我们知道指针又错了,反过来及时修改代码。

  1. #define MAGIC_NO   0x4D544C    /**< ascii for LTM */
  2. typedef struct _dfs_api_res_pkt_t
  3. {
  4.      int magic_no;       //Magic Number, 0x4D544C
  5.      buflet_state state; //缓冲区状态:RECEIVED,
  6.      int16 mark;         //标记这个packet是否是有效packet
  7.      int16 block_no;     //所属快号
  8.      u_int32 size;       //packet大小,包括本结构和后面的缓冲区
  9.      char buf[];         //存储UDP包的缓冲区
  10. }
  11. dfs_api_res_pkt_t;
  12. 1 #include 
  13. 3 #define MAGIC_NO    0x4D544C    /**< some ascii, ^.^ */
  14. union m{
  15. 5     int i;
  16. 6     char buf[4];
  17. 7 }x; 
  18. int main()
  19. 10 {
  20. 11     int i = MAGIC_NO;
  21. 12     x.i = i;
  22. 13     // I will show you the magic!
  23. 14     printf("%s/n",x.buf);
  24. 15     return 0;
  25. 16 }
  26. ~                                                       

 

你可能感兴趣的:(Magic Number 在编程中的使用)