Linux:守护进程

什么是守护进程?

守护进程是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

守护进程是个特殊的孤儿进程,这种进程脱离终端。之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

查看守护进程

ps   axj

守护进程的特点:
(1)守护进程一般都是由超级用户启动(UID为0)
(2)没有控制终端(TTY为?)
(3)终端进程组ID 为-1( TPGID 表示终端进程组 ID)

如何编写守护进程?

首先我们必须了解一个函数setsid()


#include<unistd.h>
pid_t setsid(void)

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

(1)创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
(2)需要注意的是,,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。
解决办法:先fork再调用setsid,fork创建的子进程和父进程在同一个进 程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子 进程中调用setsid就不会有问题了。
(3)成功调用该函数的结果是:
a.创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
b. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
c. 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。

创建守护进程的步骤

1,调用umask将文件模式创建屏蔽字设置为0.防止创建文件受系统默认权限的影响

文件权限掩码是屏蔽掉文件权限中的对应位。由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了很多的麻烦(比如父进程中的文件没有执行文件的权限,然而在子进程中希望执行相应的文件这个时候就会出问题)。因此在子进程中要把文件的权限掩码设置成为0,即在此时有最大的权限,这样可以大大增强该守护进程的灵活性。

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

3,调用setsid创建一个新会话。
有必要先介绍一下 Linux 中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的 shell 登录终端。 控制终端、登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们 ,使之不受它们的影响。因此需要调用 setsid() 使子进程成为新的会话组长。
setsid会导致:
1)调用进程成为新会话的首进程。
2)调用进程成为一个进程组的组长进程 。
3)调用进程没有控制终端。(再次fork一次,保证daemon进程,之后不会打开tty设备)

4,将当前工作目录更改为根目录。
防止当前目录有一个目录被删除,导致守护进程无效。
使用fork()创建的子进程是继承了父进程的当前工作目录,由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后使用会造成很多的麻烦。因此通常的做法是让“/”作为守护进程的当前目录,当然也可以指定其他的别的目录来作为守护进程的工作目录。

5,关闭不再需要的文件描述符。
同文件权限码一样,用fork()函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些文件被打开的文件可能永远不会被守护进程读写,如果不进行关闭的话将会浪费系统的资源,造成进程所在的文件系统无法卸下以及引起预料的错误。

6,忽略SINGCHLD信号
但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源,如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在 Linux 下可以简单地将 SIGCHLD 信号的操作设为 SIG_IGN 。

signal(SIGCHLD, SIG_IGN);  

这样,内核在子进程结束时不会产生僵尸进程。

这里写图片描述
这里写图片描述

结果如下:
这里写图片描述

当然,linux中有可以直接调用的创建会话的函数
这里写图片描述

(1)daemon函数主要用于希望脱离控制台,以守护进程的形式在后台运行的程序。
(2)当nochdir为0时,daemon将更改当前进程的目录为root(“/”)目录。
(3) 当noclose为0时,daemon将进程的STDIN,STDOUT,STDERR都重定向到/dev/null。
/dev/null:linux下的黑洞,写入的所有数据会直接丢弃。

如何杀死守护进程?

既然知道了如何创建,自然也要懂得如何让它消失。
(1)ps axj | grep + 守护进程名字,找到相应的守护进程,再用kill -9 + 守护进程号将其杀死。
这里写图片描述

(2)利用ps -ef命令查找相应的守护进程,再用kill命令将其杀死。

(3)也可创建shell脚本对进程的启动、关闭、重启进行自动管理。

为什么有人创建守护进程要fork两次?

int daemon(void)  
{  
    pid_t pid = fork();  //第一次fork

    if( pid != 0 ) 
        exit(0);//parent  

    //first children  
    if(setsid() == -1)  
    {  
       printf("setsid failed\n");  
       assert(0);  
       exit(-1);  
    }  

    umask(0);  
    pid = fork();  //第二次fork

    if( pid != 0) 
        exit(0);  

    //second children   
    chdir ("/");  

    for (int i = 0; i < 3; i++)  
    {  
        close (i);  
    }  

    int stdfd = open ("/dev/null", O_RDWR);  
    dup2(stdfd, STDOUT_FILENO);  
    dup2(stdfd, STDERR_FILENO);  

    return 0;  
}  

第一次fork:这里第一次fork的作用就是让shell认为这条命令已经终止,不用挂在终端输入上;再一个是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组组长(会报错Operation not permitted),如果不fork子进程,那么此时的父进程是进程组组长,无法调用setsid。所以到这里子进程便成为了一个新会话组的组长。

第二次fork:第二次fork是为了避免后期进程误操作而再次打开终端。因为打开一个控制终端的前提条件是该进程必须为会话组组长,而我们通过第二次fork,确保了第二次fork出来的子进程不会是会话组组长。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值