实现文档
如何写一个Linux守护进程
源码如下
/***********************************************************************
*
* 开发守护进程
*
**********************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include <syslog.h>
#include <sys/resource.h> // use getrlimit
#include <signal.h> // use sigaction
#include <stdlib.h> // use exit,system
void create_daemon(void)
{
// 文件描述符
int i, fd0, fd1, fd2;
// 进程ID
pid_t pid;
// 资源限制
struct rlimit rl;
// 信号动作
struct sigaction sa;
//
// 函数:umask
// 功能:设置文件模式的屏蔽属性。即,通过umask可禁掉某些属性。
//
// 目的:调用umask将文件模式创建屏蔽字设置为0,确保所有文件模式可用。
//
umask(0);
//
// 函数:getrlimit(RLIMIT_NOFILE, **);
// 功能:获取资源限制,RLIMIT_NOFILE表示每个进程能打开的最大文件数。
//
// 目的:获取文件描述符的信息
//
if(getrlimit(RLIMIT_NOFILE, &rl) < 0){
printf("can not get file limit");
return;
}
//
// 函数:fork
// 功能:创建子进程。子进程继承父进程的进程组ID,但具有一个新的进程ID。
// 这确保了,子进程不是一个进程组的组长进程。
//
// 函数:setsid
// 功能:创建新会话,当前进程是,新会话首进程,也是新会话的唯一进程。该进程
// 会成为一个新进程组的组长进程,新进程组ID就是该调用进程的进程ID
// 注意:如果调用进程是一个进程组的组长,则此函数会返回出错。为了确保不会
// 发生这种情况,通常先调用fork,然后使其父进程终止,而子进程则继续。
//
// 目的:使得目标调用进程,达到以下目的。
// a) 成为新会话的首进程
// b) 成为一个新进程组的组长进程
// c) 没有控制终端
//
if((pid = fork()) < 0){
printf("can not fork");
return;
}else if(pid != 0){
exit(0);
}
setsid();
//
// 函数:sigemtpyset(sigset_t *set)
// 功能:初始化由set指向的信号集,清楚其中所有信号。
//
// 函数:int sigaction(int signo, const struct sigaction * restrict act, struct sigaction *restrict oact);
// 功能:检查或者修改与指定信号相互关联的处理动作。其中,参数signo是要检测或修改其具体动作的信号编号。
// 若act指针非空,则要修改其动作。如果oact指针非空,则系统经由oact指针返回该信号的上一个动作。
// 注意:SIG_IGN是常量,用于代替指向函数的指针。该函数需要一个整型参数,而且无返回值。
// #define SIG_DFL (void (*)())0
// #define SIG_IGN (void (*)())1
//
// 目的:忽略连接断开信号SIGHUP
//
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0){
printf("can not ignore SIGHUP");
return;
}
// 目的:再次fork,并使父进程终止。第二个子进程作为守护进程继续运行。这样就保证了该守护进程不是会话首进程。
// 可以防止它取得控制终端。
if((pid = fork()) < 0){
printf("can not fork");
return;
}else if(pid != 0){
exit(0);
}
//
// 函数:chdir
// 功能:更改当前动作目录。当前工作目录是进程的一个属性。此目录是搜索相对路径名的起点。
//
// 目的:使得进程不与任何文件系统联系。
if(chdir("/usr/wlm/dev") < 0){
printf("can not change directory");
return;
}
// 目的:关闭不再需要的文件描述符。这使守护进程不再持有从其父进程继承来的某些文件描述符。
if(rl.rlim_max == RLIM_INFINITY){
rl.rlim_max = 1024;
}
for(i = 0;i< rl.rlim_max; i++){
close(i);
}
//
// 函数:int open(const char * pathname, int oflag, ...);
// 功能:打开或者创建一个文件。O_RDWR表示“读,写打开”
//
// 函数:int dup(int filedes);
// 功能:复制一个现存的文件描述符,返回的新文件描述符一定是当前可用文件描述符中的最小数值。
// 通常等于输入的文件描述符+1;
//
// 目的:守护进程打开/dev/null使其具有文件描述符0,1,2。这样,任何一个试图读标准输入,
// 写标准输出,或者标准出错的例程都不会产生任何效果。因为守护进程并不与终端设备
// 相关联,所以不能在终端设备上显示其输出,也无处从交互式用户那里接收输入。
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
if(fd0 != 0 || fd1 != 1 || fd2 != 2){
printf("unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
// 重点:守护进程做任务
//
// 函数:int system(const char * cmdstring);
// 功能:执行一个命令字符串。
// 示例:system("date > file");
//
system("echo \"hello,world!\\n\" >> wlm");
time_t now;
while(1){
sleep(30);
//
// 函数:FILE * fopen(const char * restrict pathname, const char * restrict type);
// 功能:打开一个标准I/O流
// 注意:type="r+ 或r+b 或rb+",表示为读写而打开。
// type="a+", 表示open or create for reading and writing at end of file
//
FILE * fd = fopen("wlm", "a+");
//
// 函数:time_t time(time_t * calptr);
// 功能:返回当前时间和日期
// 注意:返回时间值。如果参数不为空,则时间值也存放在由calptr指向的单元内
//
time(&now);
//
// 函数:int fprintf(FILE *restrict fp, const char *restrict format, ...);
// 功能:写至指定的流
//
fprintf(fd, "system time:\t%s\t\t pid:%d\n", ctime(&now), getpid());
fclose(fd);
}
}
int main()
{
printf("hello,welcome to create daemon.\r\n");
// 创建守护进程
create_daemon();
return 0;
}