守护进程(daemon)是生存期较长的一种进程,在系统开启时启动,关闭时才终止,没有控制终端在后台运行。
守护进程结构:
依赖操作系统实现,以超级用户特权运行,无控制终端,无命令行,大多数守护进程的父进程是init进程。
常见的守护进程:
init:负责启动各运行层次特定的系统任务。
kswapd:页面调出守护进程,通过脏页面以低速写到磁盘上从而使这些页面在需要时可回收使用。
bdflush和kupdated:将高速缓存页面冲洗到磁盘上。
portmap:将远程过程调用映射为网络端口号的服务。
syslog:可由帮助操作人员把系统消息记入日志的任何程序使用。
inited:侦听系统网络接口,以便取得来自网络的对各种网络服务进程的请求。
编写守护进程:步骤:
1. 创建子进程,父进程退出:所有工作在子进程中进行,形式上脱离控制终端。
2. 在进程中创建新会话:setsid()函数,使子进程完全独立,脱离控制。
3. 修改当前目录为根目录:chdir()函数,防止占用可卸载的文件系统,也可换成其他路径。
4. 重设文件权限掩码:unmask函数,防止继承的文件创建屏蔽字拒绝某些权限,增加守护进程灵活性
5. 关闭不再需要的文件描述符:继承的打开文件不会用到,浪费系统资源无法卸载。
for (i = 0 ; i 关闭打开的文件描述符close ( i ))
范例程序如下:
- void daemonize(const char *cmd)
- {
- int i, fd0, fd1, fd2;
- pid_t pid;
- struct rlimit rl;
- struct sigaction sa;
-
- umask(0);
-
-
- if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
- err_quit("%s: can't get file limit", cmd);
-
-
- if ((pid = fork()) < 0)
- err_quit("%s: can't fork", cmd);
- else if (pid != 0)
- exit(0);
- setsid();
-
-
- sa.sa_handler = SIG_IGN;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- err_quit("%s: can't ignore SIGHUP");
- if ((pid = fork()) < 0)
- err_quit("%s: can't fork", cmd);
- else if (pid != 0)
- exit(0);
-
-
- if (chdir("/") < 0)
- err_quit("%s: can't change directory to /");
-
-
- if (rl.rlim_max == RLIM_INFINITY)
- rl.rlim_max = 1024;
- for (i = 0; i < rl.rlim_max; i++)
- close(i);
-
-
- fd0 = open("/dev/null", O_RDWR);
- fd1 = dup(0);
- fd2 = dup(0);
-
-
- openlog(cmd, LOG_CONS, LOG_DAEMON);
- if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
- syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
- fd0, fd1, fd2);
- exit(1);
- }
- }
-
- #include<unsitd.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include<signal.h>
- #include<sys/types.h>
- #include<sys/resource.h>
- #include<sys/stat.h>
-
-
- void init ()
- {
- pid_t pid ;
- int i ;
- struct rlimit rl ;
- struct sigaction sa ;
-
-
- if(getrlimit(RLIMIT_NOFILE , &rl) < 0)
- {
- printf("con't get file limit") ;
- }
- if((pid = fork()) < 0)
- err_quit("%s: can't fork" , cmd);
- else if (pid != 0)
- exit(0);
-
- setsid();
-
-
- if((pid = fork()) < 0)
- err_quit("%s:can't fork", cmd);
- else if (pid != 0)
- exit(0);
-
-
-
- if(rl.rlim_max == RLM_INFINITY)
- rl.rlim_max = 1024;
- for(i = 0; i < rl.rlim_max; i++)
- close(i);
- chdir("/tmp");
- unmask(0);
- return;
- }
- int main ()
- {
- FILE *fp ;
- FILE *fstream;
- signal(SIGCHLD, SIG_IGN);
-
- init() ;
- while(1)
- {
-
- fstream = open("ps -eo pid,user,comm,lstart,etime>time.txt","r");
- if(fstream==NULL)
- {
-
- if((fp = fopen("error.log", "a+")) != NULL)
- {
- fprintf(fp, "%s\n", "执行命令失败");
- fclose(fp);
- }
- else
- exit(1);
- }
- else
- pclose(fstream);
- sleep(120);
- }
- return 0;
- }
守护进程报告出错情况:
守护进程没有控制终端,所以不是简单的写到标准输出上,而是使用syslog设施,有三种方法产生日至消息:1)内核例程调用log函数;2)进程调用syslog函数产生日志消息;3)此主机上的一个用户进程可将日志消息发向UDP端口514,然后,syslogd守护进程读取三种格式的日志信息,根据配置文件(一般是/etc/syslog.conf)决定不同种类的信息应送往何处,守护进程通常使用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 maskpri);