BBS水木清华站∶精华区

发信人: SuperSB (孤鹰), 信区: Linux        
标  题: [转载]unix环境高级编程-13 
发信站: BBS 水木清华站 (Wed Mar 15 14:30:56 2000) 
 
 
 
 
发信人: 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水木清华站∶精华区