epoll_create 与 epoll_create1 的区别:
1)int epoll_create(int size):创建一个 epoll 的句柄,size 用来告诉内核这个监听的数目一共有多大,内核根据这个参数分配最初的描述事件结构体的空间。在linux 内核版本大于2.6.8 后,参数:size 已被弃用了(内核动态的分配描述事件需要的空间),但是传入的值必须大于0,是为了新的应用能运行在旧的内核上。
2)int epoll_create1(int flags):
2.1)epoll_create1 扩展了 epoll_create 的功能,更推荐使用的是 epoll_create1(0) 来代替普通的用法
2.2)flags:
0:等价于删除了过时size参数的 epoll_create();
EPOLL_CLOEXEC:表示生成的 epoll fd 具有“执行后关闭”特性。
start_vrrp_child():
1、使用 fork() 创建vrrp子进程;
2、清除从父进程复制来的数据;
3、新建 master thread 对象,包括epoll句柄、定时器接口以及4个双向链表:event、signal、ready、unuse;
4、调用 start_vrrp(),启动 vrrp;
5、process_threads() 中循环调用 thread_fetch_next_queue(),监控各链表以及 epoll 事件,并执行对应的回调函数。
vrrp 相关数据(base_priority/wantstate/state)设置:
1、读取配置文件,获取 vrrp 优先级 base_priority(默认:100);
vrrp_parser.c -> read_unsigned_strvec(strvec, 1, &base_priority, 1, VRRP_PRIO_OWNER, false)
2、读取配置文件,获取 vrrp 状态 wantstate:back/mast(默认:VRRP_STATE_INIT);
start_vrrp -> vrrp_complete_init-> alloc_vrrp:初始化 vrrp
3、在未指定 base_priority 或配置为0的前提下,若 wantstate 为 VRRP_STATE_MAST,则 base_priority 设置为 VRRP_PRIO_OWNER(255),否则设置为 VRRP_PRIO_DFL(100)
4、vrrp internal state:(init/backup/master/fault),默认:VRRP_STATE_INIT(vrrp_complete_instance())
// vrrp_daemon.c
/* 创建vrrp子进程,实现vrrp协议,负责keepalived主要的事件的调度。*/
int start_vrrp_child(void)
{
#ifndef _ONE_PROCESS_DEBUG_
pid_t pid;
const char *syslog_ident;
/* Initialize child process */
#ifdef ENABLE_LOG_TO_FILE
if (log_file_name)
flush_log_file();
#endif
pid = fork(); // 创建vrrp子进程
if (pid < 0) {
/* 子进程创建出错 */
log_message(LOG_INFO, "VRRP child process: fork error(%s)"
, strerror(errno));
return -1;
} else if (pid) {
/* 子进程创建成功,父进程返回子进程id */
vrrp_child = pid;
log_message(LOG_INFO, "Starting VRRP child process, pid=%d"
, pid);
/* Start respawning thread */
thread_add_child(master, vrrp_respawn_thread, NULL,
pid, TIMER_NEVER);
return 0;
}
/* 子进程创建成功,子进程返回0 */
/*
PR_SET_PDEATHSIG:在子进程中使用prctl系统调用,表示当父进程挂了后,要求内核给自己(子进程)发送特定信号
SIGTERM:进程收到SIGTERM信号后,会释放自己的资源并停止
*/
prctl(PR_SET_PDEATHSIG, SIGTERM);
#ifdef _WITH_PERF_
if (perf_run == PERF_ALL)
/* 性能分析 */
run_perf("vrrp", global_data->network_namespace, global_data->instance_name);
#endif
/* 子进程类型:vrrp */
prog_type = PROG_TYPE_VRRP;
/* 初始化debug相关参数 */
initialise_debug_options();
#ifdef _WITH_BFD_
/* Close the write end of the BFD vrrp event notification pipe */
close(bfd_vrrp_event_pipe[1]);
#ifdef _WITH_LVS_
/* 关闭 BFD checker event notification pipe 读写两端*/
close(bfd_checker_event_pipe[0]);
close(bfd_checker_event_pipe[1]);
#endif
#endif
/*
openlog():打开一个到系统日志记录程序的连接
syslog()或vsyslog():向系统日志里添加信息
closelog():关闭连接
*/
if ((global_data->instance_name
#if HAVE_DECL_CLONE_NEWNET
|| global_data->network_namespace
#endif
) &&
(vrrp_syslog_ident = make_syslog_ident(PROG_VRRP)))
syslog_ident = vrrp_syslog_ident;
else
syslog_ident = PROG_VRRP;
if (!__test_bit(NO_SYSLOG_BIT, &debug))
openlog(syslog_ident, LOG_PID | ((__test_bit(LOG_CONSOLE_BIT, &debug)) ? LOG_CONS : 0)
, (log_facility==LOG_DAEMON) ? LOG_LOCAL1 : log_facility);
/*写日志到文件*/
#ifdef ENABLE_LOG_TO_FILE
if (log_file_name)
open_log_file(log_file_name,
"vrrp",
#if HAVE_DECL_CLONE_NEWNET
global_data->network_namespace,
#else
NULL,
#endif
global_data->instance_name);
#endif
/* 创建内存相关的日志文件 */
#ifdef _MEM_CHECK_
mem_log_init(PROG_VRRP, "VRRP Child process");
#endif
/* 释放从父进程复制过来的对象,包括系统日志、core_dump等 */
free_parent_mallocs_startup(true);
/* 清除父进程设置的回调函数,该回调函数用户返回进程名称 */
set_child_finder_name(NULL);
/* 将子进程id写入 pidfile */
if (!pidfile_write(vrrp_pidfile, getpid())) {
/* Fatal error */
log_message(LOG_INFO, "VRRP child process: cannot write pidfile");
exit(0);
}
/*设置回调函数,记录 vrrp dump*/
#ifdef _VRRP_FD_DEBUG_
if (do_vrrp_fd_debug)
set_extra_threads_debug(dump_vrrp_fd);
#endif
/* 创建新的 master thread */
/* 销毁从父进程复制的对象,包括epoll句柄等 */
thread_destroy_master(master);
/* 新建 master thread 对象,包括epoll句柄、定时器接口以及4个双向链表:event、signal、ready、unuse */
master = thread_make_master();
#endif
/* If last process died during a reload, we can get there and we
* don't want to loop again, because we're not reloading anymore.
*/
UNSET_RELOAD;
#ifndef _ONE_PROCESS_DEBUG_
/*
设置信号的处理函数
SIGHUP 终止进程 终端线路挂断
SIGINT 终止进程 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
SIGTERM 终止 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。
通常用来要求程序自己正常退出。
SIGUSR1 终止进程 用户定义信号1
SIGUSR2 终止进程 用户定义信号2
SIGJSON
SIGTDUMP
SIGPIPE 终止进程 向一个没有读进程的管道写数据
*/
vrrp_signal_init();
#endif
/* 启动 vrrp */
start_vrrp(NULL);
#ifdef _ONE_PROCESS_DEBUG_
return 0;
#endif
#ifdef THREAD_DUMP
register_vrrp_thread_addresses();
#endif
#ifdef _WITH_PERF_
if (perf_run == PERF_RUN)
run_perf("vrrp", global_data->network_namespace, global_data->instance_name);
#endif
/* Launch the scheduling I/O multiplexer */
/* event、epoll 事件处理 */
launch_thread_scheduler(master);
#ifdef THREAD_DUMP
deregister_thread_addresses();
#endif
/* Finish VRRP daemon process */
/*关闭ipvs(ipvs是一个socket)、iptables、master thread、日志、global_data、vrrp_data 等 */
vrrp_terminate_phase2(EXIT_SUCCESS);
/* unreachable */
exit(EXIT_SUCCESS);
}
/* Our infinite scheduling loop */
void
launch_thread_scheduler(thread_master_t *m)
{
/*
SIGCHLD:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程
按系统默认将忽略此信号,如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。
*/
signal_set(SIGCHLD, thread_child_handler, m);
/* 循环调用 thread_fetch_next_queue,监控各链表,执行回调函数 */
process_threads(m);
}
void
process_threads(thread_master_t *m)
{
thread_t* thread;
list_head_t *thread_list;
int thread_type;
/*
1、检查 event 链表,取出节点并执行回调函数;
2、若 event 链表中没有待处理的事件,则检查 ready 链表,取出节点并执行回调函数;
3、若 ready 链表中没有待处理的事件,则调用 epoll_wait(),等待 epoll 事件的发生,将 事件 添加至 ready 链表中;
4、再次检查 ready 链表,取出节点并执行回调函数;
5、event 和 ready 链表处理完成,把使用完的空间放入 master->unuse 链表中。
*/
while ((thread_list = thread_fetch_next_queue(m))) {
/* Run until error, used for debuging only */
#ifdef _MEM_ERR_DEBUG_
if (do_mem_err_debug &&
__test_bit(MEM_ERR_DETECT_BIT, &debug)
#ifdef _WITH_VRRP_
&& __test_bit(DONT_RELEASE_VRRP_BIT, &debug)
#endif
) {
__clear_bit(MEM_ERR_DETECT_BIT, &debug);
#ifdef _WITH_VRRP_
__clear_bit(DONT_RELEASE_VRRP_BIT, &debug);
#endif
thread_add_terminate_event(master);
}
#endif
/* If we are shutting down, only process relevant thread types.
* We only want timer and signal fd, and don't want inotify, vrrp socket,
* snmp_read, bfd_receiver, bfd pipe in vrrp/check, dbus pipe or netlink fds. */
thread = thread_trim_head(thread_list);
if (thread && thread->type == THREAD_CHILD_TIMEOUT) {
/* We remove the thread from the child_pid queue here so that
* if the termination arrives before we processed the timeout
* we can still handle the termination. */
rb_erase(&thread->rb_data, &master->child_pid);
}
if (!shutting_down ||
((thread->type == THREAD_READY_READ_FD ||
thread->type == THREAD_READY_WRITE_FD) &&
(thread->u.f.fd == m->timer_fd ||
thread->u.f.fd == m->signal_fd
#ifdef _WITH_SNMP_
|| FD_ISSET(thread->u.f.fd, &m->snmp_fdset)
#endif
)) ||
thread->type == THREAD_CHILD ||
thread->type == THREAD_CHILD_TIMEOUT ||
thread->type == THREAD_CHILD_TERMINATED ||
thread->type == THREAD_TIMER_SHUTDOWN ||
thread->type == THREAD_TERMINATE) {
if (thread->func)
thread_call(thread); /* 执行回调函数 */
if (thread->type == THREAD_TERMINATE_START)
shutting_down = true;
}
m->current_event = (thread->type == THREAD_READY_READ_FD || thread->type == THREAD_READY_WRITE_FD) ? thread->event : NULL;
thread_type = thread->type;
thread_add_unuse(master, thread);
/* If we are shutting down, and the shutdown timer is not running and
* all children have terminated, then we can terminate */
if (shutting_down && !m->shutdown_timer_running && !m->child.rb_root.rb_node)
break;
/* If daemon hanging event is received stop processing */
if (thread_type == THREAD_TERMINATE)
break;
}
}