Fastdfs源码分析1----内存库源码解析

fastdfs依赖库

  • libevent
  • libfastcommon

libevnet都知道是一个高性能的轻量级网络库,设计模式为reactor反应堆模式。

Fastdfs原本只有一个工程源码。在某个版本开始走模块化道路,作者把像一些通用的组件从源码中剥离,做成了一个单独的库libfastcommon。该库包含以下内容:字符串处理、内存储 、文件写缓存、base64、ini配置文件解析器、http通讯、链表、id生成器、哈希、md5、线程池、网络event、定时器、avl树、日志

本章讲libfastcommon库里面的内存池,按照我们的传统,先看代码(有详细注释)

/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.fastken.com/ for more detail.
**/
//fast_mpool.h
#ifndef _FAST_MPOOL_H
#define _FAST_MPOOL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "common_define.h"

/* 内存块结构 */
struct fast_mpool_malloc {
	int 			alloc_size;			// 本块的总size
	char *			base_ptr;			// 本块的起始地址
	char *			end_ptr;			// 本块的结束地址
	char *			free_ptr;			// 本块的写指针
	struct fast_mpool_malloc * malloc_next;			// 节点在malloc_chain_head链表的专用挂钩
	struct fast_mpool_malloc * free_next;			// 节点在free_chain_head链表的专用挂钩
};

/* 内存池结构。内粗块挂载内存池的两条链表中 */
struct fast_mpool_man {
	struct fast_mpool_malloc * malloc_chain_head; 		// malloc chain to be freed
	struct fast_mpool_malloc * free_chain_head; 		// free node chain
	int 			alloc_size_once;		// alloc size once, default: 1MB 从某个节点中分配内存的最小单位
	int 			discard_size;			/* discard size, default: 64 bytes,当fast_mpool_malloc节点剩余可分配的.
								内存少于this阀值,则从free_chain_head链表取下,插入 malloc_chain_head链表*/
};

/* 用于存储内存池的统计信息 */
struct fast_mpool_stats {
	int64_t 		total_bytes;			// 内存池总字节数
	int64_t 		free_bytes;			// 内存池空闲字节数
	int 			total_trunk_count;		// 内存池总的块数
	int 			free_trunk_count;		// 内存池剩余块数
};


#ifdef __cplusplus
extern "C"
{
#endif

/**
mpool init
parameters:
	mpool: the mpool pointer
	alloc_size_once: malloc elements once, 0 for malloc 1MB memory once
	discard_size: discard when remain size <= discard_size, 0 for 64 bytes
return error no, 0 for success, != 0 fail
*/
int fast_mpool_init(struct fast_mpool_man * mpool, 
	const int	alloc_size_once, const int discard_size);

/**
mpool destroy
parameters:
	mpool: the mpool pointer
*/
void fast_mpool_destroy(struct fast_mpool_man * mpool);

/**
reset for recycle use
parameters:
	mpool: the mpool pointer
*/
void fast_mpool_reset(struct fast_mpool_man * mpool);

/**
alloc a node from the mpool
parameters:
	mpool: the mpool pointer
	size: alloc bytes
return the alloced ptr, return NULL if fail
*/
void * fast_mpool_alloc(struct fast_mpool_man * mpool, const int size);


/**
alloc and copy string from the mpool
parameters:
	mpool: the mpool pointer
	dest: the dest string (return the alloced memory in dest->str)
	src: the source string
	len: the length of the source string
return error no, 0 for success, != 0 fail
*/
int fast_mpool_strdup_ex(struct fast_mpool_man * mpool, string_t * dest, 
	const char * src, const int len);

/**
alloc and copy string from the mpool
parameters:
	mpool: the mpool pointer
	dest: the dest string (return the alloced memory in dest->str)
	src: the source string
return error no, 0 for success, != 0 fail
*/
static inline int fast_mpool_strdup(struct fast_mpool_man * mpool, 
	string_t *	dest, const char * src) {
	int 			len;

	len 				= (src != NULL) ? strlen(src): 0;
	return fast_mpool_strdup_ex(mpool, dest, src, len);
}

/**
alloc and copy string from the mpool
parameters:
	mpool: the mpool pointer
	dest: the dest string (return the alloced memory in dest->str)
	src: the source string
return error no, 0 for success, != 0 fail
*/
static inline int fast_mpool_strdup2(struct fast_mpool_man * mpool, 
	string_t *	dest, const string_t * src) {
	return fast_mpool_strdup_ex(mpool, dest, src->str, src->len);
}

/**
get stats
parameters:
	mpool: the mpool pointer
	stats: return the stats
return none
*/
void fast_mpool_stats(struct fast_mpool_man * mpool, 
	struct fast_mpool_stats * stats);

/**
log stats info
parameters:
	mpool: the mpool pointer
return none
*/
void fast_mpool_log_stats(struct fast_mpool_man * mpool);

#ifdef __cplusplus
}


