linux守护进程以及如何编写守护进程程序

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

守护进程

理论

「守护进程」是 Linux 的一种长期运行的后台服务进程,也有人称它为「精灵进程」。我们常见的 httpd、named、sshd 等服务都是以守护进程 Daemon 方式运行的,通常服务名称以字母d结尾,也就是 Daemon 第一个字母。与普通进程相比它大概有如下特点:

  • 无需控制终端(不需要与用户交互)
  • 在后台运行
  • 生命周期比较长,一般是随系统启动和关闭

守护进程是个特殊的孤儿进程,这种进程脱离终端。为什么要脱离终端呢?

之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

如何查看守护进程

  • a 表示不仅列当前用户的进程,也列出所有其他用户的进程
  • x 表示不仅列有控制终端的进程,也列出所有无控制终端的进程
  • j 表示列出与作业控制相关的信息


    从上图可以看出守护进行的一些特点:
  • 守护进程基本上都是以超级用户启动( UID 为 0 )
  • 没有控制终端( TTY 为 ?)
  • 终端进程组 ID 为 -1 ( TPGID 表示终端进程组 ID)

一般情况下,守护进程可以通过以下方式启动:

  • 在系统启动时由启动脚本启动,这些启动脚本通常放在 /etc/rc.d 目录下;
  • 利用 inetd 超级服务器启动,如 telnet 等;
  • 由 cron 定时启动以及在终端用 nohup 启动的进程也是守护进程。

如何编写守护进程?

(1) 屏蔽一些控制终端操作的信号

这是为了防止守护进行在没有运行起来前,控制终端受到干扰退出或挂起。

	signal(SIGTTOU,SIG_IGN); 
	signal(SIGTTIN,SIG_IGN); 
	signal(SIGTSTP,SIG_IGN); 
	signal(SIGHUP ,SIG_IGN);

(2) 生成一个进程,使之脱离控制终端、登录会话和进程组

  • 创建子进程,父进程退出。
    • 程序中的父进程一般是一个进程组的组长,fork()产生子进程(将来会生成守护进程)
    • 由于在调用fork()函数时,子进程全盘复制了父进程的会话期、进程组和控制终端等,虽然父进程退出了,但原先的会话期、进程组和控制终端等并没有改变,因此,还不是真正意义上的独立;
    • 由于父进程已经先于子进程退出,会造成子进程没有父进程,从而变成一个孤儿进程。在Linux中,每当系统发现一个孤儿进程,就会自动由1号进程收养。原先的子进程就会变成init进程的子进程。
    • 为什么要父进程退出:进程组组长无法调用setsid
  • 调用setsid(),用于生成一个新的会话 注意如果当前进程是会话组长时,调用失败。第一点已经可以保证进程不是会话组长了,所以setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话对控制终端的独占性,进程同时与控制终端脱离;
    // 2.1)创建子进程,父进程退出
    if( pid=fork() ){ // 父进程
        exit(0); //结束父进程,子进程继续
	}else if(pid< 0){ // 出错
		perror("fork");
		exit(EXIT_FAILURE);
	}
	//以下是子进程
	// 2.2)脱离控制终端、登录会话和进程组
    setsid(); 

关于 setsid

• 首先内核会创建一个新的会话,并让该进程成为该会话的leader进程,
• 同时伴随该session的建立,一个新的进程组也会被创建,同时该进程成为该进程组的组长。
• 该进程此时还没有和任何控制终端关联。若需要则要另外调用tcsetpgrp

(3) 禁止进程重新打开控制终端

  • 进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端。(只有会话组长才能打开控制终端

	// 3)禁止进程重新打开控制终端
	if( pid=fork() ){ // 父进程(第一个子进程执行fork产生第一个进程的孙进程)
		exit(0);      // 结束第一子进程
	}else if(pid< 0){ // 出错
		perror("fork");
		exit(EXIT_FAILURE);
	}  
//第二子进程继续(第二子进程不再是会话组长) 

(4) 关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误;

	// NOFILE 为 <sys/param.h> 的宏定义
	// NOFILE 为文件描述符最大个数,不同系统有不同限制
	for(i=0; i< NOFILE; ++i){// 关闭打开的文件描述符
		close(i);
	}

(5) 改变当前工作目录

使用fork()创建的子进程继承了父进程的当前工作目录。

由于在进程运行过程中,当前目录所在的文件系统(如“/mnt/usb”等)是不能卸载的,这对以后的使用会造成诸多的麻烦(如系统由于某种原因要进入单用户模式)。

因此,通常的做法是让“/”作为守护进程的当前工作目录,这样就可以避免上述问题。当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir()。

chdir("/tmp"); 

(6) 重设文件创建掩模

进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);

