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

2023-05-16

转载于:http://www.cnblogs.com/mickole/p/3188321.html

侵删

一,守护进程概述

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


linux中的 umask 函数主要用于:在创建新文件或目录时 屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。

其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再和创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0777 & mode了,这样就是给后面的代码调用函数mkdir给出最大的权限,避免了创建目录或文件的权限不确定性。

  

说明:

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文件中写入当前时间

#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;
}

结果同刚才一样,也是只有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;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 读书笔记---《人月神话》

    台上一分钟 xff0c 台下十年功 宝剑锋从磨砺出 xff0c 梅花香自苦寒来 xff1b 不经一番寒彻骨 xff0c 哪得梅花扑鼻香 xff1b 业精于勤而荒于嬉 xff0c 行成于思而毁于随 不积跬步 xff0c 无以至千里 xff1b
  • Intel CPU型号官网详解

    https www intel cn content www cn zh processors processor numbers html
  • Nrf52832 freeOS系统移植

    最近因为项目开发需要 xff0c 需要多任务的操作系统在nrf52832上运行 xff0c 于是根据例程移植了下FreeOS系统 根据例程F nRF5 SDK 15 2 0 9412b96 examples ble peripheral b
  • tf 使用

    1 发布自己的tf xff1a 其实就是发布你建立的坐标系 步骤如下 xff1a 1 定义一个广播 xff0c 相当于发布话题时定义一个发布器 xff0c 还是以官方的小乌龟例程为例 xff1a span class hljs keywor
  • 解决问题:xshell6评估已过期

    一 问题描述 今天打开xshell xff0c 发现报错 xff1a 34 xshell6评估过期 34 无法打开xshell xff0c 当时急着要用xshell xff0c 所以 xff0c 是有这个问题的 二 解决方法 1 进入xsh
  • 华为机试题: 水仙花数

    描述 水仙花数又称阿姆斯特朗数 水仙花数是指一个n 位数 n 3 xff0c 它的每个位上的数字的n 次幂之和等于它本身 xff08 例如 xff1a 1 3 43 5 3 43 3 3 61 153 xff09 求输入的数字是否为水仙花数
  • 【手把手教你树莓派3 (二)】 启动wifi模块

    概述 树莓派3内置了wifi和蓝牙模块 xff0c 我们不用像以前的版本那样 xff0c 再去购买一个外接的模块练到raspberry上 当我们第一次启动了树莓派的时候 xff0c 必然使用了网线 xff0c 但是之后的每一次使用 xff0
  • 社区活动 | Apache Kylin × Apache RocketMQ Meetup 深圳站

    9 月 7 日 xff0c Apache Kylin Meetup 即将走进深圳 xff01 本次 Meetup 由 Apache Kylin 与 Apache RocketMQ 联合举办 xff0c 邀请到来自腾讯 阿里 平安云以及 Ky
  • 【手把手教你树莓派3 (六)】使用 motion 和 mjpg 做视频监控器

    概述 买了一个罗技的usb接口的摄像头 xff0c 想通过raspberry pi做一个视频的实时监控器 xff0c 看了一下这各功能可以通过两款软件实现 xff1a motion和mjpg streamer xff0c 先来简单介绍下这两
  • 【zookeeper】data/zookeeper_server.pid: No such file or directory FAILED TO WRITE PID 报错

    今天在配置zk的伪集群 xff0c 发现了如下报错 xff1a 上网查看有的说是因为zoo cfg配置文件 61 前后有空格 xff0c 查了以后我的配置文件并没有空格 xff0c 所以我排除了这个原因 最后解决 xff1a zk的配置文件
  • golang tag 之 gomodifytags

    链接 xff1a gomodifytags原文链接 gomodifytags 是go工具 xff0c 用来修改 更新struct字段的标签tag 使用gomodifytags可以很方便的update add delete struct的字段
  • Vscode Clangformat 配置

    1 xff0c vscode 安装 c 43 43 intellisense 即可自动安装clangformat 格式化工具 2 xff0c vs setting Clang format path 配置 一般位置就是 vscode ext
  • 计算机基本原理之内存编址

    内存编址目的 存储器由一块块的空间 xff08 存储单元 xff09 组成 xff0c 为了方便寻找到每一块空间 xff0c 我们需要对每一个空间进行标识 内存编址 内存编址概述 芯片 存储器由若干个芯片构成 内存容量 存储器的大小 内存容
  • PowerDesigner—你知道CDM、LDM、PDM、OOM的区别吗?

    导读 在本篇文章中 xff0c 你将会了解到PowerDesigner工具中的三种模型CDM xff0c OOM xff0c PDM的区别和联系 PowerDesigner 简称PD xff0c 是一种数据建模工具 xff0c 适合于开发大
  • 基础 HTML之目录问题(相对路径和绝对路径区别)

    导读 复习HTML知识的时候 xff0c URL的路径的写法是我们经常会用到的一块内容 相对路径和绝对路径的问题不难 xff0c 只要明白各自的道理 xff0c 同时清楚 这些字符的含义就可以了 原文链接 xff1a http www jb
  • 【Android开发—智能家居系列】(一):智能家居原理

    来到JCZB公司的第二天 xff0c 就接到了开发类似于小米智能家庭APP的任务 组长让我在手机上安装上此款APP xff0c 给了我个小米智能插座 xff0c 就让我开始了解需求 这便开启了我的智能家居旅程 说实话 xff0c 我也真是o
  • 【Android开发—智能家居系列】(二):用手机对WIFI模块进行配置

    在实际开发中 xff0c 我开发的这款APP是用来连接温控器 xff0c 并对温控器进行控制的 有图为证 xff0c 哈哈 上一篇文章 Android开发 智能家居系列 xff08 一 xff09 xff1a 智能家居原理 的文末总结中写到
  • 【POI】——获得单元格的值,并转化成字符串

    本篇文章分享一些在做导入导出EXCEL功能时用到的工具类的一些代码 span class hljs javadoc span class hljs javadoctag 64 param span cell span class hljs
  • ElementUI实现文件手动上传

    ElementUI实现文件手动上传 HTML部分 lt el upload ref 61 34 upload 34 multiple 61 34 true 34 file list 61 34 fileList 34 auto upload
  • 【工具篇】——利用EditPlus进行Json数据格式化

    从接口返回的数据基本都是json格式的数据 之前我要查看数据的内容 xff0c 为了方便我阅读 xff0c 我会直接复制这段数据到在线JSON校验格式化工具中进行格式化和校验 但是没网的时候 xff0c 就不能靠它了 而EditPlus是我

