main函数
操作序列如下图所示:
struct ZBX_TASK_EX
typedef struct
{
zbx_task_t task;
int flags;
int data;
}
ZBX_TASK_EX;
该结构体用于zabbix启动时记录zabbix任务的一些信息。
-
zbx_task_t:是一个枚举,表示任务类型,在启动时指定为:
ZBX_TASK_START
。如果启动时指定--runtime-control
(或-R
)选项(执行管理能力),则设置t.task = ZBX_TASK_RUNTIME_CONTROL
。 -
flags:代表启动时的标志。如果启动时指定
--foreground
(或-f
)选项(在前台运行zabbix守护进程),则t.flags
会多添加一个ZBX_TASK_FLAG_FOREGROUND
标志。当zabbix_server作为daemon启动时,flags
会作为daemon的参数传入。 -
data:记录
--runtime-control
的选项信息,比如改变日志的级别或cache reload的值。比如:-R log_level_increase
,-R config_cache_reload
。
struct cfg_line
struct cfg_line
{
const char *parameter;
void *variable;
int type;
int mandatory;
zbx_uint64_t min;
zbx_uint64_t max;
};
该结构体用于表示一个配置选项
-
parameter: zabbix_server.conf中的参数名
-
variable: 程序中参数的值
-
type: 参数类型,有TYPE_INT,TYPE_STRING等类型
-
mandatory: 是否是必须设定的参数
-
min: 限定参数范围,最小值
-
max: 限定参数范围,最大值
daemon_start函数
操作序列如下图所示:
zbx_set_common_signal_handlers函数
该函数用来设置通用的信号的handlers。
struct sigaction phan;
// 处理结束信号
// #define SIGINT 2 /* Interrupt (ANSI). */
// #define SIGQUIT 3 /* Quit (POSIX). */
// #define SIGTERM 15 /* Termination (ANSI). */
phan.sa_sigaction = terminate_signal_handler;
sigaction(SIGINT, &phan, NULL);
sigaction(SIGQUIT, &phan, NULL);
sigaction(SIGTERM, &phan, NULL);
// 处理致命信号
// #define SIGILL 4 /* Illegal instruction (ANSI). */
// #define SIGFPE 8 /* Floating-point exception (ANSI). */
// #define SIGSEGV 11 /* Segmentation violation (ANSI). */
// #define SIGBUS 7 /* BUS error (4.2 BSD). */
phan.sa_sigaction = fatal_signal_handler;
sigaction(SIGILL, &phan, NULL);
sigaction(SIGFPE, &phan, NULL);
sigaction(SIGSEGV, &phan, NULL);
sigaction(SIGBUS, &phan, NULL);
// 处理警告信号
// #define SIGALRM 14 /* Alarm clock (POSIX). */
phan.sa_sigaction = alarm_signal_handler;
sigaction(SIGALRM, &phan, NULL);
sigaction函数
POSIX标准定义的信号处理接口是sigaction
函数,其接口头文件及原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
-
signum:要操作的信号。
-
act:要设置的对信号的新处理方式。
-
oldact:原来对信号的处理方式。
struct sigaction
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
该结构体用来描述对信号的处理。
-
sa_handler: 函数指针,其含义是一个信号处理函数。只不过它只有一个int参数。
-
sa_sigaction:函数指针,另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
-
sa_mask: 用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。
-
sa_flags: 用于指定信号处理的行为,它可以是以下值的“按位或”组合。
-
SA_RESTART:使被信号打断的系统调用自动重新发起。
-
SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
-
SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
-
SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
-
SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
-
SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。包含了
SA_SIGINFO
标志时,系统将使用sa_sigaction
函数作为信号处理函数,否则使用sa_handler
作为信号处理函数。在某些系统中,成员sa_handler
与sa_sigaction
被放在联合体中,因此使用时不要同时设置。
-
set_daemon_signal_handlers函数
该函数用来设置daemon使用的信号的handlers。
struct sigaction phan;
// 处理user signal:SIGUSR1
// #define SIGUSR1 10 /* User-defined signal 1 (POSIX). */
phan.sa_sigaction = user1_signal_handler;
sigaction(SIGUSR1, &phan, NULL);
// 处理 pipe signal:SIGPIPE
// #define SIGPIPE 13 /* Broken pipe (POSIX). */
phan.sa_sigaction = pipe_signal_handler;
sigaction(SIGPIPE, &phan, NULL);
zbx_set_child_signal_handler函数
该函数用来设置子进程的信号的handlers。
struct sigaction phan;
// 处理子进程的信号
// #define SIGCHLD 17 /* Child status has changed (POSIX). */
phan.sa_sigaction = child_signal_handler;
sigaction(SIGCHLD, &phan, NULL);
MAIN_ZABBIX_ENTRY函数
操作序列如下图所示:
zabbix_open_log函数
该函数用于打开日志文件
int zabbix_open_log(int type, int level, const char *filename, char **error)
{
log_type = type;
zbx_log_level = level;
if (LOG_TYPE_SYSTEM == type)
{
// 打开到system logger的连接
openlog(syslog_app_name, LOG_PID, LOG_DAEMON);
}
else if (LOG_TYPE_FILE == type)
{
FILE *log_file = NULL;
// 打开文件
if (NULL == (log_file = fopen(filename, "a+")))
{
*error = zbx_dsprintf(*error, "unable to open log file [%s]: %s", filename, zbx_strerror(errno));
return FAIL;
}
strscpy(log_filename, filename);
zbx_fclose(log_file);
}
else if (LOG_TYPE_CONSOLE == type)
{
// 使用控制台,将stderr重定向到 stdout
fflush(stderr);
if (-1 == dup2(STDOUT_FILENO, STDERR_FILENO))
zbx_error("cannot redirect stderr to stdout: %s", zbx_strerror(errno));
}
else if (LOG_TYPE_UNDEFINED != type)
{
*error = zbx_strdup(*error, "unknown log type");
return FAIL;
}
return SUCCEED;
}
zbx_load_modules函数
加载可加载模块(动态链接库的形式,example中的dummy的例子)。
DBconnect函数
连接到数据库。
DCsync_configuration函数
从DB中同步配置数据
zbx_check_postinit_tasks函数
处理初始化以后的任务
int zbx_check_postinit_tasks(char **error)
{
// select taskid from task where type=%d and status=%d
result = DBselect("select taskid from task where type=%d and status=%d", ZBX_TM_TASK_UPDATE_EVENTNAMES,
ZBX_TM_STATUS_NEW);
// DBfetch:取结果
if (NULL != (row = DBfetch(result)))
{
// 开始执行事务(transaction)
DBbegin();
// update event names in events and problem tables
/* "select triggerid,description,expression,priority,comments,url,recovery_expression,"
"recovery_mode,value"
" from triggers"
" order by triggerid"
*/
if (SUCCEED == (ret = update_event_names()))
{
// 执行一个非select操作
// delete from task where taskid=%s
DBexecute("delete from task where taskid=%s", row[0]);
// 提交事务
DBcommit();
}
else
// 执行失败,回滚事务
DBrollback();
}
// 释放内存
DBfree_result(result);
if (SUCCEED != ret)
*error = zbx_strdup(*error, "cannot update event names");
return ret;
}
DBclose函数
关闭数据库连接。
创建子进程
下面分析创建子进程的过程。
struct zbx_thread_args_t
该结构体保存进程参数
typedef struct
{
int server_num;
int process_num;
unsigned char process_type;
void *args;
}
zbx_thread_args_t;
-
server_num:进程编号
-
process_num:此种类型的进程的序号
-
process_type:创建的子进程类型,如:ZBX_PROCESS_TYPE_HOUSEKEEPER,ZBX_PROCESS_TYPE_POLLER
-
args:进程的附加参数信息
ZBX_THREAD_ENTRY宏
定义宏的目的,是为了跨平台,统一使用一个名称。
// ZBX_THREAD_ENTRY_POINTER是一个函数指针,它所指的函数类型与ZBX_THREAD_ENTRY所代表的函数的类型一致
// ZBX_THREAD_ENTRY是一个宏,指代一个函数,该函数的返回值为unsigned,参数类型为void *
#if defined(_WINDOWS)
#define ZBX_THREAD_ENTRY_POINTER(pointer_name) \
unsigned (__stdcall *pointer_name)(void *)
#define ZBX_THREAD_ENTRY(entry_name, arg_name) \
unsigned __stdcall entry_name(void *arg_name)
#else /* not _WINDOWS */
#define ZBX_THREAD_ENTRY_POINTER(pointer_name) \
unsigned (* pointer_name)(void *)
#define ZBX_THREAD_ENTRY(entry_name, arg_name) \
unsigned entry_name(void *arg_name)
ZBX_THREAD_ENTRY
定义了函数,在创建新进程时,会调用这些函数
ZBX_THREAD_ENTRY(dbconfig_thread, args)
...
ZBX_THREAD_ENTRY(poller_thread, args)
...
ZBX_THREAD_ENTRY(trapper_thread, args)
...
创建各个子进程
int MAIN_ZABBIX_ENTRY(int flags)
{
...
// 总进程数。这里使用"thread",实际上下面的操作是fork进程
threads_num = CONFIG_CONFSYNCER_FORKS + CONFIG_POLLER_FORKS
+ CONFIG_UNREACHABLE_POLLER_FORKS + CONFIG_TRAPPER_FORKS + CONFIG_PINGER_FORKS
+ CONFIG_ALERTER_FORKS + CONFIG_HOUSEKEEPER_FORKS + CONFIG_TIMER_FORKS
+ CONFIG_HTTPPOLLER_FORKS + CONFIG_DISCOVERER_FORKS + CONFIG_HISTSYNCER_FORKS
+ CONFIG_ESCALATOR_FORKS + CONFIG_IPMIPOLLER_FORKS + CONFIG_JAVAPOLLER_FORKS
+ CONFIG_SNMPTRAPPER_FORKS + CONFIG_PROXYPOLLER_FORKS + CONFIG_SELFMON_FORKS
+ CONFIG_VMWARE_FORKS + CONFIG_TASKMANAGER_FORKS + CONFIG_IPMIMANAGER_FORKS
+ CONFIG_ALERTMANAGER_FORKS + CONFIG_PREPROCMAN_FORKS + CONFIG_PREPROCESSOR_FORKS;
// 分配内存
threads = (pid_t *)zbx_calloc(threads, threads_num, sizeof(pid_t));
// 到此为止,主进程[main process]已启动完成了
zabbix_log(LOG_LEVEL_INFORMATION, "server #0 started [main process]");
// 下面是启动每一个子进程
for (i = 0; i < threads_num; i++)
{
zbx_thread_args_t thread_args;
unsigned char poller_type;
// 根据threads_num来循环,获取本次循环中进程的类型和进程编号
if (FAIL == get_process_info_by_thread(i + 1, &thread_args.process_type, &thread_args.process_num))
{
THIS_SHOULD_NEVER_HAPPEN;
exit(EXIT_FAILURE);
}
thread_args.server_num = i + 1;
thread_args.args = NULL;
switch (thread_args.process_type)
{
case ZBX_PROCESS_TYPE_CONFSYNCER:
zbx_thread_start(dbconfig_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_POLLER:
poller_type = ZBX_POLLER_TYPE_NORMAL;
thread_args.args = &poller_type;
// thread_args作为进程参数传入poller_thread函数,并被调用
zbx_thread_start(poller_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_UNREACHABLE:
poller_type = ZBX_POLLER_TYPE_UNREACHABLE;
thread_args.args = &poller_type;
zbx_thread_start(poller_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_TRAPPER:
thread_args.args = &listen_sock;
zbx_thread_start(trapper_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_PINGER:
zbx_thread_start(pinger_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_ALERTER:
zbx_thread_start(alerter_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_HOUSEKEEPER:
zbx_thread_start(housekeeper_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_TIMER:
zbx_thread_start(timer_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_HTTPPOLLER:
zbx_thread_start(httppoller_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_DISCOVERER:
zbx_thread_start(discoverer_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_HISTSYNCER:
zbx_thread_start(dbsyncer_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_ESCALATOR:
zbx_thread_start(escalator_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_JAVAPOLLER:
poller_type = ZBX_POLLER_TYPE_JAVA;
thread_args.args = &poller_type;
zbx_thread_start(poller_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_SNMPTRAPPER:
zbx_thread_start(snmptrapper_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_PROXYPOLLER:
zbx_thread_start(proxypoller_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_SELFMON:
zbx_thread_start(selfmon_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_VMWARE:
zbx_thread_start(vmware_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_TASKMANAGER:
zbx_thread_start(taskmanager_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_PREPROCMAN:
zbx_thread_start(preprocessing_manager_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_PREPROCESSOR:
zbx_thread_start(preprocessing_worker_thread, &thread_args, &threads[i]);
break;
#ifdef HAVE_OPENIPMI
case ZBX_PROCESS_TYPE_IPMIMANAGER:
zbx_thread_start(ipmi_manager_thread, &thread_args, &threads[i]);
break;
case ZBX_PROCESS_TYPE_IPMIPOLLER:
zbx_thread_start(ipmi_poller_thread, &thread_args, &threads[i]);
break;
#endif
case ZBX_PROCESS_TYPE_ALERTMANAGER:
zbx_thread_start(alert_manager_thread, &thread_args, &threads[i]);
break;
}
}
if (SUCCEED == zbx_is_export_enabled())
{
zbx_history_export_init("main-process", 0);
zbx_problems_export_init("main-process", 0);
}
// 等待子进程结束,当子进程正常结束时,返回子进程的pid,这里会继续执行循环
// 当发生错误时,会返回(pid_t) -1.这时会结束循环,从而主进程也就结束了。
while (-1 == wait(&i)) /* wait for any child to exit */
{
if (EINTR != errno)
{
zabbix_log(LOG_LEVEL_ERR, "failed to wait on child processes: %s", zbx_strerror(errno));
break;
}
}
/* all exiting child processes should be caught by signal handlers */
THIS_SHOULD_NEVER_HAPPEN;
// 进程退出时的操作
zbx_on_exit();
return SUCCEED;
}
zbx_thread_start函数
zbx_thread_start是一个封装函数,在其中会调用fork()函数,创建子进程。
/*
* Parameters: handler - [IN] new thread starts execution from this *
* handler function *
* thread_args - [IN] arguments for thread function *
* thread - [OUT] handle to a newly created thread *
*/
void zbx_thread_start(ZBX_THREAD_ENTRY_POINTER(handler), zbx_thread_args_t *thread_args, ZBX_THREAD_HANDLE *thread)
{
// call fork()
zbx_child_fork(thread);
// 返回0,是子进程
if (0 == *thread) /* child process */
{
// 调用进程函数
(*handler)(thread_args);
}
// 返回-1,fork失败
else if (-1 == *thread)
{
zbx_error("failed to fork: %s", zbx_strerror(errno));
*thread = (ZBX_THREAD_HANDLE)ZBX_THREAD_ERROR;
}
}
void zbx_child_fork(pid_t *pid)
{
*pid = zbx_fork();
}
int zbx_fork(void)
{
return fork();
}
zbx_on_exit函数
当子进程发生错误,导致主进程退出时,执行zbx_on_exit
函数。
void zbx_on_exit(void)
{
// 如果还有数据库事务在执行,则回滚该事务
if (SUCCEED == DBtxn_ongoing())
DBrollback();
if (NULL != threads)
{
// 会主动执行kill SIGTERM,并等待子进程结束
zbx_threads_wait(threads, threads_num); /* wait for all child processes to exit */
// 释放内存
zbx_free(threads);
}
//释放各指标参数的内存
free_metrics();
zbx_ipc_service_free_env();
// 释放DB cache内存
DBconnect(ZBX_DB_CONNECT_EXIT);
free_database_cache();
DBclose();
// 释放配置cache内存
free_configuration_cache();
/* free history value cache */
zbx_vc_destroy();
zbx_destroy_itservices_lock();
/* free vmware support */
if (0 != CONFIG_VMWARE_FORKS)
zbx_vmware_destroy();
free_selfmon_collector();
zbx_uninitialize_events();
// 卸载可加载模块
zbx_unload_modules();
// 关闭log文件
zabbix_close_log();
exit(EXIT_SUCCESS);
}
记录日志
配置文件中的配置
zabbix_server的日志的位置,级别等配置,在zabbix_server.conf
中进行配置。
### Option: LogType
# Specifies where log messages are written to:
# system - syslog
# file - file specified with LogFile parameter
# console - standard output
#
# Mandatory: no
# Default:
# LogType=file
### Option: LogFile
# Log file name for LogType 'file' parameter.
#
# Mandatory: yes, if LogType is set to file, otherwise no
# Default:
# LogFile=
LogFile=/tmp/zabbix_server.log
### Option: DebugLevel
# Specifies debug level:
# 0 - basic information about starting and stopping of Zabbix processes
# 1 - critical information
# 2 - error information
# 3 - warnings
# 4 - for debugging (produces lots of information)
# 5 - extended debugging (produces even more information)
#
# Mandatory: no
# Range: 0-5
# Default:
# DebugLevel=3
实现
1、zabbix日志的实现,头文件在:include/log.h
,实现文件在src/libs/zbxlog/log.c
中。
2、配置文件中的LogType
指定日志文件的类型,一般记录到文本文件中,因此指定为file
。对应的源码中的设置为:
#define LOG_TYPE_UNDEFINED 0
#define LOG_TYPE_SYSTEM 1
#define LOG_TYPE_FILE 2
#define LOG_TYPE_CONSOLE 3
#define ZBX_OPTION_LOGTYPE_SYSTEM "system"
#define ZBX_OPTION_LOGTYPE_FILE "file"
#define ZBX_OPTION_LOGTYPE_CONSOLE "console"
3、当LogType
指定为file
时,需要LogFile
指定记录的日志文件的名称和位置。
4、DebugLevel
指定日志级别。默认为warnings
。对应源码中的设置为:
#define LOG_LEVEL_EMPTY 0 /* printing nothing (if not LOG_LEVEL_INFORMATION set) */
#define LOG_LEVEL_CRIT 1
#define LOG_LEVEL_ERR 2
#define LOG_LEVEL_WARNING 3
#define LOG_LEVEL_DEBUG 4
#define LOG_LEVEL_TRACE 5
#define LOG_LEVEL_INFORMATION 127 /* printing in any case no matter what level set */
5、无论日志级别设置为什么,使用LOG_LEVEL_INFORMATION
级别来设置日志,总是能打印出来。所以我们在调查源码时,可以以LOG_LEVEL_INFORMATION
级别来记录调试日志。