免费注册 查看新帖 |

ChinaUnix.net

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10674 | 回复: 19

发一个能在开辟好的共享内存内进行alloc/free的函数集(mm.c) [复制链接]

论坛徽章:
0
发表于 2009-06-13 10:44 |显示全部楼层
通常情况类似于内存池的资料比较多, 但之前开发东西在多进程/多线程混合的情况下寻求缓存解决方案时, 采用了共享内存.

而能对共享内存进行动态再分配的库有一个称之为 libmm 的模块, 但资料很少, 没有采用它. 后来我自己从 eAccelerator
(php的一个缓存扩展) 中找到一个 mm.c , 对它进行了简化和修改, 生成了一个我自己在用的 mm.c/mm.h 现在发布如下,

供有需要的参考, 但应该特别注意的是, 我这里申请的共享内存是一个匿名的内存映射, 只能共享于有亲属关系的进程中, 因
而可以在内存中再直接存放指针数据, 这一点比较方便.

而如果想用在无关系的进程之间, 那必须对代码进行修改, 支持有名的 mmap 或 IPC-shm 以及 IPC-sem 的, 并且在
mmap() 时指定同主进程相当的起始地址, 这个做法非常不值得推荐, mmap() 的起始捆绑地址一般应当由系统进行分配比较好.

好了, 代码如下, 供有需要或有兴趣的参考, 内存申请是由一个线性链表来管理的, 应当能满足大部分应用了

1. mm.h
/**
Libmm replacement used by cache design of FTPHP-searchd
Some source codes cut from eAccelerator/PHP

共享内存管理, 改自 eAccelerator 中的 mm.c, 采用 sem 信号量加锁, 线程安全!
使用时包含 mm.h 这个头文件即可, 数据类型 MM 就是这块共享内存的操作句柄类型.

常用 API 介绍:
1. MM *mm_create(size_t size);
   创建 size (单位bytes) 大小的共享内存空间(此为匿名mmap, 用于父子关系之间的进程)
   成功返回 MM 指针, 失败返回 NULL

2. void mm_destroy(MM *mm);
   销毁 mm, 它同时销毁所有的信号量锁, 多进程模型中只允许一次调用, 子进程退出时
   不必调用该函数, 以免破解整个全局的 mm 结构.

3. mm_lock(MM *mm); mm_unlock(MM *mm);
   对整个 mm 进行加锁或解锁.
   注意: 在 mm_malloc 和 mm_free 内部隐蔽地调用了 mm_lock/mm_unlock, 所以务必
         不能已加锁代码段里使用 mm_malloc/mm_free, 否则会造成死锁, 应当改用
         mm_malloc_nolock/mm_free_nolock

4. mm_lock1(MM *mm); mm_unlock1(MM *mm); ... mm_lock4(MM *mm); mm_unlock4(MM *mm);
   ... 这 4 组加锁/解锁之间互不影响, 可用于各类区间操作需加锁时使用.

5. void *mm_malloc(MM *mm, size_t size);
   void mm_free(MM *mm, void *p);

   申请和释放内存, 带全局锁

6. void *mm_malloc_nolock(MM *mm, size_t size);
   void mm_malloc_free(MM *mm, void *p);
   同上, 但不上锁


罕用的 API:
1. size_t mm_size(MM *mm);
   获取 mm 在创建时的 size.

2. int mm_protect(MM *mm, int mode);
   保护 mm , 内部调用 mprotect()
   mode 值为 MM_PROT_NONE, MM_PROT_READ, MM_PROT_WRITE, MM_PROT_EXEC 的组合

3. size_t mm_maxsize(MM *mm); size_t mm_avaiable(MM *mm);
   分别返回当前能申请到的最大内存长度和当前可用内存空间余额

4. size_t mm_sizeof(MM *mm, void *p);
   如果 p 为 mm_malloc 申请的内存, 则该调用可以返回申请时的长度

$Id: mm.h,v 1.3 2009/05/12 16:01:25 hightman Exp $
*/



#ifndef __FTPHP_MM_20090527_H__
#define    __FTPHP_MM_20090527_H__

#include <sys/types.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef MM
#define    MM void
#endif

