守护进程
引用自
一、何为守护进程
- 守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。
- 是一个生存期较长的进程
- 通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件
- 常常在系统引导装入时启动,在系统关闭时终止
- Linux系统有很多守护进程,大多数服务都是通过守护进程实现的
- 如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程
二、如何创建
1)创建子进程,父进程退出
- 编写守护进程的第一步
- 会在Shell终端里造成一程序已经运行完毕的假象
- 所有工作都在子进程中完成
- 形式上做到了与控制终端的脱离
在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。
2)在子进程中创建新会话
- 创建守护进程中最重要的一步
- 使用系统函数 setsid
1. 进程组
是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。
2. 会话周期
会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
3. setsid函数作用
- 用于创建一个新的会话,并担任该会话组的组长
- 让进程摆脱原会话的控制
- 让进程摆脱原进程组的控制
- 让进程摆脱原控制终端的控制
4. 为什么要调用setsid函数呢?
- 由于第一步调用fork函数创建了子进程再将父进程退出
- 此时子进程全盘拷贝了父进程的会话器、进程组、控制终端等,故还不是真正意义的独立开来
- setsid函数能使进程完全独立出来,从而摆脱其他进程的控制
3)改变当前目录为根目录
- 必要步骤
- 使用 fork创建的子进程继承了父进程的当前工作目录
- 由于进程运行中当前目录所在的文件系统是不能卸载的,会对以后使用造成诸多麻烦(如进入单一用户模式)
- 通常做法是让 '/' 作为守护进程的当前目录来避免上述问题
- 也可换成 '/tmp'
- 改变工作目录的常见函数 chdir
4)重设文件权限掩码
- 文件权限掩码是指屏蔽掉文件权限中的对应位
- 比如,某文件权限掩码050,屏蔽了文件组拥有者的可读与可执行权限
- 由于fork函数新建的子进程集成了父进程的文件权限掩码,则使用文件诸多麻烦
- 设置文件权限压那么设置为0,可大大增强该守护进程的灵活性
- 设置文件权限掩码的函数 umask,此 umask(0)
5)关闭文件描述符
- fork新建的子进程会从父进程继承已打开的文件
- 被打开的文件可能永远不会被守护进程读写,但消耗资源,可能导致所在的文件系统无法卸下
- 使用setsid()之后子进程与所属控制终端失去了联系,则标准输入输出不能与守护进程沟通,则文件描述符为0、1、2的文件(标准输入、输出和报错)也该被关闭
- 关闭文件描述符 close()
for(i=0;i
6)守护进程退出处理
- 需要外部kill命令停止该守护进程
- 需要接受kill的signal信号,以正常退出
signal(SIGTERM, sigterm_handler);void sigterm_handler(int arg){ _running = 0;}
7)守护进程完整实例(每隔10s在/tmp/dameon.log中写入一句话)
#include#include #include #include #include #include #include #include #define MAXFILE 65535#define LOG_FILE "/tmp/daemon.log"void sigterm_handler(int arg);volatile sig_atomic_t _running = 1;int main(int argc, char* argv[]){ std::string buf = "this is a Dameon\n"; int len = buf.length(); pid_t pc = fork(); // 第一步 if( pc < 0 ) { printf("error fork\n"); exit(1); // 退出 } else if( pc > 0 ) exit(0); // 此为父进程,pc > 0 pid_t pid = setsid(); // 第二步 if( pid < 0 ) perror("setsid error"); chdir("/"); // 第三步 umask(0); // 第四步 for(int i = 0; i < MAXFILE; ++i) close(i); signal(SIGTERM, sigterm_handler); //第六步 int fd, flag = 1; while( _running ) { if( flag == 1 && (fd = open(LOG_FILE, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0 ) { perror("open"); flag = 0; exit(1); } write(fd, buf.c_str(), len); close(fd); usleep(10*1000); // 10毫秒 }}void sigterm_handler(int arg){ _running = 0;}