一、守护进程概念和作用
守护进程是
运行在后台的特殊进程,
并非后台进程。
特点:
1.独立于控制终端
2.周期性地运行
3.因为没有控制终端,所以不能和用户直接交互
4.不受用户登录和注销的影响
作用:
因为它的特点,所以一般可将服务器进程转成守护进程
Linux大多数服务器就是用守护进程实现的。
二、如何创建守护进程
0.先了解setsid()函数
#include <unistd.h>
pid_t setsid(void);
返回值:
成功返回新创建的session会话id(就是当前进程id)
失败返回-1
特点:
执行该函数的进程不能是当前进程组的组长,否则返回-1
成功执行后的作用:
1.将当前进程设为会话首进程,当前进程id就是会话id;
2.将当前进程设为进程组组长,当前进程id就是进程组id;
3.将当前进程脱离原有的控制终端
1.第一次fork
操作内容:
在fork出的子进程中调用setsid函数,父进程直接退出
执行该步骤的原因:
避免调用setsid函数的进程是进程组组长
2.调用setsid()函数
操作内容:
子进程中调用setsid函数
执行该步骤的原因:
使得当前进程脱离原有的控制终端
3.第二次fork
操作内容:
再次调用fork创建孙子进程,将要执行的内容放在孙子进程中执行
实际点来说就是将功能代码写在孙子进程中
执行该步骤的原因:
虽然setsid是当前进程脱离了原有的控制终端
但是因为是进程组组长,所以仍然可以再次打开控制终端
因此要创建孙子进程,避免误操作再次打开控制终端
4.设置umask
操作内容:
执行umask(0);
将文件权限屏蔽字设为0
执行该步骤的原因:
因为子进程会继承父进程的文件权限屏蔽字
所以最好将文件权限屏蔽字设为0,使用户可以随自己意愿创建相应权限的文件
5.更改当前目录为根目录
操作内容:
执行chdir(“/”);
将当前目录更改为根目录(一般写根目录,可选择其他合适的目录)
执行该步骤的原因:
因为守护进程会保护当前目录下的文件或目录不被删除或卸载
那么如果某一时刻需要对该目录下的 文件进行删除操作,就无执行了
为什么不能删除或卸载守护进程当前目下的文件或目录?
因为避免出现因为删除或卸载当前目录下的文件或目录而导致守护进程运行失败
6.关闭不必要的文件0,1,2
操作内容:
手动关闭文件0,1,2,即标准输入、标准输出、标准出错
执行该步骤的原因:
子进程会继承父进程的文件描述符表,而守护进程脱离了控制终端,一般是不会再需要这三个文件,关闭这些不必要的文件避免资源浪费
三、代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
void mydaemon(void)
{
pid_t pid=0;
pid=fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid==0)//子进程
{
setsid();
if((pid=fork())<0)
{
perror("fork");
exit(4);
}
if(pid==0)孙子进程
{
umask(0);
if(chdir("/")<0)
{
perror("chdir");
exit(5);
}
int i=0;
for(i=0;i<3;++i)
{
close(i);
}
}
else
{
exit(3);
}
}
else//父进程
{
exit(2);
}
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
return 0;
}
查看守护进程:
上图中明显可以看出TPGID一栏写的是-1,代表该进程是没有控制终端的进程,即守护进程
杀死守护进程:
使用kill -9 [进程id]即可杀死守护进程
四、创建守护进程函数daemon
#include <unistd.h>
int daemon(int nochdir, int noclose);
参数:
当 nochdir为零时,当前目录变为根目录,否则不变;
当 noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信 息,否则照样输出。
返回值:
deamon()调用了fork()
如果fork成功,那么父进程就调用_exit(2)退出,
所以看到的错误信息 全部是子进程产生的。
如果成功函数返回0,否则返回-1并设置errno。
代码如下:
#include<stdio.h>
int main()
{
daemon(0,0);
while(1)
{
sleep(1);
}
return 0;
}
//结果演示跟上面一样,在这就不演示了,可自行操作