守护进程
守护进程(daemon)是在后台运行的不受终端控制的进程.Unix系统中一般有很多守护进程在后台运行(20至50个),执行不同的管理任务.
想要脱离所有的终端的原因是守护进程可能是从终端上(与初始化脚本中启动相反),在这之后这个终端要能用来执行其他任务.举例来说,如果在某终端上启动了一个守护进程后从终端上注销,其他人又从该终端登录,那么任何守护进程的错误信息不应在后面用户的终端会话过程中出现.同样,由终端上的一些键所产生的信号(如中断信号),不应对以前从该终端上启动的任何守护进程造成影响.虽然使服务器程序在后台运行很容易(只要在sheel命令后的结尾加一个&符号),但是我们还是应该使程序能自动转到后台并且脱离与终端的联系.
有好几种方法启动守护进程:
1. 在系统启动时很多守护进程都是由系统初始化脚本启动.这些脚本一般在/etc/目录或以/etc/rc开头的目录下,它们的位置和内容依赖于具体的实现.由这些脚本启动的守护进程在开始时都拥有超级用户权限.
2. 许多网络服务器是由inetd超级服务器启动的,在本文的后面会对此进行详细介绍.inetd自己是由上一步中的某个脚本启动的.inetd监听网络请求(telnet,FTP等),当请求到来时启动实际的服务器(telnet服务器,FTP服务器等)
3. cron守护进程按规则定期执行一些程序,由它启动的程序也以守护进程的方式运行.cron自己是在系统启动工程中由第一步启动的.
4. 可用at命令指定在将来的某一时刻执行程序.cron实据进程在到达相应的时间时会启动这些程序,所以它们是以守护进程的方式运行的.
5. 不管是在前台还是在后台,守护进程也可以在用户终端上启动,这在测试守护进程或守护进程因某些原因终止而要重启时经常使用.
由于守护进程没有控制终端,在发生问题时它要用一些其他方式以输出消息.这些消息既有一般的通告消息,也有需要管理员处理的紧急事件消息.syslog函数是输出这些消息的标准方式,它将消息发往syslogd守护进程.
inetd守护进程
在典型的Unix系统中都有很多服务器在运行,等待客户的请求.例如FTP,Telnet,TFPT等等.在4.3BSD版本前的Unix系统中,这些服务都有一个与之对应的进程.这些进程都是在系统启动时从/etc/rc文件里启动,它们启动时所做的工作差不多一样:创建套接口,给它捆绑作所周知端口,等待连接(如果是TCP)或数据报(如果是UDP),然后fork.子进程为客户服务,父进程继续等待下一个客户请求.该模型有两个问题:
1. 这些守护进程都有几乎相同的启动代码,首先是创建套接口,还要考虑变成守护进程(与daemon_init)函数类似).
2. 每个守护进程在进程表中 要占用一项,但它们在大部份时间里猜于睡眠状态.
4.3BSD版本通过提供一个因特网超级服务器:inetd守护进程这些问题简单化.基于TCP或UDP的服务器都可以使用这个守护进程.它不处理其他的协议,如Unix域套接口.该守护进程解决了刚才提到的两个问题.
1. 因为大部分启动时要做的工作由inetd处理了,所以守护进程的编写得到简化.这避免了每个服务器程序都要调用daemon_init函数.
2. 单个进程(inetd)能为多的服务等待客户的请求,取代了每个服务一个进程的方式,这样减少了系统的进程总数.
inetd进程使用前面介绍的daemon_init函数中的技术将自己变成一个守护进程,然后读入并处理它的配置文件,通常为/etc/inetd.conf.这个文件配置超级服务器处理的服务,以及当一个服务请求到来时怎么做.文件中每后所包含的栏目如下所示;下面是一些实况例:
ftp stream tcp nowait root /usr/bin/ftpd ftpd -l
telnet stream tcp nowait root /usr/bin/telnetd telnetd
login stream tcp nowait roto /usr/bin/rlogind rlogind -s
tftp dgram upd wait nobody /usr/bin/tftpd tftpd -s /tfptboot
服务器程序的实际名字总是在由inetd调用exec执行时作为第一个参数传送给它;上例中各个字段含义如下:
service-name
必须是在/etc/services文件作已定义的服务名
socket-type
stream(TCP)或dgram(UDP)
protocol
必须是在/etc/protocols文件中已有定义:tcp或udp
wait-flag
一般TCP是nowait,UDP是wait
login-name
/etc/password中的用户名,一般为root
server-program
exec使用的绝对路径
server-program-arguments
exec使用的参数
上表只是作为例子引用,许多厂商都在inetd中加入了一些自己的功能.如在TCP和UDP服务器之外,加入处理远程过程调用(RPC)服务器的能力;以及处理除TCP和UDP外其他协议的能力.当然,exec服务器程序使用的路径名和命令行参数依赖于具体的实现.IPv6与/etc/inetd.conf的交互依赖于厂商的实现.一些厂商用名为tcp6和udp6的protocol表示应为该服务创建一个IPv6套接口.
下面给出inetd守护进程的工作流程:
1. 启动时读/etc/inetd.conf文件并给文件中指定的所有服务创建一个相应类型的套接口(字节流或数据报).inetd能支持理当服务器的数目依赖于它最多能创建的描述字的数目.每个新创建的套接口都被加入到select调用所用到的描述字集中.
2. 为每个套接口调用bind,给它们捆绑服务器的众所周知端口和通配地址.它们的TCP或UDP端口号是通过调用getservbyname获得的,其中使用了配置文件中的service-name和protocols字段作为参数.
3. 对TCP套接口调用listen,以接受外来的连接请求.对数据报套接口则不做这一步.
4. 所有套接口建立后,调用select等待这些套接口变为可读.(当在TCP套接口上到来一个新的连接请求或在UDP套接口到来一个数据报时它们会变成可读).inetd在大部分时间里阻塞在select调用上,等待有一个套接口变成可读.
5. select返回一个可读套接口后,如果是一个TCP套接口,就调用accept这个新的连接.
6. inetd守护进程fork,由子进程处理服务请求.这个标准的并发服务器类似.
子进程关闭除要支持处理的套接口描述字之外的所有描述字;对TCP服务器来说是由accept返回的新的即连接套接口,对UDP服务器则是最初的那个UDP套接口.
子进程三次调用dup2,把套接口描述字复制到描述字0,1,2(标准输入,标准输出和标准错误输出).然后关闭原套接口描述字.这样,子进程打开的描述字就只有0,1和2.如果子进程读标准输入,它实际是从套接口读.写标准输出和标准错误输出也是写到套接口.
子进程调用getpwnam得到在配置文件中指定的login-name对应的保密字文件项.如果login-name不是root,子进程会调用setgid和setuid变为指定的用户.(因为inetd是以UID为0运行,子进程跨fork继承了这个用户ID,所以它能变成任何用户).
子进程用exec执行相应的server-program处理请求,并将配置文件中指定的参数传递给它.
如果是一个字节流套接口,父进程必须关闭已连接套接口(就像标准并发服务器那样).父进程再调用select以等待下一个变成可读的套接口.
搜索更多相关主题的帖子:
进程 守护