目录
掌握:守护进程特点、会话、控制终端、创建守护进程
1 守护进程的概念
守护进程又叫精灵进程(Daemon Process),它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
- 守护进程(Daemon Process)是Linux三种进程类型之一
- 是 Linux 中的后台服务进程
- 是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件
无法通过bg、fg切换至前台后台,它是一个后台进程。
脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
1.1 相关概念
进程组(Process Group): 进程集合,每个进程组有一个组长(Leader),其进程 ID 就是该进程组 ID。(如fork的父与子即进程组)
会话(Session): 进程组集合,每个会话有一个组长,其进程 ID 就是该会话组 ID。(即终端)
控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程(Controlling Process)。
1.2 举例
http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。
1.3 函数介绍
setsid函数:
pid_t setsid(void);
成功:返回调用进程的会话ID;失败:-1,设置errno。
调用了setsid函数的进程,既是新的会长,也是新的组长
getsid函数:
pid_t getsid(pid_t pid)
- 成功:返回调用进程的会话ID;失败:-1,设置errno
-pid为0表示察看当前进程session ID
-ps ajx命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
-组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。
getpid:pid_t getpid(void); 获取进程id
getpgid:pid_t getpgid(pid_t pid); 获取进程组id
2 守护进程的实现
2.1 简单创建
更简便地创建守护进程: nohup 命令
nohup xxxx &
2.2 守护进程创建
第一步
创建子进程,父进程退出
if (fork() > 0){
exit(0);
}
子进程变成孤儿进程,被init进程收养
子进程在后台运行
第二步
子进程创建新会话
if(setsid() < 0)
{
exit(-1);
}
子进程成为新的会话组长
子进程脱离原先的终端
第三步(非必须)
更改当前工作目录
chdir(“/”);
chdir(“/tmp”);
守护进程一直在后台运行,其工作目录不能被卸载 重新设定当前工作目录cwd
第四步(非必须,如果不会新创建文件)
重设文件权限掩码
if(umask(0) < 0)
{
exit(-1);
}
文件权限掩码设置为0
只影响当前进程
第五步
关闭打开的文件描述符
int i;
for(i=0; i<3; i++)
{
close(i);
}
关闭所有从父进程继承的打开文件
已脱离终端,stdin / stdout / stderr无法再使用
2.3 实例
编写一个程序,实现创建子进程,父进程退出,演示后台程序
方式一:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork");
return -1;
}
else if(pid == 0)
{
printf("I am a deamon\n"); //子进程
sleep(100);
}
else
{
exit(0); //父进程退出
}
}
编译后运行,不管是按ctrl+c,或者查看前台进程都不会使得mydae结束,并且查看它的父进程是init,如果想接触需要使用kill命令,至此完成守护进程的第一步,与前台终端无关,如果不把父进程结束,它还是个前台进程。
方式二:
不用exit,执行程序,此时查看进程状态,父进程和子进程都存在,ctrl+c,父子进程同时结束。
使用nohup 【执行程序】 &的方式,此时按ctrl+c,进程都在后台运行。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork");
return -1;
}
else if(pid == 0)
{
printf("I am a deamon\n");
sleep(10);
}
else
{
sleep(10);
}
}
linux@linux:~$ ps -ef|grep a.out
linux 5690 2865 0 12:08 pts/0 00:00:00 ./a.out
linux 5691 5690 0 12:08 pts/0 00:00:00 ./a.out
linux 5695 5164 0 12:08 pts/6 00:00:00 grep --color=auto a.out
linux@linux:~$ ps -ef|grep a.out //按下ctrl+c
linux 5697 5164 0 12:08 pts/6 00:00:00 grep --color=auto a.out
linux@linux:~$ nohup ./a.out &
linux@linux:~$ ps -ef|grep a.out
linux 5777 2865 0 12:18 pts/0 00:00:00 ./a.out
linux 5778 5777 0 12:18 pts/0 00:00:00 ./a.out
linux 5780 5164 0 12:18 pts/6 00:00:00 grep --color=auto a.out
方法三:
根据2.2步骤创建守护进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(){
pid_t pid;
pid = fork();
//1 创建子进程,父进程退出
if(pid<0){
perror("fork");
return 0;
}else if(pid>0){
exit(0);
//sleep(100);
}
printf("I am a deamon\n");
printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
//2 子进程创建新会话
if(setsid()<0){
perror("setsid");
exit(0);
}
//创建后,sid 、pgid以是自己新创建的了。
printf("after sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
//3 更改当前工作目录,这里创建到了根目录
chdir("/");
//4 重设文件权限掩码,文件权限掩码设置为0,只影响当前进程
if(umask(0)<0){
perror("unmask");
exit(0);
}
//关闭打开的文件描述符
close(0);
close(1);
close(2);
printf("after close \n");
sleep(100);
}
//运行结果
linux@linux:~/Desktop$ ./a.out
Father pid exit
linux@linux:~/Desktop$ Im a Deamon
sid=5164,pid=7739,pgid=7738
after sid=7739,pid=7739,pgid=7739
^C
linux@linux:~/Desktop$ ps -ef|grep "a.out"
linux 7739 2102 0 00:42 ? 00:00:00 ./a.out
linux 7761 5164 0 00:44 pts/6 00:00:00 grep --color=auto a.out
linux@linux:~/Desktop$
含义和解释:
-
SID(Session ID):会话标识符,用于标识一个会话(session)。在多用户操作系统中,一个会话可以包含多个进程组(process group),而一个进程组又可以包含多个进程。会话标识符用于区分不同的会话。
-
PID(Process ID):进程标识符,用于唯一标识一个进程。每个运行的进程都有一个独立的进程标识符。进程标识符通常从1开始自增,直到达到系统的最大限制。
-
PGID(Process Group ID):进程组标识符,用于将一组相关联的进程归为同一进程组。进程组的创建可以通过系统调用(如setpgid)来完成。PGID可以与PID相同,也可以不同。
这些标识符在进程管理和控制方面起到重要作用,例如确定进程归属关系、进程间通信、信号传递等。