#endif

#endif


//fast_mpool.c
#include <errno.h>
#include <sys/resource.h>
#include <pthread.h>
#include <assert.h>
#include "fast_mpool.h"
#include "logger.h"
#include "shared_func.h"
#include "pthread_func.h"
#include "sched_thread.h"

/*
 *mpool:指向待初始化的内存池结构体
 *alloc_size_once:从内存储某块节点申请内存时的最小单位,0则使用默认值1M,否则使用指定值
 *discard_size:内存池某块内存被使用的不足该值后,将该内存块从free_chain_head链表放入malloc_chain_head链表
 */
int fast_mpool_init(struct fast_mpool_man * mpool, 
	const int alloc_size_once, const int discard_size)
{
	if (alloc_size_once > 0) {
		mpool->alloc_size_once = alloc_size_once;
	}
	else {
		mpool->alloc_size_once = 1024 * 1024;
	}

	if (discard_size > 0) {
		mpool->discard_size = discard_size;
	}
	else {
		mpool->discard_size = 64;
	}

	mpool->malloc_chain_head = NULL;
	mpool->free_chain_head = NULL;

	return 0;
}


/*
 *new一个alloc_size内存块节点,并放入内存池
 * 返回0,成功。!=0失败
 */
static int fast_mpool_prealloc(struct fast_mpool_man * mpool, 
	const int alloc_size)
{
	struct fast_mpool_malloc * pMallocNode;
	int 			bytes;

	bytes				= sizeof(struct fast_mpool_malloc) + alloc_size;
	pMallocNode 		= (struct fast_mpool_malloc *)malloc(bytes);

	if (pMallocNode == NULL) {
		logError("file: " __FILE__ ", line: %d, " \ "malloc %d bytes fail, " \ "errno: %d, error info: %s",
			 \ __LINE__, bytes, errno, STRERROR(errno));
		return errno != 0 ? errno: ENOMEM;
	}

	pMallocNode->alloc_size = alloc_size;
	pMallocNode->base_ptr = (char *) (pMallocNode + 1);
	pMallocNode->end_ptr = pMallocNode->base_ptr + alloc_size;
	pMallocNode->free_ptr = pMallocNode->base_ptr;

	pMallocNode->free_next = mpool->free_chain_head;
	mpool->free_chain_head = pMallocNode;

	pMallocNode->malloc_next = mpool->malloc_chain_head;
	mpool->malloc_chain_head = pMallocNode;

	return 0;
}


/*
 *释放mpool指向的内存里面,全部的内存块
 */
void fast_mpool_destroy(struct fast_mpool_man * mpool)
{
	struct fast_mpool_malloc * pMallocNode;
	struct fast_mpool_malloc * pMallocTmp;

	if (mpool->malloc_chain_head == NULL) {
		return;
	}

	pMallocNode 		= mpool->malloc_chain_head;

	while (pMallocNode != NULL) {
		pMallocTmp			= pMallocNode;
		pMallocNode 		= pMallocNode->malloc_next;

		free(pMallocTmp);
	}

	mpool->malloc_chain_head = NULL;
	mpool->free_chain_head = NULL;
}


/*
 *将pMallocNode内存块,从mpool内存池的free_chain_head链表移除
 */
static void fast_mpool_remove_free_node(struct fast_mpool_man * mpool, 
	struct fast_mpool_malloc * pMallocNode)
{
	struct fast_mpool_malloc * previous;

	if (mpool->free_chain_head == pMallocNode) {
		mpool->free_chain_head = pMallocNode->free_next;
		return;
	}

	previous			= mpool->free_chain_head;

	while (previous->free_next != NULL) {
		if (previous->free_next == pMallocNode) {
			previous->free_next = pMallocNode->free_next;
			return;
		}

		previous			= previous->free_next;
	}
}


/*
 *从mpool内存池的pMallocNode节点分配size个字节的内存区域,返回起始指针。
 *如果pMallocNode剩余可分配空间不足size则返回NULL。
 *如果pMallocNode分配后,剩余空间不足discard_size字节(64)则将节点从内存池的free_chain_head链表取下,插入malloc_chain_head链表
*/
static inline void * fast_mpool_do_alloc(struct fast_mpool_man * mpool, 
	struct fast_mpool_malloc * pMallocNode, const int size)
{
	void *			ptr;

	if ((int) (pMallocNode->end_ptr - pMallocNode->free_ptr) >= size) {
		ptr 				= pMallocNode->free_ptr;
		pMallocNode->free_ptr += size;

		if ((int) (pMallocNode->end_ptr - pMallocNode->free_ptr) <= mpool->discard_size) {
			fast_mpool_remove_free_node(mpool, pMallocNode);
		}

		return ptr;
	}

	return NULL;
}


/*
 *从mpool申请size个字节
 *返回!=NULL,内存起始地址。==NULL,申请失败
 */
