typedef struct pool pool;
typedef struct pool ap_pool;
struct pool {
union block_hdr *first;
union block_hdr *last;
struct cleanup *cleanups;
struct process_chain *subprocesses;
struct pool *sub_pools;
struct pool *sub_next;
struct pool *sub_prev;
struct pool *parent;
char *free_first_avail;
};
union block_hdr *first;
union block_hdr *last;
struct cleanup *cleanups;
struct process_chain *subprocesses;
struct pool *sub_pools;
struct pool *sub_next;
struct pool *sub_prev;
struct pool *parent;
char *free_first_avail;
};
其中union block_hdr是
union block_hdr {
union align a;
struct {
char *endp;
union block_hdr *next;
char *first_avail;
} h;
};
union align a;
struct {
char *endp;
union block_hdr *next;
char *first_avail;
} h;
};
而union align又是
union align {
char *cp;
void (*f) (void);
long l;
FILE *fp;
double d;
};
union align {
char *cp;
void (*f) (void);
long l;
FILE *fp;
double d;
};
内存块管理API
malloc_block
从系统申请sizeof(block_hdr)+size大小的内存
blok->h.first_avail=(char *)(blok+1);
blok->h.endp = size + blok->h.first_avail;
free_blocks
将一条内存块链加到
block
_freelist链表开头,该过程会将该链表中所有的h.first_avail置为内存块开头即 (char *)(blok + 1)。
new_block
申请一块内存块,先从block_freelist中找,线性查找,找到第一个满足h.endp-h.first_avail>size+MINFREE条件的返回,并从freelist中去掉,否则调用malloc_block从系统申请,申请的时候也有最小块限制MINALLOC
bytes_in_block_list
返回某个内存块链的总长度(不是可用长度,而是总长度)
pool管理,pool分配大小对齐于align结构体的倍数
#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ))
#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
API
ap_block_alarms
ap_unblock_alarms
包装。。临界区?
pool *ap_make_sub_pool(pool *)
创建一个子pool,参数为NULL的时候无父pool,父pool.sub_pools是一个双向链表,新的子pool加入到链表头,free_first_avail指向第一个blok分配了pool结构体之后的内存。
ap_init_alloc
分配全局的父pool
static pool *permanent_pool;
void ap_cleanup_alloc(void)
do nothing
ap_clear_pool && ap_destroy_pool
从最底层的子pool开始,调用
run_cleanups(a->cleanups)
free_proc_chain(a->subprocesses);
free_blocks(a->first->h.next);
destroy会把含有pool结构体的block也free掉
free_blocks(a->first);
clear会保留这个结构体所在的block,其他的free掉
总体逻辑
destroy(a)的过程,先clear(a),然后如果a有父pool,那么修改a的父pool的sub_pools链表,以及a自身的sub_pre,sub_next,将a从子pool链中移除。
clear(a)的过程,先destroy掉a的所有子pool,然后调用a的
run_cleanups(a->cleanups);
free_proc_chain(a->subprocesses);
free_blocks(a->first->h.next);
像一个双函数递归过程。
其中
static void run_cleanups(struct cleanup *c)
{
while (c) {
(*c->plain_cleanup) (c->data);
c = c->next;
}
}
{
while (c) {
(*c->plain_cleanup) (c->data);
c = c->next;
}
}
struct cleanup {
void *data;
void (*plain_cleanup) (void *);
void (*child_cleanup) (void *);
struct cleanup *next;
};
void *data;
void (*plain_cleanup) (void *);
void (*child_cleanup) (void *);
struct cleanup *next;
};
struct process_chain {
pid_t pid;
enum kill_conditions kill_how;
struct process_chain *next;
};
pid_t pid;
enum kill_conditions kill_how;
struct process_chain *next;
};
enum kill_conditions {
kill_never, /* process is never sent any signals */
kill_always, /* process is sent SIGKILL on pool cleanup */
kill_after_timeout, /* SIGTERM, wait 3 seconds, SIGKILL */
just_wait, /* wait forever for the process to complete */
kill_only_once /* send SIGTERM and then wait */
};
kill_never, /* process is never sent any signals */
kill_always, /* process is sent SIGKILL on pool cleanup */
kill_after_timeout, /* SIGTERM, wait 3 seconds, SIGKILL */
just_wait, /* wait forever for the process to complete */
kill_only_once /* send SIGTERM and then wait */
};
内存分配api
API_EXPORT(void *) ap_palloc(struct pool *a, int reqsize)
先在pool的最后一个block上找,可用空间是否够,不够就申请一块新的block挂在pool的block链表之后
API_EXPORT(void *) ap_pcalloc(struct pool *a, int size)
调用上述函数之后,调用memset将内存设置为0
API_EXPORT(char *) ap_pstrdup(struct pool *a, const char *s)
在pool上分配strlen(a)+1内存后,把字符串a memcpy 过去
API_EXPORT(char *) ap_pstrndup(struct pool *a, const char *s, int n)
在pool上分配n+1大小的内存,然后从a中memcpy n个字符过去,最后补0
API_EXPORT_NONSTD(char *) ap_pstrcat(pool *a,...)
在pool上分配字符串总长度+1大小的内存,然后把各个字符串cat成一个。
apache格式化输出API
内部函数
static int psprintf_flush(ap_vformatter_buff *vbuff)
自动扩展vbuff,若空间不足则申请新的block并free掉旧的
static char *conv_10(register wide_int num, register bool_int is_unsigned,register bool_int *is_negative, char *buf_end,register int *len)
转换成十进制字符串,从右往左转换,返回开头指针
调用的时候buf_end给的是&num_buf[NUM_BUF_SIZE],没有判断越界,NUM_BUF_SIZE=512,加上精度和'/0'一共最多512个字符
static char *conv_p2(register u_wide_int num, register int nbits,char format, char *buf_end, register int *len)
转换成2的nbits次方进制字符串
register int mask = (1 << nbits) - 1;
do {
*--p = digits[num & mask];
num >>= nbits;
}
while (num);
static char *conv_fp(register char format, register double num,boolean_e add_dp, int precision, bool_int *is_negative,char *buf, int *len)
根据format为f,e,E,将num转化为字符串
static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
转化成IP:PORT字符串
%pI %pA
API_EXPORT(char *) ap_pvsprintf(pool *p, const char *fmt, va_list ap)
pool接口的vsprintf函数,从pool的最后一个block中取空间,没有则扩展,扩展的时候把该block内容copy到新block上,然后free掉旧pool,调用该函数的时候不能对该block进行访问等
API_EXPORT_NONSTD(char *) ap_psprintf(pool *p, const char *fmt, ...)
格式化输出,调用上述接口
array分配API
typedef struct {
ap_pool *pool;
int elt_size;
int nelts;
int nalloc;
char *elts;
} array_header;
ap_pool *pool;
int elt_size;
int nelts;
int nalloc;
char *elts;
} array_header;
内部函数
static void make_array_core(array_header *res, pool *p, int nelts, int elt_size)
该函数调用ap_pcalloc()从pool p中分配nelts * elt_size大小的内存。设置res->elts为内存起始地址。res->pool为p,elt_size设置为elt_size(元素大小),nalloc设置为元素个数,nelts设置为0(活动元素个数)
API_EXPORT(array_header *) ap_make_array(pool *p, int nelts, int elt_size)
该函数从p中创建一个array_header,然后调用上述接口创建array,返回指针
API_EXPORT(void *) ap_push_array(array_header *arr)
该函数首先检查容量是否超了,超了的话重新申请一块内存,大小为原先的2倍,并把原先的数据复制过去。elts指向新数据地址,然后增加nelts,返回可以用于写的新元素的地址。
API_EXPORT(void) ap_array_cat(array_header *dst, const array_header *src)
合并src到dst,空间不够就*2因子增加,直到够了为止
API_EXPORT(array_header *) ap_copy_array(pool *p, const array_header *arr)
从p中分配一个新的arr,大小同旧的,复制数据,返回array_header指针
static ap_inline void copy_array_hdr_core(array_header *res,const array_header *arr)
内部函数,拷贝array_header到res
API_EXPORT(array_header *) ap_copy_array_hdr(pool *p, const array_header *arr)
从p中分配一个array_header并从arr中拷贝header信息
API_EXPORT(array_header *) ap_append_arrays(pool *p,const array_header *first,const array_header *second)
这个函数有可能是在first的后面加上second,然后返回一个first的array_header的拷贝的指针,也有可能返回first和second合并之后的一个新的array的header的指针
取决于first的空间够不够。总之second是不会变的
API_EXPORT(char *) ap_array_pstrcat(pool *p, const array_header *arr,const char sep)
从pool中分配一块内存,把array中所有的字符串合到一起,如果指定了sep,串与串直接会加上sep字符。arr中必须全是字符串。
Table API
typedef struct {
char *key;
char *val;
} table_entry;
char *key;
char *val;
} table_entry;
#define table_push(t) ((table_entry *) ap_push_array(&(t)->a))
table实际上就是一个table_entry为元素的array,上面的宏返回一个新增的可写的table_entry指针,该table_entry是一个有两个指针的结构体。
struct table {
array_header a;
};
array_header a;
};
API_EXPORT(table *) ap_make_table(pool *p, int nelts)
从pool中申请一个table,然后申请一个nelts个元素的array,元素是table_entry
API_EXPORT(table *) ap_copy_table(pool *p, const table *t)
从p中dump出一份table
API_EXPORT(void) ap_clear_table(table *t)
元素活动个数清0
API_EXPORT(const char *) ap_table_get(const table *t, const char *key)
获得table_entry.key为key的table_entry.value
API_EXPORT(void) ap_table_set(table *t, const char *key, const char *val)
从开头查找,找到的第一个key,调用ap_pstrdup(t->a.pool, val);赋给value,再找到的删掉,后面的元素前移,元素数减少,没找到的话增加一个元素。
API_EXPORT(void) ap_table_setn(table *t, const char *key, const char *val)
不dump直接把val赋给value
API_EXPORT(void) ap_table_unset(table *t, const char *key)
删掉key的table_entry
API_EXPORT(void) ap_table_merge(table *t, const char *key, const char *val)
将val指向的字符串merge到原key的字符串后,中间", "分隔
API_EXPORT(void) ap_table_add(table *t, const char *key, const char *val)
API_EXPORT(void) ap_table_addn(table *t, const char *key, const char *val)
新增元素,dup或者不dup
API_EXPORT(table *) ap_overlay_tables(pool *p, const table *overlay, const table *base)
新分配一个res结构体,overlay和base合并到一起到res,有可能改变overlay
API_EXPORT_NONSTD(void) ap_table_do(int (*comp) (void *, const char *, const char *),void *rec, const table *t,...)
对可变参数数中所有的key,遍历table,对table中该key的表项调用函数comp(rec, key, value),重复的全部调用
API_EXPORT(void) ap_overlap_tables(table *a, const table *b, unsigned flags)
将a和b合并排序,稳定排序若flag中指定了AP_OVERLAP_TABLES_MERGE那么相同的key的value合并,否则不合并,将结果写入表a中
CLEAN UP
struct cleanup {
void *data;
void (*plain_cleanup) (void *);
void (*child_cleanup) (void *);
struct cleanup *next;
};
void *data;
void (*plain_cleanup) (void *);
void (*child_cleanup) (void *);
struct cleanup *next;
};
API
API_EXPORT(void) ap_register_cleanup_ex(pool *p, void *data,
void (*plain_cleanup) (void *),
void (*child_cleanup) (void *),
int (*magic_cleanup) (void *))
void (*plain_cleanup) (void *),
void (*child_cleanup) (void *),
int (*magic_cleanup) (void *))
注册cleanup回调函数,简单赋值,从pool上分配cleanup结构体,加入pool的cleanup链表头
magic_cleanup不为空,则调用magic_cleanup(data);
API_EXPORT(void) ap_register_cleanup(pool *p, void *data,
void (*plain_cleanup) (void *),
void (*child_cleanup) (void *))
void (*plain_cleanup) (void *),
void (*child_cleanup) (void *))
调用上述接口,magic_cleanup为空
API_EXPORT(void) ap_kill_cleanup(pool *p, void *data, void (*cleanup) (void *))
从表头开始找,去掉同时满足data==data c->plain_cleanup == cleanup的第一个cleanup结构体。
API_EXPORT(void) ap_run_cleanup(pool *p, void *data, void (*cleanup) (void *))
运行一次 (*cleanup)(data),然后从链表中去掉
static void run_cleanups(struct cleanup *c)
运行结构体cleanup链表中的所有plain_cleanup
static void run_child_cleanups(struct cleanup *c)
运行结构体cleanup链表中所有的child_cleanup
static void cleanup_pool_for_exec(pool *p)
{
run_child_cleanups(p->cleanups);
p->cleanups = NULL;
for (p = p->sub_pools; p; p = p->sub_next)
cleanup_pool_for_exec(p);
}
{
run_child_cleanups(p->cleanups);
p->cleanups = NULL;
for (p = p->sub_pools; p; p = p->sub_next)
cleanup_pool_for_exec(p);
}
递归调用所有的run_child_cleanups
API_EXPORT(void) ap_cleanup_for_exec(void)
cleanup_pool_for_exec(permanent_pool);
对全局父pool调用上述api,运行所有的child_cleanup
文件资源
API
int ap_slack(int fd, int line)
dup一个>=15的fd,关掉旧的
int ap_close_fd_on_exec(int fd)
设置文件描述符标志位为FD_CLOEXEC,在EXEC之后关掉fd
static void fd_cleanup(void *fdv)
{
close((int) (long) fdv);
}
{
close((int) (long) fdv);
}
static int fd_magic_cleanup(void *fdv)
{
return ap_close_fd_on_exec((int) (long) fdv);
}
{
return ap_close_fd_on_exec((int) (long) fdv);
}
API_EXPORT(void) ap_note_cleanups_for_fd_ex(pool *p, int fd, int domagic)
注册pool p的cleanup函数
API_EXPORT(void) ap_note_cleanups_for_fd_ex(pool *p, int fd, int domagic)
{
ap_register_cleanup_ex(p, (void *) (long) fd, fd_cleanup, fd_cleanup,
domagic ? fd_magic_cleanup : NULL);
}
{
ap_register_cleanup_ex(p, (void *) (long) fd, fd_cleanup, fd_cleanup,
domagic ? fd_magic_cleanup : NULL);
}
API_EXPORT(void) ap_note_cleanups_for_fd(pool *p, int fd)
调用上述接口,domagic设置为0,也就是说没有magic_cleanup
API_EXPORT(void) ap_kill_cleanups_for_fd(pool *p, int fd)
干掉fd的cleanup函数,该函数会调用
ap_kill_cleanup(p, (void *) (long) fd, fd_cleanup);
因为fd_cleanup是固定的。
API_EXPORT(int) ap_popenf_ex(pool *a, const char *name, int flg, int mode,
int domagic)
int domagic)
打开文件,并调用ap_slack,使描述符>=15,然后在pool a上注册cleanup函数
API_EXPORT(int) ap_popenf(pool *a, const char *name, int flg, int mode)
上述接口,domagic为0
API_EXPORT(int) ap_pclosef(pool *a, int fd)
调用close(fd),清理掉pool a上的cleanup函数
static void file_cleanup(void *fpv)
调用fclose((FILE *) fpv);
static void file_child_cleanup(void *fpv)
调用close(fileno((FILE *) fpv));
与上面函数的不同之处在于,close不会flush缓冲区的的内容
static int file_magic_cleanup(void *fpv)
调用ap_close_fd_on_exec(fileno((FILE *) fpv));
API_EXPORT(void) ap_note_cleanups_for_file_ex(pool *p, FILE *fp, int domagic)
以file_cleanup和file_child_cleanup为参数调用
ap_register_cleanup_ex(p, (void *) fp, file_cleanup, file_child_cleanup,
domagic ? file_magic_cleanup : NULL);
domagic ? file_magic_cleanup : NULL);
API_EXPORT(void) ap_note_cleanups_for_file(pool *p, FILE *fp)
调用上述接口,domagic为0
API_EXPORT(FILE *) ap_pfopen(pool *a, const char *name, const char *mode)
modeFlags为S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
mode为'a'时,只写,为'a+'时读写,调用open(name, baseFlag | O_APPEND | O_CREAT, modeFlags);
调用ap_slack处理打开的文件描述符,然后调用fdopen(fd, mode),如果mode不为'a'或者'a+',则直接调用fopen();
调用ap_note_cleanups_for_file(a, fd);注册cleanup到pool a上
API_EXPORT(FILE *) ap_pfdopen(pool *a, int fd, const char *mode)
直接调用fdopen打开FILE *描述符并注册cleanup for file
API_EXPORT(int) ap_pfclose(pool *a, FILE *fd)
调用fclose关闭fd,并干掉cleanup函数
DIR
static void dir_cleanup(void *dv)
调用closedir(dv)
API_EXPORT(DIR *) ap_popendir(pool *p, const char *name)
调用opendir(name);
并注册cleanup,ap_register_cleanup(p, (void *) d, dir_cleanup, dir_cleanup);
API_EXPORT(void) ap_pclosedir(pool *p, DIR * d)
调用closedir(d)并干掉p中相应的cleanup
SOKET API
static void socket_cleanup(void *fdv)
调用close(fdv)
static int socket_magic_cleanup(void *fpv)
调用return ap_close_fd_on_exec((int) (long) fpv);
API_EXPORT(void) ap_note_cleanups_for_socket_ex(pool *p, int fd, int domagic)
注册socket的cleanup到p,调用
ap_register_cleanup_ex(p, (void *) (long) fd, socket_cleanup,
socket_cleanup,
domagic ? socket_magic_cleanup : NULL);
socket_cleanup,
domagic ? socket_magic_cleanup : NULL);
API_EXPORT(void) ap_note_cleanups_for_socket(pool *p, int fd)
调用上述接口,domagic为0
API_EXPORT(void) ap_kill_cleanups_for_socket(pool *p, int sock)
清理掉p中sock对应的cleanup函数
API_EXPORT(int) ap_psocket_ex(pool *p, int domain, int type, int protocol,
int domagic)
int domagic)
调用fd = socket(domain, type, protocol);
并注册cleanup
API_EXPORT(int) ap_psocket(pool *p, int domain, int type, int protocol)
同上,domagic为0
API_EXPORT(int) ap_pclosesocket(pool *a, int sock)
close掉sock,并干掉sock对应的cleanup函数
REGEX API
static void regex_cleanup(void *preg)
{
regfree((regex_t *) preg);
}
{
regfree((regex_t *) preg);
}
API_EXPORT(regex_t *) ap_pregcomp(pool *p, const char *pattern, int cflags)
从pool中分配内存创建一个regext_t结构体,并注册cleanup函数
API_EXPORT(void) ap_pregfree(pool *p, regex_t * reg)
调用regfree(reg),从p中把reg对应的cleanup清理掉
进程链API
struct process_chain {
pid_t pid;
enum kill_conditions kill_how;
struct process_chain *next;
};
pid_t pid;
enum kill_conditions kill_how;
struct process_chain *next;
};
API_EXPORT(void) ap_note_subprocess(pool *a, pid_t pid, enum kill_conditions how)
从pool中分配一个新的process_chain结构体,设置pid和kill_conditions,加入到pool的subprocesses链表头
static pid_t spawn_child_core(pool *p, int (*func) (void *, child_info *),
void *data,enum kill_conditions kill_how,
int *pipe_in, int *pipe_out, int *pipe_err)
void *data,enum kill_conditions kill_how,
int *pipe_in, int *pipe_out, int *pipe_err)
创建子进程并创建了三条pipeline,子进程执行(*func)(data,NULL)
pipe_in,pipe_out,pipe_err非空的话就分别创建pipe
父进程会关掉pipe_out的写入端,返回可以读取的pipe_out,子进程会关掉pipe_out的读取端,并把写入端dup到STDOUT_FILENO
父进程会关掉pipe_in的读取端,返回可以写入的pipe_in,子进程会关掉pipe_in的写入端,并把读取端dup到STDIN_FILENO
父进程会关掉pipe_err的写入端,返回可以读取的pipe_err,子进程会关掉pipe_err的读取端,并把写入端dup到STDERR_FILENO
调用ap_note_subprocess()注册到precess_chain
API_EXPORT(int) ap_spawn_child(pool *p, int (*func) (void *, child_info *),
void *data, enum kill_conditions kill_how,
FILE **pipe_in, FILE **pipe_out,
FILE **pipe_err)
void *data, enum kill_conditions kill_how,
FILE **pipe_in, FILE **pipe_out,
FILE **pipe_err)
调用上述api,
pid = spawn_child_core(p, func, data, kill_how,
pipe_in ? &fd_in : NULL,
pipe_out ? &fd_out : NULL,
pipe_err ? &fd_err : NULL);
pipe_in ? &fd_in : NULL,
pipe_out ? &fd_out : NULL,
pipe_err ? &fd_err : NULL);
然后分别调用
*pipe_out = ap_fdopen(fd_out, "r" BINMODE);
ap_note_cleanups_for_file(p, *pipe_out);
ap_fdopen()就是直接调用fdopen()的接口
API_EXPORT(int) ap_bspawn_child(pool *p, int (*func) (void *, child_info *), void *data,
enum kill_conditions kill_how,
BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err)
enum kill_conditions kill_how,
BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err)
与上个函数不同的是,该函数不会fdopen打开FILE *,而是会调用ap_bcreate();
分别创建读写BUFF,并赋给pipe_in pipe_out pipe_err,另外为fd注册cleanup
ap_note_cleanups_for_fd_ex(p, fd_out, 0);
并且会调用
ap_bpushfd(*pipe_out, fd_out, fd_out);
API_EXPORT(void) ap_bpushfd(BUFF *fb, int fd_in, int fd_out)
{
fb->fd = fd_out;
fb->fd_in = fd_in;
}
这个函数以及ap_bcreate();都是在buff.c中定义的
{
fb->fd = fd_out;
fb->fd_in = fd_in;
}
这个函数以及ap_bcreate();都是在buff.c中定义的
static void free_proc_chain(struct process_chain *procs)
进程链表,
free_proc_chain函数会等待该链中所有的子进程终止并回收资源。
free_proc_chain的过程是
先对process_chain中的各个pid调用waitpid非阻塞返回,其中回收到的pid的kill_how设置为kill_never,然后对于process_chain中的所有pid的kill_how为kill_only_once或者kill_after_timeout的发送SIGTERM,并设置need_timeout为1,对kill_how为kill_always的发送SIGKILL。然后没延迟一段时间就waitpid,kill_after_timeout状态的pid,包括处于暂停状态的进程也回收,并修改状态为kill_never,直到回收完了,或者超时结束,超时时间一共3s,延迟间隔是递增的,先是1/64,然后是1/64,1/32,1/16,1/8。。。1/2。
超时结束之后对kill_after_timeout状态的发送SIGKILL,对于不为kill_never的进程,阻塞调用waitpid