#define    MM_SEM_NUM        5
#define    mm_lock(x)        _mm_lock(x,0)
#define    mm_unlock(x)    _mm_unlock(x,0)
#define    mm_lock1(x)        _mm_lock(x,1)
#define    mm_unlock1(x)    _mm_unlock(x,1)
#define    mm_lock2(x)        _mm_lock(x,2)
#define    mm_unlock2(x)    _mm_unlock(x,2)
#define    mm_lock3(x)        _mm_lock(x,3)
#define    mm_unlock3(x)    _mm_unlock(x,3)
#define    mm_lock4(x)        _mm_lock(x,4)
#define    mm_unlock4(x)    _mm_unlock(x,4)

#define MM_PROT_NONE 1
#define MM_PROT_READ 2
#define MM_PROT_WRITE 4
#define MM_PROT_EXEC 8

MM *mm_create(size_t size);                // create mm by mmap

size_t mm_size(MM *mm);
void mm_destroy(MM *mm);
int _mm_lock(MM *mm, int num);            // lock this mm

int _mm_unlock(MM *mm, int num);
int mm_protect(MM *mm, int mode);        // protect the mm to avoid read|write?

size_t mm_available(MM *mm);
size_t mm_maxsize(MM *mm);
void *mm_malloc(MM *mm, size_t size);
void mm_free(MM *mm, void *p);
void *mm_malloc_nolock(MM *mm, size_t size);
void mm_free_nolock(MM *mm, void *p);
size_t mm_sizeof(MM *mm, void *x);

#ifdef __cplusplus
}
#endif

#endif


2. mm.c
/**
Libmm replacement used by cache design of FTPHP-searchd
Some source codes cut from eAccelerator/PHP

$Id: mm.c,v 1.3 2009/05/12 16:01:25 hightman Exp $
*/


#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <errno.h>

typedef struct mm_mutex {
  int semid;
} mm_mutex;

typedef struct mm_free_bucket {
  size_t size;
  struct mm_free_bucket *next;
} mm_free_bucket;

typedef struct mm_core {
  size_t size;
  void *start;
  size_t available;
  mm_mutex *lock;
  mm_free_bucket *free_list;
} mm_core;

typedef union mm_mem_head {
  size_t size;
  double a1;
  int (*a2)(int);
  void *a3;
} mm_mem_head;

#define MM_SIZE(sz)        (sizeof(mm_mem_head)+(sz))
#define PTR_TO_HEAD(p)    (((mm_mem_head *)(p)) - 1)
#define HEAD_TO_PTR(p)    ((void *)(((mm_mem_head *)(p)) + 1))
#define MM mm_core
#define    MM_WORD    mm_mem_head

#if (defined (__GNUC__) && __GNUC__ >= 2)
#    define MM_PLATFORM_ALIGNMENT (__alignof__ (MM_WORD))
#else
#    define MM_PLATFORM_ALIGNMENT (sizeof(MM_WORD))
#endif
#define MM_ALIGN(n) (void*)((((size_t)(n)-1) & ~(MM_PLATFORM_ALIGNMENT-1)) + MM_PLATFORM_ALIGNMENT)

#include "mm.h"

