守护进程, 守护线程

守护进程详解及创建,daemon()使用

守护进程概述

Linux Daemon (守护进程) 是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器 sendmail 和数据库服务器MySQLd等。

守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户 (root) 权限运行,因为它们要使用特殊的端口 (1-1024) 或访问某些特殊的资源。

一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

守护进程的名称通常以d结尾,比如sshd、xinetd、crond等

二,创建守护进程步骤 首先我们要了解一些基本概念:

进程组 :

每个进程也属于一个进程组 每个进程主都有一个进程组号,该号等于该进程组组长的PID号. 一个进程只能为它自己或子进程设置进程组ID号 会话期:

会话期(session)是一个或多个进程组的集合。

setsid()函数可以建立一个对话期:

如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。

(1)此进程变成该对话期的首进程

(2)此进程变成一个新进程组的组长进程。

(3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。 如果该进程是一个进程组的组长,此函数返回错误。

(4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行

现在我们来给出创建守护进程所需步骤:

编写守护进程的一般步骤步骤:

  1. 在父进程中执行fork并exit推出;
  2. 在子进程中调用setsid函数创建新的会话;
  3. 在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;
  4. 在子进程中调用umask函数,设置进程的umask为0;
  5. 在子进程中关闭任何不需要的文件描述符

说明:

  1. 在后台运行。 为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。 if(pid=fork()) exit(0);//是父进程,结束父进程,子进程继续
  2. 脱离控制终端,登录会话和进程组 有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系: 进程属于一个进程组,进程组号 (GID) 就是进程组长的进程号 (PID) 。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长: setsid(); 说明: 当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
  3. 禁止进程重新打开控制终端 现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端: if(pid=fork()) exit(0);//结束第一子进程,第二子进程继续 (第二子进程不再是会话组长)
  4. 关闭打开的文件描述符 进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们: for(i=0;i 关闭打开的文件描述符close(i);>
  5. 改变当前工作目录 进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")
  6. 重设文件创建掩模 进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除: umask(0);
  7. 处理SIGCHLD信号 处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程 (zombie) 从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。 signal(SIGCHLD,SIG_IGN); 这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

创建守护进程

在创建之前我们先了解setsid()使用:

#include <unistd.h>

   pid_t setsid(void);

DESCRIPTION setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the process group leader of the new process group, and has no control- ling tty. The process group ID and session ID of the calling process are set to the PID of the calling process. The calling process will be the only process in this new process group and in this new session.

//调用进程必须是非当前进程组组长,调用后,产生一个新的会话期,且该会话期中只有一个进程组,且该进程组组长为调用进程,没有控制终端,新产生的group ID 和 session ID 被设置成调用进程的PID

RETURN VALUE On success, the (new) session ID of the calling process is returned. On error, (pid_t) -1 is returned, and errno is set to indicate the error.

现在根据上述步骤创建一个守护进程:

以下程序是创建一个守护进程,然后利用这个守护进程每个一分钟向daemon.log文件中写入当前时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define ERR_EXIT(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

void creat_daemon(void);
int main(void)
{
    time_t t;
    int fd;
    creat_daemon();
    while(1){
        fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
        if(fd == -1)
            ERR_EXIT("open error");
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd,buf,strlen(buf));
        close(fd);
        sleep(60);
            
    }
    return 0;
}
void creat_daemon(void)
{
    pid_t pid;
    pid = fork();
    if( pid == -1)
        ERR_EXIT("fork error");
    if(pid > 0 )
        exit(EXIT_SUCCESS);
    if(setsid() == -1)
        ERR_EXIT("SETSID ERROR");
    chdir("/");
    int i;
    for( i = 0; i < 3; ++i)
    {
        close(i);
        open("/dev/null", O_RDWR);
        dup(0);
        dup(0);
    }
    umask(0);
    return;
}

结果: 结果显示: 当我一普通用户执行a.out时,进程表中并没有出现新创建的守护进程,但当我以root用户执行时,成功了,并在/目录下创建了daemon.log文件,cat查看后确实每个一分钟写入一次。为什么只能root执行,那是因为当我们创建守护进程时,已经将当前目录切换我/目录,所以当我之后创建daemon.log文件是其实是在/目录下,那肯定不行,因为普通用户没有权限,或许你会问那为啥没报错呢?其实是有出错,只不过我们在创建守护进程时已经将标准输入关闭并重定向到/dev/null,所以看不到错误信息。

四,利用库函数daemon()创建守护进程 其实我们完全可以利用daemon()函数创建守护进程,其函数原型:

include <unistd.h>

int daemon(int nochdir, int noclose);

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  process’s  current  working
   directory to the root directory ("/"); otherwise,

   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.

功能: 创建一个守护进程

参数:

nochdir: =0将当前目录更改至“/”

noclose: =0将标准输入、标准输出、标准错误重定向至“/dev/null”

返回值:

成功: 0

失败: -1

现在我们利用daemon()改写刚才那个程序:

复制代码

include <stdio.h>

include <unistd.h>

include <stdlib.h>

include <time.h>

include <fcntl.h>

include <string.h>

include <sys/stat.h>

define ERR_EXIT(m) \

do
{
perror(m);
exit(EXIT_FAILURE);
}
while (0);\

void creat_daemon(void); int main(void) { time_t t; int fd; if(daemon(0,0) == -1) ERR_EXIT(“daemon error”); while(1){ fd = open(“daemon.log”,O_WRONLY|O_CREAT|O_APPEND,0644); if(fd == -1) ERR_EXIT(“open error”); t = time(0); char *buf = asctime(localtime(&t)); write(fd,buf,strlen(buf)); close(fd); sleep(60);

}
return 0;

} 复制代码 当daemon(0,0)时:

QQ截图20130713190523

结果同刚才一样,也是只有root才能成功,普通用户执行时看不到错误信息

现在让daemon(0,1),就是不关闭标准输入输出结果:

QQ截图20130713190932

可以看到错误信息

现在让daemon(1,0),就是不重定向,结果如下:

QQ截图20130713191221

这次普通用户执行成功了,以为没有切换到/目录下,有权限

其实我们可以利用我们刚才写的创建守护进程程序默认daemon()实现:

代码如下:

复制代码

include <stdio.h>

include <unistd.h>

include <stdlib.h>

include <time.h>

include <fcntl.h>

include <string.h>

include <sys/stat.h>

define ERR_EXIT(m) \

do
{
perror(m);
exit(EXIT_FAILURE);
}
while (0);\

void creat_daemon(int nochdir, int noclose); int main(void) { time_t t; int fd; creat_daemon(0,0); while(1){ fd = open(“daemon.log”,O_WRONLY|O_CREAT|O_APPEND,0644); if(fd == -1) ERR_EXIT(“open error”); t = time(0); char *buf = asctime(localtime(&t)); write(fd,buf,strlen(buf)); close(fd); sleep(60);

}
return 0;

} void creat_daemon(int nochdir, int noclose) { pid_t pid; pid = fork(); if( pid == -1) ERR_EXIT(“fork error”); if(pid > 0 ) exit(EXIT_SUCCESS); if(setsid() == -1) ERR_EXIT(“SETSID ERROR”); if(nochdir == 0) chdir("/"); if(noclose == 0){ int i; for( i = 0; i < 3; ++i) { close(i); open("/dev/null”, O_RDWR); dup(0); dup(0); }

umask(0);
return;

} ———————–我和我追猪的梦—————————————————————–

作者: mickole 出处: http://www.cnblogs.com/mickole/

守护进程 (daemon) 是生存期长的一种进程,没有控制终端。它们常常在系统引导装入时启动,仅在系统关闭时才终止。UNIX系统有很多守护进程,守护进程程序的名称通常以字母“d”结尾: 例如,syslogd 就是指管理系统日志的守护进程。通过ps进程查看器 ps -efj 的输出实例,内核守护进程的名字出现在方括号中

作者: 一语穿心 链接: https://www.zhihu.com/question/38609004/answer/77190522 来源: 知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

守护进程是在后台运行不受终端控制的进程 (如输入、输出等) ,一般的网络服务都是以守护进程的方式运行。守护进程脱离终端的主要原因有两点: (1) 用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。 (2) (如其他用户登录该终端后,以前的守护进程的错误信息不应出现) 由终端上的一些键所产生的信号 (如中断信号) ,不应对以前从该终端上启动的任何守护进程造成影响。要注意守护进程与后台运行程序 (即加&启动的程序) 的区别。 创建守护进程的过程: 1. 调用fork创建子进程。父进程终止,让子进程在后台继续执行。2. 子进程调用setsid产生新会话期并失去控制终端调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。3. 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。4. 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。5. 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。6. 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。7. 重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。8. 用openlog函数建立与syslogd的连接。

作者: Zyoung 链接: https://www.zhihu.com/question/38609004/answer/529315259 来源: 知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

http://blog.csdn.net/coofucoo/article/details/510712

守护进程是生存期长的一种进程。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。他们常常在系统引导装入时启动,在系统关闭时终止。unix系统有很多守护进程,大多数服务器都是用守护进程实现的。比如,网络服务inetd、Web服务http等。同时,守护进程完成许多系统任务。比如,作业规划进程crond、打印进程lqd等。

这里主要说明守护进程的进程结构,以及如何编写守护进程程序。因为守护进程没有控制终端,所以我们还要介绍在守护进程运行时错误输出的方法。

守护进程及其特性

守护进程最重要的特性是后台运行。在这一点上,DOS下的常驻内存程序TSR与之相似。

其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等。这些环境通常是守护进程从执行它的父进程 (特别是shell) 中继承下来的。

最后,守护进程的启动方式有其特殊之处。它可以在系统启动时从启动脚本/etc/rc.d中启动,可以由inetd守护进程启动,可以有作业规划进程crond启动,还可以由用户终端 (通常是shell) 执行。

总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果大家对进程的认识比较深入,就对守护进程容易理解和编程了。

首先我们来察看一些常用的系统守护进程,看一下他们和几个概念: 进程组、控制终端和对话期有什么联系。ps 命令打印系统中各个进程的状态。该命令有多个选择项,有关细节请参考系统手册。为了察看所需的信息,执行:

ps –axj

PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
  
0 1 0 0 ? -1 S 0 0:04 init
  
1 2 1 1 ? -1 SW 0 0:00 [keventd]
  
1 3 1 1 ? -1 SW 0 0:00 [kapm-idled]
  
0 4 1 1 ? -1 SWN 0 0:00 [ksoftirqd_CPU0]
  
0 5 1 1 ? -1 SW 0 0:00 [kswapd]
  
0 6 1 1 ? -1 SW 0 0:00 [kreclaimd]
  
0 7 1 1 ? -1 SW 0 0:00 [bdflush]
  
0 8 1 1 ? -1 SW 0 0:00 [kupdated]
  
1 9 1 1 ? -1 SW< 0 0:00 [mdrecoveryd]
  
1 17 1 1 ? -1 SW 0 0:02 [kjournald]
  
1 92 1 1 ? -1 SW 0 0:00 [khubd]
  
1 573 573 573 ? -1 S 0 0:03 syslogd -r -x
  
1 578 578 578 ? -1 S 0 0:00 klogd -2
  
1 598 598 598 ? -1 S 32 0:00 portmap

进程号为1、2的这些进程非常特殊,存在于系统的整个生命期中。它们没有父进程ID ,没有组进程ID ,也没有对话期ID 。syslogd 守护进程可用于任何为操作人员记录系统消息的程序中。可以在一台实际的控制台上打印这些消息,也可将它们写到一个文件中。sendmail 是标准邮递守护进程。update 程序定期将内核缓存中的内容写到硬盘上 (通常是每隔30 秒) 。为了做到这一点,该程序每隔30 秒调用sync (2 ) 函数一次。cron 守护进程在指定的日期和时间执行指定的命令。许多系统管理任务是由cron 定期地使相关程序执行而得以实现的。inetd进程监听系统的网络界面,以输入对各种网络服务器的请求。最后一个守护进程,lpd 处理对系统提出的各个打印请求。

注意,所有守护进程都以超级用户 (用户ID为0) 的优先权运行。没有一个守护进程具有控制终端,终端名称设置为问号 (?) 、终端前台进程组ID设置为-1。缺少控制终端是守护进程调用了setsid的结果。除update以外的所有守护进程都是进程组的首进程,对话期的首进程,而且是这些进程组和对话期中的唯一进程。最后,应当引起注意的是所有这些守护进程的父进程都是init进程。

在接触实际编程前,我们来看看编写守护进程要碰到的概念: 进程组合会话期。

进程组

每个进程除了有一进程ID之外,还属于一个进程组 (在讨论信号时就会涉及进程组) 进程组是一个或多个进程的集合。每个进程有一个唯一的进程组ID。进程组ID类似于进程ID——它是一个正整数,并可存放在pid_t数据类型中。

每个进程组有一个组长进程。组长进程的标识是,其进程组ID等于其进程ID,进程组组长可以创建一个进程组,创建该组中的进程,然后终止,只要在某个进程组中有一个进程存在,则该进程就存在,这与其组长进程是否终止无关。从进程组创建开始到其中最后一个进程离开为止的时间区间称为进程组的生命期。某个进程组中的最后一个进程可以终止,也可以参加另一进程组。

前面已经提到进程调用setgid可以参加一个现存的组或者创建一个新进程组 (setsid也可以创建一个新的进程组,后面将用到)

会话期

会话期(session)是一个或多个进程组的集合。其中,在一个会话期中有3个进程组,通常是有shell的管道线将几个进程编成一组的。

下面说明有关会话期和进程组的一些特性:

一个会话期可以有一个单独的控制终端 (controlling terminal) ,这一般是我们在其上登录的终端设备 (终端登录) 或伪终端设备 (网络登录) ,但这个控制终端并不是必需的。

建立与控制终端连接的会话期首进程,被称之为控制进程 (contronlling process) 。以及一个会话期中的几个进程组可被分为一个前台进程组 (foreground process group) 以及一个或几个后台进程组 (background process group)

如果一个会话期有一个控制终端,则它有一个前台进程组,其他进程组为后台进程组。无论何时键入中断键 (常常是delete或ctrl-c) 或退出键 (通常是ctrl-/) ,就会造成将中断信号或退出信号送至前途进程组的所有进程。

守护进程的编程规则

在不同Unix环境下,守护进程的具体编程细节并不一致。但所幸的是,守护进程的编程原则其实都一样,区别仅在于具体的实现细节不同,这个原则就是要满足守护进程的特性。编程规则如下:

  1. 在后台运行

为避免挂起控制终端,要将daemon放入后台执行,其方法是,在进程中调用fork使父进程终止,让daemon在子进程中后台执行。具体就是调用f o r k ,然后使父进程e x i t 。这样做实现了下面几点:

第一,如果该精灵进程是由一条简单s h e l l 命令起动的,那么使父进程终止使得s h e l l 认为这条命令已经执行完成。

第二,子进程继承了父进程的进程组I D ,但具有一个新的进程I D ,这就保证了子进程不是一个进程组的首进程。这对于下面就要做的s e t s i d 调用是必要的前提条件。

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

登录会话可以包含多个进程组,这些进程组共享一个控制终端,这个控制终端通常是创建进程的登录终端、控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。

其方法是在第一点的基础上,调用setsid () 使进程成为会话组长:

需要说明的是,当进程是会话组长时,setsid () 调用会失败,但第一点已经保证进程不是会话组长。setsid () 调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离,由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

具体是操作就是:

(a ) 成为新对话期的首进程

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

(c ) 没有控制终端。

  1. 禁止进程重新打开控制终端

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

  1. 关闭打开的文件描述符

进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在地文件系统无法卸下以及无法预料的错误。一般来说,必要的是关闭0、1、2三个文件描述符,即标准输入、标准输出、标准错误。因为我们一般希望守护进程自己有一套信息输出、输入的体系,而不是把所有的东西都发送到终端屏幕上。调用fclose () ;

  1. 改变当前工作目录

将当前工作目录更改为根目录。从父进程继承过来的当前工作目录可能在一个装配的文件系统中。因为精灵进程通常在系统再引导之前是一直存在的,所以如果精灵进程的当前工作目录在一个装配文件系统中,那么该文件系统就不能被拆卸。

另外,某些精灵进程可能会把当前工作目录更改到某个指定位置,在此位置做它们的工作。例如,行式打印机假脱机精灵进程常常将其工作目录更改到它们的s p o o l 目录上。

可以调用chdir (“目录”) ;

  1. 重设文件创建掩码

将文件方式创建屏蔽字设置为0 。由继承得来的文件方式创建屏蔽字可能会拒绝设置某些许可权。例如,若精灵进程要创建一个组可读、写的文件,而继承的文件方式创建屏蔽字,屏蔽了这两种许可权,则所要求的组可读、写就不能起作用。

  1. 处理SIGCHLD 信号

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

signal(SIGCHLD,SIG_IGN);

这样,内核在子进程结束时不会产生僵尸进程,这一点与BSD4不同,在BSD4下必须显示等 待子进程结束才能释放僵尸进程。

守护进程实例

守护进程实例包括两部分: 主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp

目录中的日志test.log 报告运行状态。初始化程序中的init_daemon 函数负责生成守护进程

void make_daemon(void)

{

pid_t pid;

FILE * lockfd;

sigset_t sighup;

int i;

extern pid_t getsid(pid_t);

pid = fork();//第一个子进程生成

if (pid < 0) {

printinfo(“fork error!",INFOERROR);

exit(FAILEXIT);

}else if (pid > 0) {

printinfo(“fork 1 ok! “, INFOSCREEN);

exit(OKEXIT);//退出父进程,摆脱shell的控制

}

pid = getpid();//获得子进程自身的id

lockfd = fopen(PIDFILE, “w”);//以下是将pid写入文件

if (lockfd != NULL) {

fprintf(lockfd, “%d/n”, pid);

fclose(lockfd);

}//写入pid

if (getsid(0) != pid) {//创建新的会话期

if (setsid() < 0) {

printinfo(“bak daemon setsid error!",INFOERROR);

perror(“setsid”);

}

}

if(pid=fork()){//再次生成子进程,这时候是孙子进程

exit(0);//退出上一代进程

}else if(pid<0){

exit(1);

}

close(1);//关闭文件

close(2);

chdir(rundir);//改变运行的目录

umask(022);//改变文件权限

}

守护进程的错误输出

守护进程不属于任何终端,所以当需要输出某些信息时,它无法像一般程序那样将信息直接输出到标准输出和标准错误输出中。我们很大时候也不希望每个守护进程将它自己的出错消息写到一个单独的文件中。因为对于系统管理人员而言,要记住哪一个守护进程写到哪一个记录文件中,并定期的检查这些文件,他一定会为此感到头疼的。所以,我们需要有一个集中的守护进程出错记录机制。目前很多系统都引入了syslog记录进程来实现这一目的。

自伯克利开发了BSD syslog并广泛应用以来,BSD syslog 机制被大多数守护进程所使用。我们下面介绍BSD syslog 的用法。有三种方法产生记录消息:

1 内核例程可以调用log函数。任何一个用户进程通过打开和读/dev/klog设备就可以读取这些消息。因为我们无意编写内核中的例程,所以不再进一步说明此函数。

2 大多数用户进程 (守护进程) 调用syslog函数以产生记录消息。我们将在下面说明其调用序列。这使消息发送至Unix域数据报 socket /dev/log。

3 在此主机上,或通过TCP/IP网络连接到此主机的某一其他主机上的一个用户进程可将记录消息发向UDP端口514。

注意: syslog 函数并不产生这些UDP数据报——它们要求产生此记录消息的进程具有显式的网络编程。

通常,syslog守护进程读取三种格式的记录消息。此守护进程在启动时读一个配置文件。一般来说,其文件名为/etc/syslog.conf,该文件决定了不同种类的消息应送向何处。例如,紧急消息可被送向系统管理员 (若已登录) ,并在控制台上显示,而警告消息则可记录到一个文件中。该机制提供了syslog函数,其调用格式如下

include

void openlog (char*ident,int option ,int facility);

void syslog(int priority,char*format,……)

void closelog();

调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog。调用closelog也是可选择的,它只是关闭被用于与syslog守护进程通信的描述符。调用openlog 使我们可以指定一个ident,以后, 此ident 将被加至每则记录消息中。ident 一般是程序的名称 (例如 ,cron ,inetd 等) 。option 有4种可能:

LOG_CONS 若日志消息不能通过Unix域数据报发送至syslog,则将该消息写至控制台。

LOG_NDELAY1 立即打开Unix域数据报 socket 至syslog守护进程,而不要等到记录第一消息。通常,在记录第一条消息之前,该 socket 不打开。

LOG_PERROR 除将日志消息发送给syslog 外,还将它至标准出错。此选项仅由4.3BSDReno及以后版本支持。

LOG_PID 每条消息都包含进程ID。此选项可供对每个请求都fork一个子进程的守护进程使用。

在openlog中设置facility参数的目的是让配置文件可以说明,来自不同设施的消息以不同的方式进行处理。如果不调用openlog,或者以facility 为0来调用它,那么在调用syslog 时,可将facility作为priority参数的一个部分进行说明。调用syslog产生一个记录消息。其priority参数是facility和level的组合,它们可选取的值分别列于下面。level值按优先级从高级到最低按序排列。


java 守护线程 (Daemon Thread)

JVM中存在两种线程: 用户线程 (User Thread)、守护线程 (Daemon Thread)

所谓的守护线程,是指用户程序在运行的时候后台提供的一种通用服务的线程,比如用于垃圾回收线程

这类线程并不是用户线程不可或缺的部分,只是用于提供服务的"服务线程”。

守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。

守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。

当正在运行的线程都是守护线程时,Java 虚拟机退出。

setDaemon()方法必须在启动线程前调用。该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException (在当前线程中) 。

参数:

on – 如果为 true,则将该线程标记为守护线程。

抛出:

IllegalThreadStateException – 如果该线程处于活动状态。

SecurityException – 如果当前线程无法修改该线程。

另请参见:

isDaemon(), checkAccess()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  

import java.util.concurrent.TimeUnit;

public class DaemonThread {
      
public static void main(String[] args) {
          
Thread t1 = new MyCommon();
          
Thread t2 = new Thread(new MyDaemon());
          
t2.setDaemon(true); // set as daemon thread

t2.start();
          
t1.start();
      
}

static class MyCommon extends Thread {
          
public void run() {
              
for (int i = 0; i < 5; i++) {
                  
System.out.println("common thread:" + i);
                  
try {
                      
TimeUnit.SECONDS.sleep(3);
                  
} catch (InterruptedException e) {
                      
e.printStackTrace();
                  
}
              
}
          
}
      
}

static class MyDaemon implements Runnable {
          
public void run() {
              
for (int i = 0; i < Integer.MAX_VALUE; i++) {
                  
System.out.println("daemon thread:" + i);
                  
try {
                      
TimeUnit.SECONDS.sleep(3);
                  
} catch (InterruptedException e) {
                      
e.printStackTrace();
                  
}
              
}
          
}
      
}
  
}

http://baike.baidu.com/view/407213.htm

http://topic.csdn.net/t/20060115/00/4517316.html

http://lavasoft.blog.51cto.com/62575/221845/

http://blog.csdn.net/lcore/article/details/12280027


https://www.cnblogs.com/mickole/p/3188321.html