随机推荐

  • 【GIS】——mapnik在windows上的安装

    mapnik是瓦片生成器 这里先不解释了 xff0c 等用过了再谈理解 下载步骤 1 下载安装包 官网地址 xff1a http mapnik org http mapnik org pages downloads html 3 0 12还
  • 【GIS】——使用Python bindings操作mapnik

    背景介绍 使用mapnik有三种方式 xff1a 1 使用XML配置文件 2 使用Python bindings 3 使用C 43 43 中的API 这里我们先介绍第二种Python bindings xff0c 并采用这种方式做一个Dem
  • 【MongoDB】(一)——关于MondoDB索引的总结

    导读 为数据创建索引有助于提高查询数据的性能 xff0c 本篇文章总结了创建MongoDB索引应遵循的规则 我将这些规则分成四类 xff1a 1 query 2 sort 3 RAM 4 selectivity query db span
  • 【开发也是好测试】(四)—Mock

    有关Mock的思维导图 xff1a
  • 【MongoDB】——TTL Index

    TTL Index
  • ubuntu下的串行口通讯编程

    Linux 操作系统从一开始就对串行口提供了很好的支持 xff0c 本文就 Linux 下的串行口通讯编程进行简单的介绍 串口简介 串 行口是计算机一种常用的接口 xff0c 具有连接线少 xff0c 通讯简单 xff0c 得到广泛的使用
  • stm32 摄像头寻迹+平衡车

    链接 xff1a http download csdn net download u010925447 9866006
  • 【书籍推荐】自己动手写操作系统

    于渊 编著 尤晋元 审校 2005年8月出版 ISBN 7 121 01577 3 48 00元 xff08 含光盘1张 xff09 374页 用理论指导动手实践 xff0c 用实践深化理解理论 xff01 本书在详细分析操作系统原理的基础
  • 贝塔、伽马分布

    最近开始自学PRML xff0c 为此又补了概率论中的一些知识点 相较于古典概率通过各种估计手段来确定参数的分布 xff0c 贝叶斯学派则是使用后验概率来确定 xff0c 为了方便计算后验概率 xff0c 引入共轭先验分布来方便计算 xff
  • elementUI中el-dropdown的command如何传递多个参数

    el dropdown的command事件默认传递一个参数 xff0c 即每个下拉选项el dropdown item中设定的command的值 xff0c 那么如何传递多个参数呢 xff1f 实现方法 xff1a 动态设置el dropd
  • 记录ubuntu18.04下搭建nuttx RTOS的过程

    官方参考链接 xff1a https nuttx apache org docs latest quickstart install html Getting started 61 61 gt Installing 主要记录一下按照链接指导
  • 词袋

    brief描述子 一般Sb 61 48 Lb为256 brief描述子不具备旋转尺度不变性 词袋 提取大量图片的描述子 xff0c 将描述子用k means聚类成K堆 xff0c 这是第n层 xff0c 把每一堆再次聚类形成下一层 xff0
  • 板子和电脑配置ros通信

    它们需要连在同一个路由器上 1 获取板子和电脑的ip 比如板子名为RV1126 RV1109 xff0c ip为192 168 5 48 电脑名为qian hw xff0c ip为192 168 5 25 2 在电脑端设置 xff1a ba
  • 通过跟踪效果来看vins输出结果

    下面是vins跑出来的结果 xff0c vio输出是绿线轨迹 线速度是0 28m s xff0c 拐角的地方是我根据蓝线把vio轨迹掰正了 vio在初始阶段走的比较弯曲 xff0c 后有一段笔直的轨迹 xff0c 这里旋转非常慢 xff0c
  • vins-fusion 融合rtk原理

    vins fusion融合rtk原理 xff1a 使用优化的方式融合 xff0c 假设融合后的位姿是fusion T n vio输出的位姿是vio T n xff0c rtk输出的位姿是rtk T 只有最后一帧 那么 fusion T的初值
  • Docker 方式搭建 Prometheus + grafana

    prometheus 官方仓库 prometheus 官方文档 GETTING STARTED 参考 基于docker 搭建Prometheus 43 Grafana的过程详解 按照官方仓库文档中写的 Docker images Docke
  • 报错:The following signatures couldn‘t be verified because the public key is not available: NO_PUBKEY

    apt update 最后报错 sudo apt update 报错 91 packages can be upgraded Run 39 apt list upgradable 39 to see them W An error occu
  • ubuntu上 eclipse+arm-linux-gcc+jlink+s3c2440a开发环境搭建

    0 环境搭建概述 所需工具 xff1a xff08 1 xff09 Eclipse Eclipse的本身只是一个框架平台 xff0c 但是众多插件的支持 xff0c 使得Eclipse拥有较好的灵活性 xff08 2 xff09 CDT C
  • 在函数里修改全局变量

    names 61 39 Lilei 39 def change name global name 全局变量我来撑控 names 61 39 Hi 39 print names change name 39 Hi 39 print names
  • 守护进程详解及创建,daemon()使用

    转载于 xff1a http www cnblogs com mickole p 3188321 html 侵删 一 xff0c 守护进程概述 Linux Daemon xff08 守护进程 xff09 是运行在后台的一种特殊进程 它独立于