粉丝不过W
守护进程概述
守护进程,一般为 Daemon进程,常在系统引导载入时启动,在系统关闭时终止。许多后台服务都是通过守护进程实现的
如,要使某个进程脱离控制终端,那就必须把该进程转换为守护进程
编写守护进程
编写守护进程要遵循一个特定的流程,创建一个简单的守护进程分 5 步
1 创建子进程,父进程退出
当父进程退出 就会在 shell 终端里造成一种程序已经运行完毕的假象,之后的工作都在子进程中完成,而用户在 shell 终端里就可以执行其他的命令,从而实现了 与控制终端的脱离
由于父进程已经先于 子进程 退出,会造成子进程没有父进程,这样就变成了一个孤儿进程
在 Linux 中,每当系统发现一个孤儿进程,就会自动由 1 号进程(也就是 init 进程)收养它,这样,原先的子进程就会变成 init 进程的子进程
关键代码:
pid = fork();
if(pid > 0)
{
exit(0); //父进程退出
}
2 在子进程中创建新会话
进程组:
进程组是一个或多个进程的集合,进程组由进程组 ID 来惟一标识
除了进程号(PID)之外,进程组 ID 也是一个进程的必备属性
每个进程组都有一个组长进程,而组长进程的进程号 = 进程组 ID,且该进程 ID 不会因组长进程的退出而受到影响
会话期:
会话组是一个或多个进程组的集合,一般 一个会话开始于用户登录,终止于用户退出,此期间的该用户运行的所有进程都属于这个会话期
关系图:
#include <sys/types.h>
#include <unistd.h>
/*
*创建一个新的会话,并担任该会话组的组长
*return:
*成功:进程ID
*失败:-1
*/
pid_t setsid(void);
调用 setsid()的3 个作用:
让进程摆脱原会话的控制
让进程摆脱原进程组的控制
让进程摆脱原控制终端的控制
fork()函数:创建子进程再令父进程退出,子进程全盘复制了父进程的会话期、进程组和控制终端等,虽然父进程退出,但原先的会话期、进程组和控制终端等并没有改变
setsid()函数:能使进程完全独立出来,从而脱离所有其他进程的控制
3 改变当前目录为根目录
fork():创建的子进程继承了父进程的当前工作目录
通常可以 让“ / ”作为守护进程的当前工作目录,也可以把当前工作目录换成其他的路径,如/tmp
chdir():改变工作目录
4 重设文件权限掩码
文件权限掩码: 指屏蔽掉文件权限中的对应位
如,有一个文件权限掩码是 050,它就屏蔽了文件组拥有者的可读与可执行权限
由于fork() 新建的子进程继承了父进程的文件权限掩码,这样 子进程使用文件有许多的麻烦。因此,把文件权限掩码设置为 0,可以大大增强该守护进程的灵活性
umask() : 设置文件权限掩码
5 关闭文件描述符
同文件权限掩码一样, fork():新建的子进程会从父进程那里继承一些已经打开了的文件,这些被打开的文件可能永远不会被守护进程读或写
方式:
for(i = 0; i < MAXFILE; i++)
{
close(i);
}
创建守护进程的流程图:
守护进程的出错处理:
由于守护进程完全脱离了控制终端,因此,不能像其他普通进程一样将错误信息输出到控制终端来通知程序员, 即使使用 gdb 也无法正常调试
通用的办法:通过守护进程 syslogd 来维护将程序中的出错信息输入到系统日志文件中(例如:“/var/log/messages”),从而可以直观地看到程序的问题所在
该机制提供了 3 个 syslog 相关函数:
openlog()函数:用于打开系统日志服务的一个连接
syslog()函数: 是用于向日志文件中写入消息
closelog()函数: 是用于关闭系统日志服务的连接
#include <syslog.h>
/*
*ident:
* 通常为程序的名称, 要向每个消息加入的字符串
*option:
* LOG_CONS:如 消息无法送到系统日志服务,则直接输出到系统控制终端
* LOG_NDELAY:立即打开系统日志服务的连接。正常情况下,直接发送到第一条消息时才打开连接
* LOG_PERROR:将消息也同时送到 stderr 上
* LOG_PID:在每条消息中包含进程的 PID
*Facility:
* 指定程序发送的消息类型
* LOG_AUTHPRIV:安全/授权信息
* LOG_CRON:时间守护进程(cron 及 at)
* LOG_DAEMON:其他系统守护进程
* LOG_KERN:内核信息
* LOG_LOCAL[0~7]:保留
* LOG_LPR:行打印机子系统
* LOG_MAIL:邮件子系统
* LOG_NEWS:新闻子系统
* LOG_SYSLOG:syslogd 内部所产生的信息
* LOG_UUCP:UUCP子系统
*/
void openlog(char *ident, int option, int facility);
#include <syslog.h>
/*
*priority:
* 指定消息的重要性
* LOG_EMERG:系统无法使用
* LOG_ALERT:需要立即采取措施
* LOG_CRIT:有重要情况发生
* LOG_ERR:有错误发生
* LOG_WARNING:有警告发生
* LOG_NOTICE:正常情况,但也是重要情况
* LOG_INFO:信息消息
* LOG_DEBUG:调试信息
*format:
* 以字符串指针的形式表示输出的格式,类似 printf 中的格式
*/
void syslog(int priority, char *format,...);
#include <syslog.h>
void closelog(void);
利用 syslog 服务的守护进程实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <syslog.h>
int main()
{
pid_t pid,sid;
int i,fd;
char *buf = "This is a Daemon\n";
pid = fork();
if(pid < 0)
{
printf("Error fork\n");
exit(1);
}
else if(pid > 0)
{
exit(0); /*父进程退出*/
}
/*打开系统日志服务, openlog*/
openlog("daemon_syslog",LOG_PID, LOG_DAEMON);
if((sid = setsid()) < 0)
{
syslog(LOG_ERR, "%s\n", "setsid");
exit(1);
}
if((sid = chdir("/")) < 0)
{
close(i);
}
/*这时创建完守护进程,以下开始正式进入守护进程工作*/
while(1)
{
if((fd = open("/tmp/daemon.log", O_CREAT |
O_WRONLY |
O_APPEND, 0600)) < 0)
{
syslog(LOG_ERR,"open");
exit(1);
}
write(fd, buf, strlen(buf)+1);
close(fd);
sleep(10);
}
closelog();
exit(0);
}