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初始化的代码,初始化函数主要是创建内存管理相关的及参数解析或者配置文件解析等等