守护进程

啥叫守护进程

不想打字,抄一段,嘿嘿嘿嘿嘿。。。。。。。。。。。

守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。

守护进程的特点

守护进程有几个特点:

  • 守护进程基本上都是以超级用户启动( UID 为 0 )
  • 没有控制终端( TTY 为 ?)
  • 终端进程组 ID 为 -1 ( TPGID 表示终端进程组 ID)

可在终端中输入 ps axj 进行查看。

编写守护进程的步骤

1. 屏蔽控制终端的信号

这是为了防止守护进程在没有运行起来前,控制终端受到干扰退出或挂起。

signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);

2. 使守护进程在后台运行

这是为避免挂起控制终端将守护进程放入后台执行。方法是在进程中调用 fork() 使父进程终止, 让守护进程在子进程中后台执行。

pid = fork();
if(pid < 0)
{
    perror("fork");
    exit(-1);
}
else if(pid > 0)
{   //退出父进程,子进程继续
    exit(0);
}

3. 脱离控制终端、登录会话和进程组

Linux 中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的 shell 登录终端。 控制终端、登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们 ,使之不受它们的影响。因此需要调用 setsid() 使子进程成为新的会话组长。

setsid();

4. 禁止进程重新打开终端

现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端,采用的方法是再次创建一个子进程。

pid = fork();
if(pid < 0)
{
    perror("fork");
    exit(-1);
}
else if(pid > 0)
{
    exit(0);
}

5. 关闭所有文件描述符

进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。

for(int i=0;i<NOFILE;i++)  // NOFILE是文件描述符的最大个数,定义在 sys/patam.h 中
{
    close(i);
}

6. 改变当前工作目录

进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如 /tmp。

chdir("/tmp");

7. 重设文件创建掩模

进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取权限。为防止这一点,将文件创建掩模清除。(也不知道掩模是个啥,以后知道补上)

umask(0);

8. 处理 SIGCHLD 信号

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

signal(SIGCHLD,SIG_IGN);

代码

#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<stdlib.h>
#include<time.h>
#include<stdio.h>

void init_protection_process();

int main()
{
    init_protection_process();

    while(1);
    
    return 0;
}

void init_protection_process()
{
    pid_t pid;

    /* 1. 屏蔽控制终端的信号 */
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGTSTP,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    /* 2. 是守护进程在后台运行 */
    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid > 0)
    {   //退出父进程,子进程继续
        exit(0);
    }

    /* 3. 脱离控制终端、登录会话和进程组 */
    setsid();

    /* 4. 禁止进程重新打开终端 */
    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid > 0)
    {
        exit(0);
    }

    /* 5. 关闭所有文件描述符 */
    for(int i=0;i<NOFILE;i++)  // NOFILE是文件描述符的最大个数,定义在 sys/patam.h 中
    {
        close(i);
    }

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

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

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

值得注意的是,创建守护进程必须使用管理员权限,否则无法成功。

[lingyun@manjaro study]$ gcc study.c -o study
[lingyun@manjaro study]$ sudo ./study
[sudo] lingyun 的密码:
[lingyun@manjaro study]$

用 ps axj | grep study 查看守护进程是否创建成功:

[lingyun@manjaro ~]$ ps axj | grep study
    1 13597 13596 13596 ?           -1 R        0   0:02 ./study
 7722 13599 13598  7722 pts/0    13598 S+    1000   0:00 grep --colour=auto study

可以看到,我们已经创建了一个 pid 为13597的守护进程,他具有守护进程的特征。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值