Linux---守护进程

1.什么是守护进程

守护进程也称为精灵进程,是运行在后台的一种特殊进程。它独立于控制终端,与用户交互断开并且周期性地执行某种任务或等待处理某些发生的事情。

大部分的守护进程都是孤儿进程,父进程是init进程,并且自成进程组,自成会话。它没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都被丢到/dev/null中。

守护进程是一种很有用的进程。Linux的大多数服务器都是用守护进程实现的。比如:ftp服务器,ssh服务器,Web服务器httpd等。同时,守护进程完成很多系统任务。比如:作业规划进程crond等。

2.如何查看守护进程

我们用ps axj命令来查看进程。

ps axj | more

参数:
a —- 不仅列当前用户的进程,也列出所有其他用户的进程。
x —- 不仅列有控制终端的进程,也列出所有无控制终端的进程。
j —- 列出与作业控制相关的信息。
这里写图片描述
凡是TPGID一栏写着 -1 的都是没有控制终端的进程,也就是守护进程。
从上图可以看出 ,守护进程通常采用以 d 结尾的名字,表示Daemon。

3.怎样去创建守护进程

守护进程创建的步骤:

1.使之成为后台进程
用fork创建子进程后,让父进程退出,子进程变为孤儿进程被init收养,子进程变为后台进程。
2.脱离父进程控制终端,登录会话和进程组
调用setsid()函数,创建新的会话,让子进程成为新会话的组长,脱离父进程的会话期。setsid() 在调用者是某进程组的组长进程时会失败,但是上面第一步我们已经保证了子进程不是进程组的组长,所以setsid之后,子进程变为新会话的组长。
3.处理SIGCHLD信号
SIGCHLD的处理并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程退出,那么这些子进程就会变成僵尸进程从而占用系统资源。如果父进程等待子进程退出,将增加父进程的负担,影响服务器进程的并发性能。所以在Linux可以将对SIGCHLD信号的处理设为SIG_IGN.
4.禁止进程重新开启控制终端
新会话的组长有权打开控制终端,所以我们这里第二次fork将子进程退出,保留孙子进程,孙子进程不是会话的组长进程没有权利打开控制终端,这样,整个程序就与控制终端隔离开来。
5.改变工作目录
进程活动时,其目录所在的文件系统不能卸下,一般需要将工作目录改到根目录。
6.关闭文件描述符或重定向文件描述符
子进程从父进程那继承了打开的文件描述符。如果不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。因此,我们可以关掉不需要的文件描述符或者将它重定向到/dev/null中。
7.清除文件掩模
子进程继承了父进程的文件掩模,他可能修改守护进程所创建的文件的存取位。为防止这一点,将文件掩模清除掉:umask(0).

下面我们来实现一个守护进程,每隔5秒向 ./mydaemon.log 输入时间信息,一共输入三次。
具体代码如下:

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

void mydaemon()
{
    int fd;

     // 保证子进程不是一个进程组的组长进程
    pid_t pid = fork();//创建子进程
    if(pid < 0)
    {
        perror("fork");
    }
    else if(pid > 0)
    {
        exit(0);//父进程退出
    }

    //创建一个新的会话
    if(setsid() == -1)
    {
        perror("setsid");
    }

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

    //禁止进程重新打开控制终端
    //第二次fork
    pid = fork();
    if(pid < 0)
    {
        perror("error");
    }
    else if(pid > 0)
    {
        exit(0);
    }

    //将当前工作目录更改为根目录
    if(chdir("/") < 0)
    {
        printf("chdir error\n");
        return;
    }
    // 关闭不需要的文件描述符,或者重定向到/dev/null
    close(0);
    fd = open("/dav/null",O_RDWR);
    dup2(fd,1);
    dup2(fd,2);

    //调用umask将文件模式创建屏蔽字设为0
    umask(0);
}

int main()
{
    time_t t;
    int fd,i;

    mydaemon();

    //往日志里循环打印当前的时间信息
    fd = open("./mydaemon.log",O_RDWR,0644);//创建并打开日志文件
    if(fd < 0)
    {
        perror("open error");//打开失败
    }

    for(i = 0;i < 3;i++)
    {
        t = time(0);
        char *buf = asctime(localtime(&t));//将时间信息转化为我们常规的字符串形式
        write(fd,buf,strlen(buf));//往文件里写
        sleep(5);
    }
    close(fd);
    return 0;
}

结果:
这里写图片描述
这里打开文件出错了,为什么?原因是我们打开/mydaemon.log文件时没有权限,此时创建的mydaemon程序的工作目录已经切换到了根目录,所以普通用户没有在根目录下创建文件的权限。切换到root账户就执行成功了。如下图:

这里写图片描述

这里我们思考一个问题,我们已经将标准输出(1)和标准错误(2)重定向到/dev/null里,为什么我们还能在显示屏上看到出现打开错误?

4.daemon()函数原型
我们可以用man手册来查看一下daemon函数,下面是函数原型及描述:

 #include <unistd.h>
  int daemon(int nochdir, int noclose);

DESCRIPTION:
     The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.

     If nochdir  is  zero,  daemon()  changes the process’s current working directory to the root directory("/");otherwi-se,

     If noclose is zero,daemon() redirects standard input, standard output and standard error to /dev/null;otherwise, no changes are made to these file descriptors.

daemon有两个参数:
nochdir:如果是0,将当前工作目录切换到根目录”/”,否则不变。
noclose:如果是0,将0,1,2重定向到 /dev/null,否则不变。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值