Linux下创建维护服务进程Daemon的注意点,附有详细案例讲解
来源:互联网 发布:主角与配角知乎 编辑:程序博客网 时间:2024/06/07 00:16
unix系统下所有进程都工作在前台和后台,在前台工作的进程通常和用户直接交互(通常是通过terminal,比如我们ls -ltr,会在当前terminal显示文件列表),但是在后台运行的进程都是自己运行。用户可以检查它的工作状态,但是不知道它在干嘛。维护进程指的就是这种工作在后台的进程。
接下来分几步来讲解下如何创建一个维护进程,并且实现标准输入输出重定向的工作。
1. 维护进程化(让进程工作在后台程序)【fork】
fork()函数会让系统调复制我们当前的进程,然后让父进程退出(此时,子进程就会被unix第一进程init()接管)我们称这样的子进程为孤儿进程,作为此时的结果,子进程会完全脱离它之前的父进程开始在后台工作。
i=fork();if (i<0) exit(1); /* fork error */if (i>0) exit(0); /* parent exits *//* child (daemon) continues */
2. 让子进程独立【setsid】
一个进程通常通过tty来接受一些信号,任何继承这个进程的子进程也会被tty控制,一个服务进程不应该从开始它的进程中接受信息(父进程),所以它必须完全脱离控制它的tty。
在unix系统中,进程在一个进程组中运行,所以,在同一个进程组的所有进程都会被认为一个整体,进程组和对话组也会被继承。服务进程应当和其他所有进程独立开来。
setsid() ; // obtain a new process group
上面这个函数会将服务进程放在一个新的进程组,并且完全脱离它的控制tty。 setpgrp() 同样也适用于这种情况
3. 继承的文件符,标准输入输出文件
打开的文件号同样会被子进程继承,这样同样会导致系统中可用的文件符不够用,不必要的文件号需要在fork()前被关闭,
for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */
i=open("/dev/null",O_RDWR); /* open stdin */dup(i); /* stdout */dup(i); /* stderr */
由于unix系统顺次分配文件符,fopen会stdin(0)然后dup会提供一份复制给stdout,stderr。
转载该文章请标明出处: http://blog.csdn.net/elfprincexu
4.文件符mask 【umask】
绝大部分服务进程以超级用户运行,处于安全因素考虑,他们理应保护他们生成的文件,设置文件保护属性会防止不安全文件属性的产生。
umask(027);上述会限制文件权限为750, (027相补)
5. 运行文件路径 【chdir】
一个服务进程应该在一个确定的路径下运行,这有很多优势,同样的也会有很多坏处,试想在用户更目录下运行,它不可能找到输入输出文件。
chdir("/servers/");
6.文件互斥锁【open,lockf,getpid】
大部分服务进程要求同一时间下只有一个进程访问,文件锁是一个很好地方法来解决进程之间的互斥,只有第一个实例可以访问,操作,其他的所有进程需要等待文件锁的释放。这保证了文件的安全。
lfp=open("exampled.lock",O_RDWR|O_CREAT,0640);if (lfp<0) exit(1); /* can not open */if (lockf(lfp,F_TLOCK,0)<0) exit(0); /* can not lock *//* only first instance continues */sprintf(str,"%d\n",getpid());write(lfp,str,strlen(str)); /* record pid to lockfile */
7. 信号处理 【signal()】
进程可以从用户或者其他进程获取信号,最好是对这些信号进行适当处理。子进程会释放SIGCHLD信号当他们结束的时候。服务进程必须忽略或者正确处理这些信号。
signal(SIG_IGN,SIGCHLD); /* child terminate signal */
上面的代码会忽略子进程释放出来的SIGCHLD,
void Signal_Handler(sig) /* signal handler function */ int sig;{switch(sig){case SIGHUP:/* rehash the server */break;case SIGTERM:/* finalize the server */exit(0)break;}}signal(SIGHUP,Signal_Handler); /* hangup signal */signal(SIGTERM,Signal_Handler); /* software termination signal from kill */我们先建立一个信号处理函数,然后把信号和它捆绑。
8. 记录信息【syslogd,syslog.conf,openlog,syslog,closelog】
服务进程通常会创建一些信息,有些会很重要。对之后的维护很重要,因此文件重定向也变得尤为重要。
#mydeamon 2> erro.log
上一行代码会将输出信息重定向到文件中,当然这是最为简单的terminal重定向。
通常我们会使用unix系统自带的syslog进程。该进程会收集信息加以分类,syslog同时使用配置文件/etc/syslog.conf来重定向
openlog("mydaemon",LOG_PID,LOG_DAEMON)syslog(LOG_INFO, "Connection from host %d", callinghostname);syslog(LOG_ALERT, "Database Error !");closelog();openlog中,mydeamon用来识别我们的进程,LOG_PID使用syslog来记录信息,第一个参数是优先级,其余的类似于sprintf的信息。
这里也附上常用的重定向方法:
#include <sys/stat.h>#include <string.h>#include <fcntl.h>#include <io.h>int main(void){ #define STDOUT 1 //标准输出文件描述符 号 int nul, oldstdout; char msg[] = "This is a test"; /* create a file *///打开一个文件,操作者具有读写权限 如果文件不存在就创建 nul = open("DUMMY.FIL", O_CREAT | O_RDWR, S_IREAD | S_IWRITE); /* create a duplicate handle for standard output *///创建STDOUT的描述符备份 oldstdout = dup(STDOUT); /* redirect standard output to DUMMY.FIL by duplicating the file handle onto the file handle for standard output. *///重定向nul到STDOUT dup2(nul, STDOUT); /* close the handle for DUMMY.FIL *///重定向之后要关闭nul close(nul); /* will be redirected into DUMMY.FIL *///写入数据 write(STDOUT, msg, strlen(msg)); /* restore original standard output handle *///还原 dup2(oldstdout, STDOUT); /* close duplicate handle for STDOUT */ close(oldstdout); return 0;} //结果就是msg写到了文件中而不是STDOUT
9. 完整的案例:
该案例来自Levent Karakas:
/*UNIX Daemon Server Programming Sample ProgramLevent Karakas <levent at mektup dot at> May 2001To compile:cc -o exampled examped.cTo run:./exampledTo test daemon:ps -ef|grep exampled (or ps -aux on BSD systems)To test log:tail -f /tmp/exampled.logTo test signal:kill -HUP `cat /tmp/exampled.lock`To terminate:kill `cat /tmp/exampled.lock`*/#include <stdio.h>#include <fcntl.h>#include <signal.h>#include <unistd.h>#define RUNNING_DIR"/tmp"#define LOCK_FILE"exampled.lock"#define LOG_FILE"exampled.log"void log_message(filename,message)char *filename;char *message;{FILE *logfile;logfile=fopen(filename,"a");if(!logfile) return;fprintf(logfile,"%s\n",message);fclose(logfile);}void signal_handler(sig)int sig;{switch(sig) {case SIGHUP:log_message(LOG_FILE,"hangup signal catched");break;case SIGTERM:log_message(LOG_FILE,"terminate signal catched");exit(0);break;}}void daemonize(){int i,lfp;char str[10];if(getppid()==1) return; /* already a daemon */i=fork();if (i<0) exit(1); /* fork error */if (i>0) exit(0); /* parent exits *//* child (daemon) continues */setsid(); /* obtain a new process group */for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */i=open("/dev/null",O_RDWR); dup(i); dup(i); /* handle standart I/O */umask(027); /* set newly created file permissions */chdir(RUNNING_DIR); /* change running directory */lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0640);if (lfp<0) exit(1); /* can not open */if (lockf(lfp,F_TLOCK,0)<0) exit(0); /* can not lock *//* 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(SIGHUP,signal_handler); /* catch hangup signal */signal(SIGTERM,signal_handler); /* catch kill signal */}main(){daemonize();while(1) sleep(1); /* run */}/* EOF */
- Linux下创建维护服务进程Daemon的注意点,附有详细案例讲解
- unix/linux创建新进程,父子进程详解,附有案例
- linux下创建守护进程(daemon process)代码-详细注释
- Linux中系统服务/守护进程 (daemon) 的详细说明
- linux下的daemon进程
- linux下的daemon进程
- linux下的daemon进程
- linux下创建守护进程(daemon process)
- linux下创建守护进程(daemon process)
- [linux] 创建daemon进程
- [收藏] Linux中系统服务/守护进程 (daemon) 的详细说明
- [收藏] Linux中系统服务/守护进程 (daemon) 的详细说明
- [收藏] Linux中系统服务/守护进程 (daemon) 的详细说明
- [收藏] Linux中系统服务/守护进程 (daemon) 的详细说明
- Linux daemon守护进程的创建
- Linux 下 套接字 Socket 通信原理和实践,附有详细案例
- linux 守护进程(daemon)的创建方法
- CentOS下的Daemon进程
- rabbitmq 集群功能讲解
- 关联挖掘算法Apriori和FP-Tree学习
- codeforces round 291 div2 题解
- javaWEB项目中如何实现验证码
- C语言之第七课:C语言的字符型数据
- Linux下创建维护服务进程Daemon的注意点,附有详细案例讲解
- 工作中常用的linux操作整理
- win8.1+ubuntu 14.04 双系统安装
- 关于在VM上安装RAC ASM UDEV 无法返回UUID 问题
- java通过JRI调用R自定义脚本
- unix网络编程卷一: 第八章:简单UDP客户端服务器通信(二)-解决客户端一直等待问题
- 重写drawRect方法之后,绘图区域之外为黑
- java.lang.OutOfMemoryError: PermGen space
- 华为荣耀6