精华内容
下载资源
问答
  • 编写Linux守护进程

    2010-10-08 14:03:32
    编写Linux守护进程
  • 主要介绍了C语言编写Linux守护进程实例,本文讲解了守护进程及其特性、守护进程的编程要点、守护进程代码实例等内容,需要的朋友可以参考下
  • 编写Linux守护进程.pdf

    2021-09-07 00:55:07
    编写Linux守护进程.pdf
  • 如今的编程是一场程序员和上帝的竞赛,程序员要开发出更大更好、傻瓜都会用到软件,下面这篇文章主要给大家介绍了关于利用C#编写Linux守护进程的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
  • php 编写linux守护进程

    2017-01-10 14:51:02
    问题:将一个php程序以linux守护进程(在后台运行)的方式运行?...编写守护进程的一般步骤步骤: (1)在父进程中执行fork并exit推出; (2)在子进程中调用setsid函数创建新的会话; (3)在子进程中调用ch

    问题:将一个php程序以linux守护进程(在后台运行)的方式运行?


    解决方法:(见代码)

    // index.php文件

    <?php
    
    /*实现守护进程化,当你的PHP程序需要转为后台运行时,
    只需要调用一次封装好的函数init()即可。
    编写守护进程的一般步骤步骤:
    (1)在父进程中执行fork并exit推出;
    (2)在子进程中调用setsid函数创建新的会话;
    (3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;
    (4)在子进程中执行fork并exit推出;
    (5)在子进程中调用umask函数,设置进程的umask为0;
    (6)在子进程中关闭任何不需要的文件描述符
    */
    class Daemon{
        public function init(){
            //创建一个子进程
            $pid = pcntl_fork();
    
            if ($pid == -1){
                throw new Exception('fork子进程失败');
            }elseif ($pid > 0){
                //父进程退出,子进程变成孤儿进程被1号进程收养,进程脱离终端
                exit(0) ;
            }
    
            //创建一个新的会话,脱离终端控制,更改子进程为组长进程
            $sid = posix_setsid();
            if ($sid == -1) {
                throw new Exception('setsid fail');
            }
    
            //修改当前进程的工作目录,由于子进程会继承父进程的工作目录,修改工作目录以释放对父进程工作目录的占用。
            chdir('/');
    
            /**
             * 通过上一步,我们创建了一个新的会话组长,进程组长,且脱离了终端,但是会话组长可以申请重新打开一个终端,为了避免
             * 这种情况,我们再次创建一个子进程,并退出当前进程,这样运行的进程就不再是会话组长。
             */
            $pid = pcntl_fork();
            if ($pid == -1) {
                throw new Exception('fork子进程失败');
            } elseif ($pid > 0) {
                //再一次退出父进程,子进程的子进程(孙子进程)成为最终的守护进程
                exit(0);
            }
    
            umask(0); //重设文件创建掩模
    
    //由于守护进程用不到标准输入输出,关闭标准输入,输出,错误输出描述符
            fclose(STDIN);
            fclose(STDOUT);
            fclose(STDERR);
        }
    }
    /*
     * 当前操作系统为linux时,启动linux守护进程*/
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'Linux') {
        $daemon = new Daemon() ;
        $daemon -> init();
    }
    //这里可以是你的程序代码,我将程序代码写入了另一个文件中了,故在此加载文件
    include_once __DIR__."/log_analysis.php" ;
    ?>
    
    
    在linux系统中测试:
    
    
    
    
    查看index.php文件是否在后台运行:
    
    
    
    

    展开全文
  • linux 守护进程编写

    2020-07-30 19:08:01
    守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待。处理某些发生的事件。守护进程是一种很有用的进程
  • linux编程-守护进程编写 守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待 处理某些发生的事件。守护进程是一种很有用的进程Linux的大多数服务器就是用守护...
    linux编程-守护进程编写
    
        守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待
        处理某些发生的事件。守护进程是一种很有用的进程。 
        Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。
        同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。
    
      守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,
        造成不同 Unix环境下守护进程的编程规则并不一致。
        需要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。
        下面结合一些前辈的文档和自己的例子说说守护进程的编程。
    
    
    .基本概念
    .进程
        .每个进程都有一个父进程
        .当子进程终止时,父进程会得到通知并能取得子进程的退出状态。
    .进程组
        .每个进程也属于一个进程组
        .每个进程主都有一个进程组号,该号等于该进程组组长的PID号
        .一个进程只能为它自己或子进程设置进程组ID号
    .会话期
        .对话期(session)是一个或多个进程组的集合。
        .setsid()函数可以建立一个对话期:
        如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。
        (1)此进程变成该对话期的首进程
        (2)此进程变成一个新进程组的组长进程。
        (3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。
        如果该进程是一个进程组的组长,此函数返回错误。
        (4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,
        子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能是新会话的进程组的PID。
        从而保证了这一点。
    
        if((pid=fork())>0)  //parent
            exit(0);
        else if(pid==0){        //th1 child
            setsid();           //th1是成为会话期组长
            if(fork() ==0){     //th2不会是会话期组长(变成孤儿进程组)
                ...
            }
        }
    
    
    一. 守护进程及其特性
     
        (1)守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。
        (2)其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,
        会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)
        中继承下来的。
        (3)最后,守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,
        可以由作业规划进程crond启动,还可以由用户终端(通常是 shell)执行。
      总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。
        因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。
    
    二. 守护进程的编程要点  (来自UEAP)
    
      前面讲过,不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样,
          区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。
          同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点如下;
    
    1. 在后台运行。
      为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,
        让Daemon在子进程中后台执行。
    
    if(pid=fork())
        exit(0); //是父进程,结束父进程,子进程继续
    
    2. 脱离控制终端,登录会话和进程组
      进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。
        这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
        控制终端,登录会话和进程组通常是从父进程继承下来的。
        我们的目的就是要摆脱它们,使之不受它们的影响。
        方法是在第1点的基础上,调用setsid()使进程成为会话组长:
    
    setsid();
    
      说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。
        setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
        由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
    
    3. 禁止进程重新打开控制终端
      现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。
        可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
    
    if(pid=fork())
         exit(0); //结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
    
    4. 关闭打开的文件描述符
      进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,
        造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
    
    for(i=0;i 关闭打开的文件描述符close(i);>
    
    5. 改变当前工作目录
      进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。
        对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如 /tmpchdir("/")
    
    6. 重设文件创建掩模
      进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。
        为防止这一点,将文件创建掩模清除:umask(0);
    
    7. 处理SIGCHLD信号
      处理SIGCHLD信号并不是必须的。
        但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。
        如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。
        如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。
        在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
    
    signal(SIGCHLD,SIG_IGN);
    
      这样,内核在子进程结束时不会产生僵尸进程。
        这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。
    
    三. 守护进程实例
      守护进程实例包括两部分:主程序test.c和初始化程序init.c。
        主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。
        初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。
    
    1. init.c清单
    
    #include < unistd.h >
    #include < signal.h >
    #include < sys/param.h >
    #include < sys/types.h >
    #include < sys/stat.h >
    
    void init_daemon(void)
    {
        int pid;
        int i;
        if(pid=fork())
            exit(0);        //是父进程,结束父进程
        else if(pid< 0)
            exit(1);        //fork失败,退出
        //是第一子进程,后台继续执行
        setsid();           //第一子进程成为新的会话组长和进程组长
        //并与控制终端分离
        if(pid=fork())
            exit(0);        //是第一子进程,结束第一子进程
        else if(pid< 0)
            exit(1);        //fork失败,退出
        //是第二子进程,继续
        //第二子进程不再是会话组长
        for(i=0;i< NOFILE;++i)  //关闭打开的文件描述符
            close(i);
    
        chdir("/tmp");      //改变工作目录到/tmp
        umask(0);           //重设文件创建掩模
        return;
    }
    
    2. test.c清单
    
    #include < stdio.h >
    #include < time.h >
    
    void init_daemon(void);//守护进程初始化函数
    
    main()
    {
        FILE *fp;
        time_t t;
        init_daemon();//初始化为Daemon
    
        while(1)//每隔一分钟向test.log报告运行状态
        {
            sleep(60);//睡眠一分钟
            if((fp=fopen("test.log","a")) >=0){
                t=time(0);
                fprintf(fp,"Im here at %sn",asctime(localtime(&t)) );
                fclose(fp);
            }
        }
    }
    
    以上程序在RedHat Linux6.0下编译通过。步骤如下:
    编译:gcc -g -o test init.c test.c
    执行:./test
    查看进程:ps -ef
    
    
    说明:在系统调用库中有一个库函数可以直接使一个进程变成守护进程,
           #include <unistd.h>
           int daemon(int nochdir, int noclose);
    
     
    
    转自:http://blog.csdn.net/zg_hover/article/details/2553321

    展开全文
  • Linux守护进程编写

    2019-04-12 00:09:38
    Linux守护进程编写 环境:VMware 15 + ubuntu 16 护进程的特点 后台服务程序,随系统启动而启动。 很多系统服务通过守护进程完成。 守护进程的名字往往以字母‘d’结尾 生存周期长。系统装入时...

    Linux守护进程编写

    环境:VMware 15 + ubuntu 16 内核:4.19

    护进程的特点

    • 后台服务程序,随系统启动而启动。

    • 很多系统服务通过守护进程完成。

      • 守护进程的名字往往以字母‘d’结尾
    • 生存周期长。系统装入时启动,系统关闭时终止。

    • 周期性的执行某种任务或等待处理某些特别的事件。

    • 在Linux中,每一个与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端。但守护进程能突破这种限制,它从被执行开始,直到整个系统关闭时才退出.

    依附:当控制终端被关闭时,依附于这个终端的进程都会自动的关闭。

    守护进程编程一般步骤

    • 第一步、创建子进程、父进程退出

      编写守护进程的第一步就是使其独立与父进程,可以将父进程退出,子进程变成孤儿进程,并由init进程收养。为避免终端挂起,将父进程退出,造成程序已经退出的假象,所有后面的工作都在子进程完成,这样控制终端也可以继续执行其他命令,从而在形式上脱离控制终端的控制。

      • pid = fork(); //创建子进程

      • if (pid > 0)

        exit(0); //父进程退出

    • 第二步、在子进程中创建新的会话

      经过第一步,子进程已经有后台运行,而在fork()的时候,子进程复制了大量父进程的PCB,包括会话、进程组、控制终端等信息。尽管父进程已经退出,但子进程的会话、进程组、控制终端的信息没有改变。为使子进程完全摆脱父进程的环境,需要调用 setsid 函数。

      • setsid( ) //让进程脱离控制终端。

        通过调用 setsid 函数可以创建一个新会话,调用进程担任新会话的首进程,其作用有:

        • 使当前进程脱离原会话的控制
        • 使当前进程脱离原进程组的控制
        • 使当前进程脱离原控制终端的控制
    • 第三步、改变当前目录为根目录

      • chdir(“/”); //避免原工作目录不能被卸载

        直接调用 chdir 函数将切换到根目录下。
        由于进程运行过程中,当前目录所在的文件系统(如:“/mnt/usb”)是不能卸载的,为避免对以后的使用造成麻烦,改变工作目录为根目录是必要的。如有特殊需要,也可以改变到特定目录,如“/tmp”。

    • 第四步、重设文件权限掩码

      • umask(0); // 文件权限掩码设置成为0

        fork 函数创建的子进程,继承了父进程的文件操作权限,为防止对以后使用文件带来问题,需要重设文件权限掩码。调用 umask 设置文件权限掩码,通常是重设为 0,清除掩码,这样可以大大增强守护进程的灵活性。

        权限掩码:设定了文件权限中要屏蔽掉的对应位。这个跟文件权限的八进制数字模式表示差不多,将现有存取权限减去权限掩码(或做异或运算),就可产生新建文件时的预设权限。

    • 第五步、关闭文件描述符

      • for(i = 0;i < MAXFILE;i++)

        close(i); //关闭继承自父进程的文件

        同文件权限掩码一样,子进程可能继承了父进程打开的文件,而这些文件可能永远不会被用到,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下,因此需要一一关闭它们。由于守护进程脱离了终端运行,因此标准输入、标准输出、标准错误输出这3个文件描述符也要关闭。通过调用函数 getdtablesize 返回进程文件描述符表中的项数(即打开的文件数目):

        for (i=0; i < getablesize(); i++)
            close(i);

    测试守护进程是否正常运行

    /*
    	向文件/home/emmmm/Desktop/program.log中隔一秒钟写入时间
    */
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<time.h>
    #include<string.h>
    #include<stdlib.h>
    
    int main(void){
        pid_t child;
        time_t rawTime;
    	struct tm *timeInfor;
        FILE *pf;
        child = fork();
        if(child){
            exit(0);
        }
        setsid();//让进程脱离控制终端
        chdir("/");//避免原工作目录不被卸载
        umask(0);//文件权限掩码设置为0
        for(int i = 0; i<getdtablesize();i++){//关闭继承自父进程的文件
            close(i);
        }
        
        for(int i = 0;i<60;i++){
            pf = fopen("/home/emmmm/Desktop/program.log", "a");
            if(pf==NULL){
                printf("can't open the file!");
                return 0;
            }
            time(&rawTime);
    	    timeInfor = localtime(&rawTime);
            fprintf(pf, "pid = %d,and the time is ", getpid());
            fprintf(pf, "%d%d%d%d%d%d%d%d%d%d%d\n", timeInfor->tm_year + 1900, 							(timeInfor->tm_mon + 1)/10,
                    (timeInfor->tm_mon + 1)%10, 
                    (timeInfor->tm_mday)/10, 
                    (timeInfor->tm_mday) % 10, 
                    (timeInfor->tm_hour)/10, 
                    (timeInfor->tm_hour)%10, 
                    (timeInfor->tm_min)/10, 
                    (timeInfor->tm_min)%10, (timeInfor->tm_sec)/10, 
                    (timeInfor->tm_sec)%10);
            fclose(pf);
            sleep(1);
        }
        return 0;
    
    }

    测试结果:
    在这里插入图片描述

    如何获得正在运行的进程

    方法一:

      在目录/proc/下有许多以数字命名的文件夹,这个目录下储存着系统所有进程的task_struct结构,研究了一下发现,那些数字命名的文件夹是正在运行进程的pid,进入这些文件夹找到state文件,这里面记录了正在运行pid的名称、状态等等信息。可以搜索/proc/目录来查看。

    /*程序的状态*/
    static const char * const task_state_array[] = {
    	"R (running)",		/* 0x00 */
    	"S (sleeping)",		/* 0x01 */
    	"D (disk sleep)",	/* 0x02 */
    	"T (stopped)",		/* 0x04 */
    	"t (tracing stop)",	/* 0x08 */
    	"X (dead)",		/* 0x10 */
    	"Z (zombie)",		/* 0x20 */
    	"P (parked)",		/* 0x40 */
    
    	/* states beyond TASK_REPORT: */
    	"I (idle)",		/* 0x80 */
    }

    1.搜索该目录下的子文件名称:获取文件夹中文件夹的名称

    2.读取/proc/xxx/state文件中关于进程的信息:一定要注意名字的长度管够,不然可能出现乱码

    /*已知PID在文件中查找进程的状态*/
    char* getNamebyPID(char *pid){
        char *name = (char*)malloc(sizeof(char*)*PRONUM);
        char path[BUFNUM];
        char buf[BUFNUM];
    
        sprintf(path, "/proc/%s/status", pid);
        FILE *pf = fopen(path, "r");
        if(pf == NULL){
            return name;
        }
        else{
            if( fgets(buf, BUFNUM-1, pf)== NULL ){
                 fclose(pf);
                 return name;
             }
            fclose(pf);
            sscanf(buf, "%*s %s", name);
        }
    
        return name;
    }

    方法二:

    建立和命令行之间的管道,将输入命令行的结果重定向给文件流,通过文件流可以读取结果

    参考:popen介绍

    popen 建立管道

    #include<stdio.h>//测试popen()
    
    int main(void){
        FILE *fp;
        char buf[1024];
        fp = popen("ps -A -o pid,user", "r");
        if(fp==NULL){
            printf("The commad doesn't work!");
            return 0;
        }
        else {
            while(fgets(buf, 1023, fp)!=NULL){
                printf("%s", buf);
            }
            pclose(fp);
        }
        return 0;
    }

    测试结果:
    在这里插入图片描述

    如何获得运行程序的起止时间

    我选用的方法是每隔1秒钟判断查看一次正在运行的程序,如果程序在文件中出现,更新其终止时间,如果进程在原来的文件中不存在,则将不存在的进程相关信息写到文件的结尾。

    但是还有一种方法,用上面的popen()方法,查看PS -A -o lstart,etime(etime表示进程持续运行的时间)

    将该程序加入开机自启

    我的内核版本是4.19的,因此用这个方法可以成功,但是在5.0.1版本以上就不能使用

    开机自启for4.19

    开机自启for5.0.1

    检验

    将文件删除后关机重启,看到有文件生成。点开文件后,不停有提示该文件内容发生改变,这样基本上就成功了。

    在这里插入图片描述

    参考:

    Linux——ps指令介绍

    通过pid 获取运行程序的名称

    已知进程名获得pid

    Linux下守护进程的实现

    linux下是时间转字符串和字符串转时间

    sscanf()函数介绍(真的超好用!!!!)

    多种文件操作和字符串操作:fseek(), strcmp(), strcat()

    展开全文
  • Linux编写守护进程获取进程信息

    千次阅读 2019-04-07 15:46:14
    记录系统运行期间所有运行的进程,记录信息包括:进程PID,可执行程序名称,用户名,创建时间,撤销时间,开机能自启动

    目的

     记录系统运行期间所有运行的进程,记录信息包括:进程PID,可执行程序名称,用户名,创建时间,撤销时间,开机能自启动

    守护进程

    进程

    • 每个进程都有一个父进程
    • 当子进程终止时,父进程会得到通知并能取得子进程的退出状态。

    进程组

    • 每个进程也属于一个进程组
    • 每个进程主都有一个进程组号,该号等于该进程组组长的PID号
    • 一个进程只能为它自己或子进程设置进程组ID号

    会话期

    • 对话期(session)是一个或多个进程组的集合。
    • setsid()函数可以建立一个对话期:
    • 如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。
        (1)此进程变成该对话期的首进程
        (2)此进程变成一个新进程组的组长进程。
        (3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。如果该进程是一个进程组的组长,此函数返回错误。
        (4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,
      子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能是新会话的进程组的PID。从而保证了这一点。
    if((pid=fork())>0)  //parent
        exit(0);
    else if(pid==0){        //th1 child
        setsid();           //th1是成为会话期组长
        if(fork() ==0){     //th2不会是会话期组长(变成孤儿进程组)
            ...
        }
    }
    

    守护进程及其特性

    • 守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。
    • 其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。
    • 最后,守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,
      可以由作业规划进程crond启动,还可以由用户终端(通常是 shell)执行。
    • 总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。
      因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。

    守护进程的编程要点 (来自UEAP)

      前面讲过,不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样,区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点如下;

    • 在后台运行。
        为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
        if(pid=fork())
            exit(0); //是父进程,结束父进程,子进程继续
    
    • 脱离控制终端,登录会话和进程组
        进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
    setsid();
    

    说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长 setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

    • 禁止进程重新打开控制终端
        现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。
      可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
    if(pid=fork())
         exit(0); //结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
    
    
    • 关闭打开的文件描述符
        进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
    for(i = 0;i<NOFILE;i++)           //关闭打开的文件描述符
            close(i);
    
    • 改变当前工作目录
        进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如 /tmp
    chdir("/")
    
    • 重设文件创建掩模
        进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除。
    umask(0);
    
    • 处理SIGCHLD信号
        处理SIGCHLD信号并不是必须的。 但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN
    signal(SIGCHLD,SIG_IGN);
    

    这样,内核在子进程结束时不会产生僵尸进程。 这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

    实例

    编写创建守护进程的函数

    (博主将文件放在init.c文件中

    #include<unistd.h>
    #include<signal.h>
    #include<stdio.h>
    #include<sys/param.h>
    #include<sys/stat.h>
    #include <stdlib.h>
    
    void init_daemon()
    {
        int pid;
        int i;
        if (pid = fork())
            exit(0);           //结束父进程
        else if (pid < 0)
            exit(1);           //fork失败,退出
        //第一个子进程,后台继续执行
        setsid();              //第一个子进程成为新的会话组长和进程组长并与控制终端分离
        if(pid = fork())
            exit(0);            //结束第一个子进程
        else if(pid < 0)
            exit(1);            //fork失败,退出
        //第二个子进程,继续,此进程不再是回话组长
        for(i = 0;i<NOFILE;i++)           //关闭打开的文件描述符
            close(i);
    
        chdir("/");          //改变工作目录到/
        umask(0);               //重设文件创建掩模
        return ;
    }
    

    通过处理ps命令获取进程的信息

    (此文件在pid.c文件中

    
    
    
    #define _XOPEN_SOURCE 700          //解决strptime函数不能用的问题
    
    
    #include <stdlib.h>
    #include<stdio.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <dirent.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include<time.h>
    #include<sys/types.h>
    #include<string.h>
    
    
    
    #define BUFF_LEN   1024    //行缓冲区的长度
    
    
    typedef struct{
    	pid_t pid;         //进程的pid
    	char user[20];     //进程用户名
    	char name[256];    //进程名称
    	char stime[50];    //开始时间
    	char etime[50];  //撤销时间
    }proc_info_st;
    
    void init_daemon(); //守护进程初始化函数
    void get_endTime(char *stime,char *etime,char *string);  //时间转换
    
    
    int main(int argc, char *argv[])
    {
        init_daemon();  //初始化为Daemon,守护进程的创建
        while(1){     
            FILE *fo;
            fo = fopen("/tmp/test.log","w");  //初始化输出文件
            fclose(fo);
    
            FILE *fp;
            char buffer[BUFF_LEN];            //存储文件信息
    
            fp = popen("ps -eo  pid,user,comm,etime,lstart","r");   //读取信息
            int i=0;
            while(fgets(buffer,sizeof(buffer),fp)){
                if(i == 0){           //第一行为标题,舍弃
                    i++;
                    continue; 
                }
                proc_info_st info;
                char e[50];          //存储进程运行时间
                sscanf(buffer,"%d\t%s\t%s\t%s\t%[^\n]",
                    &info.pid,info.user,info.name,e,info.stime);
                get_endTime(info.stime,e,info.etime);    //将运行时间和开始时间相加,得到结束时间
                if((fo = fopen("/tmp/test.log","a")) >= 0){   //输出到test.log文件中
                    fprintf(fo, "pid:%d\nuser:%s\nname:%s\nstart_time:%s\nend_time:%s\nrun_time:%s\n\n\n",
                        info.pid,info.user,info.name,info.stime,info.etime,e);  
                    fclose(fo);
                }
            }
            fclose(fp);
            sleep(60);    //每隔1min,保存一下信息
        }
    
    	
        return 0;
    }
    
    
    void get_endTime(char *stime,char *etime,char *string)
    {
    	struct tm s,e;
    	memset(&s, 0, sizeof(s));
    	memset(&e, 0, sizeof(e));
        strptime(stime, "%a\t%b\t%d\t%H:%M:%S\t%Y", &s);
        time_t ee = 0;
        if(strlen(etime) > 6) {
        	strptime(etime, "%H:%M:%S", &e);
        	ee+=e.tm_hour * 3600 + e.tm_min * 60 + e.tm_sec;
        }
        else{
        	strptime(etime, "%M:%S", &e);
        	ee+=e.tm_min * 60 + e.tm_sec;
        }
        time_t ss = mktime(&s);
        time_t all = ss + ee + 8 * 3600;
        struct tm *a;
        a = gmtime(&all);
        strftime(string, 50, "%a\t%b\t%d\t%H:%M:%S\t%Y",a);  
    }
    

    通过命令行编译文件

    gcc -g -o pid init.c pid.c   
    ./pid
    

    查看/tmp下,test.log文件是否有输出

    若成功,结果如下
    在这里插入图片描述

    设置开机自启动

    Ubuntu18.04 不再使用initd管理系统,改用systemd.

    然而systemd很难用,改变太大,跟之前的完全不同。

    使用systemd设置开机启动
    为了像以前一样,在/etc/rc.local中设置开机启动程序,需要以下几步:

    1. /lib/systemd/system/rc-local.service 链接到 /etc/systemd/system/ 目录下面来:
    sudo ln -fs /lib/systemd/system/rc-local.service /etc/systemd/system/rc-local.service
    
    1. 创建/etc/rc.local文件
    sudo touch /etc/rc.local 
    
    1. 赋可执行权限
    sudo chmod 755 /etc/rc.local 
    
    1. 编辑rc.local,添加需要开机启动的任务
    sudo gedit /etc/rc.local
    

    (我将编译好的pid文件放在了/usr下面,根据你的目录文件设置即可
    在这里插入图片描述
    5. 重启,完成。

    展开全文
  • Linux编写守护进程

    2015-04-10 11:00:37
    守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统...
  • linux守护进程编写

    千次阅读 2018-10-23 20:57:42
    掌握编写守护进程的方法 一、基础知识 1)进程的类型,进程大体分为三类  (1)交互进程  (2)批处理进程  (3)守护进程 2)守护进程的特点  (1)运行方式: 守护进程,也就是通常所说的Daemon进程,是Linux...
  • 守护进程的父进程一般都是init进程,因为它真正的父进程在fork出守护进程后就直接退出了,所以守护进程都是孤儿进程,由init接管; 2、有哪些常见的守护进程 日志服务进程 syslogd 数据库守护进程 mysqld 3、创建...
  • Linux守护进程编写.pdf
  • 编写linux守护进程

    2013-02-01 14:32:06
    linux中,系统与用户交流的界面为终端,每一个从此终端开始运行的进程都将依附于这个终端,当终端关闭时,相应的进程都会自动关闭。但在实际使用中,经常需要让进程脱离终端并在后台运行。让进程脱离终端有多种...
  • 守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。 它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些事件的发生。 守护进程常常在系统引导载入时启动,在系统...
  • 如何正确编写linux守护进程

    千次阅读 2012-11-12 09:21:22
    1、守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。如果想让某个进程不因为用户或终端或其他地...
  • 原文:http://blog.csdn.net/djstavaV/article/details/47810445软硬件环境Ubuntu ...典型的守护进程一般都会在进程名后加上字母”d”,表示daemon,如常见的mysqld、telnetd、sshd等。这些进程没有控制终端,不受用...
  • Linux编写守护进程(syslog详解)

    千次阅读 2014-06-27 17:27:38
    Linux编写守护进程 Linux 守护进程  守护进程概述  守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或...
  • 利用网上下载的这段代码,成功实现了守护进程,原来守护进程是很简单的事情。
  • 主要介绍了使用Python编写Linux系统守护进程实例,本文先是讲解了什么是守护进程,然后给出了一个Python语言的简单实现,需要的朋友可以参考下
  • 守护进程介绍:linux中的守护进程是一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待处理某些事件的发生。 linux中大多数服务器就是守护进程实现的。例如: internet服务器中inetd, Web服务器中的...
  • linux编写守护进程的例程  摘自《开放系统世界》2004年第5期郭吉平、任莲的文章“亲自动手编写守护进程”。 /*郭吉平、任莲 亲自动手编写守护进程*/ #include #include #include void main(in

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,848
精华内容 11,539
关键字:

linux编写进程守护

linux 订阅