void * fast_mpool_alloc(struct fast_mpool_man * mpool, const int size)
{
	struct fast_mpool_malloc * pMallocNode;
	void *			ptr;
	int 			result;
	int 			alloc_size;

	// 从现有free_chain_head链表分配size字节
	pMallocNode 		= mpool->free_chain_head;

	while (pMallocNode != NULL) {
		if ((ptr = fast_mpool_do_alloc(mpool, pMallocNode, size)) != NULL) {
			return ptr;
		}

		pMallocNode 		= pMallocNode->free_next;
	}

	// 确定分配的最小单位
	if (size < mpool->alloc_size_once) {
		alloc_size			= mpool->alloc_size_once;
	}
	else {
		alloc_size			= size;
	}

	// new一个alloc_size内存块节点,并放入内存池,再次从内存池申请size个内存区域
	if ((result = fast_mpool_prealloc(mpool, alloc_size)) == 0) {
		return fast_mpool_do_alloc(mpool, mpool->free_chain_head, size);
	}

	return NULL;
}


/*
 *从内存池分配一片内存区域给dest字符串使用。拷贝[src,src+len]到dest
 */
int fast_mpool_strdup_ex(struct fast_mpool_man * mpool, string_t * dest, 
	const char * src, const int len)
{
	dest->str			= (char *)fast_mpool_alloc(mpool, len);

	if (dest->str == NULL) {
		logError("file: " __FILE__ ", line: %d, "
		"alloc %d bytes from mpool fail", __LINE__, len);
		return ENOMEM;
	}

	if (len > 0) {
		memcpy(dest->str, src, len);
	}

	dest->len			= len;
	return 0;
}


/*
 *内存池malloc_chain_head链表,全部覆盖free_chain_head链表
 */
void fast_mpool_reset(struct fast_mpool_man * mpool)
{
	struct fast_mpool_malloc * pMallocNode;
	mpool->free_chain_head = NULL;
	pMallocNode 		= mpool->malloc_chain_head;

	while (pMallocNode != NULL) {
		pMallocNode->free_ptr = pMallocNode->base_ptr;

		pMallocNode->free_next = mpool->free_chain_head;
		mpool->free_chain_head = pMallocNode;

		pMallocNode 		= pMallocNode->malloc_next;
	}
}


/*
 *统计内存池状态,写入stats结构体
 */
void fast_mpool_stats(struct fast_mpool_man * mpool, 
	struct fast_mpool_stats * stats)
{
	struct fast_mpool_malloc * pMallocNode;
	stats->total_bytes	= 0;
	stats->free_bytes	= 0;
	stats->total_trunk_count = 0;
	stats->free_trunk_count = 0;

	pMallocNode 		= mpool->malloc_chain_head;

	while (pMallocNode != NULL) {
		stats->total_bytes	+= pMallocNode->alloc_size;
		stats->free_bytes	+= (int) (pMallocNode->end_ptr - pMallocNode->free_ptr);
		stats->total_trunk_count++;

		pMallocNode 		= pMallocNode->malloc_next;
	}

	pMallocNode 		= mpool->free_chain_head;

	while (pMallocNode != NULL) {
		stats->free_trunk_count++;
		pMallocNode 		= pMallocNode->free_next;
	}
}


void fast_mpool_log_stats(struct fast_mpool_man * mpool)
{
	struct fast_mpool_stats stats;
	fast_mpool_stats(mpool, &stats);
	logInfo("alloc_size_once: %d, discard_size: %d, "
	"bytes: {total: %" PRId64 ", free: %" PRId64 "}, "
	"trunk_count: {total: %d, free: %d}", 
		mpool->alloc_size_once, mpool->discard_size, 
		stats.total_bytes, stats.free_bytes, 
		stats.total_trunk_count, stats.free_trunk_count);
}



总结:显然,Fastdfs的内存储的设计,部分参照了linux kernel内存的设计思想(即malloc()、free()的内核源码):
内存管理分为两个部分:元数据部分管理数据部分的申请和释放、数据部分存储数据。应用层只能看到数据部分而感知不到元数据部分的存在。

余庆大神写的代码,简洁干净,几乎没有丝毫冗余。 如果非要提点建议,我认为在fastdfs作为文件存储时,其内存池是作为各种组件的基础。特别是对tracker的性能有影响。 当前最新版本的libfastcommon库中内存储的构建,起始是两条链表作为内存块的寻道载体。众所周知,链表的索引是极其消耗资源的,会整体拉低tracker的响应时间。 因此,在下的看法是: 用linux kernel的其他设计思想(文件系统里面大量使用的位图、散列表等),来修复这种设计上面的缺陷。 位图的高效率,可以使一个500G的磁盘快速的定位数据,那么也可以用作内存池里面管理内存块。 关于具体代码,需要经过编辑和测试后,如果可以使用,在下就会将代码pull 到github上面。

发布了75 篇原创文章 · 获赞 71 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/jacky128256/article/details/104448152