Linux守护进程详解

Linux守护进程详解,第1张

在Linux服务器实际应用中,经常会有需要长时间执行的任务。这类任务若在前台运行,用户无法进行其他 *** 作或者断开与服务器的连接,否则任务将被中止。此时适合使用守护进程。为了使用守护进程,需要了解Linux前台、后台、守护进程的概念与使用,本文将对此进行讲解。

可以看出,”后台任务”与”前台任务”的重要区别: 是否继承标准输入 。所以扒饥,执行后台任务的同时,用户还可以输入其他命令

为了理解守护任务为何在结束session时也不退出,需要先了解Linux下退出session时发生的 *** 作。

Session退出时,linux系统设计如下:

前台任务会随着session的退出而退出是因为它收到了 SIGHUP信号

后台任务是否会受到SIGNUP信号,取决于shell的 huponexit 参数。可以通过 $ shopt | grep huponexit 查看该参数的值。大多数Linux系统,这个参数默认关闭(off)。因此,session退出的时候,不会把SIGHUP信号发给”后台任务”,即此时的后台任务是守护进程,但这显然不够安全。并不保险,因为有的系统的 huponexit 参数可能是打开的(on)状态。

更保险的方法是使用 disown命令。它可以将指定任务从”后台任务”列表(jobs命令的返回结果)之中移除 。一个”后台任务”只要不在这个列表之中,session 就肯定不会向它发出SIGHUP信号。

执行上面的命令以后, server.js 进程就被移出了”后台任务”列表。你可以执行 jobs 命令验证,输出结果里面,不会有这个进程。

但是,这样还存在问题。因为 ”后台任务”的标准 I/O 继承自当前 session, disown 命令并没有改变这一点 。一旦”后台任务”读写标准 I/O,就会发现它已经不存在了,所以就 报错终止执行 。 为了解决这个问题,需要对”后台任务”的 标准 I/O 进行重定向

这样基本上就没有问题了。

注:

/dev/null 文件的作用

这是一个无底洞,任何东西都可以定向到这里,但是却无法打开。

所以一般很大的stdou和stderr当你不关心的时候可以利用stdout和stderr定向到这里

nohup命令对server.js进程做了三件事。

阻止SIGHUP信号发到这个进程。

关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。

重定向标准输出和标准枣此兆错误到文件nohup.out。

也就是说,nohup命令实际上将子进程与它所在的 session 分离了。 注意,nohup命令不会自动把进程变为”后台任务”,所以必须加上&符号

守护进程创建方法:

方法一:

方法二:

方法三:

fg、bg、jobs、&、nohup、ctrl+z、ctrl+c 命令

一、&

加在一个命令的最后,可以把这个命令放到后台执行,如:

二、ctrl + z

可以将一个正在前台执行的命令放到后台,并且处于暂停状态。

CTRL+Z 和 CTRL+C的对比

CTRL+Z 和 CTRL+C 都是中断命令,但是他们的作用却不一样.

CTRL+C 是强制中断程序的执行,而 CTRL+Z 的是将任务中断,但是此任务并没有结束,仍然在进程中,只是维持挂起的状态,用户可以使用 fg/bg *** 作继续前台或后台的任务。

三、jobs

查看当前有多少在后台运行的进程

jobs -l选项可显示所有任务的PID,jobs的状态可以是running, stopped, Terminated。但是如果任务被终止了(kill),shell 从当前的shell环境已知的列表中删除任务的进程标识。

四、fg

将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %jobnumber (jobnumber是命令编号,凳租不是进程号)将选中的命令调出。

五、bg

将一个在后台暂停的命令,变成在后台继续执行。

如果后台中有多个命令,可以用 bg %jobnumber 将选中的命令调出。

六、kill

方法1:通过jobs命令查看job号(假设为num),然后执行

方法2:通过ps命令查看job的进程号(PID,假设为pid),然后执行

前台进程的终止:Ctrl+c

七、nohup

如果想让程序即使在关闭当前的终端后也始终在后台执行(之前的&做不到),需要使用nohup命令。

nohup命令可以在你退出帐户/关闭终端之后继续运行相应的进程。

关闭终端后,在另一个终端jobs已经无法看到后台跑的程序了,此时利用ps(进程查看命令)查看进程。

http://m.2cto.com/os/201301/185701.html

http://www.cnblogs.com/kaituorensheng/p/3980334.html

http://m.blog.csdn.net/article/details?id=50766752

1、守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。

2、创建守护进程步骤

1)创建子进程,父进程举液退出

之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。

在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。

2)在子进程中创建新会话

进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。

会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。

(1)pid_t setsid(void)

setsid() creates a new session if the calling process is not a process group leader. The calling process will be the only process in this new process group and in this new session.

setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:

① 让进程摆脱原会话的控制

② 让进程摆脱丛答猜原进程组的控制

③ 让进程摆脱原控制终端的控制

