daemon守护进程初识

原创 2016年06月01日 20:39:57

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信号,当接收到该信号,会重新读配置文件。
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

守护进程daemon

  • 2014-07-16 20:08
  • 660B
  • 下载

daemon守护进程

  • 2013-12-19 17:45
  • 78KB
  • 下载

守护进程(daemon)

8.1  守护进程(daemon)介绍 守护进程,也称为精灵进程,是一种运行在后台的特殊进程,它不存在控制终端,并周期性地执行某项任务或等待处理某项任务。 图8.1所示为使用ps命令查看Linux...
  • jacy_y
  • jacy_y
  • 2012-02-02 10:38
  • 2015

编写daemon守护进程

编写daemon守护进程 如何编写daemon守护进程?? 1.   调用fork()函数创建子进程后,让父进程立即exit(),这样产生的子进程变成孤儿进程,由init进程接管。 2.   调用se...

Mjpeg-streamer源码学习笔记-Main-守护进程Daemon(二)

目标文件:mjpg-stream/mjpg-stream.c + utils.c 这一篇的主要难点是main()中的一段守护进程daemon编程. 新手写,有不对的请大神指正,鼓励。 ...

守护进程(Daemon)

守护进程(精灵进程):独立于控制终端,不能直接和用户交互,不受用户登录注销的影响的一种进程。守护进程特点: 独立于控制端(不受控制端的影响) 自成进程组 自成回话 本身是孤儿进程(fork之后父进程...
  • ADDw1
  • ADDw1
  • 2017-06-07 17:49
  • 161

如何编写Linux Daemon后台程序(守护进程)

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,...

【Linux编程】守护进程(daemon)详解与创建

本文主要参考自:linux系统编程之进程(八):守护进程详解及创建,daemon()使用 一、概述 Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地...

守护进程(Daemon)

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,...

用perl实现守护进程(daemon)代码

有时候需要用perl实现一个服务程序,即linux常说的守护程序(daemon),而我们往往希望这样的程序能够在后台运行。有些人一般是直接用bg命令把程序放到了后台,但感觉不尽人意。这里提供一个标准的...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)