Linux中编写简单的守护进程

1、守护进程概述

守护进程就是通常所说的daemon进程,他是linux中的后台服务进程。他是一个生存期很长的进程,通常独立于控制终端并且周期性的执行某种任务,或等待处理魔种发生的事件。守护进程通常在系统开机启动时开始执行,在系统关闭时终止。linux中很多服务器都是通过守护进程实现的。

由于在linux中,每一个系统与用户进行交流的界面成称为终端。每一个从终端开始执行的进程都会依赖于终端,这个终端称为这些进程的终端。当控制终端关闭时,相应的进程都会自动结束。但是守护进程能够突破这种限制,不受终端关闭的影响。反之,如果希望某个进程不因为用户、终端或者其他的变化而受影响,那么就必须把这给进程变成守护进程。

2、编写守护进程

编写守护进程看似复杂,但实际上也是遵循一个特定的流程的。就分5个步骤来讲解怎么创建一个简单守护进程。在讲解的同时,也会同时介绍与创建守护进程相关的概念与函数。

(1)创建子进程,父进程推出

这是编写守护进程的第一步。由于守护进程时脱离控制终端的,因此完成第一部后子进程变成后台进程,就给用户程序已经运行完毕的感觉。只有的所有工作都在子进程中完成,而用户可以通过shell执行其他的命令,从而在形式上做到了与控制端的脱离。到这里父进程推出之后,子进程就会称成为一个孤儿进程,在linux中当系统发现一个孤儿进程,就会由1号进程(也就是init进程)收养它,这样,原来的进程就会变成init进程的子进程了。其实现代码如下:

pid = fork;
if(pid > 0)
{
    exit(0);    //    父进程退出
}

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

这个步骤时创建子进程中最重要的一步,虽然他的实现非常简单,但它的意义却非常重大。在这里使用的函数时setsid()。在介绍setsid之前,要了解两个概念:进程组和会话期。

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

会话期:会话组是一个或多个进程组的集合。通常一个会话开始于用户的登陆,终止与用户退出;或者开始于终端打开,结束于终端关闭。会话期的第一个进程称为会话组长。在此期间该用户运行的所有进程都属于这个会话期。

接下来具体介绍setsid()的相关内容。

setsid函数的作用:用于创建一个新会话,并且担任该会话的组长。

  • 让进程摆脱原会话的控制
  • 让进程摆脱原进程组的控制
  • 让进程摆脱原控制端的控制

在父进程调用fork函数时,子进程全盘复制了父进程的会话期、进程组、控制端等。虽然父进程推出了,但原先的会话期、进程组、控制终端并没有改变,因此这不是真正意义上的独立,而setsid函数能够使进程完全独立出来,从而摆脱所有进程的控制。

(2)setsid函数格式

所需头文件

#include <unistd.h>

#include <sys/types.h>

函数原型 pid_t setsid(void);
函数返回值

成功:进程组ID

出错:-1

(3)改变当前目录

这一步也是必要的步骤。使用fork函数创建的子进程继承了父进程的当前工作目录。由于在进程运行过程中,当前目录所在的文件系统(比如“/mnt/usb”等)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常做法就是让“/”作为守护进程的当前工作目录,这样就可以避免上述的问题。当然,如有特殊需要,也可以将当前工作目录换作其他的工作路径,如“/tmp”。改变工作目录的函数就是chdir();

(4)重设文件权限掩码

文件权限掩码(通常用8进制表示)的作用是屏蔽文件权限中的对应位。例如,如果文件权限掩码是050,他表示屏蔽文件组拥有者的可读与可执行权限。有使用fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了一定的麻烦。因此将文件权限掩码设置为0,可以增强该守护进程的灵活性。设置文件权限掩码的函数是umask()。在这里通常使用方法是umask(0)。

(5)关闭文件描述符

同文件权限掩码一样,用fork函数新创建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不可能被守护进程访问,但他们一样占用系统资源,而且还可能导致所在的文件系统无法被卸载。特别是守护进程与终端无关的,所以指向终端设备标准输入、标准输出和标准错误流已经失去了存在的价值,应当被关闭。通常按照下面的方式关闭文件描述符。

int num;
num = getdtablesize();    //获取当前进程文件描述符表大小
for(i = 0; i < num; i++)
{
    close(i);
}

这样一个简单的守护进程就建立起来了。

下面实现一个守护进程的完整实例:

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

int main(int argc, char **argv)
{
    pid_t pid;
    int i, fd;

    pid = fork();		//第一步
    if(pid < 0)
    {
        printf("Error fork\n");
		exit(1);
    }
	else if(pid > 0)
	{
		exit(0);	//父进程退出
	}
	
	setsid();		//第二步
	chdir("/tmp");	//第三步
	umask(0);		//第四步
	
	for(i = 0; i < getbtablesize(); i++)	//第五步
	{
		close(i);
	}
	
	/*下面开始守护进程的工作,一般都是一个死循环程序*/
	while(1)
	{
		
	}
	
	exit(0)	
}

 

猜你喜欢

转载自blog.csdn.net/David_361/article/details/86102359