有以下三个结果:

(a)成为新会话的首进程

(b)成为一个新进程组的组长进程

(c)没有控制终端。

有些人建议在此时再次调用fork,并使父进程终止。第二个子进程作为守护进程继续运行。这样就保证了该守护进程不是会话首进程。

setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。

setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。 子进程可以自己组成一个新的进程组,即调用setpgrp()与原进程组脱离关系,产生一个新的进程组,进程组号与它的进程号相同.这样,父进程退出运行后就不会影响子进程的当前运行.

3)改变当前目录为根目录

使用fork创建的子进程继承了父进程的当渗型前工作目录;进程活动时,其工作目录所在的文件系统不能卸下。通常的做法是让"/"作为守护进程的当前工作目录,也可以是其他目录,如/tmp,使用chdir。

4)重设文件权限掩码

文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。mask = mask &~050

通常,把文件权限掩码设置为0,umask(0)。

5)关闭文件描述符

用fork函数新建的子进程会从父进程那里继承已经打开了的文件描述符。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。

在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。

for(i=0i<MAXFILEi++)

close(i)

6)守护进程退出处理

当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程的正常退出。

signal(SIGTERM, sigterm_handler)

void sigterm_handler(int arg)

{

_running = 0

}

7)处理SIGCHLD信号

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

signal(SIGCHLD,SIG_IGN)

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

可以分三步来做:

做两个简单的守护进程,并能正常运行

监控进程是否在运行

启动进程

综合起来就可以了,代码如下:

被监控进程thisisatest.c(来自):

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>者氏

#include<sys/param.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<time.h>

void init_daemon()

{

int pid

int i

pid=fork()

if(pid<0)    

    exit(1) //创建错误,退出

else if(pid>0) //父进程退出

    exit(0)

    

setsid()//使子进程成为组长

pid=fork()

if(pid>0)

    exit(0)//再次退出,使进程不是组长,这样进程就不会打开控制终端

else if(pid<0)    

    exit(1)

//关闭进程打开的文件句柄

for(i=0i<NOFILEi++)

    close(i)

chdir("/root/test") //改变目录

umask(0)//重设文件创建的掩码

return

}

void main()

{

    FILE *fp

    time_t t

    init_daemon()

    while(1)

    {

        sleep(60)//等待一分钟再写入

        fp=fopen("testfork2.log","a")

        if(fp>=0)

        {

 首搏散           time(&t)

     银历       fprintf(fp,"current time is:%s\n",asctime(localtime(&t))) //转换为本地时间输出

            fclose(fp)

        }

    }

    return

}

监控进程monitor.c:

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

#include<stdlib.h>

#include<sys/param.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<time.h>

#include<sys/wait.h>

#include<fcntl.h>

#include<limits.h>

#define BUFSZ 150

void init_daemon()

{

int pid

int i

pid=fork()

if(pid<0)

    exit(1) //创建错误,退出

else if(pid>0) //父进程退出

    exit(0)

setsid()//使子进程成为组长

pid=fork()

if(pid>0)

    exit(0)//再次退出,使进程不是组长,这样进程就不会打开控制终端

else if(pid<0)

    exit(1)

//关闭进程打开的文件句柄

for(i=0i<NOFILEi++)

    close(i)

chdir("/root/test") //改变目录

umask(0)//重设文件创建的掩码

return

}

void err_quit(char *msg)

{

perror(msg)

exit(EXIT_FAILURE)

}

// 判断程序是否在运行

int does_service_work()

{

FILE* fp

int count

char buf[BUFSZ]

char command[150]

sprintf(command, "ps -ef | grep thisisatest | grep -v grep | wc -l" )

if((fp = popen(command,"r")) == NULL)

err_quit("popen")

if( (fgets(buf,BUFSZ,fp))!= NULL )

{

count = atoi(buf)

}

pclose(fp)

    return count

// exit(EXIT_SUCCESS)

}

void main()

{

    FILE *fp

    time_t t

    int count

    init_daemon()

    while(1)

    {

        sleep(10)//等待一分钟再写入

        fp=fopen("testfork3.log","a")

        if(fp>=0)

        {

            count = does_service_work()

            time(&t)

            if(count>0)

                fprintf(fp,"current time is:%s and the process exists, the count is %d\n",asctime(localtime(&t)), count) //转换为本地时间输出

            else

            {

                fprintf(fp,"current time is:%s and the process does not exist, restart it!\n",asctime(localtime(&t))) //转换为本地时间输出

                system("/home/user/daemon/thisisatest")//启动服务

            }

            fclose(fp)

        }

    }

    return

}

具体CMD命令:

cc thisisatest.c -o thisisatest

./thisisatest

cc monitor.c -o monitor

./monitor

tail -f testfork3.log   -- 查看日志


欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/bake/11995219.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-20
下一篇 2023-05-20

发表评论

登录后才能评论

评论列表(0条)

保存