/* MM-lock implement */
static int mm_init_lock(mm_mutex *lock) {
  union semun arg;
  int n = MM_SEM_NUM;

  if ((lock->semid = semget(IPC_PRIVATE, n, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0) {
    return 0;
  }

  arg.val = 1;
  while (n--) {
    if (semctl(lock->semid, n, SETVAL, arg) < 0) {
     semctl(lock->semid, n, IPC_RMID, 0);
    }
  }
  return 1;
}

static int mm_do_lock(mm_mutex *lock, int num) {
  struct sembuf op;
  int rc;

  op.sem_num = (unsigned short)num;
  op.sem_op = -1;
  op.sem_flg = SEM_UNDO;

  do {
    rc = semop(lock->semid, &op, 1);
  } while (rc < 0 && errno == EINTR);

  return (rc == 0);
}

static int mm_do_unlock(mm_mutex *lock, int num) {
  struct sembuf op;

  op.sem_num = (unsigned short)num;
  op.sem_op = 1;
  op.sem_flg = SEM_UNDO;
  if (!semop(lock->semid, &op, 1))
     return 1;
  return 0;
}

static void mm_destroy_lock(mm_mutex *lock) {
  int n = MM_SEM_NUM;
  while (n--) {
    semctl(lock->semid, n, IPC_RMID, 0);
  }
}

int _mm_lock(MM *mm, int num) {
  return mm_do_lock(mm->lock, num);
}

int _mm_unlock(MM *mm, int num) {
  return mm_do_unlock(mm->lock, num);
}

/* shared memory implement */
static MM *mm_create_shm(size_t size) {
  MM *p;

  p = (MM *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
  if (p != (MM *)-1) {
    p->size = size;
    p->start = (char *)p + sizeof(MM);
  }
  return p;
}

static void mm_destroy_shm(MM *mm) {
  if (mm != NULL && mm != (MM *)-1) {
    munmap(mm, mm->size);
  }
}

static void mm_init(MM *mm) {
  mm->start = MM_ALIGN(mm->start);
  mm->lock = mm->start;
  mm->start = MM_ALIGN((void *)(((char *)(mm->start)) + sizeof(mm_mutex)));
  mm->available = mm->size - (((char *)(mm->start))-(char *)mm);
  mm->free_list = (mm_free_bucket *)mm->start;
  mm->free_list->size = mm->available;
  mm->free_list->next = NULL;
}

void *mm_malloc_nolock(MM *mm, size_t size) {
  if (size > 0) {
    mm_mem_head *x = NULL;
    size_t realsize = (size_t) MM_ALIGN(MM_SIZE(size));
    if (realsize <= mm->available) {
      /* Search for free bucket */
      mm_free_bucket *p = mm->free_list;
      mm_free_bucket *q = NULL;
      mm_free_bucket *best = NULL;
      mm_free_bucket *best_prev = NULL;
      while (p != NULL) {
        if (p->size == realsize) {
          /* Found free bucket with the same size */
          if (q == NULL) {
            mm->free_list = p->next;
            x = (mm_mem_head *)p;
          } else {
            q->next = p->next;
            x = (mm_mem_head *)p;
          }
          break;
        } else if (p->size > realsize && (best == NULL || best->size > p->size)) {
          /* Found best bucket (smallest bucket with the grater size) */
          best = p;
          best_prev = q;
        }
        q = p;
        p = p->next;
      }
      if (x == NULL && best != NULL) {
        if (best->size-realsize < sizeof(mm_free_bucket)) {
          realsize = best->size;
          x = (mm_mem_head *)best;
          if (best_prev == NULL) {
            mm->free_list = best->next;
          } else {
            best_prev->next = best->next;
          }
        } else {
          if (best_prev == NULL) {
            mm->free_list = (mm_free_bucket *)((char *)best + realsize);
            mm->free_list->size = best->size-realsize;
            mm->free_list->next = best->next;
          } else {
            best_prev->next = (mm_free_bucket *)((char *)best + realsize);
            best_prev->next->size = best->size-realsize;
            best_prev->next->next = best->next;
          }
          best->size = realsize;
          x = (mm_mem_head *)best;
        }
      }
      if (x != NULL) {
        mm->available -= realsize;
      }
    }
    if (x != NULL) {
      return HEAD_TO_PTR(x);
    }
  }
  return NULL;
}

void mm_free_nolock(MM *mm, void *x) {
  if (x != NULL) {
    if (x >= mm->start && x < (void *)((char *)mm + mm->size)) {
      mm_mem_head *p = PTR_TO_HEAD(x);
      size_t size = p->size;
      if ((char *)p+size <= (char *)mm + mm->size) {
        mm_free_bucket *b = (mm_free_bucket *)p;
        b->next = NULL;
        if (mm->free_list == NULL) {
          mm->free_list = b;
        } else {
          mm_free_bucket *q = mm->free_list;
          mm_free_bucket *prev = NULL;
          mm_free_bucket *next = NULL;
          while (q != NULL) {
            if (b < q) {
              next = q;
              break;
            }
            prev = q;
            q = q->next;
          }
          if (prev != NULL && (char *)prev+prev->size == (char *)b) {
            if ((char *)next == (char *)b+size) {
              /* merging with prev and next */
              prev->size += size + next->size;
              prev->next = next->next;
            } else {
              /* merging with prev */
              prev->size += size;
            }
          } else {
            if ((char *)next == (char *)b+size) {
              /* merging with next */
              b->size += next->size;
              b->next = next->next;
            } else {
              /* don't merge */
              b->next = next;
            }
            if (prev != NULL) {
              prev->next = b;
            } else {
              mm->free_list = b;
            }
          }
        }
        mm->available += size;
      }
    }
  }
}

size_t mm_maxsize(MM *mm) {
  size_t ret = MM_SIZE(0);
  mm_free_bucket *p;

  if (!mm_lock(mm)) {
    return 0;
  }

  p = mm->free_list;
  while (p != NULL) {
    if (p->size > ret) {
      ret = p->size;
    }
    p = p->next;
  }
  mm_unlock(mm);
  return ret - MM_SIZE(0);
}

void *mm_malloc(MM *mm, size_t size) {
  void *ret;

  if (!mm_lock(mm)) {
    return NULL;
  }
  ret = mm_malloc_nolock(mm,size);
  mm_unlock(mm);
  return ret;
}

void mm_free(MM *mm, void *x) {
  mm_lock(mm);
  mm_free_nolock(mm, x);
  mm_unlock(mm);
}

MM *mm_create(size_t size) {
  MM *p;

  if (size == 0) {
    size = 32 * 1024 * 1024;
  }
  p = mm_create_shm(size);
  if (p == (MM *)-1) {
    return NULL;
  }

  mm_init(p);
  if (!mm_init_lock(p->lock)) {
    mm_destroy_shm(p);
    return NULL;
  }
  return p;
}

void mm_destroy(MM *mm) {
  if (mm != NULL) {
    mm_destroy_lock(mm->lock);
    mm_destroy_shm(mm);
  }
}

size_t mm_size(MM *mm) {
  if (mm != NULL) {
    return mm->size;
  }
  return 0;
}

size_t mm_sizeof(MM *mm, void *x) {
  mm_mem_head *p;
  size_t ret;

  if (mm == NULL || x == NULL || !mm_lock(mm)) {
    return 0;
  }
  p = PTR_TO_HEAD(x);
  ret = p->size;
  mm_unlock(mm);
  return ret;
}

size_t mm_available(MM *mm) {
  size_t available;

  if (mm != NULL && mm_lock(mm)) {
    available = mm->available;
    mm_unlock(mm);
    return available;
  }
  return 0;
}

论坛徽章:
0
发表于 2009-06-13 22:31 |显示全部楼层
好东西 先收藏 慢慢看

论坛徽章:
0
发表于 2009-06-13 22:55 |显示全部楼层
牛啊,支持

论坛徽章:
0
发表于 2009-06-14 09:08 |显示全部楼层
不错,顶

论坛徽章:
3
天蝎座
日期:2014-10-25 13:44:312015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:48:31
发表于 2009-06-14 10:03 |显示全部楼层
提一个小小小的意见,

最好能把单向链表的操作封装起来。(我对数据结构理解能力很差,看别人代码的时候,总会都要想想这样访问会不会出问题)

论坛徽章:
0
发表于 2009-06-14 14:16 |显示全部楼层
不错,有时间研究下。

论坛徽章:
0
发表于 2009-06-14 20:57 |显示全部楼层
原帖由 ilex 于 2009-6-14 10:03 发表
提一个小小小的意见,

最好能把单向链表的操作封装起来。(我对数据结构理解能力很差,看别人代码的时候,总会都要想想这样访问会不会出问题)


alloc/free 这一段代码都是剪自 eAccelerator, 不过我每行每句都读通了, 确认放心了呵. 我有和你类似的问题, 不光数据结构这块, 每次用现成的库函数总是产生一些类似的焦虑.

这个链表设计得还是不错的, 把可用空间的片段的头部直接存储链表结构..

论坛徽章:
0
发表于 2009-06-26 18:40 |显示全部楼层
这个要支持,好好学习下~~

论坛徽章:
0
发表于 2009-06-30 17:02 |显示全部楼层
有使用示例吗?最好给个使用示例

论坛徽章:
0
发表于 2012-03-23 18:30 |显示全部楼层
回复 1# hightman


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

基于案例的 SQL 优化实战训练营

讲师:中电福富特级专家梁敬彬,参与本次课程培训,你将收获:
1. 能编写出较为高效的 SQL;
2. 能解决70%以上的数据库常见优化问题;
3. 能得到老师提供的高效的相关工具和解决方案;
4. 能举一反三,收获不仅仅是 SQL 优化。
现在购票享受8.8折优惠!
----------------------------------------
优惠时间:2019年3月20日前

大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP