守护进程
某些服务需要一直在后台跑。
守护进程一般是一个会话的Leader,也是一个进程组的Leader。
一个session(会话)中存在多个进程,前台进程组,后台进程组。最多只有一个前台进程组,可以没有。只有前台进程组能使用标准输入输出
。
守护进程的特点:
setsid();//创建一个会话,并设置进程组ID。
只有子进程才能创建会话,调用方会成为新的会话的leader,会成为当前新的进程组的leader,并脱离控制终端。
执行ps axj命令:
守护进程的tty为’?’,sid,pgid,pid相同。如果创建的子进程是守护进程,那么其父进程不会一直等,
所以守护进程的ppid为1。(不一定,ubuntu较新的发行版都用 upstart 代替 init 来收养孤儿进程
。)
几个进程组函数:
int setpgid(pid_t pid, pid_t pgid); //将指定pid号的进程放到pgid进程组
pid_t getpgid(pid_t pid); //返回指定pid号的进程的进程组id,为0时返回当前进程id
pid_t getpgrp(void); //返回当前进程的进程组id,POSIX.1方言
pid_t getpgrp(pid_t pid); //查看某个进程的进程组id,BSD方言
单实例守护进程:锁文件/var/run/name.pid ,记录了某些守护进程的pid。
(一些守护进程如sshd、rsyslogd,同一时间只能跑一份,利用锁文件来完成单实例守护进程的控制)
启动脚本文件:/etc/rc*…,人为将某个守护进程添加到启动项
系统日志文件 /var/log
“每个系统服务都有必要写系统日志,但我又不能让人人都写” --> 权限分隔层。root将写系统日志的权限交给syslogd服务,所有要写系统日志的进程都将日志信息提交syslogd, 由syslogd统一地接收、写系统日志。
相关函数:
#include <syslog.h>
openlog(); //与syslog关联
syslog(); //提交
closelog();
①函数原型:void openlog(const char *ident, int option, int facility);
ident:可以是任何字段,一般是程序名字
option:一些控制标志,如LOG_PID表示每条日志信息包含PID;LOG_PERROR表示同时将error信息打印到stderr。
facility:提交日志信息的服务类型,也可以说是日志信息来源;
如LOG_DAEMON表示系统守护进程的日志,LOG_FTP表示是FTP的日志信息
②函数原型:void syslog(int priority, const char *format, …);
priority:是facility或上level的结果,level是级别,如LOG_ERR,LOG_WARNING,LOG_INFO
format:类似printf的格式,不用’\n’,因为我们只是提交信息,由syslogd服务实际写日志。
/var/log/syslog记录了系统大部分日志信息
/etc/rsyslog.conf为syslog配置文件,如约定level>LOG_INFO才往日志中写信息。
程序实例
创建一个守护进程,守护进程负责不断往/tmp/out中写数字;并将程序中的错误提交系统日志。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#define FNAME "/tmp/out"
static int creatdaemon(void)
{
int fd;
pid_t pid;
pid = fork();
if(pid < 0)
return -1; //fork失败
if(pid > 0)
{
//printf("[%d]:parent exit\n",pid);
exit(0); //父进程直接退出
}
//printf("[%d]:child is working\n",pid);
fd = open("/dev/null",O_RDWR);
if(fd < 0)
return -2; //open失败
//脱离控制终端,将0,1,2重定向到/dev/null
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
close(fd);
setsid();
chdir("/");//守护进程一直在跑,最好就将PWD切换成根,因为根是一直存在的
return 0;
}
static void daemontask(void)
{
int i = 0;
FILE *fp;
fp = fopen(FNAME,"w");
if(fp == NULL)
{
syslog(LOG_ERR,"fopen() failed!");
exit(1);
}
while(1)
{
fprintf(fp,"%d\n", i++);
fflush(fp); //文件是全缓冲,要刷新缓冲区
sleep(1);
}
fclose(fp);
}
int main(int argc, char **argv)
{
int ret = 0;
openlog("mydaemon",LOG_PID,LOG_DAEMON);//关联syslog,想要写日志的人物为mydaemon,日志包含其
pid,日志的来源为守护进程
ret = creatdaemon();
if(ret < 0)
{
syslog(LOG_ERR,"creatdaemon() failed!");
if(ret == -1)
syslog(LOG_ERR,"fork() failed!");
if(ret == -2)
syslog(LOG_ERR,"open() failed!");
exit(1);
}
else
syslog(LOG_INFO,"creatdaemon() success!");
daemontask();
exit(0);
}
测试运行:
①先将文件设置为不可写:chmod u-w /tmp/out
②运行程序并检查守护进程是否创建成功
③查看系统日志文件/var/log/syslog