书接上文守护进程的基础知识,这一篇继续来说明守护进程相关的出错处理。
四、守护进程的出错处理
因为守护进程完全脱离终端控制,所以不能像其它进程一样将错误信息输出到控制终端。所以如何处理错误消息是一个问题。在Linux系统中,一般通用的办法是使用syslog服务,将程序中出错信息输入到系统日志文件中(如"/var/log/messages",此文件需要用用root权限的用户查看),从而可以直观地看到程序的问题所在。在不同的Linux发行版中,系统日志的文件路径全名可能有所不同(比如可能是"/var/log/syslog")。
syslog 是Linux系统日志管理服务,通过守护进程 syslogd 来维护。改守护进程在启动时会读取一个配置文件“/etc/syslog.conf”。该文件决定了不同种类的信息会发送到何处。
自4.2BSD依赖,BSD的syslog设施得到了广发的应用,大多数守护进程都使用这一设施。syslog的详细组织结构如图4.1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/5444cb76c9462bdb52f9da8fe86aba04.png)
该机制提供了 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、代码示例
借用上面创建守护进程的代码,将写入文件的功能修改成为写入到系统日志文件中,每隔2秒时间向日志文件“/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所示。
![](https://i-blog.csdnimg.cn/blog_migrate/74d88a5d31a96645d031521af68a5e20.png)
好啦,废话不多说,总结写作不易,如果你喜欢这篇文章或者对你有用,请动动你发财的小手手帮忙 **点个赞**,当然 **关注一波** 那就更好了,就到这儿了,么么哒(*  ̄3)(ε ̄ *)。
![](https://i-blog.csdnimg.cn/blog_migrate/4ad81fbf60af0f0fe83f6b314f283e35.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7da657b8a306bb48321b7bdf69e5225f.gif)