目录
前言
本文介绍Linux系统中的三大进程类型之一——守护进程。
一、介绍
1.1 概念
守护进程又叫精灵进程(Daemon Process),它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
其他相关概念:
- 进程组(Process Group): 进程集合,每个进程组有一个组长(Leader),其进程 ID 就是该进程组 ID。
- 会话(Session): 进程组集合,每个会话有一个组长,其进程 ID 就是该会话组 ID。
- 控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程(Controlling Process)。
图1 进程、进程组和会话的示意图
1.2 特点
始终在后台运行,独立于任何终端,周期性的执行某种任务或等待处理特定事件。
它是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
1.3 举例
http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。
更简便地创建守护进程: nohup 命令
nohup xxxx &
二、系统编程
2.1 setsid函数
pid_t setsid(void);
功能:子进程创建新会话 。
成功:返回调用进程的会话ID;失败:-1,设置errno。
调用了setsid函数的进程,既是新的会长,也是新的组长
2.2 getpid函数
pid_t getpid(void);
功能:获取当前进程的pid。
2.3 getsid函数
pid_t getsid(pid_t pid)
功能:返回调用进程的会话ID
成功:返回调用进程的会话ID;失败:-1,设置errno
如若pid是0,
getsid
返回调用进程的会话ID;如若pid并不属于调用者所在的会话,那么调用进程就不能得到该会话ID。
2.4 getpgid函数
pid_t getpgid(pid_t pid);
功能:获取指定进程(通过其进程标识符 pid
)所属的进程组id
2.5 chdir函数
int chdir(const char * path);
功能:将当前的工作目录改变成以参数路径所指的目录。
2.6 umask函数
mode_t umask(mode_t mask);
功能:umask()会将系统umask值设成(mode&~mask)的权限值。
例如:
在建立文件时指定文件权限为0666, 通常umask 值默认为022, 则该文件的真正权限则为0666&~022=0644, 也就是rw-r--r--返回值此调用不会有错误值返回. 返回值为原先系统的umask 值。
三、代码例程
3.1 使子进程在后台运行
pid_t pid; pid = fork(); if(pid<0){ perror("fork"); return 0; } else if(pid>0){ exit(0); } printf("I am a deamon\n"); printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
3.2 使子进程脱离原终端
if(setsid()<0){ perror("setsid"); exit(0); } printf("after sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
3.3 更换目录,并设定权限(非必须)
chdir("/"); if(umask(0)<0){ perror("unmask"); exit(0); }
3.4 关闭打开的文件描述符
已脱离终端,stdin / stdout / stderr无法再使用,故关闭当前的文件描述符。
close(0); close(1); close(2);
3.5 完整代码
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> int main(){ pid_t pid; pid = fork(); if(pid<0){ perror("fork"); return 0; } else if(pid>0){ exit(0); } printf("I am a deamon\n"); printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid())); if(setsid()<0){ perror("setsid"); exit(0); } printf("after sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid())); chdir("/"); if(umask(0)<0){ perror("unmask"); exit(0); } close(0); close(1); close(2); printf("after close \n"); sleep(100); return 0; }
3.6 代码实验
四、总结
从代码实验可以看出,我们以及成功的创建了守护进程,并且让它“当家做主人”,成为了会话的组长,同时我们也可以看到,因为关闭了打开的文件描述符,最后的“after close \n”并没有被成功打印出来。