一,守护进程概述

  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)在子进程中关闭任何不需要的文件描述符

说明:

2. 脱离控制终端,登录会话和进程组
有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
setsid();
说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
4. 关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
for(i=0;i 关闭打开的文件描述符close(i);>
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文件中写入当前时间

#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)时:

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

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

可以看到错误信息

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

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

其实我们可以利用我们刚才写的创建守护进程程序默认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;
}

守护进程详解及创建,daemon()使用的更多相关文章

  1. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  2. linux系统编程:守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  3. Linux学习之守护进程详解

    Linux系统守护进程详解                                                              ---转自:http://yuanbin.blog ...

  4. Linux守护进程详解(init.d和xinetd) [转]

    一 Linux守护进程 Linux 服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台 的守护进程来执行的 ...

  5. Linux守护进程详解(init.d和xinetd)

    一 Linux守护进程 Linux 服务器在启动时需要启动很多系统服务,它们向本地和网络用户提供了Linux的系统功能接口,直接面向应用程序和用户.提供这些服务的程序是由运行在后台的守护进程来执行的. ...

  6. 守护进程详解以及start-stop-daemon命令

    1.概念:守护进程是在后台运行的不受终端控制的进程,通常守护进程在系统启动时自动运行,守护进程的名称通常以d结尾,比如sshd.xinetd.crond等. 2.创建守护进程的步骤:a.调用fork( ...

  7. Linux系统守护进程详解ntsysv 可以关掉那些服务

    acpid, haldaemon, messagebus, klogd,network, syslogd  以上几个服务必须开启!其他的分析如下: 1.NetworkManager,NetworkMa ...

  8. kibana启动--nohup在关闭终端后无效&&守护进程详解

    https://blog.csdn.net/ty_0930/article/details/70184705 https://blog.csdn.net/Dream_Flying_BJ/article ...

  9. Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量

    Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程:    1.线程是一堆指令,是操作系统调度 ...

随机推荐

  1. Atcoder B - Boxes 玄学 + 数学

    http://agc010.contest.atcoder.jp/tasks/agc010_b 预处理出每两个相邻的数的差值,那么首先知道是一共取了sum / ((1 + n) * n / 2)次,因 ...

  2. h5学习-h5嵌入android中

    嵌入Android中的h5界面: 将此页面复制到android项目中的assets目录下边: <!DOCTYPE html> <html lang="en"> ...

  3. Android学习备忘笺01Activity

    01.设置视图 在Android Studio新建的项目中,通过 setContentView(R.layout.activity_main);方法将res/layout/activity_main. ...

  4. [BZOJ1009][HNOI2008]GT考试 DP+矩阵快速幂+KMP

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1009 我们令$dp(i,j)$表示已经填了$i$位,而且后缀与不幸运数字匹配了$j$位,那 ...

  5. 501在全志r16平台tinav3.0系统下调通pwm1的10KHZ波形

    501在全志r16平台tinav3.0系统下调通pwm1的10KHZ波形 2018/10/19 19:52 版本:V1.0 开发板:SC3817R SDK:tina v3.0 1.01原始编译全志r1 ...

  6. mac 下使用gcc 编译c函数

    mac 终端其实和window 的cmd类似,由于mac 的os x 采用了unix 系统,所以,各种类似UNIX下的命令都有用.最近在看computer science ,用到了命令行. 下面是一个 ...

  7. R in action读书笔记(14)第十一章 中级绘图 之一:散点图(高能预警)

    第十一章中级绘图 本章内容: 二元变量和多元变量关系的可视化 绘制散点图和折线图 理解相关图 学习马赛克图和关联图 本章用到的函数有: plot hexbin ablines iplot scatte ...

  8. Node.js——异步上传文件

    前台代码 submit() { var file = this.$refs.fileUpload.files[0]; var formData = new FormData(); formData.a ...

  9. laravel学习:模块化caffeinated

    # Modules Extract and modularize your code for maintainability. Essentially creates "mini-larav ...

  10. day21-4 菱形继承问题(类的查找顺序)

    目录 菱形继承问题 经典类(了解) 新式类 mro方法 菱形继承问题 在Python中子类可以同时继承多个父类,如A(B,C,D) 如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这条分 ...