守护进程

一、什么是守护进程?

守护进程也称精灵进程(Daemon),运⾏在后台的⼀种特殊进程。它独⽴于控制终端并且周期性地执⾏某种任务或等待处理某些发⽣的事件。守护进程是⼀种很有⽤的进程。
Linux的⼤多数服务器就是⽤守护进程实现的。⽐如, Internet服务器inetd, Web服务器httpd等。同时,守护进程完成许多系统任务。⽐如,作业规划进程crond等
Linux系统启动时会启动很多系统服务进程,这些系统服 务进程没有控制终端,不能直接和⽤户交互。其它进程都是在⽤户登录或运⾏程序时创建,在运⾏结束或⽤户注销时终⽌,但系统服务进程不受⽤户登录注销的影响,它们⼀直在运⾏着。这种进程有⼀个名称叫守护进程(Daemon)。

二、如何查看守护进程

(1)命令

ps axj

(2)参数详解

  • 参数a表⽰不仅列当前⽤户的进程,也列出所有其他⽤户的进程,

  • 参数x表⽰不仅列有控制终端的进程,也列出所有⽆控制终端的进程,

  • 参数j表⽰列出与 作业控制相关的信息。

凡是TPGID⼀栏写着-1的都是没有控制终端的进程,也就是守护进程

COMMAND⼀列⽤[]括起来的名字表⽰内核线程,这些线程在内核⾥创建,没有⽤户空间代码,因此没有程序⽂件名和命令⾏, 通常采⽤以k开头的名字,表⽰Kernel。

init进程我们已经很熟悉了
udevd负责维护/dev⽬录下的 设备⽂件
acpid负责电源管理
syslogd负责维护/var/log下的⽇志⽂件,
可以看出,守护进程通 常采⽤以d结尾的名字,表⽰Daemon。

三、守护进程的创建,及关键步骤详解!

创建守护进程关键的⼀步调⽤setsid函数创建⼀个新的Session,并成为Session Leader

(1)详解setsid函数

#include<unistd.h>

setsid();
  • 该函数调⽤成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。

注意:

调⽤这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1;

要保证当前进程不是进程组的Leader也很容易,只要先fork再调⽤setsid就⾏了。 因为:fork创建的⼦进程和⽗进程在同⼀个进 程组中,进程组的Leader必然是该组的第⼀个进程,所以⼦进程不可能是该组的第⼀个进程,在⼦ 进程中调⽤setsid就不会有问题了。

  • 成功调⽤该函数的结果是:

    1. 创建⼀个新的Session,当前进程成为SessionLeader,当前进程的id就是Session的id。

    2. 创建⼀个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。

    3. 如果当前进程原本有⼀个控制终端,则它失去这个控制终端,成为⼀个没有控制终端的进 程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是⼀个普 通的打开⽂件⽽不是控制终端了

(2)创建守护进程

  1. 调⽤umask将⽂件模式创建屏蔽字设置为0

  2. 调⽤fork,⽗进程退出(exit) 。
    原因:
    1)如果该守护进程是作为⼀条简单的shell命令启动的,那么⽗进程终⽌使得shell认为该命令已经执⾏完毕。
    2)保证⼦进程不是⼀个进程组的组长进程

  3. 调⽤setsid创建⼀个新会话
    setsid会导致:
    (1)调⽤进程成为新会话的⾸进程。
    (2)调⽤ 进程成为⼀个进程组的组长进程 。
    (3)调⽤进程没有控制终端。(再次fork⼀次,保证 daemon进程,之后不会打开tty设备)

  4. 将当前⼯作⽬录更改为根⽬录。

  5. 关闭不在需要的⽂件描述符。

  6. 其他:忽略SIGCHLD信号。

#include <unistd.h>   
#include <signal.h>   
#include <fcntl.h>  
#include <sys/syslog.h>  
#include <sys/param.h>   
#include <sys/types.h>   
#include <sys/stat.h>   
#include <stdio.h>  
#include <stdlib.h>  
#include <time.h>  

int init_daemon(void)  
{   
     int pid;   
     int i;  

     // 1)屏蔽一些控制终端操作的信号  
     signal(SIGTTOU,SIG_IGN);   
     signal(SIGTTIN,SIG_IGN);   
     signal(SIGTSTP,SIG_IGN);   
     signal(SIGHUP ,SIG_IGN);  

     // 2)在后台运行  
     if( pid=fork() ){ // 父进程  
         exit(0); //结束父进程,子进程继续  
     }else if(pid< 0){ // 出错  
         perror("fork");  
         exit(EXIT_FAILURE);  
     }  

     // 3)脱离控制终端、登录会话和进程组  
     setsid();    .       
     // 4)禁止进程重新打开控制终端  
     if( pid=fork() ){ // 父进程  
         exit(0);      // 结束第一子进程,第二子进程继续(第二子进程不再是会话组长)   
     }else if(pid< 0){ // 出错  
         perror("fork");  
         exit(EXIT_FAILURE);  
     }    

     // 5)关闭打开的文件描述符  
     // NOFILE 为 <sys/param.h> 的宏定义  
     // NOFILE 为文件描述符最大个数,不同系统有不同限制  
     for(i=0; i< NOFILE; ++i){  
         close(i);  
     }  

     // 6)改变当前工作目录  
     chdir("/tmp");   

     // 7)重设文件创建掩模  
     umask(0);    

     // 8)处理 SIGCHLD 信号  
     signal(SIGCHLD,SIG_IGN);  

     return 0;   
 }   

 int main(int argc, char *argv[])   
 {  
     init_daemon();  

     while(1);  

     return 0;  
 }  

这里写图片描述

四、为什么创建守护进程时有人fork两次?

第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。

fork第二次主要目的是:防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长*。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端第2次fork不是必须的。开源服务没有fork第二次*

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值