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

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是
union block_hdr {
    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;
};
 
内存块管理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;
    }    
}
 
struct cleanup {
    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;
};
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) - 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;
 
内部函数
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;
 
#define table_push(t)   ((table_entry *) ap_push_array(&(t)->a))
table实际上就是一个table_entry为元素的array,上面的宏返回一个新增的可写的table_entry指针,该table_entry是一个有两个指针的结构体。
 
struct table {
    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;
};
 
API
API_EXPORT(void) ap_register_cleanup_ex(pool *p, void *data,
        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 *))
调用上述接口,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
 
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);  
}
 
static int fd_magic_cleanup(void *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);
}
 
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)
打开文件,并调用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);
 
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);
 
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)
调用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);
}
 
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;
};
 
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)
创建子进程并创建了三条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)
调用上述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_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)
与上个函数不同的是,该函数不会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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mission Planner是一款功能强大的地面站软件,可用于控制和监测无人机、自动驾驶车辆或船舶等载具。Mission Planner 1.3.39是其中的一个版本。 Mission Planner 1.3.39具有以下特点和功能: 1. 可视化地图界面:Mission Planner提供了直观的地图界面,用户可以在地图上标记航点、绘制飞行路径,实时监测载具的位置和状态。 2. 配置参数设置:用户可以通过Mission Planner调整载具的各种参数,如飞行高度、速度、遥控器通道映射等,以满足不同飞行或驾驶需求。 3. 飞行计划管理:Mission Planner支持创建、保存和加载飞行计划,用户可以提前规划好载具的飞行任务,并在需要时调用。 4. 传感器数据监测:Mission Planner可以显示载具各种传感器的实时数据,如姿态传感器、气压计、GPS等,以便用户实时了解载具的状态。 5. 自动驾驶模式:Mission Planner提供了多种自动驾驶模式,如自动起降、航线飞行、定高定点等,在设定好飞行任务后,载具可以自主执行。 6. 数据日志记录:Mission Planner能够将载具的飞行数据记录下来,用户可以通过后期分析数据,了解飞行过程中的各种参数和性能情况。 Mission Planner 1.3.39是Mission Planner软件的一个更新版本,可能修复了之前版本存在的一些问题,并且可能加入了一些新的功能和优化了用户界面。所以,用户可以根据自己的需求和喜好选择Mission Planner的不同版本来进行载具的控制和监测操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值