#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
void main(int argc, char ** argv){
time_t now;
int childpid, fd, fdtablesize;
int error, in, out;
/*忽略终端 I/O信号,STOP信号*/
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
/*父进程退出,程序进入后台运行*/
if( fork()!=0 ) exit(1);
if( setsid()<0 ) exit(1); /*创建一个新的会议组*/
/*子进程退出,孙进程没有控制终端了*/
if( fork()!=0 ) exit(1);
if( chdir("/tmp")==-1 )exit(1);
/*关闭打开的文件描述符,包括标准输入、标准输出和标准错误输出*/
for( fd=0, fdtablesize=getdtablesize(); fd< fdtablesize;fd++) close(fd);
umask(0);/*重设文件创建掩模*/
signal(SIGCHLD,SIG_IGN);/*忽略SIGCHLD信号*/
/*打开log系统*/
syslog(LOG_USER|LOG_INFO,"守护进程测试!\n");
while(1){
time(&now);
syslog(LOG_USER|LOG_INFO,"当前时间:\t%s\t\t\n",ctime(&now));
sleep(6);
}
}
守护进程在Linux/Unix系统中有着广泛的应用。有时,开发人员也想把自己的程序变成守护进程。在创建一个守护进程的时候,要接触到子进程、进程组、会晤期、信号机制、文件、目录和控制终端等多个概念。因此守护进程还是比较复杂的,在这里详细地讨论Linux/Unix的守护进程的编写,总结出八条经验,并给出应用范例。
编程要点
1.屏蔽一些有关控制终端操作的信号。防止在守护进程没有正常运转起来时,控制终端受到干扰退出或挂起。示例如下:
signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHUP ,SIG_IGN);
所有的信号都有自己的名字。这些名字都以“SIG”开头,只是后面有所不同。开发人员可以通过这些名字了解到系统中发生了什么事。当信号出现时,开发人员可以要求系统进行以下三种操作:
◆ 忽略信号。大多数信号都是采取这种方式进行处理的,这里就采用了这种用法。但值得注意的是对SIGKILL和SIGSTOP信号不能做忽略处理。
◆ 捕捉信号。最常见的情况就是,如果捕捉到SIGCHID信号,则表示子进程已经终止。然后可在此信号的捕捉函数中调用waitpid()函数取得该子进程的进程ID和它的终止状态。另外,如果进程创建了临时文件,那么就要为进程终止信号SIGTERM编写一个信号捕捉函数来清除这些临时文件。
◆ 执行系统的默认动作。对绝大多数信号而言,系统的默认动作都是终止该进程。
对这些有关终端的信号,一般采用忽略处理,从而保障了终端免受干扰。
这类信号分别是,SIGTTOU(表示后台进程写控制终端)、SIGTTIN(表示后台进程读控制终端)、SIGTSTP(表示终端挂起)和SIGHUP(进程组长退出时向所有会议成员发出的)。
2.将程序进入后台执行。由于守护进程最终脱离控制终端,到后台去运行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。这就是常说的“脱壳”。子进程继续函数fork()的定义如下:
#include <sys/types.h>#include <unistd.h> pid_t fork(void);
该函数是Linux/Unix编程中非常重要的函数。它被调用一次,但返回两次。这两次返回的区别是子进程的返回值为“0”,而父进程的返回值为子进程的ID。如果出错则返回“-1”。
3.脱离控制终端、登录会话和进程组。开发人员如果要摆脱它们,不受它们的影响,一般使用 setsid() 设置新会话的领头进程,并与原来的登录会话和进程组脱离。这只是其中的一种方法,也有如下处理的办法:
if ((fd = open("/dev/tty",O_RDWR)) >= 0) { ioctl(fd,TIOCNOTTY,NULL); close(fd); }
其中/dev/tty是一个流设备,也是终端映射,调用close()函数将终端关闭。
4.禁止进程重新打开控制终端。进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端。开发人员可以通过不再让进程成为会话组长的方式来禁止进程重新打开控制终端,需要再次调用fork函数。
上面的程序代码表示结束第一子进程,第二子进程继续(第二子进程不再是会话组长)。
5. 关闭打开的文件描述符,并重定向标准输入、标准输出和标准错误输出的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如果不关闭,将会浪费系统资源,引起无法预料的错误。关闭三者的代码如下:
for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) close(fd);
但标准输入、标准输出和标准错误输出的重定向是可选的。也许有的程序想保留标准输入(0)、标准输出(1)和标准错误输出(2),那么循环应绕过这三者。代码如下:
for (fd =3, fdtablesize = getdtablesize();fd < fdtablesize; fd++) close(fd);
有的程序有些特殊的需求,还需要将这三者重新定向。示例如下:
error=open("/tmp/error",O_WRONLY|O_CREAT,0600); dup2(error,2); close(error); in=open("/tmp/in",O_RDONLY|O_CREAT,0600); if(dup2(in,0)==-1) perror("in"); close(in);out=open("/tmp/out",O_WRONLY|O_CREAT,0600); if(dup2(out,1)==-1) perror("out"); close(out);
6.改变工作目录到根目录或特定目录进程活动时,其工作目录所在的文件系统不能卸下。
一般需要将工作目录改变到根目录或特定目录,注意用户对此目录需要有读写权。防止超级用户卸载设备时系统报告设备忙。
7.处理SIGCHLD信号。SIGCHLD信号是子进程结束时,向内核发送的信号。
如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。因此需要对SIGCHLD信号做出处理,回收僵尸进程的资源,避免造成不必要的资源浪费。可以用如下语句:
signal(SIGCHLD,(void *)reap_status);
捕捉信号SIGCHLD,用下面的函数进行处理:
void reap_status() { int pid; union wait status; while ((pid = wait3(&status,WNOHANG,NULL)) > 0) …… }
8.在Linux/Unix下有个syslogd的守护进程,向用户提供了syslog()系统调用。任何程序都可以通过syslog记录事件。
由于syslog非常好用和易配置,所以很多程序都使用syslog来发送它们的记录信息。一般守护进程也使用syslog向系统输出信息。syslog 有三个函数,一般只需要用syslog(...)函数,openlog()/closelog()可有可无。syslog()在shslog.h定义如下:
#include <syslog.h>void syslog(int priority,char *format,...);
其中参数priority指明了进程要写入信息的等级和用途。第二个参数是一个格式串,指定了记录输出的格式。在这个串的最后需要指定一个%m,对应errno错误码。
分享到:
相关推荐
总结,本文详细介绍了如何在Python中编写守护进程,包括脱离控制终端、改变工作目录、关闭标准文件描述符和重新打开日志文件等关键步骤。同时,我们也展示了如何实现当守护进程被杀后自动重启的功能,通过监控进程...
本篇文章提供一个软件实现守护进程的办法,原理就是udp通信,单独写个守护进程程序,专门负责检测主程序是否存在,不存在则启动。主程序只需要启动live类监听端口,收到hello就回复ok就行。 为了使得兼容任意程序,...
因此,编写守护进程不仅仅是编写一个普通的程序,而是需要遵循特定规则以确保其在后台的稳定运行和高效服务。 守护进程的编程涉及多个系统调用,如`fork()`用于创建子进程,`setsid()`用于创建新的会话,以及`chdir...
编写 Linux 守护进程 ...8. 编写守护进程的日志记录。可以使用 syslog() 函数记录守护进程的日志,例如: syslog(LOG_INFO, "Daemon started"); 通过遵循这八条经验,可以编写一个稳定、可靠的 Linux 守护进程。
这篇教程将带你了解如何在JavaScript中编写守护进程。 首先,我们来看`main.js`这个文件,它很可能是实现守护进程的核心代码。在Node.js环境中,我们可以使用内置的`child_process`模块来创建子进程,从而实现守护...
在Windows操作系统中,我们可以利用Delphi这样的编程环境来编写守护进程,以实现对指定程序的持续监控与自动启动。下面,我们将深入探讨Delphi编写守护进程的核心概念、实现原理以及压缩包中的文件功能。 首先,...
C++编写的守护进程,守护进程以windows服务的方式运行,可通过配置文件配置需要守护的exe。
编写守护进程通常涉及以下几个步骤: 1. 分离控制终端:通过fork两次,第一次创建子进程,第二次使子进程再次fork,让孙子进程成为孤儿进程,由init接管,从而脱离控制终端。 2. 改变工作目录:通常更改为根目录(/...
### 如何编写守护进程 守护进程(Daemon Process)是一种在后台持续运行的进程,它不依附于任何用户终端并且独立于控制台之外。守护进程主要用于执行特定的任务或提供服务,例如日志管理、定时任务处理等。本文将...
主要功能: 可设置检测的程序名称。 可设置udp通信端口。 可设置超时次数。 自动记录已重启次数。 自动记录最后一次重启时间。 是否需要重新刷新桌面。 可重置当前重启次数和最后重启时间。 自动隐藏的托盘运行或者...
在编写守护进程时,需要注意的是,不同的Unix系统实现可能会有所不同,Linux作为System V的变种,遵循Posix标准,实现起来相对更简单。在遵循上述编程要点的同时,需要根据具体系统特性进行适当的调整。编写守护进程...
- 在PHP中,编写守护进程通常涉及到重定向标准输入输出、改变工作目录、关闭文件描述符等一系列操作,以确保进程脱离终端。 - 示例代码中,`ExampleWorker` 类实现了基本的数据库连接,并通过`run`方法处理数据。`...
创建守护进程通常涉及编写shell脚本(如bash)或用C、Python等编程语言编写。Linux中的一些常用工具,如systemd或init,可以帮助管理守护进程的生命周期。 例如,一个名为"守护进程1.sh"的Linux脚本可以通过以下...
在C语言中编写守护进程通常涉及以下步骤: 1. ** fork() 操作**:首先,调用fork()函数创建一个子进程,父进程退出,子进程成为孤儿进程。 2. ** setsid()**:调用setsid()函数创建新的会话并使进程成为会话领导者的...
这要求开发者在编写守护进程时要格外谨慎,防止因编写不当或恶意程序对系统造成损害。 2. **父进程ID为1**:守护进程的父进程ID通常是1,这意味着它们的父进程是init进程,这是Linux系统中最顶级的进程。 3. **无...
### 用Shell编写守护进程详解 #### 知识点概览 1. **守护进程(Daemon)的概念** 2. **Shell脚本基础** 3. **Shell脚本中的进程管理** 4. **守护进程的设计与实现** 5. **错误处理与日志记录** #### 守护进程概念 ...
首先,我们需要编写守护进程程序。在Linux环境下,通常使用C、Python、Perl或其他支持后台运行的编程语言来编写。守护进程应具备以下特性: 1. 与终端分离:守护进程不应有控制终端,通常通过调用`fork()`两次实现...