实现守护进程
守护进程
守护进程:也称为精灵进程,守护进程是一个在后台运行并且不受任何终端控制的进程。Unix操作系统有很多典型的守护进程(其数目根据需要或20—50不等),它们在后台运行,执行不同的管理任务。用户使守护进程独立于所有终端是因为,在守护进程从一个终端启动的情况下,这同一个终端可能被其他的用户使用。
有关命令
ps命令:Linux中的ps命令是Process Status的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。
ps [options] [--help]
例子:
ps -ef
显示所有进程的完整信息
-e
:显示所有进程
-f
:显示完整格式的进程信息
-o
:用户自定义格式
grep命令:Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。grep全称是Global Regular Expression Print,表示全局正则表达式版本,它的使用权限是所有用户。
普通进程:普通进程在终端运行时,输入命令不会有效果。
在运行程序时,在末尾加上&
,或者在运行是按ctrl+z
都可以使程序到后台运行
(1.将程序在后台运行,2.将程序暂停)
但是如果终端关闭,所以程序都将终止
守护进程:守护进程却能够突破这种限制,它从被执行开始运转,直到接收到某种信号或者整个系统关闭时才会退出。如果想让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程。
如何创建守护进程
- 创建子进程,退出父进程
为了脱离控制终端需要退出父进程,之后的工作都由子进程完成。在Linux中父进程先于子进程退出
会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,
这样,原先的子进程就会变成init进程的子进程。
- 在子进程中创建新的会话
pid_t setsid(void);
用于创建一个新的会话,并担任该会话组的组长。
- 让进程摆脱原会话的控制;
- 让进程摆脱原进程组的控制;
- 让进程摆脱原控制终端的控制;
- 改变当前目录为根目录
使用fork创建的子进程继承了父进程的当前的工作目录。由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后的使用会造成诸多的麻烦。因此,通常的做法是让根目录”/”作为守护进程的当前工作目录。这样就可以避免上述的问题。如有特殊的需求,也可以把当前工作目录换成其他的路径。改变工作目录的方法是使用chdir函数。
- 重设文件权限掩码
由于fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0(即,不屏蔽任何权限),可以增强该守护进程的灵活性。设置文件权限掩码的函数是umask。通常的使用方法为umask(0)。文件权限掩码:是指屏蔽掉文件权限中的对应位。
- 关闭文件描述符
用fork创建的子进程也会从父进程那里继承一些已经打开了的文件。在使用setsid调用之后,守护进程已经与所属的控制终端失去了联系,因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1、2(即,标准输入、标准输出、标准错误输出)的三个文件已经失去了存在的价值,也应该关闭。
文件描述符
当你打开一个存在的文件或者创建一个新文件,操作系统都会返回这个文件描述符(其实就是代表这个文件的),后续对这个文件的操作的一些函数,都会用到这个文件描述符作为参数;
linux中三个特殊的文件描述符,数字分别为0,1,2
- 0:标准输入【键盘】,对应的符号常量叫STDIN_FILENO
- 1:标准输出【屏幕】,对应的符号常量叫STDOUT_FILENO
- 2:标准错误【屏幕】,对应的符号常量叫STDERR_FILENO
代码实现
```c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(){
pid_t pid;
//1. 创建子进程,退出父进程
pid = fork();
if(pid<0){
printf("fork errno\n");
}
else if(pid>0){
exit(0);
}
//2. 在子进程中创建新的会话
setsid();
//3. 改变当前目录为根目录
chdir("/");
//4. 重设文件权限掩码
umask(0);
//5. 关闭文件描述符
close(0);
int fd0;
fd0=open("dev/null",O_RDWR);
dup2(fd0,1);
dup2(fd0,2);
while(true){
sleep(1);
}
return 0;
}
```
运行程序后,使用命令查看系统进程ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|mydaemon'
PID PPID SID TT PGRP COMMAND STAT CMD
23000 1 23000 ? 23000 mydaemon Ss ./mydaemon
PPID父进程ID为1,说明fork子进程后,父进程退出,子进程被1号进程收养,TT=?说明已和终端脱离