我正在编写守护程序,能够恢复工作流程CentOS release 5.7 (Final)
。
以下是代码示例:
#define CHILD_NEED_WORK 1
#define CHILD_NEED_TERMINATE 2
int ReloadConfig()
{
....
return 0;
}
void DestroyWorkThread()
{...}
int InitWorkThread()
{
...
return 0;
}
int LoadConfig(char* FileName)
{
...
return 0;
}
void SetPidFile(char* Filename)
{
FILE* f;
f = fopen(Filename, "w+");
if (f)
{
fprintf(f, "%u\n", getpid());
fclose(f);
}
}
int SetFdLimit(int MaxFd)
{
struct rlimit lim;
int status;
lim.rlim_cur = MaxFd;
lim.rlim_max = MaxFd;
status = setrlimit(RLIMIT_NOFILE, &lim);
return status;
}
//Monitor process
int MonitorProc()
{
int pid;
int status;
int need_start = 1;
sigset_t sigset;
siginfo_t siginfo;
parent_pid = getpid();
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGCHLD);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetPidFile(PID_FILE);
for (;;)
{
if (need_start)
{
pid = fork();
}
need_start = 1;
if (pid == -1)
{
}
else if (!pid)
{
status = WorkProc();
exit(status);
}
else
{
sigwaitinfo(&sigset, &siginfo);
if (siginfo.si_signo == SIGCHLD)
{
wait(&status);
status = WEXITSTATUS(status);
if (status == CHILD_NEED_TERMINATE)
{
Write("[MONITOR] Child stopped");
break;
}
else if (status == CHILD_NEED_WORK)
{
Write("[MONITOR] Child restart");
}
}
else if (siginfo.si_signo == SIGUSR1)
{
kill(pid, SIGUSR1);
need_start = 0;
}
else if (siginfo.si_signo == 0)
{
need_start = 0;
continue;
}
else
{
Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
kill(pid, SIGTERM);
status = 0;
break;
}
}
}
Write("[MONITOR] Stop");
unlink(PID_FILE);
return status;
}
//Work process
int WorkProc()
{
struct sigaction sigact;
sigset_t sigset;
int signo;
int status;
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = signal_error_for_backtrace;
sigemptyset(&sigact.sa_mask);
sigaction(SIGFPE, &sigact, 0);
sigaction(SIGILL, &sigact, 0);
sigaction(SIGSEGV, &sigact, 0);
sigaction(SIGBUS, &sigact, 0);
sigemptyset(&sigset);
sigaddset(&sigset, SIGQUIT);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
SetFdLimit(FD_LIMIT);
status = InitWorkThread();
if (!status)
{
for (;;)
{
sigwait(&sigset, &signo);
if (signo == SIGUSR1)
{
status = ReloadConfig();
if (status)
{
Write("[DAEMON] Reload config failed");
}
else
{
Write("[DAEMON] Reload config OK");
}
}
else
{
break;
}
}
DestroyWorkThread();
}
else
{
Write("[DAEMON] Create work thread failed");
}
Write("[DAEMON] Stopped");
return CHILD_NEED_TERMINATE;
}
int main(int argc , char *argv[])
{
if (argc != 2)
{
printf("Usage: ./test_daemon.conf failed!\n");
return -1;
}
status = LoadConfig(argv[1]);
if (status)
{
printf("Error: Load config failed\n");
return -1;
}
if (CheckForAnotherInstance())
{
printf("Daemon is already running!\n");
return 1;
}
pid = fork();
if (pid == -1)
{
printf("Error: Start Daemon failed (%s)\n", strerror(errno));
return -1;
}
else if (!pid)
{
umask(0);
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
//Monitor process startup
status = MonitorProc();
return status;
}
else
{
return 0;
}
return 0;
}
我使用两个过程:工作过程,它产生主要工作和监视过程,等待来自工作过程的信号,并在它收到所需信号时重新启动它们。当我尝试向父进程发送信号 - 监视进程 - 使用命令 kill -s SIGCHLD 时,它会收到此信号。
当我尝试终止子进程时,父进程没有收到SIGCHLD信号 - 它等待信号的连接,子进程转换为僵尸。
但是当我使用实用程序 strace 和父进程时,一切正常 - 子进程成功终止,父进程收到SIGCHLD信号。
我读到了函数waitpid(),它用于接收SIGCHLD信号,但我也希望在父进程中接收另一个信号。
有什么想法吗?
答案 0 :(得分:0)
我的猜测,在第一次分叉之前没有安装信号处理程序?
答案 1 :(得分:0)
您SIG_BLOCK SIGCHLD所以不会收到任何信号。但是当你继续使用sigwaitinfo()但是在执行wait()时你没有使用siginfo.si_pid时,你应该使用waitpid()来清理由于通过sigwaitinfo同步接收信号的PID ()。
您首先使用WEXITSTATUS()而不检查WIFEXITED(状态)。请参阅wait()手册页。
您的监视器和工作进程似乎使用与执行fork()之后的exec()相同的可执行文件。所以要小心,因为你可能会恢复子进程中的信号处理程序状态,以使子进程中的代码正常运行。 例如,监控进程是父进程?所以为了得到一个孩子它做fork()然后调用WorkProc()。在WorkProc()内部,它会阻止一堆信号(但不是SIGCLD)。但是执行是来自MonitorProc()的sigprocmask(SIG_BLOCK,...)仍然在WorkProc()内部处于活动状态。
我不确定“if(siginfo.si_signo == 0)”是什么。
所以另一个你的主要查询是什么SIGCHLD没有从运行WorkProc()函数的进程传递的原因,是因为你已经在MonitorProc()中阻止了那个信号。所以修复这个问题使用sigprocmask()的第三个参数来保存MonitorProc()中的原始块/解除阻塞掩码,当你fork()并跳转到WorkProc()之前恢复阻塞/解除阻塞掩码。