LinuxLinux学习之路

APUE读书笔记-13守护进程(3)

2020-07-02  本文已影响0人  QuietHeart

4、登陆错误

守护进程的一个问题就是如何处理错误消息。不能将消息简单地写入到标准错误输出中,因为守护进程并没有控制终端。我们也不想让守护进程往console设备上面写,因为在许多工作站上面,console设备运行在窗口系统中。我们也不想要每个守护进程将信息写入到一个特定的文件中,因为这样管理员需要知道哪些守护进程向哪些文件写,需要基于某个规则来检查这些文件,这是很麻烦的。所以,需要一个集中管理错误日志登记的守护进程工具。

BSD的syslog工具在4.2BSD中应用很多,大多数继承自BSD的系统都支持syslog。

直到SVR4,System V从来没有一个用来集中登记日志的守护进程工具。

syslog函数作为Single UNIX Specification的XSI扩展被包含。

由于BSD的syslog从BSD4.2之后工具应用很广,大多数的守护进程都使用这个工具。

                                    BSD的syslog工具

 +---------+        +---------+
 |   user  |        | syslogd |
 | process |        +-^--^--^-+
 +---------+          |  |  |
      |          -----+  |  +-------
  syslog        /        |          \
      |        /         |           \
+- - -|- - - -/- - - - - + - - - - - -\- - - - - - - - - +
|     v      /           |             \                 |
   +----------+     +---------+      +-----------+
|  | /dev/log |     |   UDP   |      | /dev/klog |       |
   +----------+     | port514 |      +-----^-----+
|  UNIX domain      +---------+            |             |
  datagram socket  Internet domain      log|
|                  datagram socket         |             |
                         ^           +-----------+
|                        |           |  kernel   |       |
                         |           |  routines |
|kernel                  |           +-----------+       |
+ - - - - - - - - - - - -|- - - - - - - - - - - - - - - -+
                         |
                   TCP/IP network

对于上图:

  1. syslogd守护进程读取/dev/log,UDP的514号端口,或/dev/klog,并把日志写入到文件或者发送到别的主机上面。
  2. 对于上面三个文件,用户进程通过syslog写/dev/log;来自TCP/IP的网络写UDP的514号端口;内核进程通过log写/dev/klog。

有三种方法生成日志消息:

  1. 内核例程可以调用log函数。这些消息可以通过打开/dev/klog文件被其他用户进程读取。我们这里不详细讨论这个函数了,因为我们不打算讨论编写内核例程。
  2. 大多数用户进程(守护进程)通过调用syslog函数生成日志消息。我们后面描述它。这会导致消息被发送到UNIX套接字/dev/log上面。
  3. 一个本地用户进程,或者通过TCP/IP网络连接到本地机器上面的其它主机,可以把日志消息发送到UDP的514端口上面。需要注意的是,syslog函数不会生成这些UDP数据报,他们需要进程通过网络编程的方式生成日志消息。

一般来说,syslogd守护进程读取所有三种类型的日志消息。在启动的时候,这个守护进程读取一个配置文件(一般为/etc/syslog.conf),这个配置文件决定了不同种类的消息被发送到哪里。例如,比较紧急的消息可以给发送到系统管理员,并且打印到console屏幕上面,而一些警告类型的消息可以被登记到一个特定的文件当中。

我们把syslog函数做为使用这个工具的接口。

#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int maskpri);

返回:前次的日志优先级mask值。

openlog函数的调用是可选的,如果第一次使用syslog的时候没有调用openlog,那么会自动调用openlog函数。closelog也是可选的,它只是关闭用户来和syslogd守护进程通信的文件描述符号。

openlog函数允许我们向每一条日志消息添加一个ident,一般它就是程序的名称(例如cron,inetd,等等)。option参数是一个位码(bitmask)的组合,可以用来指定各种选项。(注意对Single UNIX Specification 的XSI扩展支持)详细请见参考网址。大致如下:

  1. LOG_CONS :如果日志消息没有通过UNIX域的数据报发送给syslogd,那么会被写到console上面。
  2. LOG_NDELAY :立即打开到syslogd守护进程的UNIX 域数据报套接字,而不是等到第一条日志消息的登记。一般来说,这个套接字知道登记第一条日志的时候才会被打开。
  3. LOG_NOWAIT :不会等待那些可能会在登记日志过程中创建的子进程。这样会防止与捕获SIGCHLD的进程产生冲突,因为程序可能会在syslog调用wait的时候已经获取了子进程的状态(???)。
  4. LOG_ODELAY :延迟打开到syslogd守护进程的连接,直到登记第一条日志消息。
  5. LOG_PERROR :除了将日志消息发送给syslogd之外,也将其写到标准错误输出。(Solaris上不可用)
  6. LOG_PID :为每一条消息登记进程ID。这个用于调用fork创建子进程的守护进程来处理不同的请求(相对于一些守护进程,例如syslogd守护进程从来不会使用fork创建子进程)。

