1.当数据在过滤器中流动传递的时候,数据是以存储段组的形式组织的。每一个存储段组包含多个存储段,每一个存储段是一种数据类型的抽象。
2.存储段组包含多个存储段
struct apr_bucket
{
APR_RING_ENTRY(apr_bucket) link;
const apr_bucket_type_t *type;
apr_size_t length;
apr_off_t start;
void *data;
void (*free)(void *e);
apr_bucket_alloc_t *list;
};
3.存储段的内部数据结构
在apr_bucket结构中,存储段的实际数据保存在data指针中,数据的长度由length进行标记。
4.所有的外部数据操作都是以存储段组为单位进行的,其数据结构定义在apr_buckets.h中:
5.在内存池中,所有的内存分配最终都由内存池分配子进行.
存储段分配子为apr_bucket_alloc_t,其具体结构定义如下:
struct apr_bucket_alloc_t
{
apr_pool_t *pool; //所有内存的来源内存池
apr_allocator_t *alloctor; //内存池的分配子
node_header_t *freelist; //用于保存回收的小于SAMLL_NODE_SIZE的结点
apr_memnode_t *blocks; //当前存储段所属的内存池结点
};
------------------
typedef struct node_header_t
{
apr_size_t size; //该内存的大小
apr_bucket_alloc_t *alloc; //分配该内存块的存储段内存分配子
apr_memnode_t *memnode; //记录该内存所在的内存结点
struct node_header_t *next; //所有分配的存储段内存通过next指针形成链表
} node_header_t;
6.一旦分配成功,函数将为各个成员赋值,同时调整内存结点的first_avail指针指向实际的可用空间的起点,如下图:
7.存储段的内存使用函数apr_bucket_alloc进行分配。
目前Apache支持多种类型的存储段,比如文件存储段等。分配的原则按照最大的存储段类型进行。
typedef union apr_bucket_structs apr_bucket_structs;
union apr_bucket_structs
{
apr_bucket b; /*Bucket*/
apr_bucket_heap heap; /*Heap*/
apr_bucket_pool pool; /*pool*/
apr_bucket_mmap mmap; /*mmap*/
apr_bucket_file file; /*file*/
};
8.释放存储段内存通过apr_bucket_free函数实现。
9.apr_bucket在Apache中定义如下:
struct apr_bucket
{
APR_RING_ENTRY(apr_bucket) link;
const apr_bucket_type_t *type;
apr_size_t length;
apr_off_t start;
void *data;
void (*free)(void *e);
apr_bucket_alloc_t *list;
};
通常情况下,存储段组用来保存的是一块连续的内存数据,而存储段组中的每一个存储段仅保存整个数据块的一小部分。
10.通用存储段接口和实际存储段的关系
apr_bucket_read | apr_bucket_read函数用于从存储段中读取数据 |
apr_bucket_destroy | 将释放存储段引用的聂村,而不释放存储段本身的内存 |
apr_bucket_split | 使用split函数可以将一个存储段分割成两个存储单。 |
apr_bucket_copy | copy函数可以接受一个存储段,并对其进行精确的复制 |
apr_bucket_setaside | 如果过滤器希望存储段组中的所有数据在下次过滤器调用的时候仍然可以使用,就要调用这个函数。 |
11.存储段类型包括11种数据:
堆存储段 |
内存池存储段 |
文件存储段 |
MMAP存储段 |
管道存储段 |
套接字存储段 |
持久存储段 |
临时存储段 |
刷新存储段 |
流中止(EOS)存储段 |
(1) 堆存储段
typedef struct apr_bucket_heap apr_bucket_heap;
struct apr_bucket_heap
{
apr_bucket_refcount refcount;
char *base;
apr_size_t alloc_len;
void (*free_func)(void *data);
};
(2) 内存池存储段(Pool Bucket)
内存池存储段的读取函数为pool_bucket_read.
struct apr_bucket_pool
{
apr_bucket_heap heap;
const char *base;
apr_pool_t *pool;
apr_bucket_alloc_t *list;
};
(3) 文件存储段(file bucket)
最重要的就是它的读取函数file_bucket_read.
typedef struct apr_bucket_file apr_bucket_file;
struct apr_bucket_file
{
apr_bucket_refcount refcount;
apr_file_t *fd;
apr_pool_t *readpool;
#if APR_HAS_MMAP
int can_mmap;
#endif /* APR_HAS_MMAP*/
};
(4) MMAP存储段(MMAP bucket)
读取数据由接口mmap_bucket_read完成。
typedef struct apr_bucket_mmap apr_bucket_mmap;
struct apr_bucket_mmap
{
apr_bucket_refcount refcount;
apr_mmap_t *mmap;
};
(5) 套接字存储段(Socket Bucket)
读取函数为socket_bucket_read.
(6) 管道存储段(pipe bucket)
读取函数为pipe_bucket_read;
(7) 持久存储段(immortal bucket)
(8) 临时存储段(transient bucket)
(9) 刷新存储段(flush bucket)
(10) 流中止(EOS)存储段
(11) HTTP错误存储段
struct ap_bucket_error
{
apr_bucket_refcount refcount;
int status;
const char *data;
};
12.不管是堆存储段还是文件存储段,都调用apr_bucket_read函数,而不是直接调用heap_bucket_read和file_bucket_read.
apr_bucket_read本身实际是一个宏定义,如下所示:
#define apr_bucket_read(e,str,len,block) (e)->type->read(e, str, len, block)
13.存储段类型判断
14.存储段结点获取
APR_BRIGADE_EMPTY、APR_BRIGADE_SENTINEL、APR_BRIGADE_FIRST、APR_BRIGADE_LAST、APR_BUCKET_NEXT、APR_BUCKET_PREV
15.存储段结点增加和删除
(1) 存储段组头部插入结点(APR_BRIGADE_INSERT_HEAD)
(2) 存储段组尾部插入结点(APR_BRIGADE_INSERT_TAIL)
(3) 存储段组在指定存储段之前插入新的存储段(APR_BUCKET_INSERT_BEFORE)
(4) 存储段组中在指定存储段之后插入一个新的存储段(APR_BUCKET_INSERT_AFTER)
(5) 将一个存储段组追加到另一个存储段组的尾部(APR_BRIGADE_CONCAT)
(6) 存储段结点移除(APR_BUCKET_REMOVE)
16.存储段组操作
(1) 创建存储段组apr_brigade_create
(2) 存储段组的销毁 apr_brigade_cleanup
(3) 存储段组的分裂apr_brigade_split
(4) 偏移分裂apr_brigade_partition
(5) 行分裂apr_brigade_split_line
(6) 统计存储段长度
apr_brigade_length函数用以统计当前存储段组所有的存储段的长度。
(7) 存储段转换 apr_brigade_flatten 用于将存储段组转换为字符串。
(8) 将数据写入存储段组apr_bucket_write.
(9) 一次向存储段组批量写入多个字符串数据apr_bucket_writev
(10) 其余写入函数 apr_brigade_puts、apr_brigade_putc、apr_brigade_putstrs
(11) ap_r*函数写入ap_rputc、ap_rputs、ap_rwrite、ap_rvputs、ap_vrprintf
17.存储段组和过滤器的关系
ap_get_brigade函数正是用于获取下一个过滤器的存储段组内容。