daemon守护进程初识

来源:互联网 发布:点位图软件 all 编辑:程序博客网 时间:2024/06/05 11:51

daemon介绍

守护进程是一种生存期长的进程。常常在系统引导装载时启动,直到系统关闭时才终止。它们在后台运行,没有控制终端。

daemon编写规则

  • 1.调用umask将文件模式创建屏蔽字设置为一个已知值(通常为0);
umask(0);
  • 2.调用fork,然后使父进程exit,调用setsid创建一个新会话(下边有介绍);
if((pid = fork()) < 0)
syslog(LOG_ERR,"FORK ERROR");
else if(pid != 0)
exit(0);
 
setsid();
  • 3.将当前工作目录更改为根目录; 
    子进程的当前工作目录是从父进程继承来的,守护进程(也就是子进程)在系统退出前一直存在,若当前工作目录是在一个挂载的文件系统中,就无法卸载,为了防止守护进程所处的挂载目录不能卸载,使用chdir()函数即可。
chdir("/");
  • 4.关闭不再需要的文件描述符; 
    守护进程不需要再持有从父进程继承来的任何文件描述符,为了防止资源浪费,关闭所有打开的文件描述符。可以使用getrlimit()函数判断最大文件描述符来完成该功能(下边介绍)。
if(r1.rlim_max == RLIM_INFINITY)
r1.rlim_max = 1024;
for(i = 0; i < r1.rlim_max; i++)
close(i);
  • 5.某些守护进程打开/dev/null使其具有文件描述符0、1和2,使得任何一个试图都标准输入、写标准输出或者标准错误输出的库例程都不会产生任何效果。因为守护进程不与终端设备相关联,所以其输出无处显示,也无处从交互式用户接收输入。

进程调用setsid()建立一个新会话

会话(session)是一个或多个进程组的集合。调用setdis()可以建立一个新的会话。

#include <unistd.h>
pid_t setsid(void)

进程组和进程组组长概念 
进程组是一个或多个的集合,每个有一个进程ID,还属于一个进程组。同一进程组中的各进程接收来自同一终端的各种信号。每个进程组有一个组长进程,其进程ID等于组长进程的进程组ID。 
进程组组长可以创建一个进程组、创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,与其组长进程是否终止无关。从进程组创建开始到其中最后一个进程离开为止的时间区间称为进程组的生命期。

若调用setsid函数的进程不是一个进程组的组长,则此函数创建一个新会话:

  • 1.该进程编程新会话的会话首进程(session leader,会话首进程是创建该会话的进程)。此时,该进程是新会话中的唯一进程。
  • 2.该进程称为一个新进程组的组长进程。新进程组ID是该调用进程的进程ID。
  • 3.该进程没有控制终端,若在调用setsid之前该进程有一个控制终端,那么这种练习也被切断。

如果该调用进程已经是一个进程组的组长,则此函数返回出错。为了保证不处于这种情况,通常先调用fork,然后使其父进程终止,而子进程则继续。因为子进程继承了父进程的进程组ID,而其进程ID则是新分配的,两者不可能相等,这就保证了子进程不是一个进程组的组长。

getrlimit()获取资源限制

要想关闭所有打开的文件描述符号,就必须先知道系统最大的文件描述符号是多少,从而关闭从0至最大文件描述符。对于最大的文件描述符号,也属于一个进程的资源,Linux中每一个进程都有一组资源限制,对于这些资源可以使用getrlimit()函数获取。 
函数定义如下:

#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);

该函数有两个参数: 
第一个为整型resource(有许多种取值),这里只说下 RLIMIT_NOFILE,表示对每个进程能打开的最大文件描述符数资源的限制。 
第二个参数为指向rlimit结构体类型的指针,如下:

struct rlimit {
rlim_t rlim_cur; /* Soft limit 软限制*/
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) 硬限制*/
};

rlim_t 为unsigned long int型。 


对资源限制的更改,必须遵循以下几条规则:

  • 1.任何一个进程都可将一个软限制值更改为小于或者等于其硬限制值;
  • 2.任何一个进程都可降低其硬限制值,但它必须大于或等于其软限制值。这种降低,对普通用户而言是不可逆的。
  • 3.只有超级用户进程可以提高硬限制值。

RLIM_INFINITY指定了一个无限量的限制。

该实例完整代码

运行完整代码测试,ps查看如下,程序名为daemon,PID为14250,父进程PID为1(即就是init进程),“?”代表无终端:

$ ps -ef
ty 14250 1 0 05:34 ? 00:00:00 ./daemonize

单实例守护进程

 某些守护进程可能会排他的访问一个设备,若一个守护进程存在多个实例运行,可能会影响某个操作,比如Linux中常用的定时执行工具cron,如果出现多个cron实例执行一个任务,肯定会出错。为了防止这种错误,我们必须保证一个时刻只能执行一个守护进程的副本。 保证一个时刻只有一个副本,可以依靠文件记录锁机制来实现。所谓的文件记录锁机制,就是在指定目录中(/var/run/)为每一个守护进程创建一个固定名字的文件(一般为守护进程名),并给该文件加上一把写锁,一个时刻只允许创建一把这样的写锁,若还有其它进程创建写锁,都会失败,除非该进程退出。一次保证守护进程只有一个副本运行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <sys/stat.h>
 
#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR |S_IWUSR | S_IRGRP | S_IROTH)
 
int already_running(void)
{
int fd;
char buf[16];
struct flock lock;
 
fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE);
if(fd < 0) {
printf("open\n");
syslog(LOG_ERR, "can't open %s:%s",LOCKFILE,strerror(errno));
exit(1);
}
 
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
 
if(fcntl(fd,F_SETLK,&lock) < 0) { //判断文件锁能否获取成功
syslog(LOG_ERR,"can't lock %s:%s",LOCKFILE,strerror(errno));
if(errno == EACCES|| errno == EAGAIN) { //EACCES、EAGAIN其他进程操作被禁止
close(fd);
syslog(LOG_ERR,"Operation is prohibited");
return(1);
}
exit(1);
}
syslog(LOG_ERR,"over");
ftruncate(fd,0); //将文件长度截断为0
sprintf(buf,"%ld",(long)getpid());
write(fd,buf,strlen(buf)+1);
return(0);
}
 
main()
{
int i;
i = already_running();
printf("i = %d\n",i);
while(1); //第一个进程持有文件锁,不退出,第二个进程运行该程序获取文件锁会失败
}

A进程运行该程序获得文件写锁,不退出:

ty@ubuntu:~/program/apue/13$ sudo ./lockfile
i = 0

B进程继续运行程序试图获取写锁,报错如下:

Jun 1 05:24:32 ubuntu lockfile: can't lock /var/run/daemon.pid:Resource temporarily unavailable
Jun 1 05:24:32 ubuntu lockfile: Operation is prohibited

daemon的惯例

在unix系统中,守护进程遵循下列通用惯例。

  • 1.若守护进程使用锁文件,则该文件通常存储在/var/run目录中。锁文件名字通常为daemonname.pid
  • 2.若守护进程支持配置选项,则配置文件通常存放在/etc目录中。配置文件名字通常为daemonname.conf。
  • 3.守护进程可用命令行启动,通常是由系统初始化脚本之一(/etc/rc或/etc/init.d/)启动。
  • 4.若一个守护进程有一个配置文件,那么当该守护进程启动时会读该文件,但在此之后一般就不会再查看它。更改了配置文件时,某些守护进程将捕捉SIGHUP信号,当接收到该信号,会重新读配置文件。
0 0
原创粉丝点击