openlog函数的facility参数参见参考网址给出的表格,注意Single UNIX Specification 只给出了在一些平台上可用的常用的facility码的子集。facility参数存在的意义就是允许使用配置文件指出,来自不同facility的消息使用不同的方式处理。如果我们不使用openlog或者我们使用facility值为0来调用这个函数,我们仍然可以指定facility做为syslog函数priority参数的一个部分。 facility参数的值(例如LOG_FTP,LOG_CRON,LOG_DAEMON等)如下表所列:

                                openlog的参数值
+----------------------------------------------------------------------------------------+
|   facility   | XSI |                            Description                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_AUTH     |     | authorization programs: login, su, getty, ...                     |
|--------------+-----+-------------------------------------------------------------------|
| LOG_AUTHPRIV |     | same as LOG_AUTH, but logged to file with restricted permissions  |
|--------------+-----+-------------------------------------------------------------------|
| LOG_CRON     |     | cron and at                                                       |
|--------------+-----+-------------------------------------------------------------------|
| LOG_DAEMON   |     | system daemons: inetd, routed, ...                                |
|--------------+-----+-------------------------------------------------------------------|
| LOG_FTP      |     | the FTP daemon (ftpd)                                             |
|--------------+-----+-------------------------------------------------------------------|
| LOG_KERN     |     | messages generated by the kernel                                  |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL0   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL1   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL2   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL3   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL4   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL5   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL6   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LOCAL7   |  •  | reserved for local use                                            |
|--------------+-----+-------------------------------------------------------------------|
| LOG_LPR      |     | line printer system: lpd, lpc, ...                                |
|--------------+-----+-------------------------------------------------------------------|
| LOG_MAIL     |     | the mail system                                                   |
|--------------+-----+-------------------------------------------------------------------|
| LOG_NEWS     |     | the Usenet network news system                                    |
|--------------+-----+-------------------------------------------------------------------|
| LOG_SYSLOG   |     | the syslogd daemon itself                                         |
|--------------+-----+-------------------------------------------------------------------|
| LOG_USER     |  •  | messages from other user processes (default)                      |
|--------------+-----+-------------------------------------------------------------------|
| LOG_UUCP     |     | the UUCP system                                                   |
+----------------------------------------------------------------------------------------+

syslog函数用来产生日志消息,其中priority参数可以是openlog函数的facility参数和一个表示优先级别(level)的值的组合(按位或)。级别的值按照由高到低的顺序排列如下:

format参数以及剩下的参数被传送给了vsprintf函数,如果在format中遇到了'%m'那么它会被替换成相应于errno的错误消息的字符串。

setlogmask函数可以为进程用来设置日志的优先级屏蔽码(priority mask)。这个函数返回之前的屏蔽码。当日志优先级屏蔽码被设置的时候,消息不会被登记除非它们的优先级别被设置到优先级屏蔽码中。需要注意的是,如果设置日志优先级屏蔽码为0将会没有任何作用。

许多系统也提供logger程序,这个程序可以给syslog工具发送日志消息。尽管Single UNIX Specification没有定义任何选项,有些系统实现允许为这个程序指定一些选项参数,例如facility,level,和ident.logger命令可以用于没有交互但是需要产生日志消息的shell脚本。

举例:在一个打印机守护进程中,你可能会遇到这样的行:

openlog("lpd", LOG_PID, LOG_LPR);
syslog(LOG_ERR, "open error for %s: %m", filename);

第一个调用设置ident参数为程序的名称(lpd),用LOG_PID指定始终打印进程ID,设置默认的facility参数为行打印系统(LOG_LPR)。调用syslog函数指定为错误条件(LOG_ERR),并且指定了消息字符串。如果我们没有调用openlog那么我们可以用如下方式调用第二条语句:

syslog(LOG_ERR | LOG_LPR, "open error for %s: %m", filename);

这里我们将priority 参数指定为level和facility的组合。

除了syslog之外,有一些系统也提供了一个变体的函数,这个函数可以处理可变参数列表:

#include <syslog.h>
#include <stdarg.h>
void vsyslog(int priority, const char *format, va_list arg);

本书中的所有四个平台都支持这个vsyslog函数,但是这个函数并没有被包含在Single UNIX Specification标准中。

大多数的syslogd实现都会对消息进行一个短期的排队。如果在这个期间有重复的消息到达,那么syslog守护进程会不登记这个消息,而是打印出一条消息,内容类似“上次这个消息已经重复了N次”。

译者注

原文参考

参考: APUE2/ch13lev1sec4.html

上一篇下一篇

猜你喜欢

热点阅读