umask(0);  

(7) 处理 SIGCHLD 信号

但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源(关于僵尸进程的更多详情, 请看《特殊进程之僵尸进程》)。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在 Linux 下可以简单地将 SIGCHLD 信号的操作设为 SIG_IGN 。关于信号的更详细用法, 请看《信号中断处理》

	signal(SIGCHLD, SIG_IGN);

 总结:

#include <unistd.h> 
#include <signal.h> 
#include <fcntl.h>
#include <sys/syslog.h>
#include <sys/param.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int init_daemon(void)
{ 
	int pid; 
	int i;
	
	// 1)屏蔽一些控制终端操作的信号
	signal(SIGTTOU,SIG_IGN); 
	signal(SIGTTIN,SIG_IGN); 
	signal(SIGTSTP,SIG_IGN); 
	signal(SIGHUP ,SIG_IGN);
 
	// 2)在后台运行
    if( pid=fork() ){ // 父进程
        exit(0); //结束父进程,子进程继续
	}else if(pid< 0){ // 出错
		perror("fork");
		exit(EXIT_FAILURE);
	}
	//以下为第一个子进程的操作;
	// 3)脱离控制终端、登录会话和进程组
	setsid();  
	
	// 4)禁止进程重新打开控制终端
	if( pid=fork() ){ // 第一子进程调用fork生成孙进程
		exit(0);      // 结束第一子进程 
	}else if(pid< 0){ // 出错
		perror("fork");
		exit(EXIT_FAILURE);
	}  
    //第二子进程(孙进程)继续(第二子进程不再是会话组长),以下为第二子进程的操作;

	// 5)关闭打开的文件描述符
	// NOFILE 为 <sys/param.h> 的宏定义
	// NOFILE 为文件描述符最大个数,不同系统有不同限制
	for(i=0; i< NOFILE; ++i){
		close(i);
	}
	
	// 6)改变当前工作目录
	chdir("/tmp"); 
	
	// 7)重设文件创建掩模
	umask(0);  
	
	// 8)处理 SIGCHLD 信号
	signal(SIGCHLD,SIG_IGN);
	
	return 0; 
} 
 
int main(int argc, char *argv[]) 
{
	init_daemon();
	
	while(1);
 
	return 0;
}

SIGTERM

一个daemon通常是在系统关闭时才终止。可以在系统关闭时执行特定的应用程序的脚本来停止的。

