守护进程
编程步骤:
1、umask将屏蔽字改为已知值(通常为0)
2、调用fork然后师父进程exit。(子进程继承父进程的组ID,子进程获得新的ID,并且不是组长。)
3、setsid创建新的会话。(做了三件事:成为新会话首进程、成为新进程组的组长、没有控制终端。)
忽略掉SIGHUP信号,这个信号和控制终端有关。
再次fork并且使父进程exit,使守护进程不是会话首进程,防止取得控制终端。因为SVR4中:如果进程是会话首进程,并且没有控制终端,当该首进程打开终端设备时,如果这个终端不是其他会话的控制终端,这个终端就会自动成为该会话的控制终端。所以第二次fork使得守护进程不是会话首进程,就无法取得控制终端。
4、将当前工作目录改为根目录(从父进程继承的工作目录可能在一个挂载文件系统中。)
5、关闭不需要的文件描述符。
6、打开/dev/null使其具有0,1,2文件描述符。
实验:创建守护进程
1 #include <stdio.h>
2 #include "apue.h"
3 #include <sys/resource.h>
4 #include <signal.h>
5 #include <syslog.h>
6 int main()
7 {
8 pid_t pid;
9 pid_t fd0,fd1,fd2;
10 struct sigaction sa;
11 struct rlimit rl;
12 if(getrlimit(RLIMIT_NOFILE,&rl)<0)
13 {
14 perror("getrlimt");
15 exit(1);
16 }
17 umask(0);
18 pid = fork();
19 if(pid<0)
20 {
21 perror("fork");
22 exit(2);
23 }
24 else if(pid>0)
25 {
26 exit(3);
27 }
28 setsid();
29 sa.sa_flags=0;
30 sa.sa_handler=SIG_IGN;
31 sigemptyset(&sa.sa_mask);
32 if(sigaction(SIGHUP,&sa,NULL)<0)
33 {
34 perror("sigaction");
35 exit(4);
36 }
37 if((pid=fork())<0)
38 {
39 perror("fork");
40 exit(5);
41 }
42 else if(pid>0)
43 {
44 exit(0);
45 }
46 if(chdir("/")<0)
47 {
48 perror("chdir");
49 exit(6);
50 }
51 if(rl.rlim_max==RLIM_INFINITY)//限制值可能是一个“无穷大”值:RLIM_INFINITY。
52 rl.rlim_max=1024;
53 for(int i=0;i<rl.rlim_max;i++)
54 {
55 close(i);
56 }
57 fd0 = open("/dev/null",O_RDWR);
58 fd1 = dup(fd0);
59 fd2 = dup(fd0);
60 while(1);
61 }
出错记录
1、没有终端,因此错误没法输出。
2、每个守护进程输出到一个文件,比较乱,不好管理。
3、syslog设施。
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int mask);
介绍如何使自己的程序输出信息到日志文件
环境ubantu
1、找到syslog.h函数的头文件
/usr/include/x86_64-linux-gnu/sys里面有syslog.h头文件
下面是这个头文件的一部分内容:一会儿要用到
CODE prioritynames[] =
75 {
76 { "alert", LOG_ALERT },
77 { "crit", LOG_CRIT },
78 { "debug", LOG_DEBUG },
79 { "emerg", LOG_EMERG },
80 { "err", LOG_ERR },
81 { "error", LOG_ERR }, /* DEPRECATED */
82 { "info", LOG_INFO },
83 { "none", INTERNAL_NOPRI }, /* INTERNAL */
84 { "notice", LOG_NOTICE },
85 { "panic", LOG_EMERG }, /* DEPRECATED */
86 { "warn", LOG_WARNING }, /* DEPRECATED */
87 { "warning", LOG_WARNING },
88 { NULL, -1 }
89 };
#ifdef SYSLOG_NAMES
122 CODE facilitynames[] =
123 {
124 { "auth", LOG_AUTH },
125 { "authpriv", LOG_AUTHPRIV },
126 { "cron", LOG_CRON },
127 { "daemon", LOG_DAEMON },
128 { "ftp", LOG_FTP },
129 { "kern", LOG_KERN },
130 { "lpr", LOG_LPR },
131 { "mail", LOG_MAIL },
132 { "mark", INTERNAL_MARK }, /* INTERNAL */
133 { "news", LOG_NEWS },
134 { "security", LOG_AUTH }, /* DEPRECATED */
135 { "syslog", LOG_SYSLOG },
136 { "user", LOG_USER },
137 { "uucp", LOG_UUCP },
138 { "local0", LOG_LOCAL0 },
139 { "local1", LOG_LOCAL1 },
140 { "local2", LOG_LOCAL2 },
141 { "local3", LOG_LOCAL3 },
142 { "local4", LOG_LOCAL4 },
143 { "local5", LOG_LOCAL5 },
144 { "local6", LOG_LOCAL6 },
145 { "local7", LOG_LOCAL7 },
146 { NULL, -1 }
147 };
148 #endif
2、/var/log里面是各种日志文件和目录,日志都在这里面找
3、修改配置文件:
找到/etc/rsyslog.conf
看到有一行信息:$IncludeConfig /etc/rsyslog.d/*.conf (说明了配置文件的位置)
进入/etc/rsyslog.d/
里面两个文件:20-ufw.conf 50-default.conf
配置信息存在于50-default.conf中
在这个配置文件添加下面一行:
user.debug /var/log/mylog.log(格式:facility.level action)
重启rsyslogd /etc/init.d/rsyslog restart
解释:
facility.level action//之间是TAB键分隔(action可以制定一个文件,systlog输出的信息就在这个文件中)
void syslog(int priority, const char *format, ...);//priority是facility.level 的组合
4、编写代码测试
1 #include <stdio.h>
2 #include <syslog.h>
3 int main()
4 {
5 openlog("test",LOG_CONS | LOG_PID,0);
6 syslog(LOG_USER | LOG_DEBUG,"%s\n","hello");
7 closelog();
8 return 0;
9 }
10
运行:
可以看到在 /var/log/mylog.log中出现下面信息
Sep 13 21:58:19 myname-HP-Pavilion-14-Notebook-PC test[23764]: helllllllooooo
单实例守护进程:
建立一个文件,加一把写锁。
测试守护进程是否在运行,尝试给锁文件加锁就能测试。
守护进程的惯例:
配置文件被修改了,重启守护进程比较麻烦
因此可以让守护进程接收SIGHUP信号(守护进程没有控制终端,一般不会收到这个信号),重新读取配置文件。
sigwait() :提供了一种等待信号的到来,以串行的方式从信号队列中取出信号进行处理的机制。
sigwait()只等待函数参数中指定的信号集,即如果新产生的信号不在指定的信号集内,则 sigwait()继续等待。
int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset)用作在主调线程里控制信号掩码。
执行时关闭:
服务器fork exec提供服务。
exec后,原来的文件描述符不关闭(在不设置close_on_exec位的情况下),设置执行时关闭,可以防止被执行程序的恶意行为。
如果对文件描述符设置了FD_CLOEXEC,exec后,文件描述符被关闭,fork出来的子进程中,该文件描述符不关闭,仍可使用。