守护进程(精灵进程):独立于控制终端,不能直接和用户交互,不受用户登录注销的影响的一种进程。
- 守护进程特点:
- 独立于控制端(不受控制端的影响)
- 自成进程组
- 自成回话
- 本身是孤儿进程(fork之后父进程退出),所以父进程为init(孤儿进程都被1号进程收养)
- 守护进程通常采用d结尾,例如:Internet服务器inted;Web服务器httpd;acpid电源管理;sshd远程登录
- 守护进程一些应用场景:
- Linux的大多服务器使用Linux守护进程实现的;
- 作业规划进程(crond)
守护进程的创建
- 调用umask设置屏蔽字为0(如果不设置创建文件是权限可能会有一定的影响)
- 创建子进程(fork),父进程退出;
- 父进程创建的子进程与父进程不属于同一作业,父进程退出表示该作业已执行完
- 创建的子进程是第二个进程,保证了子进程不是组长进程
调用setsid函数创建一个新会话;
setsid函数:
#include <unistd.h>
pid_t setsid(void)
返回值:成功返回新建session的ID,出错返回-1;
调用该函数的进程不能是进程组的Leader,否则返回-1;将当前工作目录更改为根目录
- 因为根目录一般是不会删除的,而其他的目录极有可能会被删除,工作目录都被删除了,当然运行的作业也就不了了之了
- 关闭文件描述符
- 前面我们提到守护进程不能直接和用户交互,即没有标准输入,输出等,所以文件描述符不在需要
- 忽略SIGCHLD信号
代码(fork一次)
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<signal.h>
void mydaemon()
{
int i;
pid_t id ;
umask(0);//设置屏蔽字
id = fork();//创建子进程
if(id < 0)
{
printf("create error");
}else if(id >0)
{
exit(0);//父进程退出
}
setsid();//新建session
i = chdir("/");//更改当前目录为根目录
//sussess 0
if(i<0)
{
printf("change error");
}
close(0);//关闭文件描述符
close(1);
close(2);
signal(17,SIG_DFL);//屏蔽SIGCHLD信号
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
return 0;
}
程序运行:
fork 1 次与 2 次
- 首先分析前面用到的setsid函数
- 调用setsid函数后,创建一个新的session,当前进程成为session的 Leader,即当前进程的ID就是session的ID;
- 也创建一个新的进程组,当前进程成为进程组的Leader,组ID即当前进程ID;
- 当前进程失去原有的控制端,成为一个无控制端的进程
- 上面的分析,调用完setsid函数后,当前进程成为会话首进程(控制进程),会话首进程能够建立与终端的连接,后序如果再打开终端,有可能就会建立连接;所以再次fork保证子进程不是会话首进程
//fork两次代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
void mydaemon()
{
int i;
pid_t id ;
umask(0);
id = fork();
if(id < 0)
{
printf("create error");
}else if(id >0)
{
exit(0);
}
setsid();
//第二次fork
id = fork();
if(id < 0)
printf("create error");
else if(id >0)
exit(0);
i = chdir("/");
//sussess 0
if(i<0)
{
printf("change error");
}
close(0);
close(1);
close(2);
signal(17,SIG_DFL);
}
int main()
{
mydaemon();
daemon(0,0);//调用系统的
while(1)
{
sleep(1);
}
return 0;
}
Linux标准Daemon
#include <unistd.h>
int daemon(int nochdir, int noclose);
//第一个参数:nochdir 如果为0,则改变当前工作目录为根目录
//第二个参数:noclose 如果为0,关闭文件描述符