因为在系统关闭的时候 init 进程会向所有其子进程发送SIGTERM信号。默认情况下,SIGTERM信号会终止一个进程。如果daemon在终止之前需要做些清理工作,那么就需要为这个信号建立一个处理器。这个处理器必须能快速地完成清理工作,因为 init 在发完 SIGTERM 信号的 5 秒之后会发送一个 SIGKILL 信号。(这并不意味着这个daemon 能够执行 5 秒的 CPU 时间,因为 init 会同时向系统中的所有进程发送信号,而它们可能都试图在 5 秒内完成清理工作。

由于 daemon 是长时间运行的,因此要特别小心潜在的内存泄露问题和文件描述符泄露

很多 daemon 需要确保同一时刻只有一个实例处于活跃状态。如让两个 cron daemon 都试图实行计划任务毫无意义。

使用 SIGHUP 重新初始化一个 daemon

由于很多 daemon 需要持续运行,因此在设计 daemon 程序时需要克服一些障碍。

  • 通常 daemon 会在启动时从相关的配置文件中读取操作参数,但有些时候需要在不重启 daemon 的情况下快速修改这些参数。
  • 一些 daemon 会产生日志文件。如果 daemon 永远不关闭日志文件的话,那么日志文件就会无限制地增长,最终会阻塞文件系统。这里需要有一种机制来告诉 daemon 关闭其日志文件并打开一个新文件,这样就能够在需要的时候旋转日志文件了

logrotate 程序可以用来自动旋转 daemon 的日志文件,具体可参考 logrotate(8)手册。

解决这两个问题的方案是让 daemon 为 SIGHUP 建立一个处理器,并在收到这个信号时采取所需的措施。由于 daemon 没有控制终端,因此内核永远不会向 daemon 发送这个信号。这样 daemon就可以使用 SIGHUP 信号来达到目的。

总结

daemon是一个长时间运行并且没有控制终端的进程(即它运行在后台)。daemon执行特定的任务,如提供一个网络登录工具或服务 Web 页面。一个程序要成为 daemon 需要按序执行一组步骤,包括调用 fork()和 setsid()。

daemon应该在合适的地方处理SIGTERM和SIGHUP信号。SIGTERM信号的处理方式应该是按序关闭这个 daemon,而SIGHUP信号则是提供了一种机制让daemon通过读取器配置文件并重新打开所使用的所有日志文件来重新初始化自身

 

使用C语言编写Linux下的守护进程 守护进程是在后台运行的进程,它独立于终端并且在系统启动时启动。在Linux环境中,我们可以使用C语言编写一个守护进程。下面是一个详细的示例,演示了如何创建一个简单的守护进程。这只是一个简单的示例,你可以根据自己的需求来编写更复杂的守护进程。首先,我们创建一个子进程,并检查创建子进程的操作是否成功。接下来,我们关闭标准输入、输出和错误输出,以避免守护进程与终端进行任何交互。运行后,你将看到守护进程在后台运行,并且每隔5秒输出一条消息到系统日志中。在主循环中,我们可以编写守护进程的主要逻辑。 阅读详情

相关推荐

绕过ROS2!在RDK X5上直接用Python调用海康相机C++ SDK的避坑指南

本文详细介绍了在RDK X5上绕过ROS2,直接使用Python调用海康相机C++ SDK的轻量化方案。通过Cython构建Python绑定,提取海康相机SDK核心组件,实现高效图像采集。文章涵盖环境准备、Cython接口设计、编译系统配置及性能优化技巧,特别适合ARM64平台开发者。

weixin_33712987的博客 233

linux C++守护进程保障启动其它程序

; #include&lt;sys/stat.h&gt; #include&lt;stdio.h&gt; #include&lt;stdlib.h&gt; #include&lt;time.h&gt; #include&lt;fcntl.h&gt; #include&lt;string.h&gt; #include&lt;termios.h&gt; #include &lt

干煸四季豆 2627

C#实现Socket通信同时监听多客户端(附完整源码)

C#实现Socket通信同时监听多客户端(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 411

[Linux]----守护进程

这节课我来给大家讲解在Linux下如何让进程守护化,运行在后台,处理我们的任务.正文开始!守护进程也称为精灵进程(Daemon),是运行在后台的一种特殊进程.它独立于控制中断并且周期性的执行某种任务或者等待处理某些发生的事件.Linux系统启动是会启动很多服务清楚,这些系统服务进程没有控制终端,不能直接和用户交互.其他进程都是在用户登录或运行程序时创建.在运行结束或者用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直运行这.这种进程都有一个名称叫守护进程(Daemon).

拾至名瑰的博客 8921

如何在Linux下自定义/编写 一个守护进程

本文主要讲述何为守护进程,以及如何自编或者利用现有程序将其伪装成守护进程

Leon的博客 1641

Linux守护进程

守护进程: 后台执行,运行周期长,无需和用户交互

长期更新自学笔记 2053

详解Supervisor进程守护监控

一、v博客前言 很多时候,我们想要在后台运行一个程序时,通常会用到nohup等命令(例:nohup sh ./test.sh &)。但是类似于nohup这样的命令并不能完整的控制和监控进程。 Supervisor在百度百科上给的定义是超级用户,监管员。Supervisor是一个进程管理工具,当进程中断的时候Supervisor能自动重新启动它。可以运行在各种类unix...

搁浅在PHP沙滩上的博客 3331

linux程序配置成守护进程,c – 编写linux守护进程

Linux编写/配置应用程序的正确方法是什么,它始终运行并提供外部请求(TCP,数据库,文件系统,任何类型).我特意不要调用这个守护进程,因为它可能意味着我不想在Linux环境中使用它.我已经阅读了多个主题,包括:但是没有一个能够充分比较使用哪种方法.我看到以下选项:>编写应用程序,分叉,调用setpid,umask等,但这个要求应用程序自己执行许多步骤; (使用自动启动通过init.d...

weixin_39834780的博客 207

linux编写python程序_Python实现Linux守护进程编写方法

本文实例讲述了Python实现Linux守护进程编写方法,分享给大家供大家参考,相信对于大家的Python程序设计会起到一定的帮助作用。具体方法如下:1. 调用fork()以便父进程可以退出,这样就将控制权归还给运行你程序的命令行或shell程序。需要这一步以便保证新进程不是一个进程组头领进程(process group leader)。下一步,‘setsid()',会因为你是进程组头领进程而...

weixin_39932939的博客 109

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

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任 务。比如,作业规划进程crond,打印进程lpd等。 守护进程的编程本身并不复杂,复杂的是各种版本的Un

SUNMOONSTAR专栏 1711

c语言计算器程序编写_Linux编写守护进程(C语言程序示例)

当我们在Linux的终端下执行某个程序后,如果将终端关闭,那么,你之前运行的那个程序也会随着终端的关闭而结束。在Linux中需要有这样一类程序程序启动后,可以默默地在后台一直运行,即使我们把终端退出,之前运行的程序还是可以在服务器上保持运行的状态的。如果说,你没遇到过这种情况,那说明你可能没使用过Linux系统,赶快行动起来吧。Linux守护进程是一类在后台运行的特殊进程,用于执行特定...

weixin_39564368的博客 205

linux 关闭csh_Linux编写守护进程(C语言程序示例)

当我们在Linux的终端下执行某个程序后,如果将终端关闭,那么,你之前运行的那个程序也会随着终端的关闭而结束。在Linux中需要有这样一类程序程序启动后,可以默默地在后台一直运行,即使我们把终端退出,之前运行的程序还是可以在服务器上保持运行的状态的。如果说,你没遇到过这种情况,那说明你可能没使用过Linux系统,赶快行动起来吧。Linux守护进程是一类在后台运行的特殊进程,用于执行特定...

weixin_39968801的博客 330

linux下使用daemon函数编写后台程序,如何编写Linux Daemon后台程序(守护进程) 第2页...

三. 守护进程实例守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。1. init.c清单#include < unistd.h >#include < signal.h >...

weixin_31920157的博客 158

【网摘】如何编写Linux Daemon后台程序(守护进程)

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任 务。比如,作业规划进程crond,打印进程lpd等。 守护进程的编程本身并不复杂,复杂的是各种版本的Un

zhangshumin的专栏 337

.NET 应用程序 Linux守护进程脚本编写

下面的脚本是生产可用,可靠的sh脚本,用于监控 .NET 应用程序并自动重启。推荐打包模式为框架依赖:需要在Linux上安装对应的donet版本;假如你打包发布到Linux程序名称为MyAspDemo;

darrenzzz的博客 521

编写Linux系统下Daemon程序的方法步骤

<br /><br />一、引言 Daemon程序是一直运行的服务端程序,又称为守护进程。<br />  本文介绍了在Linux编写Daemon程序的步骤,并给出了例子程序。<br />  二、Daemon程序简介<br />  Daemon是长时间运行的进程,通常在系统启动后就运行,在系统关闭时才结束。一般说Daemon程序在后台运行,是因为它没有控制终端,无法和前台的用户交互。Daemon程序一般都作为服务程序使用,等待客户端程序与它通信。我们也把运行的Daemon程序称作守护进程。<br />  三

joshua2011的专栏 519

Linux下后台守护进程编写实例

转载:http://zhoulifa.bokee.com/4627106.html Linux下后台守护进程编写实例 关键词: Linux    守护    后台    daemon    进程  相信随着Linux的普及,大家都在写不少Linux下的程序了,尤其是一些服务端程序慢慢地都转到Linux下了吧。大家必然碰到问题是如何让自己的进程成为守护进程,在系统启动的时候

pingnanlee的专栏 1564

Linux 系统守护进程实验

Linux 系统守护进程实验

m0_64562382的博客 468
上一篇: Linux基础概念--进程、子进程、进程组和会话
下一篇: Huffman编码及实现
土豆西瓜大芝麻
博客等级 码龄18年 1151粉丝 238原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值