守护进程就是生存期长的一种进程,没有控制终端,就像是在后台运行,进程ID等于进程组ID 等于会话ID
1.编写规则
(1).调用umask码设为0 直接调用umask()函数
umask(0);
(2).将父进程退出
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork()");
return -1;
}
if(pid > 0)
exit(0);
(3).调用setsid创建一个新的会话,调用进程成为新会话的首进程,新的进程组组长,并且没有控制终端
if (setsid() == -1)
{
perror("setsid()");
return -1;
}
// PID == PGID == SID no tty
(4).将调用进程的工作目录改为根目录
chdir("/");
(5).关闭不需要的文件描述符
(6).将文件描述符0,1,2 指向 /dev/null
fd = open("/dev/null", O_RDWR);
if (fd == -1)
{
perror("open()");
return -1;
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
2.守护进程库函数
daemon(3)
#include <unistd.h>
int daemon(int nochdir, int noclose);
If nochdir is zero, daemon() changes the process's current working directory to the root directory ("/"); otherwise, the current working directory is left unchanged.
当nochdir为0的时候,将工作目录改为“/”目录
If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made to these file descriptors.
当noclise为0的时候,将文件描述符为1,2,3的指向/dev/null
返回值:
成功返回0;失败返回-1并且设置errno
3.出错记录
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
openlog()
打开与日志的连接(ubuntu下守护进程日志路径“/var/log/syslog”)。 ident指向的字符串前置于每条消息,通常设置为程序名称。 如果ident为NULL,则使用程序名称。
option可选项
LOG_CONS Write directly to system console if there is an error while sending to system logger.
LOG_NDELAY Open the connection immediately (normally, the connection is opened when the first message is logged).
LOG_NOWAIT Don't wait for child processes that may have been created while logging the message. (The GNU C library does not create a child process,so this option has no effect on Linux.)
LOG_ODELAY The converse of LOG_NDELAY; opening of the connection is delayed until syslog() is called. (This is the default, and need not be specified.)
LOG_PERROR (Not in POSIX.1-2001 or POSIX.1-2008.) Print to stderr as well.
LOG_PID Include PID with each message.
facility
可选项查看man手册
具体用法
openlog(NULL, LOG_PID | LOG_PERROR, LOG_DAEMON);
fp = fopen(FLNAME, "w");
if (NULL == fp)
{
// perror("fopen()");
syslog(LOG_ERR, "fopen():%s", strerror(errno));
exit(1);
}
while (1)
{
time(&tm);
tmp = localtime(&tm);
if (NULL == tmp)
{
// fprintf(stderr, "localtime() failed\n");
syslog(LOG_ERR, "localtime() failed");
goto ERROR;
}
strftime(buf, BUFSIZE, "%Y-%m-%d %H:%M:%S\n", tmp);
fputs(buf, fp);
// 调试
syslog(LOG_INFO, "%s write into the file %s", buf, FLNAME);
fflush(NULL);
sleep(1);
}
fclose(fp);
4.单实例守护进程
一个守护进程可以开启多个,如果加以限制,只能开启一个,就是单实例守护进程
static int single_instance(void)
{
int fd;
char buf[BUFSIZE] = {};
fd = open(DAEMON_FILE, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
syslog(LOG_ERR, "open():%s", strerror(errno));
return -1;
}
if (lockf(fd, F_TLOCK, 0) < 0) {
if (errno == EACCES || errno == EAGAIN) {
// 已被占用
close(fd);
return -1;
}
syslog(LOG_ERR, "lockf():%s", strerror(errno));
exit(1);
}
ftruncate(fd, 0);
snprintf(buf, BUFSIZE, "%d", getpid());
write(fd, buf, strlen(buf));
}
综合程序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 1024
#define FLNAME "/tmp/out"
#define DAEMON_FILE "/var/run/daemon.pid"
static int mydaemon(void);
static int single_instance(void);
int main(void)
{
time_t tm;
struct tm *tmp = NULL;
char buf[BUFSIZE] = {};
FILE *fp = NULL;
// 守护进程
#if 0
if (mydaemon() < 0) {
fprintf(stderr, "daemon failed\n");
exit(1);
}
#endif
// 提交日志
openlog(NULL, LOG_PID | LOG_PERROR, LOG_DAEMON);
#if 1
if (daemon(0, 0) == -1) {
perror("daemon()");
exit(1);
}
#endif
// 单实例
if (single_instance() < 0) {
syslog(LOG_ERR, "single_instance() failed");
exit(1);
}
fp = fopen(FLNAME, "w");
if (NULL == fp) {
// perror("fopen()");
syslog(LOG_ERR, "fopen():%s", strerror(errno));
exit(1);
}
while (1) {
time(&tm);
tmp = localtime(&tm);
if (NULL == tmp) {
// fprintf(stderr, "localtime() failed\n");
syslog(LOG_ERR, "localtime() failed");
goto ERROR;
}
strftime(buf, BUFSIZE, "%Y-%m-%d %H:%M:%S\n", tmp);
fputs(buf, fp);
// 调试
syslog(LOG_INFO, "%s write into the file %s", buf, FLNAME);
fflush(NULL);
sleep(1);
}
fclose(fp);
closelog();
exit(0);
ERROR:
closelog();
fclose(fp);
exit(1);
}
static int mydaemon(void)
{
pid_t pid;
int fd;
pid = fork();
if (pid < 0) {
perror("fork()");
return -1;
}
if (pid > 0)
exit(0);
if (setsid() == -1) {
perror("setsid()");
return -1;
}
// PID == PGID == SID no tty
fd = open("/dev/null", O_RDWR);
if (fd == -1) {
perror("open()");
return -1;
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2)
close(fd);
umask(0);
chdir("/");
return 0;
}
// 单实例
static int single_instance(void)
{
int fd;
char buf[BUFSIZE] = {};
fd = open(DAEMON_FILE, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
syslog(LOG_ERR, "open():%s", strerror(errno));
return -1;
}
if (lockf(fd, F_TLOCK, 0) < 0) {
if (errno == EACCES || errno == EAGAIN) {
// 已被占用
close(fd);
return -1;
}
syslog(LOG_ERR, "lockf():%s", strerror(errno));
exit(1);
}
ftruncate(fd, 0);
snprintf(buf, BUFSIZE, "%d", getpid());
write(fd, buf, strlen(buf));
// close(fd);
}