技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 670|回复: 0
收起左侧

【C】教你如何在CentOS下写一个“呆萌”(守护进程Daemon)

[复制链接]

1008

主题

2235

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
200
威望
265 点
宅币
16762 个
贡献
33385 次
宅之契约
0 份
在线时间
1598 小时
注册时间
2014-1-26
发表于 2017-1-18 18:46:53 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
写“呆萌”,也就是后台守护进程(“Daemon”的谐音是“呆萌”),需要注意以下几点:
1、用户如果用ssh控制他的服务器,然后服务器跑了你的呆萌,当用户断开ssh连接的时候,你的呆萌也能继续运行。
2、你的呆萌应该把运行的过程写入到日志里,而不是输出到屏幕上。
3、保证稳定性,别因为无视了一个signal而挂掉。
4、为了便于管理,你应该在临时文件夹写一个lock文件,里面用文本记录你的PID号,以便于用户用脚本控制你的呆萌进程(典型的,使其能兼容chkconfig的配置方式)

一般来说你的守护进程被直接运行的时候,它的父进程应该是bash,然后bash会一直等待你的进程结束,才会继续执行后面的脚本。
为了能让你的程序不堵住bash的脚本执行过程,一般的做法是启动一个新的进程,然后旧进程退出,让bash继续执行,而新进程则作为呆萌保持执行过程。
这里最常用的函数就是fork()了,它在unistd.h里面有定义,这个函数的作用是:
  • 复制自身进程,新的进程拥有完全一样的内存空间(但它一般并不需要完全复制自身的内存数据,虽然此时新的进程的内存数据和旧进程完全相同)。
  • 如果自己是父进程,返回子进程的PID;如果自己是子进程,返回0;如果分身失败,则返回-1.
调用fork()后,旧进程就可以退出了,然后bash继续运行,与此同时你的新进程也在后台运行。但这还不够,因为此时你的旧进程和新进程都不是一个进程组的组长,因此当你的旧进程被杀的时候,新进程也会死。此时你需要调用setsid()来设置新的进程会话ID,也就是让你的新进程成为一个进程组的组长,这样的话它就真正从父进程独立出来了。

此时新进程需要做的事情有以下几个:
  • 把PID写到lock文件里,便于让用户找到你。
  • 关闭没用的文件描述符,比如stdin,stdout和stderr,或者你把stdout和stderr重定向到你的log,但其实自己fopen一个新的fd比做一次重定向方便得多。
  • 设置信号(signal)处理,将无关信号屏蔽,然后对SIGPIPE、SIGHUP、SIGTERM做出响应。


这里给一份例子,展示一个程序如何进入呆萌模式。
[C] 纯文本查看 复制代码
// 需要的头文件
#include<stdio.h>
#include<signal.h>
#include<stdarg.h>
#include<unistd.h>

// 程序信息
#define EXEC_NAME foo
#define LOCK_FILE "/var/tmp/"EXEC_NAME".lock"
#define LOG_FILE EXEC_NAME".log"

static volatile int g_Quit = 0; // 是否退出
static void _signal_handler(int sig);

void Inst_Log(char *format, ...); // 记录log

//==============================================================================
//Func: _Inst_Daemonize
//Desc: Make program run as a daemon
//------------------------------------------------------------------------------
static int _Inst_Daemonize()
{
	int i,lfp;
	char str[10];
	
	if(getppid() == 1)
	{
		fprintf(stderr, EXEC_NAME" was still running.\n");
		return 0;
	}
	
	i = fork();
	if(i < 0)
	{
		// fork error
		return 0;
	}
	if(i > 0)
	{
		// Parent exits, child (daemon) continues
		return 0;
	}
	
	setsid(); // obtain a new process group
	
	fclose(stdin);
	fclose(stdout);
	fclose(stderr);
	
	lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
	if(lfp<0)
	{
		// Could not open lock file.
		Inst_Log("Open lock file failed.\n");
		return 0;
	}
	
	if(lockf(lfp, F_TLOCK, 0) < 0)
	{
		// can not lock
		Inst_Log("Lock lock file failed.\n");
		return 0;
	}
	
	// first instance continues
	sprintf(str,"%d\n",getpid());
	write(lfp,str,strlen(str)); // record pid to lockfile
	
	signal(SIGCHLD,SIG_IGN); // ignore child
	signal(SIGTSTP,SIG_IGN); // ignore tty signals
	signal(SIGTTOU,SIG_IGN);
	signal(SIGTTIN,SIG_IGN);
	signal(SIGPIPE,SIG_IGN);
	signal(SIGHUP,_signal_handler); // catch hangup signal
	signal(SIGTERM,_signal_handler); // catch kill signal
	
	return 1;
}

//==============================================================================
//Func: Inst_Log
//Desc: Print log to a specified file.
//------------------------------------------------------------------------------
void Inst_Log(char *format, ...)
{
	FILE *logfile = NULL;
	va_list ap;
	
	logfile=fopen(LOG_FILE,"a");
	if(logfile)
	{
		va_start(ap, format);
		vfprintf(logfile, format, ap);
		va_end(ap);
		fclose(logfile);
	}
}

//==============================================================================
//Func: _signal_handler
//Desc: 处理信号
//------------------------------------------------------------------------------
static void _signal_handler(int sig)
{
	switch(sig)
	{
	case SIGHUP:
		break;
	case SIGTERM:
		Inst_Log("Terminate signal.\n");
		g_Quit = 1;
		break;
	}
}
参考资料:
技术上来说,呆萌、服务、进程都有些什么区别?http://askubuntu.com/questions/1 ... service-and-process
为什么呆萌要运行setsid?http://unix.stackexchange.com/qu ... emonizing-a-process


本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-11-21 15:57 , Processed in 0.082784 second(s), 14 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表