Linux守护进程 —— Linux系统编程

一、什么是守护进程

1、守护进程
守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等。

3、守护进程的特点 
 没有控制终端,不能直接和用户交互。不受用户登录注销的影响,它们一直在运行着。

在Linux中,每个系统与用户进行交流的界面成为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭。但是守护进程却能突破这种限制,它脱离于终端并且在后台运行,并且它脱离终端的目的是为了避免进程在运行的过程中的信息在任何终端中显示并且进程也不会被任何终端所产生的终端信息所打断。它从被执行的时候开始运转,知道整个系统关闭才退出(当然可以认为的杀死相应的守护进程)。如果想让某个进程不因为用户或中断或其他变化而影响,那么就必须把这个进程变成一个守护进程。

守护进程与终端无任何关联? 
用户的登录与注销与守护进程无关系,不受其影响,守护进程自成进程组,自成会话 ,即pid = gid = sid。

二、守护进程的创建

守护进程应达到以下目标:

在后台运行。

脱离控制终端,登录会话和进程组。

实现方法有手动创建和API创建。

一)手动创建:

因此实现步骤如下:

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

setsid();

说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。
    setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。
    由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
 如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。
    (1)此进程变成该对话期的首进程
    (2)此进程变成一个新进程组的组长进程。
    (3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。
    如果该进程是一个进程组的组长,此函数返回错误。
    (4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,
    子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能是新会话的进程组的PID。
    从而保证了这一点。

3. 禁止进程重新打开控制终端(非必须)
  现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。
    可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:

if(pid=fork())
     exit(0); //结束第一子进程,第二子进程继续(第二子进程不再是会话组长)

4. 关闭打开的文件描述符(非必须)
   一般只需将标准输入、输出、错误关闭,然后重定向到NULL。
  进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:

for(i=0;i 关闭打开的文件描述符close(i);>

5. 改变当前工作目录(非必须)
  进程活动时,其工作目录所在的文件系统不能卸下,而守护进程被fork时,所在目前就是其工作目录,此目录有可能被卸载。一般需要将工作目录改变到根目录,这里不可能被卸载。
    对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如 /tmpchdir("/")

6. 重设文件创建掩码(非必须)
  文件权限掩码是屏蔽掉文件权限中的对应位。由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带了很多的麻烦(比如父进程中的文件没有执行文件的权限,然而在子进程中希望执行相应的文件这个时候就会出问题)。
因此在子进程中要把文件的权限掩码设置成为0,即在此时有最大的权限,这样可以大大增强该守护进程的灵活性

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

signal(SIGCHLD,SIG_IGN);//表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的,忽略后,当子进程接收,由内核回收,也不会产生僵尸进程。

  这样,内核在子进程结束时不会产生僵尸进程。
    这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。
#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>

void init_daemon(void)
{
    pid_t pid;
    int i;
    if(pid=fork())
        exit(0);        	//是父进程,结束父进程
    else if(pid< 0)
    	{
    		perror("fork error:");
        	exit(1);        	//fork失败,退出
    	}
    						//是第一子进程,后台继续执行
    setsid();           	//第一子进程成为新的会话组长和进程组长并与控制终端分离
    
    if(pid=fork())
        exit(0);        	//禁止进程重新打开控制终端,结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
    else if(pid< 0)
        {
    		perror("fork error:");
        	exit(1);        	//fork失败,退出
    	}
	
	//是第二子进程,继续      ,第二子进程不再是会话组长
    for(i=0;i< NOFILE;++i)  //linux 最大文件打开数nofile,/etc/security/limits.conf中指定的nofile的值,nofile有上限,不是无穷大。nofile由内核参数nr_open定义的.
        close(i);
 
    chdir("/");      		//改变工作目录到/
    umask(0);          		//重设文件创建掩模
    return;
}

 
int 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);
        }
    }
	return  0;
}


运行后,此进程变成守护进程。 

在“/”目录下有创建的文件。

二)C库函数

有一个库函数可以直接使一个进程变成守护进程,
       #include <unistd.h>
       int daemon(int nochdir, int noclose);
SYNOPSIS
       #include <unistd.h>

       int daemon(int nochdir, int noclose);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       daemon(): _BSD_SOURCE || (_XOPEN_SOURCE && _XOPEN_SOURCE < 500)

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 calling process's current working directory to the root  directory  ("/");  otherwise,
       the current working directory is left unchanged.

       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.

RETURN VALUE
       (This function forks, and if the fork(2) succeeds, the parent calls _exit(2), so that further errors  are  seen  by  the  child
       only.)   On  success daemon() returns zero.  If an error occurs, daemon() returns -1 and sets errno to any of the errors speci‐
       fied for the fork(2) and setsid(2).

 

#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>

int main()
{
    FILE *fp;
    time_t t;
    daemon(0,0);			//初始化为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);
        }
    }
	return  0;
}

效果与手动实现一样。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值