`
pwk907cx
  • 浏览: 11646 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

apache1.3.39源码alloc.c阅读笔记

阅读更多

apache1.3.39源码alloc.c阅读笔记
2011年06月03日
  typedef struct pool pool;   内存块管理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);
  像一个双函数递归过程。
  其中
  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 */
  };
  内存分配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;
  }
  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
  内部函数
  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
  #define table_push(t)   ((table_entry *) ap_push_array(&(t)->a))
  table实际上就是一个table_entry为元素的array,上面的宏返回一个新增的可写的table_entry指针,该table_entry是一个有两个指针的结构体。
  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
  注册cleanup回调函数,简单赋值,从pool上分配cleanup结构体,加入pool的cleanup链表头
  magic_cleanup不为空,则调用magic_cleanup(data);
  调用上述接口,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
  递归调用所有的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
  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是固定的。
  打开文件,并调用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为参数调用
  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,调用
  调用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
  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
  API_EXPORT(void) ap_note_subprocess(pool *a, pid_t pid, enum kill_conditions how)
  从pool中分配一个新的process_chain结构体,设置pid和kill_conditions,加入到pool的subprocesses链表头
  创建子进程并创建了三条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
  与上个函数不同的是,该函数不会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中定义的
  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
分享到:
评论

相关推荐

    apache_1.3.39.tar.gz

    Apache HTTP Server是Apache软件基金会的一个开放源码的网页服务器,可以在大多数计算机操作系统中运行,由于其多平台和安全性被广泛使用,是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩展,将...

    Python库 | yangson-1.3.39.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:yangson-1.3.39.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    mod_ssl-2.8.30-1.3.39.tar.gz

    The mod_ssl package has three sets of documents: First various administrative documents in plain text format which describe install-time issues, second a comprehensive ...

    apache_1.3.39

    linux下的apache

    MissionPlanner-1.3.39.zip

    开源APM地面站,开源调参软件。

    apache-win32-x86

    最新的PHP开发软件.我自己也在用.非常好.

    mission planner地面站

    mission planner地面站MissionPlanner-1.3.39,全中文地面站,适用于APM,PIX等飞控的设置和基笨调参。

    乐迪PIX说明指导V1.4.1

    1. .net framework 文件(安装地面站环境文件:XP WIN7 WIN8 WIN10) ...2. Mission Planner 1.3.39版本(地面站) 3. 电脑地面站驱动安装故障解决办法 4. 手机地面站 5. PIX说明书 6.飞行日志故障代码

    taro-user-cases:使用 Taro 开发的案例

    Taro 案例使用说明:,修改第一行 userCases 变量,找到对应平台键值,往数组里添加对象:name: (必填)用名url: (必填)二维码图片地址framework:... "react", taroVersion: "1.3.39", screenshot: ["http://img12.36

    C#编译的GDAL(支持HDF4+HDA5)

    压缩文件下,包含VS2010编译的32位的GDAL,支持HDF2.4.6/HDF5-1.8.7(swig1.3.39),修正了常见的错误,亲测可用!如果需要添加其他库,可根据文档进行相关编译,附有编译过程中常见错误的修改文档。

Global site tag (gtag.js) - Google Analytics