【Linux应用编程】一文弄懂Linux守护进程

本文详细介绍了守护进程(daemon)的概念,其在系统中的作用及如何创建。守护进程是一种特殊的后台进程,不受终端控制,常在系统启动时启动,直至系统关闭。文章列出了常见的守护进程及其功能,并提供了创建守护进程的具体步骤,包括fork、setsid、重定向输出流等关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

守护进程

  守护进程(daemon)是一类在后台运行的特殊进程,守护进程特点是,不受任何终端控制、不受用户登录注销影响。通常在系统启动的时候启动,仅在系统运行结束后才终止。

  可以通过“ps ajx”命令查看系统的守护进程,其中TPGID为-1的就是守护进程。
在这里插入图片描述

守护进程作用

  守护进程在后台中周期性地执行某种特殊任务;或者等待某些发生地事件而唤醒该进程进程处理事件,处理完则进入休眠。
【1】系统守护进程提供用户和应用程序必要的功能
【2】守护进程可以监测某一事件,如某进程异常崩溃,守护进程监测到则重新启动该进程


常用守护进程

  Linux通常针对一个领域都有专门的守护进程,日志服务、网络服务、时间、数据库等,只列举常见易理解的守护进程,其他的待增加。

【1】amd:自动安装NFS(网络文件系统)守护进程
【2】apmd:高级电源管理
【3】Arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和IP地址对数据库
【4】Autofs:自动安装管理进程automount,与NFS相关,依赖于NIS
【5】Bootparamd:引导参数服务器,为LAN上的无盘工作站提供引导所需的相关信息
【5】crond:Linux下的计划任务
【7】Dhcpd:启动一个DHCP(动态IP地址分配)服务器
【8】Gated:网关路由守候进程,使用动态的OSPF路由选择协议
【9】Httpd:WEB服务器
【10】Inetd:支持多种网络服务的核心守候程序
【11】Innd:Usenet新闻服务器
【12】Linuxconf:允许使用本地WEB服务器作为用户接口来配置机器
【13】Lpd:打印服务器
【14】Mars-nwe:mars-nwe文件和用于Novell的打印服务器
【15】Mcserv:Midnight命令文件服务器
【16】named:DNS服务器
【17】netfs:安装NFS、Samba和NetWare网络文件系统
【18】network:激活已配置网络接口的脚本程序
【19】nfs:打开NFS服务
【20】nscd:nscd(Name Switch Cache daemon)服务器,用于NIS的一个支持服务,它高速缓存用户口令和组成成员关系
【21】portmap:RPC portmap管理器,与inetd类似,它管理基于RPC服务的连接
【22】postgresql:一种SQL数据库服务器
【23】routed:路由守候进程,使用动态RIP路由选择协议
【24】rstatd:一个为LAN上的其它机器收集和提供系统信息的守候程序
【25】ruserd:远程用户定位服务,这是一个基于RPC的服务,它提供关于当前记录到LAN上一个机器日志中的用户信息
【26】rwalld:激活rpc.rwall服务进程,这是一项基于RPC的服务,允许用户给每个注册到【27】LAN机器上的其他终端写消息
【28】rwhod:激活rwhod服务进程,它支持LAN的rwho和ruptime服务
【29】sendmail:邮件服务器sendmail
【30】smb:Samba文件共享/打印服务
【31】snmpd:本地简单网络管理候进程
【32】squid:激活代理服务器squid
【33】syslog:一个让系统引导时起动syslog和klogd系统日志守候进程的脚本
【34】xfs:X Window字型服务器,为本地和远程X服务器提供字型集
【35】xntpd:网络时间服务器
【36】ypbind:为NIS(网络信息系统)客户机激活ypbind服务进程
【37】yppasswdd:NIS口令服务器
【38】ypserv:NIS主服务器
【39】gpm:管理鼠标
【40】identd:AUTH服务,在提供用户信息方面与finger类似
【41】 time :以TCP协议从远程主机获取时间和日期守护进程
【42】 time-udp :以udp协议从远程主机获取时间和日期守护进程
【43】 mysqld:一个快速高效可靠的轻型SQL数据库引擎守护进程
【44】yum:RPM操作系统自动升级和软件包管理守护进程


守护进程创建步骤

1. fork子进程,父进程exit退出

  根据守护进程“脱离终端”特点,fork子进程后,父进程应该退出,后续所有任务和事件由子进程处理,子进程成为“孤儿进程”,达到脱离终端目的。另一方面,需要创建新的会话,而创建会话的进程不能是组长进程,让父进程创建一个子进程,那么子进程一定不是组长进程。

2. 子进程创建会话

  我们知道,fork()子进程复制父进程的会话期、进程组、控制终端等信息,即使父进程退出了。子进程调用setsid函数创建新会话,子进程成为新会话的首进程,并成为新进程组的组长进程,到这一步,如果setsid执行成功,已经实现进程脱离终端的目标。

3. 再次 fork() 一个子进程,父进程退出

  子进程虽然已经成为无终端的会话组长,但它仍可以重新申请打开一个控制终端,所以再 fork() 一个子进程,该子进程不是会话首进程;然后退出父进程(第一个子进程)。

