四、守护进程的出错处理
- 4.1、openlog()函数
- 4.2、syslog()函数
- 4.3、closelog()函数
- 4.4、代码示例
书接上文守护进程的基础知识,这一篇继续来说明守护进程相关的出错处理。
四、守护进程的出错处理
因为守护进程完全脱离终端控制,所以不能像其它进程一样将错误信息输出到控制终端。所以如何处理错误消息是一个问题。在Linux系统中,一般通用的办法是使用syslog服务,将程序中出错信息输入到系统日志文件中(如"/var/log/messages",此文件需要用用root权限的用户查看),从而可以直观地看到程序的问题所在。在不同的Linux发行版中,系统日志的文件路径全名可能有所不同(比如可能是"/var/log/syslog")。
syslog 是Linux系统日志管理服务,通过守护进程 syslogd 来维护。改守护进程在启动时会读取一个配置文件“/etc/syslog.conf”。该文件决定了不同种类的信息会发送到何处。
自4.2BSD依赖,BSD的syslog设施得到了广发的应用,大多数守护进程都使用这一设施。syslog的详细组织结构如图4.1所示。
图4.1 syslog的详细组织结构图
3 个 syslog 相关的函数,分别是 openlog()、syslog()、closelog()。通常,openlog() 用来打开系统日志服务的一个连接,syslog() 函数用来想日志文件中写入消息,在这里可以规定消息的优先级,消息输出格式等,closelog()
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
4.1、openlog()函数
功能: 用于打开与程序的系统记录器的连接参数: ident :要向每个消息加入的字符串,通常为程序的名称 option:用于指定各个选项的为屏蔽。可选的参数为: LOG_CONS :如果消息无法发送到系统日志服务,则直接输出到系统控制终端上 LOG_NDELAY:立即打开系统日志服务的连接 LOG_NOWAIT:不要等待可能已经创建的子进程,此选项对Linux无效果 LOG_ODELAY:与LOG_NDELAY相反,连接的打开被延迟,直到调用 syslog() LOG_PERROR:将消息同时发送到标准出错(stderr)上 LOG_PID :在每条消息中都包含进程的PID facility:指定程序发送的消息类型,可选的参数为: LOG_AUTH :安全/授权信息 LOG_AUTHPRIV : 安全/授权信息(私有) LOG_CRON :时间守护进程(cron 和 at) LOG_DAEMON :其他系统的守护进程 LOG_FTP :ftp的守护进程 LOG_KERN :内核消息(不能从用户进程生成) LOG_LOCAL[0~7]:保留给本地使用 LOG_LPR :行打印机子系统 LOG_MAIL :邮件子系统 LOG_NEWS :新闻子系统 LOG_SYSLOG :syslogd()内部产生的消息 LOG_USER(默认): 一般使用者等级消息 LOG_UUCP :UUCP子系统返回: 函数无返回
调用 openlog() 是可选择的。假设不调用 openlog(),则在第一次调用 syslog() 时,自己主动调用 openlog()。
4.2、syslog()函数
功能: 用于把日志消息发给系统程序syslogd记录 参数: priority:指定消息的重要性,可有以下选项: LOG_EMERG :系统无法使用 LOG_ALERT :需要立即采取措施 LOG_CRIT :有重要情况发生 LOG_ERR :有错误发生 LOG_WARNING:有警告发生 LOG_NOTICE :正常情况,但也有重要情况 LOG_INFO :信息消息 LOG_DEBUG :调试信息 format :以字符串指针的形式表示输出的格式,类似于printf中的格式返回: 函数无返回
4.3、closelog()函数
closelog() 函数关闭用来编写系统 closelog logger 的描述符。closelog() 函数的使用是可选的,因为在程序结束后,会自动关闭与 system log
4.4、代码示例
“/daemon.log”文件中写入内容。实现的代码如下所示。
#include <fcntl.h> // for O_APPEND ..
#include <stdio.h> // for perror ..
#include <stdlib.h> // for exit ..
#include <string.h> // for strlen
#include <sys/stat.h> // for umask
#include <sys/types.h> // for setsid
#include <syslog.h> // for openlog ..
#include <unistd.h> // for setsid
int main(int argc, const char *argv[])
{
pid_t pid = 0, sid = 0;
/* 创建子进程,父进程退出 */
pid = fork();
if (pid == -1) /* fork出错 */
{
perror("fork error");
exit(EXIT_FAILURE);
}
else if (pid == 0) /* 子进程 */
{
/* 打开系统日志服务器 */
openlog("daemon.syslog", LOG_PID, LOG_DAEMON);
/* 创建新的会话 */
if ((sid = setsid()) < 0)
{
/* 向系统日志服务器写入错误信息 */
syslog(LOG_ERR, "%s\n", "setsid() error");
exit(EXIT_FAILURE);
}
/* 改变当前的工作路径 */
if ((sid = chdir("/")) < 0)
{
syslog(LOG_ERR, "%s\n", "chdir() error");
exit(EXIT_FAILURE);
}
/* 改变进程本身的umask */
umask(0);
/* 关闭所有可能已打开的文件描述符 */
int num = getdtablesize(); /* 获取当前进程文件描述符表大小 */
for (i = 0; i < num; i++)
{
close(i);
}
/* 周期计数的变量 */
int cycleCnt = 0;
/* 至此,守护进程创建完成,以下正式开始守护进程的工作 */
while (1)
{
/* 周期运行计数自加 */
cycleCnt++;
/* 写入到系统日志中 */
syslog(LOG_NOTICE, "I'm Daemon Process, Running %d", cycleCnt);
/* 休眠片刻 */
sleep(2);
}
}
else /* 父进程 */
{
/* 父进程退出 */
exit(EXIT_SUCCESS);
}
/* 关闭系统日志服务器 */
closelog();
return 0;
}
编译并运行上述程序,输入指令 ps -ajx | grep "./syslog" 查看对应的进程状态,使用指令 cat /var/log/syslog 查看系统日志,总体显示效果图4.2所示。
图4.2 syslog的测试显示效果图