Linux守护进程
1. 守护进程简介
守护进程(Daemon)是Linux三种进程类型之一,是后台服务进程,始终在后台运行。通常在系统启动时运行,系统关闭时结束。守护进程独立于任何终端,周期性的执行某种任务或等待处理特定事件。
Linux以会话(session)、进程组的方式管理进程,每个进程属于一个进程组。会话数一个或多个进程组的集合,通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话。终端关闭时,所有相关进程会被结束。但是守护进程却能突破这种限制,不受终端关闭的影响。
2. 编写守护进程
守护进程的创建分为五个步骤
2.1 创建子进程,父进程退出
父进程创建子进程后退出,子进程变成孤儿进程后被1号进程(init进程)收养,子进进入后台运行
pid = fork();
if(pid > 0){
exit(0); //父进程退出
}
2.2 子进程创建新会话
- 进程组:是一个或过个进程的集合。由进程组ID来唯一表示。除了进程号(PID)之外,进程组也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID,且进程组ID不会因组长进程的退出而受到影响
- 会话期:是一个或多个进程组的集合。一个会话开始于终端打开,结束于终端关闭。会话期的第一个进程成为会话组长。在此期间该用户运行的所有进程都属于这个会话期,它们之间的关系如下图所示
这个步骤使用的函数是setsid(),该函数用于创建一个新的会话,并担任该会话组长。有三个作用:让进程摆脱原会话的控制;让进程摆脱原进程组的控制;让进程摆脱原控制终端的控制;
/*****setsid()函数*****/
函数原型: pid_t setsid(void)
函数返回值:成功返回该进程组ID;失败返回-1
子进程创建新会话,子进程成为新的会话组长,摆脱原先的终端
if(setsid() < 0){
exit(-1);
}
2.3 改变当前目录
子进程继承了父进程的当前工作目录,由于守护进程一直在后台运行,其工作目录不能被卸载,因此需要改变工作目录。通常让根目录作为守护进程的当前工作目录。
使用 chdir() 函数,例如 chdir("/tmp")
2.4 重设文件权限掩码
子进程继承了父进程的文件权限掩码,给该子进程使用文件带来一定的影响,因此把文件权限掩码设置为0,可以增强该守护进程的灵活性。
使用 umask() 函数,例如 umask(0)
2.5 关闭文件描述符
子进程继承了父进程的一些已经打开了的文件,这些被打开的文件可能永远不会被守护进程访问,但它们一样占用系统资源,而且还可能导致所在的文件系统无法被卸载。
使用 close() 函数,如下示例
int num;
num = getdtablesize(); //获取当前进程文件描述符表大小
for(i = 0;i < num;i++){
close(i);
}
创建守护进程实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main()
{
pid_t pid;
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); //父进程退出
}
setsid(); //第二步
chdir("/tmp"); //第三步
umask(0); //第四步
for(i = 0;i < getdtablesize();i++){ //第五步
close(i);
}
/*这时创建完守护进程,以下开始正式进入守护进程工作*/
while(1){
if((fd = open("daemon.log",O_CREAT|O_WRONLY|O_TRUNC,0600)) < 0){
printf("OPen file error\n");
exit(1);
}
write(fd,buf,strlen(buf));
close(fd);
sleep(2);
}
exit(0);
}
编译并执行,使用tail -f /tmp/daemon.log可以看到每隔2s就会在对应的文件中写入字符串。使用ps命令可以看到该进程在后台运行
linux@linux-virtual-machine:~/andy/proc$ tail -f /tmp/daemon.log
This is a daemon
...
linux@linux-virtual-machine:~/andy/proc$ ps -ef|grep daemon
linux 3184 2001 0 13:01 ? 00:00:00 ./daemon
linux 3247 2001 0 13:04 ? 00:00:00 ./daemon
linux 3250 2667 0 13:05 pts/0 00:00:00 grep --color=auto daemon
3. 守护进程的出错处理
守护进程由于完全脱离了控制终端,因此不能像普通进程一样将错误信息输出到控制终端。一般情况下,守护进程通过使用 syslog 服务来调试,将程序中的出错信息输入到系统日志文件中(如"/var/log/messages"),从而可以直观地看到程序问题所在。
syslog 是Linux中的系统日志管理服务,通过守护进程 syslogd 来维护。该守护进程在启动时会读一个配置文件"/etc/syslog.conf",该文件觉得了不同种类的消息会发送向何处。例如紧急消息可被送向系统管理员并在控制台上显示,而警告消息则可被记录到一个文件中。
该机制提供了3个 syslog 相关函数,分别为:
openlog():打开系统日志服务的一个连接
syslog():向日志文件中写入消息,可规定消息的优先级、消息输出格式等
closelog():关闭系统日志服务的连接
/************openlog()函数************/
函数原型:void openlog(char *ident, int option, int facility)
传入值: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_USER 一般使用者等级信息
-->LOG_UUCP UUCP子系统
/************syslog()函数************/
函数原型:void syslog(int priority, char *format,...)
传入值:priority
-->LOG_EMERG 系统无法使用
-->LOG_ALERT 需要立即采取措施
-->LOG_CRIT 有重要情况发生
-->LOG_ERR 有错误发生
-->LOG_WARNING 有警告发生
-->LOG_NOTICE 正常情况,但也是重要情况
-->LOG_INFO 信息消息
-->LOG_DEBUG 调试信息
format 以字符串指针的形式表示输出的格式,类似于printf中的格式
/************closelog()函数************/
函数原型:void closelog(void)
关注我的公众号,共同交流学习嵌入式开发相关技术: