有关会话与进程组的概念,请参考上篇文章进程间关系
https://blog.youkuaiyun.com/aaronlanni/article/details/80184460
一、基本概念
1、守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。不受用户登录与注销的影响,只要机器不关机,则 一直存在。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,ftp服务器,ssh服务器,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond(定时任务)等。
2、Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但 系统服务进程(守护进程)不受用户登录注销的影响 ,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。
3、守护进程一般自成进程组,自成会话,一般不受用户的注销与登录时的影响,且始终运行在后台,完成特定的工作。
4、查看守护进程ps -axj|grep -E ‘d$’也可以用它来专门查看操作系统中的守护进程
总结:
- 凡是TPGID(与该终端关联的作业)一栏写着-1的都是没有控制终端的进程,也就是守护进程。
- 在COMMAND一列⽤用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码(操作系统自己启动,不用用户自己的操作),因此没有程序文件名和命令行,
- 通常采用以k开头的名字,表示Kernel。 init进程我们已经很熟悉了,udevd负责维护/dev目录下的设备⽂件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件
- 可以看出,守护进程通常采用以d结尾的名字,表示Daemon。
5、后台进程与守护进程的区别
后台进程受用户注销与登录的影响,而守护进程不受用户登录与注销的影响,因此可以说守护进程比后台进程更加靠后。通常守护进程不区分其前后台的概念。
6、守护进程的特点
1)守护进程本质上也是孤儿进程
2)守护进程自成进程组,自成会话
3)守护进程与终端无关(因为其自成会话)
二、守护进程的创建
创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader
#include <unistd.h>
pid_t setsid(void);
注意事项: 调⽤用这个函数之前 , 当前进程不允许是进程组的 Leader(原因:组长进程有可能就是会话首进程,如果要以这个进程在创建一个会话,则可能导致两个会话都有相同的会话首进程,从而导致混乱), 否则该函数返回 -1。要保证当前进程不是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调⽤用setsid就不会有问题了。
成功调用该函数setsid的结果是:
- 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
- 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id,会话的话首。
- 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。
- 守护进程一般不直接与用户打交道,而是通过服务从而使得与用户交互,使得用户可以直接使用,例如:sshd,远程连接
1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<fcntl.h>
6 #include<sys/stat.h>
7 void dameon()
8 {
9 int fd;
10 pid_t pid=fork();
11 struct sigaction sa;
12 umask(0);
13 //创建进程,但是此处的守护进程不能是进程组的组长,一般,父进程默认为进程组的组长,因此在此处,让其父进程退出,子> 进程不是一个进程组的组长,且将子进程设置为一个会话,即自成会话
14 if(pid<0)
15 {
16 perror("fork");//创建进程失败
17 }
18 else if(pid>0)//father
19 {
20 exit(0);
21 }
22 setsid();//设置子进程为一个会话
23 //忽略SIGCHLD信号,让其子进程自成进程组,使得内核在子进程结束之时,不会产生僵尸进程
24 signal(SIGCHLD,SIG_IGN);
25 //让子进程继续fork,从而保证子进程不是这个会话的首会话
26 pid=fork();
27 if(pid<0)
28 {
29 printf("fork error\n");
30 }
31 else if(pid>0)
32 {
33 exit(0);
34 }
35 chdir("/");//将当前守护进程的工作目录改至根目录下
36 close(0);
37 fd=open("/dev/null",O_RDWR);
38 dup2(fd,1);
39 dup2(fd,2);
40 }
41 int main()
42 {
43 dameon();
44 while(1)
45 {
46 sleep(1);
47 }
48 return 0;
49 }
运行此程序,可以看出结果,如下所示:
总结:
在创建守护进程之时,一般遵循以下的步骤:
(1)在父进程中执行fork并exit推出;
(2)在子进程中调用setsid函数创建新的会话;
(3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;
(4)在子进程中调用umask函数,设置进程的umask为0;
(5)在子进程中关闭任何不需要的文件描述符
三、利用库函数daemon创建守护进程
#include <unistd.h>
int daemon(int nochdir, int noclose);
//函数的返回值为:成功返回0,失败返回-1且将fork与setsid的错误信息
//参数nochdir表示:如果当前参数为0,表示改变进程的当前目录到根目录下,否则不做任何变化
//参数noclose表示:如果当前参数为0,表示将标准输入,标准输出,标准错误重定向到/dev/null,否则不对这些文件描述符做任何改变
1 #include<stdio.h>
2
3 int main()
4 {
5 daemon(0,0);
6 while(1);
7
8 }
结果如下所示:
有关守护进程的内容大概就这么多,有什么问题,希望大家提出,谢谢!!!
只有不停的奔跑,才能不停留在原地!!!