• 守护进程


    Linux或者Unix操作系统中,在系统引导的时候会开启很多服务,这些服务就叫做守护进程。守护进程是脱离于终端并且在后台运行的进程。守护进程脱离终端是为了避免进程在执行过程中产生的信息在任何终端上显示,另外进程也不会被任何终端所产生的信息所打断。

    守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的。同时,守护进程还能完成许多系统任务。例如,计划任务进程crond、http进程httpd等(这里的结尾字母d就是Daemon的意思)。

    在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行时开始运转,直到整个系统被关闭时才退出。如果想让某个进程不因为终端或其它的变化而受到影响,那么必须把这个进程变成一个守护进程。

    Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。守护进程的编程要点如下;
    1. 在后台运行。
    为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
    if(pid=fork())
    exit(0);//是父进程,结束父进程,子进程继续
    2. 脱离控制终端,登录会话和进程组
    有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
    控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
    setsid();
    说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
    3. 禁止进程重新打开控制终端
    现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
    if(pid=fork())
    exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
    4. 关闭打开的文件描述符
    进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
    for(i=0;i<=getdtablesize();i++)
    close(i);
    5. 改变当前工作目录
    进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmp:
    chdir("/tmp") ;
    6. 重设文件创建掩模
    进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
    7. 处理SIGCHLD信号
    处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
    signal(SIGCHLD,SIG_IGN);
    这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

    守护进程实例如下

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #include<unistd.h>
    #include<sys/stat.h>
    #include<sys/param.h>
    #include<sys/types.h>
    #include<signal.h>
    
    int init_daemon()
    {
    pid_t pid;
    int i;
    
    if((pid=fork())== -1)
        exit(-1);
    if(pid>0)
        exit(0);//父进程退出,shell成为前台进程
    setsid();//child becomes head,lost shell
    if((pid=fork())== -1)
        exit(-1);
    if(pid>0)
        exit(0);
    //grandson
    for(i = 0; i< NOFILE;++i)
        close(i);
    chdir("/tmp");
    umask(0);
    return;
    }
    
    int main()
    {
    FILE *fp;
    FILE *p;
    char buf[256];
    
    init_daemon();
    while(1)
    {
        sleep(30);
        if((fp = fopen("/tmp/test.log","a"))>=0)
        {
            p =popen("ps -ef","r");
            while(fgets(buf,sizeof(buf),p)!=0)
            {fprintf(fp,"%s
    ",buf);}
            pclose(p);
        }
        pclose(fp);
        }
        exit(0);
    }
  • 相关阅读:
    Java入门(6)方法
    重构改善已有代码
    .NET面试题系列(八)电商瞬时抢购的方案
    .NET面试题系列(七)IIS
    设计模式综述
    基于ELK5.1(ElasticSearch, Logstash, Kibana)的一次整合测试
    acl.go
    单机部署-consul
    baseFileWriter.go
    config.go
  • 原文地址:https://www.cnblogs.com/houkai/p/3503469.html
Copyright © 2020-2023  润新知