第十三章-守护进程
用户级守护进程是如何启动的?
在/etc/init.d下,所有开机自启动的程序都有一个对应的shell脚本,开机时,会自动执行这些脚本,进而启动了一些程序。 https://jingyan.baidu.com/article/9f63fb91e6b26288400f0e81.html
守护进程的特征
守护进程是一种生存期长的进程,常常在系统引导装入时启动,仅在系统关闭时才终止。
守护进程英文为daemon,所以很多守护进程的名字最后会有个d。
它们没有控制终端,都是在后台运行的。
使用 ps -axj 显示所有用户的进程(a),显示没有控制终端的进程(x),显示与作业相关的信息(j):进程ID,进程组ID,会话ID,控制终端,以及终端进程组ID。

方括号括起来的就是守护进程。
系统进程依赖系统实现,父进程ID为0的进程通常为内核进程,也是当系统引导装入时启动。
init进程是一个由内核在装入时启动的用户层次的命令。
Linux用kthreadd这一个特殊的内核进程来创建其他内核进程,所以会看到很多内核进程的父进程ID为2.所以内核进程的父进程ID要么是0要么是1.
内核进程是特殊的,通常存在于系统的整个生命周期,以超级用户特权执行,没有控制终端,无命令行。
对于需要在进程上下文执行工作但却不被用户层进程调用的每一个内核组件,都有自己的内核守护进程。可以理解成一些内核的功能用户进程不能直接调用,但内核守护进程可以使用。

守护进程分为内核守护进程(inetd)和用户级守护进程(例如mysqld)。
要注意的是,用户级守护进程的父进程均为init进程。
所以总结为:用户级守护进程的父进程ID为1,也即从init进程中fork而来。

大多数用户级守护进程是进程组的组长进程,且是会话的首进程,且是进程组和会话的唯一进程。
编程规则
1.首先要讲文件创建屏蔽字通过umask设置为想要的值,因为继承而来的文件创建屏蔽字可能屏蔽某些想要权限,如果守护进程需要创建文件,可能就需要设定特定的屏蔽字。如果守护进程调用了会创建文件的库函数,那么也需要给出屏蔽字,因为这些库函数没有传入创建文件屏蔽字的参数。
2.调用fork,然后让父进程exit。这样,虽然子进程继承了父进程的组ID,但是其进程ID肯定不等于组ID,这样满足了创建会话的进程不能是组长进程的前提。是调用setsid的先决条件。
3.调用setsid创建一个会话。这样该进程变成会话首进程,进程组组长进程,且没有控制终端。
4.将工作目录更改到根目录。或者更改到某个指定位置,并在此执行它的全部工作。下图可以理解成fork之前,父进程引用了一个文件系统,那么如果子进程不更改工作目录,由于是守护进程,会一直存在到系统关闭,那么该文件系统就不能被关闭/卸载。

5.关闭不再需要的文件描述符。
6.某些守护进程打开/dev/null使得其具有文件描述符0,1,2,也即此时的标准输入、输出、标准错误都是对/dev/null进行操作,然而这样的操作不会有任何效果,用以保证不会读取或显示内容到终端上。虽然本身守护进程就是没有控制终端的。
出错记录
守护进程的出错记录使用了syslog设施。

有三种办法能够产生日志消息:
1.内核例程可以调用log函数。任何一个用户进程都可以open并read /dev/klog设备来读取这些消息。
2.大多数用户进程调用syslog函数来产生日志消息。
3.本机或其他通过网络连接到本机的主机都可以向UDP514端口发送日志消息。
对于/dev/log 和/dev/klog上的日志消息,syslogd守护进程直接读取,对于发送到UDP514端口的数据,syslogd监听该端口并将传过来的信息读取出来。

#include<syslog.h>
void openlog(char* ident,int option, int facility);
void syslog(int facility,char* format);
void closelog();
int setlogmask(int maskpri);返回之前的日志记录优先级屏蔽字

***openlog是可选的,不调用openlog时,syslog会自动调用openlog;clostlog也是可选的,它只是关闭曾被用于与syslogd守护进程进行通信的描述符。
***openlog的作用相当于是配置了如何记录日志信息,option指定了记录的方式,facility在日志记录中表明该日志信息式由谁产生的。
***通过调用syslog记录一个日志信息,priority中给出level表明该日志信息的级别。
参数ident指定一个信息,这样ident的字样每次都会被加入到记录中,ident一般为程序的名字。
option参数是指定各种选项的位屏蔽:

facility参数表明指定某种设施的日志消息以不同的方式进行处理,如果不调用openlog或facility置为0来调用,那么可以在调用syslog时将facility作为priority参数的一部分说明。

调用syslog产生一个日志消息,其priority参数时facility和level的组合(通过按位或组合):

format参数则是要进行转换的格式化字符串,这个字符串会被写到日志记录中,而其中转换控制%m会被转换成与errno对应的出错消息字符串。
setlogmask设置一个记录优先级屏蔽字,这里不是说设置要屏蔽的优先级信息,而是如果没有被设置,则会被屏蔽。例如,一个LOG_DEBUG信息没有被设置到记录有限屏蔽字中 ,则这类信息不会被记录。
在这低8位中,如果相应位是0,则不记录,如果是1,相应级别log记录。例如:bit6,bit5,bit4位是1,其他位是0,则只记录LOG_INFO,LOG_NOTICT,LOG_WARNING级别的日志,其他的过滤掉。但是有一点要注意,如果bit7是1,LOG_DEBUG级别日志依然不记录到syslog中,其他位如果有1,相应级别日志则依然记录。猜测bit7已经不属于屏蔽字中。


大多数syslog实现会使消息短时间处于队列中,如果一个消息在队列中出现了多次,syslog守护进程不会写入多次该记录,而是打印输出一条类似"上一条消息重复了N次"的消息。
单实例守护进程

守护进程的惯例
守护进程遵循以下通用惯例:
1.若守护进程使用锁文件,那么该文件通常存储在/var/run中。可能需要超级用户权限才能在该目录下创建文件,锁文件的名字通常为name.pid,name是守护进程或服务的名字。
2.若守护进程需要配置文件,则配置文件应当存储在/etc目录下,名字通常为name.conf,例如syslog的配置文件为syslog.conf。
3.守护进程可以用命令行启动,但通常是由系统初始化脚本之一(例如/etc/rc或/etc/init.d/)启动的。如果当守护进程终止时,想要自动重启它,则可以在/etc/inittab中为该守护进程包括respawn记录项。
4.若守护进程有配置文件,应当在启动的是读且只读一次,若配置文件被修改了,应该接收一定的信号,来重新读取该配置文件,一般为用SIGHUP信号来处理。
客户进程-服务器进程模型

getlogin是否调用了类似getpwuid的函数,而不是真的看有无控制终端?
通过ps -ax显示出调用damonize函数(13-1的程序)后,进程确实是没有控制终端的,但是getlogin还是返回了登录名,stackflow论坛中指出getlogin可能实际上调用了getpwuid函数,所以还是能返回登录名,而不是getlogin说的在没有控制终端时返回NULL。
为什么关闭了标准输入后,cout没反应?
猜测cout和cin用的是同一FILE对象,该对象可读可写,所以关闭了标准输入或标准输出其一都不能正常执行。关闭其一后,cout不能工作,但程序不会终止,而cin不仅不能工作,还会导致程序终止。
init是所有linux进程的父进程,除去一些内核守护进程以外。所以我们的用户进程,追本溯源,其都是从init fork后exec而来的