main流程
- 保存命令行
- 解析命令行
- 解析配置文件
- master进程创建多个worker进程,循环处理信号
- worker进程循环处理事件和定时器
设置进程标题
nginx在unix系统中能设置进程标题,这是一个很酷炫的功能,看看是怎么实现的呢
#ifdef __unix__
/*
* memory layout
* argv[0]\0argv[1]\0argv[n]\0env[0]\0env[1]\0env[n]\0
*/
void setproctitle(const char* title) {
//printf("proctitle=%s\n", title);
memset(g_main_ctx.os_argv[0], 0, g_main_ctx.arg_len + g_main_ctx.env_len);
strncpy(g_main_ctx.os_argv[0], title, g_main_ctx.arg_len + g_main_ctx.env_len);
}
#endif
解析命令行
// unix short style
static char options[] = "hvc:ts:d";
static char detail_options[] = "\
-h : print help\n\
-v : print version\n\
-c confile : set configure file, default etc/${program}.conf\n\
-t : test configure file and exit\n\
-s signal : send signal to process\n\
signal=[start, stop, restart, status]\n\
-d : daemon\n\
";
void print_version() {
printf("%s version %s\n", g_main_ctx.program_name, get_compile_version());
}
void print_help() {
printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);
printf("Options:\n%s\n", detail_options);
}
#define INVALID_OPTION -1
#define FLAG_OPTION 1
#define PARMA_OPTION 2
int get_option(char opt) {
char* p = options;
while (*p && *p != opt) ++p;
if (*p == '\0') return INVALID_OPTION;
if (*(p+1) == ':') return PARMA_OPTION;
return FLAG_OPTION;
}
int parse_cmdline(int argc, char** argv) {
int i = 1;
while (argv[i]) {
char* p = argv[i];
if (*p != '-') {
printf("Invalid argv[%d]: %s\n", i, argv[i]);
exit(-10);
}
while (*++p) {
switch (get_option(*p)) {
case INVALID_OPTION:
printf("Invalid option: '%c'\n", *p);
exit(-20);
case FLAG_OPTION:
g_main_ctx.arg_kv[std::string(p, 1)] = "true";
break;
case PARMA_OPTION:
if (*(p+1) != '\0') {
g_main_ctx.arg_kv[std::string(p, 1)] = p+1;
++i;
goto next_option;
} else if (argv[i+1] != NULL) {
g_main_ctx.arg_kv[std::string(p, 1)] = argv[i+1];
i += 2;
goto next_option;
} else {
printf("Option '%c' requires param\n", *p);
exit(-30);
}
}
}
++i;
next_option:
continue;
}
return 0;
}
pid文件
int create_pidfile() {
FILE* fp = fopen(g_main_ctx.pidfile, "w");
if (fp == NULL) {
printf("fopen [%s] error: %d\n", g_main_ctx.pidfile, errno);
return -10;
}
char pid[16] = {0};
snprintf(pid, sizeof(pid), "%d\n", g_main_ctx.pid);
fwrite(pid, 1, strlen(pid), fp);
fclose(fp); atexit(delete_pidfile);
hlogd("create_pidfile [%s] pid=%d", g_main_ctx.pidfile, g_main_ctx.pid);
return 0;
}
void delete_pidfile() {
remove(g_main_ctx.pidfile);
hlogd("delete_pidfile [%s]", g_main_ctx.pidfile);
}
pid_t getpid_from_pidfile() {
FILE* fp = fopen(g_main_ctx.pidfile, "r");
if (fp == NULL) {
//printf("fopen [%s] error: %d\n", g_conf_ctx.pidfile, errno);
return -1;
}
char pid[64];
int readbytes = fread(pid, 1, sizeof(pid), fp);
fclose(fp);
if (readbytes <= 0) {
printf("fread [%s] bytes=%d\n", g_main_ctx.pidfile, readbytes);
return -1;
}
return atoi(pid);
}
信号控制
在unix系统中使用signal,在windows系统中使用event
const char* signal = get_arg("s");
if (signal) {
if (strcmp(signal, "start") == 0) {
if (g_main_ctx.oldpid > 0) {
printf("%s is already running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
exit(0);
}
} else if (strcmp(signal, "stop") == 0) {
if (g_main_ctx.oldpid > 0) {
#ifdef __unix__
kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
#else
SetEvent(s_hEventTerm);
#endif
printf("%s stop/waiting\n", g_main_ctx.program_name);
} else {
printf("%s is already stopped", g_main_ctx.program_name);
}
exit(0);
} else if (strcmp(signal, "restart") == 0) {
if (g_main_ctx.oldpid > 0) {
#ifdef __unix__
kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
#else
SetEvent(s_hEventTerm);
#endif
printf("%s stop/waiting\n", g_main_ctx.program_name);
msleep(1000);
}
} else if (strcmp(signal, "status") == 0) {
if (g_main_ctx.oldpid > 0) {
printf("%s start/running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
} else {
printf("%s stop/waiting\n", g_main_ctx.program_name);
}
exit(0);
} else {
printf("Invalid signal: '%s'\n", signal);
exit(0);
}
printf("%s start/running\n", g_main_ctx.program_name);
}
master-worker模型
#ifdef __unix__
// unix use signal
// we use SIGTERM to quit process
#define SIGNAL_TERMINATE SIGTERM
#include <sys/wait.h>
#define MAXNUM_WORKER 1024
static pid_t s_worker_processes[MAXNUM_WORKER];
void worker_process_cycle() {
char proctitle[256] = {0};
snprintf(proctitle, sizeof(proctitle), "%s: worker process", g_main_ctx.program_name);
setproctitle(proctitle);
while(1) {
msleep(1);
}
}
int create_worker_process(int worker_processes) {
for (int i = 0; i < worker_processes; ++i) {
pid_t pid = fork();
if (pid < 0) {
hloge("fork error: %d", errno);
return errno;
}
if (pid == 0) {
hlogi("worker process start/running, pid=%d", getpid());
worker_process_cycle();
exit(0);
}
for (int i = 0; i < MAXNUM_WORKER; ++i) {
if (s_worker_processes[i] <= 0) {
s_worker_processes[i] = pid;
break;
}
}
}
return 0;
}
static int s_signo = 0;
void master_process_signal_handler(int signo) {
hlogd("pid=%d recv signo=%d", getpid(), signo);
s_signo = signo;
}
void master_process_init() {
for (int i = 0; i < MAXNUM_WORKER; ++i) {
s_worker_processes[i] = -1;
}
}
void master_process_cycle() {
char proctitle[256] = {0};
snprintf(proctitle, sizeof(proctitle), "%s: master process", g_main_ctx.program_name);
setproctitle(proctitle);
signal(SIGINT, master_process_signal_handler);
signal(SIGCHLD, master_process_signal_handler);
signal(SIGNAL_TERMINATE, master_process_signal_handler);
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGCHLD);
sigaddset(&sigset, SIGNAL_TERMINATE);
sigprocmask(SIG_BLOCK, &sigset, NULL);
create_worker_process(get_ncpu());
sigemptyset(&sigset);
while (1) {
sigsuspend(&sigset);
switch (s_signo) {
case SIGINT:
case SIGNAL_TERMINATE:
hlogi("killall worker processes");
for (int i = 0; i < MAXNUM_WORKER; ++i) {
if (s_worker_processes[i] <= 0) break;
kill(s_worker_processes[i], SIGKILL);
s_worker_processes[i] = -1;
}
msleep(1000);
exit(0);
break;
case SIGCHLD:
{
pid_t pid = 0;
int status = 0;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
hlogw("worker process stop/waiting, pid=%d status=%d", pid, status);
for (int i = 0; i < MAXNUM_WORKER; ++i) {
if (s_worker_processes[i] == pid) {
s_worker_processes[i] = -1;
break;
}
}
create_worker_process(1);
}
}
break;
default:
break;
}
}
}
#endif
完整的main模板
见https://github.com/ithewei/hw.git下的main.cpp.tmpl
文件
repo中提供了Makefile文件
编译步骤如下:
make test
bin/test -d
ps aux | grep test