一、什么是守护进程
守护进程,也就是通常所说的Daemon进程,是 Linux中的后台服务进程。它是一个生存期较长
的进程,通常独立于控制终端并且周期性的执行 某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是用
守护进程实现的。在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会被自动关闭。守护进程能够突破这种限制,它从被执行开始运转,直到整个系统关闭才会退出。如果想让某个进程不因为用户或终端或其他的变化而受到影响,就必须把这个进程变成一个守护进程。
二、查看常用的系统守护进程
父进程ID : PPID
进程ID : PID
进程组ID : PGID
会话期ID : SID
终端ID : TTY
终端进程组ID : TPGID
状态 : STAT
用户 : UID
运行时间 : TIME
指令: COMMAND
三、守护进程的制作流程
代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include<syslog.h>
void deamon_init(void);
int main(){
deamon_init();
openlog("opening",LOG_PID,LOG_DAEMON);
int fd = open("1.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd<0){
syslog(LOG_ERR,"open error");
exit(-1);
}
char buf[50]="hello,this is deamo\n";
while(1){
sleep(3);
write(fd,buf,strlen(buf));
syslog(LOG_INFO,"%s",buf);
}
closelog();
return 0;
}
void deamon_init(void){
if(fork()!=0)
exit(0);
if(setsid()<0){
perror("setsid:");
exit(-1);
}
if(fork()!=0)
exit(0);
int i;
for(i = 0;i <= sysconf(_SC_OPEN_MAX);i++)
close(i);
chdir("/home/pm");
umask(0);
int fd = open("/dev/null",O_RDWR);
dup(0);
dup(0);
}
四、守护进程的编写
步骤:
1、创建子进程,杀死父进程:
为了制作终端进程而做准备:组长进程无法成为终端
//创建子进程,exit结束父进程,fork返回值是父进程pid且大于0
if(fork()!=0)
exit(0);
2、设置当前子进程为一个终端:
为了摆脱原来终端的控制关系
//设置子进程为终端,返回值为0没有参数
if(setsid()<0){
perror("setsid");
exit(-1);
}
3、创建子进程,杀死父进程 :
彻底成为无法被进程管理和控制终端概念影响的守护进程雏形。
再次创建子进程杀死父进程,让新创建的子进程完全成为摆脱终端关系的进程
if(fork()!=0)
exit(0);
//此时,守护进程雏形已完成,剩下的操作,子进程2(守护进程雏形)做
4、关闭所有文件描述符:
为第七点做准备
int i;
for(i=0;i<=sysconf(_SC_OPEN_MAX);i++)//for里面写表达式并不好:容易出现bug
close(i); //不要再for里写任何表达式,引以为戒
5、更改执行目录:
为避免守护进程影响源目录的删除和移动操作
//执行目录更改到根目录下,运行时记得添加sudo
chdir("/");
6、修改掩码:
为了让守护进程操作不被权限影响
umask(0);
7、将标准输入,标准输出,标准错误输出重定向到设备NULL
标准输入输出错误输出一开始都有对应的触发设备
//7>将标准输入、标准输出、标准错误输出重定向到设备null
int fd=open("/dev/null",O_RDWR);
dup(0);
dup(0);
API学习:
2>设置当前子进程为一个终端
setsid
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
功能:
让执行该函数的进程,变成终端
参数:
无
返回值:
成功返回0
失败返回-1,并设置错误码
4>关闭所有文件描述符
sysconf
#include <unistd.h>
long sysconf(int name);
功能:
找寻name选项的最大值
参数:
name:_SC_OPEN_MAX -->找寻文件描述符的最大值
返回值:
成功找到选项的最大值
失败返回-1,并设置错误码
5>更改执行目录
chdir
#include <unistd.h>
int chdir(const char *path);
功能:
改变目录
参数:
path:需要修改到的目录
返回值:
成功返回0
失败返回-1,并设置错误码
6>修改掩码: 两种方式
1>命令修改 --->适用于终端
2>函数修改 --->适用于进程 ---!
umask --》和命令同名
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
功能:
修改某个进程当中操作的掩码
参数:
mask:掩码值
返回值:
成功返回掩码值
7>文件描述符重定向(复制)
dup
#include <unistd.h>
int dup(int oldfd);
功能:
以oldfd为模板,复制出一个新fd
新fd所有的参数 都 继承 oldfd
操作oldfd = 操作 新的fd
参数;
oldfd:旧文件描述符
返回值:
成功创建newfd并返回 newfd
失败返回-1,并设置错误码
五、守护进程日志记录
为什么做日志:
守护进程它没有输入输出,也没有报错 ,我们无法对他进行相对简单的跟踪看,也无法得知其在我们背后做了什么操作 。所以我们:利用文件日志系统保存守护进程的操作信息
文件日志系统
ubuntu的文件系统是:Ext4
Ext4:两种系统:
1>文件日志系统 --->保留下来
2>时间回溯系统 ---->快照
系统日志:
1>打开日志
openlog
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
功能:
打开日志
参数:
ident:标签:char * 随意
option:选项
LOG_PID :进程信息
facility:
LOG_DAEMON :守护进程
2>写日志
syslog
#include <syslog.h>
void syslog(int priority, const char *format, ...);
功能:
编写日志
参数:
priority:优先级(报文)
LOG_ERR:出错
LOG_WARNING:警告
LOG_INFO:正常
format:类似printf
3>关闭日志
closelog
#include <syslog.h>
void closelog(void);
功能:
关闭日志
4>命令:查看日志
vi /var/log/syslog
六、查看守护进程
ps -ajx 当TPGID显示-1时则表示为守护进程
运行程序:
执行后显示:
当关闭终端守护进程也不会关闭,而是会在后台运行:
后台依旧有显示
重启虚拟机会杀死我们写的守护进程:
home目录下出现:守护进程中运行的写入程序。
至此守护进程结束.