(华清远见书本上整理,以后复习用)
守护进程就是通常所说的Daemon进程,是linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。linux系统有很多守护进程,大多数服务都是通过守护进程实现的。
每一个系统与用户进行交流的界面叫终端,每个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能突破这种控制,想要某个进程不因为用户或终端或其他变化而受到影响,那么就必须把这个进程变成守护进程、
编写守护进程(5个步骤)
1、创建子进程,父进程退出
由于守护进程是脱离控制终端的,因此完成第一步后就会在shell终端里造成一个程序已经运行完毕的假象,然后的工作都在子进程中完成,而用户在shell终端里则可以执行其他的命令了,从而在形式上做到了与控制终端的脱离。
父进程的退出使得它的子进程变成孤儿进程,每当系统发现一个孤儿进程,就会自动由1号进程(init进程)收养,从而编程init进程的子进程。
/*父进程退出*/
pid = fork();
if(pid > 0){
exit(0);
}
2、在子进程中创建新的会话(最重要)
进程组:一个或者多个进程的集合,进程组由进程ID来标识,除了进程号PID之外,进程组ID也是一个进程的必备属性。
每个进程组都有个组长进程,组长进程的进程号等于进程组的进程组ID。且该进程ID不会因组长进程的退出而受影响。
会话期:一个或者多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
这里要使用系统函数
setsid
(1)setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid的
作用:
1、让进程摆脱原会话的控制。
2、让进程摆脱原进程组的控制。
3、让进程摆脱原控制终端的控制。
由于在fork的时候,子进程完全拷贝了父进程的会话期,进程组,控制终端等,虽然父进程退出了,但原先的这些属性都没有变,因此不是真正意义上的独立出来,而setsid函数能够完成这个任务。
(2)setsid的函数格式
需要头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t setsid(void)
返回值:成功返回
该进程组ID,错误返回
-1.
3、改变当前目录为根目录
使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行过程中,当前目录所在的文件系统是不能卸载的,这对以后的使用会造成诸多麻烦,因此通常让“/”作为守护进程的当前工作目录,这样就可以避免上述的问题。当然有特殊要求的话可以换成其他的目录,改变工作目录常见的函数是chdir.
4、重设文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了诸多麻烦。所以把文件权限掩码设置为0,可以增加灵活性。设置文件权限掩码的函数是umask.通常使用umask(0)。
5、关闭文件描述符
子进程从父进程那继承了一些已经打开的文件,这些打开的文件可能永远不会被守护进程读写,但是还一样消耗资源,而且可能导致所在的文件系统无法卸载。
在上面两步后,守护进程已经与所属的控制终端失去了联系,因此从终端输入的字符不可能达到守护进程,守护进程中用常规的方法输出的字符也不能在终端上显示出来,所以文件描述符0,1,2这三个文件(输入、输出和报错三个文件)已经失去了存在的价值,也应该被关闭:
for (i=0;i<MAXFILE;i++)
close(i);
实例演示:
首先建立一个守护进程,然后让守护进程每隔10s在/tmp/dameno.log中写入一句话。
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#define MAXFILE 65535
int main(){
pid_t pid;
int i,fd,len;
char * buf="This is a Dameno\n";
len = strlen(buf);
//first step
pid = fork();
if (pid < 0){
printf("Err on fork!\n");
exit(1);
}
else if (pid >0){
exit(0);
}
//second step
setsid();
//third step
chdir("/");
//fourth step
umask(0);
//fifth step
for(i=0;i<MAXFILE;i++)
close(i);
//work done ,Dameno begin
while(1){
if((fd=open("/tmp/dameno.log",
O_RDONLY|O_CREAT|O_APPEND,0600))<0){
printf("open file error!\n");
exit(1);
}
write(fd,buf,len+1);
close (fd);
sleep(5);
}
return 0;
}
守护进程的出错处理
由于守护进程已经完全脱离控制终端了,所以出错了也不能像别的程序通过输出错误信息到控制终端来通知程序员,即使使用GDB也无法正常调试。那么该怎么调试呢,一种办法是使用syslog服务,将程序中的出错信息输入到"/var/log/messages"系统日志文件中,从而可以直观看到程序问题所在(这个文件只能root权限才能查看)。
syslog是linux中的系统日志管理服务,通过守护进程syslogd来维护。该守护进程在启动时会读一个配置文件"/etc/syslog.conf"。该文件决定了不同种类的消息会发送到什么地方。该机制提供了3个syslog函数,分别是openlog,syslog和closelog.
openlog用于打开系统日志服务的一个连接,syslog是用于向日志文件中写入消息,在这里可以规定消息的优先级、消息输出的格式,closelog是用于关闭系统日志服务的连接。
openlog函数用法:
头文件:#include<syslog.h>
函数:
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函数语法:
头文件:#include<syslog.h>
函数:
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函数语法:
头文件:#include <syslog.h>
函数:
void closelog(void)
实例分析:
syslog_test.c
#include <syslog.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#define MAXFILE 65535
int main(){
pid_t pid,sid;
int i,len,fd;
char * buf="this is dameno\n";
len = strlen(buf);
pid=fork();
if(pid<0){
printf("Error on fork!\n");
exit(1);
}
else if(pid>0){
exit(0);
}
openlog("demo_update",LOG_PID,LOG_DAEMON);
if((sid=setsid())<0){
syslog(LOG_ERR,"%s\n","setsid");
exit(1);
}
if((sid=chdir("/"))<0){
syslog(LOG_ERR,"%s\n","chdir");
exit(1);
}
umask(0);
for(i=0;i<MAXFILE;i++)
close(i);
while(1){
if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0){
syslog(LOG_ERR,"open");
exit(1);
}
write(fd,buf,len+1);
close(fd);
sleep(5);
}
closelog();
exit(0);
}
在root下编译后
先切换到普通用户:su triton
然后执行程序,但是这里open一定要有root权限,所以会产生错误信息写入到/var/log/message文件中。
上面看到在message里面记录了信息。
用kill命令关闭进程