现在继续上一篇博文分析一下haproxy的初始化函数,haproxy的初始化函数为void init(int argc, char **argv),其跟主函数在同一源文件中(haproxy.c)。该函数也很长,我们一段一段来分析:
void init(int argc, char **argv)
{
int i;
int arg_mode = 0; /* MODE_DEBUG, ... */
char *tmp;
char *cfg_pidfile = NULL;
int err_code = 0;
struct wordlist *wl;
char *progname;
/*
* Initialize the previously static variables.
*/
totalconn = actconn = maxfd = listeners = stopping = 0;
#ifdef HAPROXY_MEMMAX
global.rlimit_memmax = HAPROXY_MEMMAX;
#endif
tv_update_date(-1,-1);
start_date = now;
/* 初始化信号队列 */
signal_init();
/* 初始化timer树和rqueue树,另外创建task的pool内存管理池 */
init_task();
/* 创建session的内存管理池 */
init_session();
/* warning, we init buffers later */
/* 创建pendconn的内存管理池 */
init_pendconn();
/* http协议相关的初始化 */
init_proto_http();
这边主要是相关内存管理的初始化,haproxy是怎么样进行内存管理的呢?我们可以阅读memory.c源文件,其实其内存管理很简单,是通过链表来管理的,总的内存管理的链表头是pools,其结构为struct list,链表的节点就是由相关模块创建的内存管理pool头,该结构如下所示:
struct pool_head {
void **free_list; /* 空闲内存链表,free内存时就把其加入到该链表 */
struct list list; /* list of all known pools */
unsigned int used; /* how many chunks are currently in use */
unsigned int allocated; /* how many chunks have been allocated */
unsigned int limit; /* hard limit on the number of chunks */
unsigned int minavail; /* how many chunks are expected to be used */
unsigned int size; /* chunk size */
unsigned int flags; /* MEM_F_*,该标志主要是用来是否可以同别的pool共享 */
unsigned int users; /* number of pools sharing this zone */
char name[12]; /* name of the pool */
};
每个pool能申请的数据长度的按照16字节对齐的,在创建一个pool后都是连接pools为链表头的链表中,我们要在以"name"的pool池中申请内存是调用pool_alloc2宏,先是在查看free_list是否有空闲节点,有直接从那边取,没有则调用pool_refill_alloc函数申请,该函数会判断该pool申请的内存是否超过了限制,超过则返回NULL,没有则调用malloc申请,malloc申请不到则会释放一些别的pool的free_list节点以获取内存。释放内存是调用pool_free2(pool, ptr)宏,其实说是释放,只是把该内存加入到free_list链表中。总的来说haproxy内存管理相对简单,通过该内存管理我们可以监测各个pool的使用情况及限制各个pool的申请情况。说了那么多内存管理相关的,现在我们回到初始化函数来,我们继续分析:
……
略过支持事件的代码,我们来分析解析程序运行参数的代码
while (argc > 0) { char *flag; if (**argv == '-') { flag = *argv+1; /* 1 arg */ if (*flag == 'v') { /* 显示haproxy的version */ display_version(); if (flag[1] == 'v') /* -vv */ display_build_opts(); exit(0); } /* * de、ds、dp、dk删除相关的事件 */ #if defined(ENABLE_EPOLL) else if (*flag == 'd' && flag[1] == 'e') global.tune.options &= ~GTUNE_USE_EPOLL; #endif #if defined(ENABLE_SEPOLL) else if (*flag == 'd' && flag[1] == 's') global.tune.options &= ~GTUNE_USE_SEPOLL; #endif #if defined(ENABLE_POLL) else if (*flag == 'd' && flag[1] == 'p') global.tune.options &= ~GTUNE_USE_POLL; #endif #if defined(ENABLE_KQUEUE) else if (*flag == 'd' && flag[1] == 'k') global.tune.options &= ~GTUNE_USE_KQUEUE; #endif #if defined(CONFIG_HAP_LINUX_SPLICE) else if (*flag == 'd' && flag[1] == 'S') global.tune.options &= ~GTUNE_USE_SPLICE; #endif else if (*flag == 'V') arg_mode |= MODE_VERBOSE; else if (*flag == 'd' && flag[1] == 'b') arg_mode |= MODE_FOREGROUND; else if (*flag == 'd') arg_mode |= MODE_DEBUG; else if (*flag == 'c') arg_mode |= MODE_CHECK; else if (*flag == 'D') arg_mode |= MODE_DAEMON; else if (*flag == 'q') arg_mode |= MODE_QUIET; else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) { /* list of pids to finish ('f') or terminate ('t') */ if (flag[1] == 'f') oldpids_sig = SIGUSR1; /* finish then exit */ else oldpids_sig = SIGTERM; /* terminate immediately */ argv++; argc--; if (argc > 0) { oldpids = calloc(argc, sizeof(int)); while (argc > 0) { oldpids[nb_oldpids] = atol(*argv); if (oldpids[nb_oldpids] <= 0) usage(progname); argc--; argv++; nb_oldpids++; } } } else { /* >=2 args */ argv++; argc--; if (argc == 0) usage(progname); switch (*flag) { case 'n' : cfg_maxconn = atol(*argv); break; /* -n: 最大连接数 */ case 'm' : global.rlimit_memmax = atol(*argv); break; /* -m: 最大内存限制 */ case 'N' : cfg_maxpconn = atol(*argv); break; /* -N: 每个proxy的最大连接数 */ case 'f' : /* -f: 配置文件 */ wl = (struct wordlist *)calloc(1, sizeof(*wl)); if (!wl) { Alert("Cannot load configuration file %s : out of memory.\n", *argv); exit(1); } wl->s = *argv; LIST_ADDQ(&cfg_cfgfiles, &wl->list); break; case 'p' : cfg_pidfile = *argv; break; default: usage(progname); } } } else usage(progname); argv++; argc--; } global.mode = MODE_STARTING | /* during startup, we want most of the alerts */ (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_VERBOSE | MODE_QUIET | MODE_CHECK | MODE_DEBUG)); if (LIST_ISEMPTY(&cfg_cfgfiles)) usage(progname); /* NB: POSIX does not make it mandatory for gethostname() to NULL-terminate * the string in case of truncation, and at least FreeBSD appears not to do * it. */ memset(hostname, 0, sizeof(hostname)); gethostname(hostname, sizeof(hostname) - 1); have_appsession = 0; global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */ init_default_instance(); /* 解析配置文件 */ list_for_each_entry(wl, &cfg_cfgfiles, list) { int ret; ret = readcfgfile(wl->s); if (ret == -1) { Alert("Could not open configuration file %s : %s\n", wl->s, strerror(errno)); exit(1); } if (ret & (ERR_ABORT|ERR_FATAL)) Alert("Error(s) found in configuration file : %s\n", wl->s); err_code |= ret; if (err_code & ERR_ABORT) exit(1); } /* 检查配置是否正确 */ err_code |= check_config_validity(); if (err_code & (ERR_ABORT|ERR_FATAL)) { Alert("Fatal errors found in configuration.\n"); exit(1); } if (global.mode & MODE_CHECK) { qfprintf(stdout, "Configuration file is valid\n"); exit(0); } /* now we know the buffer size, we can initialize the buffers */ init_buffer(); if (have_appsession) appsession_init(); if (start_checks() < 0) exit(1); if (cfg_maxconn > 0) global.maxconn = cfg_maxconn; if (cfg_pidfile) { free(global.pidfile); global.pidfile = strdup(cfg_pidfile); } if (global.maxconn == 0) global.maxconn = DEFAULT_MAXCONN; if (!global.maxpipes) { /* maxpipes not specified. Count how many frontends and backends * may be using splicing, and bound that to maxconn. */ struct proxy *cur; int nbfe = 0, nbbe = 0; for (cur = proxy; cur; cur = cur->next) { if (cur->options2 & (PR_O2_SPLIC_ANY)) { if (cur->cap & PR_CAP_FE) nbfe += cur->maxconn; if (cur->cap & PR_CAP_BE) nbbe += cur->fullconn ? cur->fullconn : global.maxconn; } } global.maxpipes = MAX(nbfe, nbbe); if (global.maxpipes > global.maxconn) global.maxpipes = global.maxconn; global.maxpipes /= 4; } .......
这主要是haproxy初始化的代码,初始化函数主要是创建内存管理相关的及参数解析或者配置文件解析等等