BBS水木清华站∶精华区
发信人: forest (轻寒小楼~~小吉篇), 信区: Unix
标 题: [转载]unix环境高级编程-13 (转载)
发信站: BBS 水木清华站 (Fri Mar 17 17:52:08 2000)
【 以下文字转载自 Linux 讨论区 】
【 原文由 SuperSB 所发表 】
发信人: taosm (128+64-->cool), 信区: unix
标 题: unix环境高级编程--第13章 精灵进程
发信站: 西十八BBS (Sat Mar 11 13:53:31 2000), 转信
第十三章 精灵进程
13.1 介绍
精灵(Daemons)是生存期长的一种进程。它们常常在系统引导装入时启动,在系
统关闭(shutdown)时终止。因为它们没有控制终端,所以我们说它们是在后台运
行的。UNIX系统有很多精灵进程,它们执行日常事物活动。
本章说明精灵的进程结构,以及如何编写精灵进程程序,因为精灵没有控制终端,
我们需要了解在有关事物出问题时,精灵进程如何报告出错情况。
13.2 精灵进程的特征
让我们先来察看一些常用的系统精灵进程,以及它们怎样和第九章中所叙述的概念
:进程组、控制终端和对话期相关联。ps(1)命令打印系统中各个进程的状态。该
命令有多个选择项,有关细节请参考系统手册。为了察看本节讨论中所需的信息,
在4.3+BSD或SUN OS 系统下执行:
ps -axj
选择项-a显示由其他用户(others)所拥有的进程的状态;-x显示没有控制终端的
进程的状态。-j显示与作业有关的信息:对话期ID、进程组ID、控制终端以及终端
进程组ID。在SVR4之下,与此相类似的命令是ps -efjc。(在某些符合"美国国防
部安全性准则"要求的UNIX系统中,只能使用ps 查看自己所拥有的进程。)ps的输
出大致是:
PPID PID PGID SID TT TPGID UID COMMAND
0 0 0 0 ? -1 0
wapper
0 1 0 0 ? -1 0
sbin/init -
0 2 0 0 ? -1 0
agedaemon
1 80 80 80 ? -1 0
yslogd
1 88 88 88 ? -1 0
usr/lib/sendmail -bd -qlh
1 105 37 37 ? -1 0
pdate
1 108 108 108 ? -1 0
ron
1 114 114 114 ? -1 0
netd
1 117 117 117 ? -1 0
usr/lib/lpd
其中,已移去了一些我们并无兴趣的列,例如累计CPU时间。按照顺序,各列标题
的意义是:父进程ID、进程ID、进程组ID、终端名称、终端进程组ID(与该控制终
端相关的前台进程组)、用户ID以及实际命令字符串。
这些ps命令在支持对话期ID的系统(SUN OS)上运行,我们曾在9.5节的sets
id函数中提及对话期ID。它是对话期首进程的进程ID。但是,4.3+BSD系统将打印
与本进程所属进程组对应的session结构的地址(见9.11)
进程0、1以及2是8.2节中所述的进程。这些进程非常特殊,存在于系统的整个生命
期中。它们没有父进程ID,没有组进程ID,也没有对话期ID。syslogd精灵进程可
用于任何为操作人员记录系统消息的程序。可以在一台实际的控制台上打印这些消
息,也可将它们写到一个文件中(在13.4.2中将对syslog设施进行说明)。sendm
ail是标准邮递精灵进程。update程序定期将核心缓存中的内容写到硬盘上(通常
是每隔30秒)。为了做到这一点,该程序每隔30秒调用sync(2)函数一次(在4.
24节中已对sync进行了说明)。cron精灵进程在指定的日期和时间执行指定的命令
。许多系统管理任务是由cron定期地使相关程序执行而得以实现的。我们已在9.3
节中提到inetd精灵进程。它监听系统的网络界面,以输入对各种网络服务器的请
求。最后一个精灵进程,lpd,处理对系统提出的各个打印请求。
注意,所有精灵进程都以超级用户(用户ID为0)的优先权运行。没有一个精灵进
程具有控制终端-终端名称设置为问号(?)、终端前台进程组ID设置为-1。缺少
控制终端可能是精灵进程调用了setsid的结果。除update以外的所有精灵进程都是
进程组的首进程,对话期的首进程,而且是这些进程组和对话期中的唯一进程。u
pdate是它所在进程组(37)和对话期(37)中的唯一进程,但是该进程组的首进
程(可能也是该对话期的首进程)已经终止。最后,应当引起注意的是所有这些精
灵进程的父进程都是init进程。
13.3 编程规则
在编写精灵进程程序时需遵循一些基本规则,以便防止产生并不希望的交互作用。
下面先说明这些规则,然后是一个按照规则编写的函数daemon-init。
1. 首先做的是调用fork,然后使父进程终止。这样做实现了下面几点。第一,如
果该精灵进程是由一条简单shell命令启动的,那么使父进程终止使得shell认为这
条命令已经执行完成。第二,子进程继承了父进程的进程组ID,但具有一个新的进
程ID,这就保证了子进程不是一个进程组的首进程。这对于下面就要做的setsid调
用是必要的前提条件。
2. 调用setsid以创建一个新对话期。于是执行9.5节中列举的三个操作。使调用进
程:(1)成为新对话期的首进程,(2)成为一个新进程组的首进程,(3)没有
控制终端。
在SVR之下,有些人建议在此时再调用fork,并使父进程终止。第二个子进程作为
精灵进程继续运行。这样就保证了该精灵进程不是对话期首进程,于是按照SVR4规
则(见9.6节)可以防止它取得控制终端。另一方面,为了避免取得控制终端,无
论何时打开一个中断设备都要指定O-NOCTTY。
3. 将当前工作目录更改为根目录。从父进程继承过来的当前工作目录可能在一个
装配的文件系统中。因为精灵进程通常在系统再引导之前是一直存在的,所以如果
精灵进程的当前工作目录在一个装配文件系统中,那么该文件系统就不能被拆卸。
另外,某些精灵进程可能会把当前工作目录更改到某个指定位置,在此位置做它们
的工作。例如,行式打印机假脱机精灵进程常常将其工作目录更改到它们的spool
目录上。
4. 将文件方式创建屏蔽字设置为0。由继承得来的文件方式创建屏蔽字可能会拒绝
设置某些许可权。例如,若精灵进程要创建一个组可读、写的文件,而继承的文件
方式创建屏蔽字,屏蔽了这两种许可权,则所要求的组可读、写就不能起作用。
5. 关闭不再需要的文件描述符。这样使精灵进程就不再持有从其父进程继承来的
某些文件描述符(父进程可能是shell进程,或某个其它进程)。但是,究竟关闭
那些描述符则与具体的精灵进程有关,所以在下面的例子中不包含此步骤。可以使
用程序2.3中的open_max函数来决定最高文件描述符值,并关闭直到该值的所有描
述符。
实例
程序13.1是个函数,可由想初始化成为一个精灵进程的程序调用。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ourhdr.h"
int
daemon_init(void)
{
pid_t pid;
if ( (pid = fork()) < 0)
return(-1);
else if (pid != 0)
exit(0); /* parent goes bye-bye */
/* child continues */
setsid(); /* become session leader */
chdir("/"); /* change working directory */
umask(0); /* clear our file mode creation mask */
return(0);
}
程序13.1 初始化一个精灵进程
若daemon-init函数由main函数调用,然后进入睡眠状态,那么我们可以用ps命令
检查该精灵进程的状态:
$a.out
$ps_axj
PPID PID PGID SID TT TPGID UID COMMAND
1 735 735 735 ? -1 224 a.out
从中可以看到,该精灵进程已被正确地初始化。
13.4 出错记录
与精灵进程有关的一个问题是如何处理出错消息。因为它没有控制终端,所以不能
只是写到标准出错输出上。在很多工作站上,控制台设备运行一个窗口系统,所以
我们不希望所有精灵进程都写到控制台设备上。我们也不希望每个精灵进程将它自
己的出错消息写到一个单独的文件中。对系统管理人员而言;如果要关心哪一个精
灵进程写到哪一个记录文件中,并定期地检查这些文件,那么一定会使他感到头痛
。所以,需要有一个集中的精灵进程出错记录设施。
贝克莱开发了BSD syslog 设施,并广泛应用于4.2BSD。从4.×BSD导出的很多系统
都支持syslog。在13.4.2中说明该设施。
在系统V中,从来没有一个集中的精灵进程记录设施。SVR4支持BSD风格的syslog设
施,在SVR4之下的inetd精灵进程使用syslog。在SVR中,syslog的基础是/dev/lo
g流设备驱动程序,在下一节对此进行说明。
13.4.1 SVR4流log驱动程序
SVR4提供了一种流设备驱动程序,其界面具有流出错记录,流事件跟踪以及控制台
记录功能。有关文献包含在[AT&T1990d]的log[7]中。图13.1详细给出了这种设施
的整个结构。
图13-1 SVR4 log 设施
有三个记录进程(logger),它们是:出错记录进程、跟踪记录进程以及控制
台记录进程。每一条记录消息可以送给其中之一。
下面介绍三种产生记录消息的方法,三种读记录消息的方法。
·产生记录消息
1. 在核中的例程可以调用strlog以产生记录消息。这种方法通常由流模块和流设
备驱动程序用于出错消息或跟踪消息(跟踪消息常用在新的流模块或驱动程序的排
错中)。因为我们无意编写核中的例程,所以不详细说明这种消息产生方法。
2. 一个用户进程(例如一个精灵进程)可以用putmsg将消息送到/dev/log。这种
消息可被送到三个记录进程中的任一个。
3. 一个用户进程(例如一个精灵进程)可以用write将消息写到/dev/conslog。这
种消息只能送向控制台记录进程。
·读记录消息
4.标准的出错记录进程是strerr(1M)。它将记录消息增写到在目录/var/adm/st
ream下的一个文件中。该文件名是error.mm_dd,其中,mm是月份,dd是天数。St
rerr本身是个精灵进程,通常在后台运行,它将记录消息增写到该文件中。
5.标准的跟踪记录进程是strace(1M)。它能有选择地将一套指定的跟踪消息写至
其标准输出。
6.标准控制台记录进程是syslogd,这是一个BSD导出程序,我们将在下一节对此进
行叙述。此进程是个精灵进程,它读一个配置文件,然后将记录消息写至一个指定
的文件(控制台是一个文件)或登录用户,或将该消息发送给在另一台主机上的s
yslog精灵进程。
虽然上面没有提及,但用户也可以用自己的进程代换任何一个系统提供的标准精灵
进程。我们可以提供自己的出错记录进程、跟踪记录进程或控制台记录进程。
每则记录消息除消息本身外,还包含有一些其它信息。例如,由log驱动程序
沿逆流方向发送的消息,还包含有下列消息:哪个模块产生此消息(如果该消息是
由核中的一个流模块产生的)、级别(level)、优先级、某些标志、以及消息产
生的时间。有关细节请参阅手册中的log(7)。如果使用prtmsg产生一则记录消息
。我们也可以设置这些字段中的几个。如果调用write将一则消息发送至控制台记
录进程(通过/dev/conslog),则只能发送消息字符串。
图13.1中没有显示的另一种可能性是:由一个SVR4精灵进程调用BSD syslog(
3)函数。用这种方法可将消息发送至控制台记录进程,这与用putmsg向/dev/log
发送消息类似。使用syslog,我们可以设置消息的优先权字段。我们将在下一节讨
论此函数。
当产生了某种类型的记录消息,但是相应类型的记录进程却不在运行时,log驱动
程序丢弃该消息。
不幸的是,在SVR4中,使用这种log设施带有任怠性。一些精灵进程使用它,而大
多数由系统提供的精灵进程则编写成,直接写向控制台。
syslog(3)函数和syslogd(1M)精灵进程的有关文档在BSD兼容库文档部分[AT&T1
990c],但是它们本身并不在此库中,它们在所有用户进程(精灵进程)都可使用
的标准C库中。
13.4.2 4.3+BSD syslog 设施
自4.2BSD以来,广泛地应用了BSD syslog设备。大多数精灵进程使用这一设施
。图13.2出示了syslog设施的详细组织结构。
图13.2 4.3+BSD syslog设施
有三种方法产生记录消息:
1. 核例程可以调用log函数。任何一个用户进程通过打开和读/dev/klog设备就可
以读取这些消息。因为我们无意编写核中的例程,所以不再进一步说明此函数。
2. 大多数用户进程(精灵进程)调用syslog(3)函数以产生记录消息。我们将在
下面说明其调用序列。这使消息发送至UNIX域数据报套接口/dev/log。
3. 在此主机上,或用TCP/IP网联到此主机的某一其它主机上的一个用户进程可将
记录消息发向UDP端口514。注意:syslog函数并不产生这些UDP数据报文-它们要求
产生此记录消息的进程具有显式的网络编程。
关于UNIX域套接口以及UDP套接口的细节,请参阅stevens[1990]。
通常,syslogd精灵进程读取三种格式的记录消息。此精灵进程在启动时读一个配
置文件。一般,其文件名为/etc/syslog.conf,该文件决定了不同种类的消息应送
向何处。例如,紧急消息可被送向系统管理员(若已登录),并在控制台上显示,
而警告消息则可记录到一个文件中。
该设施的界面是syslog函数。
_______________________________________________________________________
___
#include <syslog.h>
void openlog(char *ident, int option, int facility);
void syslog(int priority, char *format, …);
void closelog(void);
_______________________________________________________________________
___
调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调
用openlog。调用closelog也是可选择的-它只是关闭被用于与syslogd精灵进程通
信的描述符。
调用openlog使我们可以指定一个ident,以后,此ident将被加至每则记录消息中。
Ident一般是程序的名称(例如,cron,inetd等)。图13.3说明了4种可能的选择
项。
openlog中的参数facility可以选取图13.4中列举的值。设置facility参数的目的
是让配置文件可以说明,来自不同设施的消息以不同的方式进行处理。如果不调用
openlog,或者以facility为0来调用它,那么我们在调用syslog时,可将facilit
y作为priority参数的一个部分进行说明。
调用syslog产生一个记录消息。其priority参数是facility和level的组合,它们
可选取的值分别列于facility(示于图13.4)和level(示于图13.5)中。level值
按优先级从最高到最低按序排列。
format参数以及其它参数传至vsprintf函数以便进行格式化。在format中,每个%
m都被代换成对应于errno值的出错消息字符串(strerror)。
SVR4和4.3+BSD都提供logger(1)程序,以其作为向syslog设施发送出错消息的方法
。送至该程序的可选择参数可以指定facility、level以及ident。logger的意图是
用于以非交互方式运行,又要产生记录消息的shell过程。
logger命令的格式正由POSIX.2标准化。
实例
在第十七章的PostScript打印机精灵进程中,包含有下面的调用序列:
openlog("lprps",LOG_PID,LOG_LPR);
syslog(LOG_ERR,"open error for %s: %m",filename);
第一个调用将ident字符串设置为程序名,指定打印该进程ID,并且将系统默认的
facility设定为行式打印机系统。对syslog的实际调用指定一个出错条件和一个消
息字符串。如若不调用openlog,则第二个调用的形式可能是:
systog(LOG_ERR|LOG_LPR,"open error for %s: %m",filename);
其中,我们将priority参数指定为level和facility的组合。
图13.3 openlog的选择参数
图13.4 openlog的facility参数
图13.5 syslog中的levels(按序排列)
13.5 客户-服务器模型
精灵进程常常用作为服务者进程。确实,在图13.2中,我们可以称syslogd进程为
服务员,用户进程(客户)用UNIX域数据报套接口向其发送消息。
一般而言,服务者是一个进程,它等待客户与其联系,提出某种类型的服务要求。
在图13.2中,由syslogd服务者提供的服务是记录出错消息。
在图13.2中,在客户和服务者之间的通信是单向的。客户向服务者发送其服务要求
,服务者则不向客户回送任何消息。在下面有关进程通信的几章中,有大量实例,
其中有客户和服务者之间的双向通信。客户向服务者发送要求,服务者则向客户回
送回答。
13.6 摘要
在大多数Unix系统中,精灵进程是一直运行的。为了初始化我们自己的精灵进程,
需要一些审慎的思索并理解在第九章中说明过的进程之间的关系。本章中,我们开
发了一个可由精灵进程调用,对其自身正确地进行初始化的函数。
我们也讨论了精灵进程记录出错消息的几种方法,精灵进程通常没有控制终端。在
SVR4下,可以使用流记录驱动程序,在4.3+BSD之下,提供了syslog设施。因为SV
R4也提供BSD syslog设施,所以在下面的章节中,当精灵进程需要记录出错消息时
,将调用syslog函数。在第十七章中,PostScript打印机精灵进程就包含有这种情
况。
习题:
13.1 从图13.2可以看出,直接调用openlog或第一次调用syslog都可以初始化sys
log,此时一定要打开用于Unix域的数据报套接口的特殊设备文件/dev/log。如果
调用openlog前用户进程(精灵进程)先调用了chroot,结果如何?
13.2 列出你的系统中所有的精灵进程,并说明它们的功能。
13.3 编写一段调用程序13.1中daemon_init函数的程序。调用该函数后调用getlo
gin(8.14节)查看该精灵进程是否有登录名。若程序带有3 > /tmp/name1运行时(
Bourne Shell或Kornshell),则将登录名打印到文件描述符3,并重定向到一个临
时文件。在调用deamon_init和getlogin之间关闭描述符1、2和3,此时再运行该程
序会有什么不同?
--
--
※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.38.248.38]
BBS水木清华站∶精华区