4. 更改工作目录

  子进程复制了父进程的信息,与父进程处于同一工作目录下。如果父进程处于挂载目录下(如U盘、光驱),由于子进程持续运行导致文件会不能被卸载。因此,需要调用chdir函数使得子进程工作于根目录(“/”)下。

5. 处理SIGCHLD信号

  子进程终止时,会向父进程发送一个SIGCHLD信号,父进没有wait/waitpid会导致子进程变成一个僵尸进程。父进程可以重写信号handler,忽略SIGCHLD信号。

6. 关闭文件描述符

  子进程复制了父进程的信息,子进程与父进程拥有相同的文件描述符表,如果父进程打开的文件,子进程也能操作,通常把从父进程继承过来的文件描述符关闭。

7. 更改文件操作权限

  子进程继承父进程的文件操作权限(读、写、执行),为增强守护进程的 灵活性,将文件操作权限设为无任何权限(0),即通过文件掩码函数umask设置文件权限掩码(umask(0))。

8. 重定向输出流

  避免父进程向终端输出信息,需将标准输入、标准输出、标准错误等重定向至“/dev/null”。

8. 守护进程退出

  一般情况下守护进程是随系统关闭而终止。如果守护进程支持用户主动终止,守护进程内部需实现 kill 发出的signal信号handler,用户通过kill终止守护进程。


守护进程创建

  实现一个周期记录时间日志的守护进程。

【1】根据守护进程创建步骤fork进程实现

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/stat.h>

 
static bool flag = true;

void create_daemon()
{
	int i= 0;
	pid_t pid;
	
	pid = fork();
	
	if(pid == -1)
	{
		perror("fork first");
		exit(1);
	}
	else if(pid > 0)
	{
		exit(0);			/* 创建子进程,父进程退出 */
	}
 
	if(setsid() == -1)		/* 创建新会话 */
	{
		perror("setsid");
		exit(1);
	}
 
	pid = fork();			/* 创建子子进程,子进程退出 */
	if(pid == -1)
	{
		perror("fork second");
		exit(1);
	}
	else if(pid > 0)
	{
		exit(0);
	}
 
	if(chdir("/") < 0)			/* 更改工作目录为根目录 */
	{	
		perror("chdir");
		exit(0);
	}

	signal(SIGCHLD,SIG_IGN);	/* 忽略SIGCHLD信号 */

	//close(fd);				/* 关闭文件 */
	
	umask(0);					/* 更改文件权限 */
	
	int fd;
    fd=open("dev/null",O_RDWR);

	if(fd < 0)
	{
		perror("open 'dev/null'");
		exit(0);
	}
    dup2(fd,STDIN_FILENO);				/* 重定向标准输入、输出、错误输出 */
    dup2(fd,STDOUT_FILENO);
	dup2(fd,STDERR_FILENO);
	close(fd);
	
	return;
}

void handler(int sig)
{
	printf("Recv exit signal %d\n", sig);
	flag = false;
}

int main(int argc, char** argv)
{
	time_t now;
	int fd;
	char *p;
	struct sigaction act;
	
	create_daemon();
	
	act.sa_handler = handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	
	if(sigaction(SIGQUIT, &act, NULL) < 0)
	{
		perror("sigaction");
		exit(0);
	}
	syslog(LOG_USER|LOG_INFO,"Daemon LOG!\n");
	while(flag)
	{
		time(&now);
		syslog(LOG_USER|LOG_INFO,"%s\t\n",ctime(&now));
		sleep(30);
	}
	closelog();
	
	return 0;
}

   使用syslog函数前需要配置。在“/etc/rsyslog.conf”配置文件末尾加上关于自定义日志。

user.*    /var/log/daemon.log

  配置完成重启syslog服务。

/etc/init.d/rsyslog restart

  编译运行,可以查看守护进程(ps ajx),及周期日志记录(cat /var/log/daemon.log)。

【守护进程】
在这里插入图片描述
【log】
在这里插入图片描述
在这里插入图片描述

【2】调用系统函数

#include <unistd.h>
int daemon(int nochdir,int noclose);

  Linux系统已经提供守护进程创建函(daemon)。

参数含义
nochdir指定工作目录,0表示使用根目录(“/”);非0使用当前工作目录
noclose指定设备,0表示STDIN、STDOUT、STDERR重定向至“/dev/null”;非0使用原来设备

  函数执行成功返回0,失败返回-1,失败原因存于errno中。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/stat.h>

static bool flag = true;

void handler(int sig)
{
	printf("I got a signal %d\nI'm quitting.\n", sig);
	flag = false;
}
 
int main(int argc, char** argv)
{
	time_t now;
	int fd;
	struct sigaction act;
	
	if(daemon(0, 0) == -1)
	{
		perror("daemon\n");
		exit(1);
	}
	
	act.sa_handler = handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if(sigaction(SIGQUIT, &act, NULL) < 0)
	{
		perror("sigaction");
		exit(0);
	}
	
	syslog(LOG_USER|LOG_INFO,"Daemon LOG!\n");
	while(flag)
	{
		time(&now);
		syslog(LOG_USER|LOG_INFO,"%s\t\n",asctime(&now));
		sleep(30);
	}
	closelog();
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acuity.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值