daemon
守护进程
特点:后台运行,没有控制终端。
常见的系统守护进程:
keventd:为在内核中运行 计划执行的函数 提供进程上下文。
kapmd:对计算机系统中具有的高级电源管理提供支持。
kswapd(pageout daemon):页面调出守护进程,它通过将脏页面以低速写到磁盘上,从而使这些页面在需要时仍可回收使用。即页面回收。
bdflush:将脏缓冲区从缓冲池(cache buffer)中冲洗到磁盘上。即cache回收。
Kupdated:每隔一定时间间隔,将脏页面冲洗到磁盘上,以便系统在失效时减少丢失的数据。
portmap:端口映射,将RPC(Remote procedure call,远程过程调用)程序号映射为网络端口号。
syslogd:记录系统日志
xinetd:侦听系统网络接口,以便取得来自网络的对各种网络服务进程的请求。
nfsd,lockd,rpciod提供对网络文件系统的支持。
cron:在指定的日期和时间执行指定的命令。
大多数守护进程都以超级用户(用户id为0)特权运行。没有一个守护进程具有控制终端,其终端名设置为问号(?),终端前台进程组ID设置为-1.
父进程ID为0的各进程通常为内核进程,进程1通常是init。
编写守护进程的步骤:
1. 设置文件模式创建屏蔽字;
umask(0);清除文件模式创建屏蔽字
2. fork;
调用fork(),接着让父进程退出
3. setsid;
setsid()设置session leader and process group leader
4. chdir;
chdir("/"),更改当前工作目录为根目录
5. close fd;
关闭从父进程进程的文件描述符
6. 打开/dev/null使其具有fd 0 1 2;
禁掉交互
禁掉fd 0,1,2后,进程出错怎样跟踪呢,可以通过syslog记录守护进程的信息。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <sys/resource.h> 6 #include <unistd.h> 7 #include <fcntl.h> 8 #include <string.h> 9 10 #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) 11 12 int main(int argc, char** argv) 13 { 14 int i, fd0, fd1, fd2, fd; 15 pid_t pid; 16 pid_t sid; 17 struct rlimit rl; 18 char acCwd[128]={0}; 19 20 //1. clear file creation mask 21 umask(0); 22 //2. fork() 23 pid = fork(); 24 if (pid < 0) 25 { 26 printf("fork error\n"); 27 exit(1); 28 } 29 else if (pid != 0) 30 exit(0); 31 32 printf("child process ID:%d\n", getpid()); 33 //3. setsid() 34 sid = setsid(); 35 printf("child process session ID:%d\n", sid); 36 //4. chdir() 37 printf("cwd real len:%d", getcwd(acCwd, 128)); 38 printf("\npath:%s\n", acCwd); 39 if (chdir("/") < 0) 40 { 41 printf("can't change current work dir\n"); 42 exit(1); 43 } 44 memset(acCwd, '\0', 128); 45 getcwd(acCwd, 128); 46 printf("after chdir, current work dir:%s\n", acCwd); 47 48 //5. close all open file descriptor 49 if (0 != getrlimit(RLIMIT_NOFILE, &rl)) 50 { 51 printf("get max number of fd failed\n"); 52 exit(1); 53 } 54 55 if (rl.rlim_max == RLIM_INFINITY) 56 rl.rlim_max = 1024; 57 for (i = 0; i < rl.rlim_max; ++i) 58 close(i); 59 60 //6. attach file descriptors 0 1 2 to /dev/null 61 //way 1: 62 fd0 = open("/dev/null", O_RDWR); 63 fd1 = dup(0); 64 fd2 = dup(0); 65 66 //way 2: 67 //fd = open("/dev/null", O_RDWR); 68 //dup2(fd, 1); 69 //dup2(fd, 2); 70 71 //7.initialize log file 72 openlog("frank-daemon", LOG_CONS, LOG_DAEMON); 73 if (fd0 != 0 | fd1 != 1 || fd2 != 2) 74 { 75 syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2); 76 exit(1); 77 } 78 79 80 while(1) 81 sleep(999); 82 83 return 0; 84 } //这一段代码存在的一个隐含问题是,子进程先执行而父进程并没有结束。所以需要有方式保证让父进程先终止
第二次fork()是可选的,其作用是确保不会打开控制终端。
因为打开一个控制终端的条件是该进程必须是会话组长(session leader),而通过二次fork后,确保第二次fork出来的子进程不是session leader.
下面个函数是调用两次fork函数的daemon初始化函数:
1 void daemonize2(const char* cmd) 2 { 3 int i, fd0, fd1, fd2, fd; 4 pid_t pid; 5 pid_t sid; 6 struct rlimit rl; 7 8 pr_ids("xxxxxxxx"); 9 //1. clear file creation mask 10 umask(0); 11 //2. fork() 12 pid = fork(); 13 if (pid < 0) 14 { 15 printf("fork error\n"); 16 exit(1); 17 } 18 else if (pid > 0)//parent 19 { 20 pr_ids("parent"); 21 exit(0); 22 } 23 24 sleep(1);//sleep的作用是让父进程先终止 25 printf("child process ID:%d\n", getpid()); 26 //3. setsid() 27 sid = setsid(); 28 printf("child process session ID:%d\n", sid); 29 pr_ids("child"); 30 31 //ensure future opens won't allocate controlling TTYs 32 pid = fork(); 33 if (pid < 0) 34 { 35 printf("fork error\n"); 36 exit(1); 37 } 38 else if (pid > 0)//parent 39 { 40 exit(0); 41 } 42 sleep(1);//sleep的作用是让父进程先终止 43 44 pr_ids("grandchild"); 45 46 //4. chdir() 47 if (chdir("/") < 0) 48 { 49 printf("can't change current work dir\n"); 50 exit(1); 51 } 52 53 //5. close all open file descriptor 54 if (0 != getrlimit(RLIMIT_NOFILE, &rl)) 55 { 56 printf("get max number of fd failed\n"); 57 exit(1); 58 } 59 60 if (rl.rlim_max == RLIM_INFINITY) 61 rl.rlim_max = 1024; 62 for (i = 0; i < rl.rlim_max; ++i) 63 close(i); 64 65 //6. attach file descriptors 0 1 2 to /dev/null 66 //way 1: 67 fd0 = open("/dev/null", O_RDWR); 68 fd1 = dup(0); 69 fd2 = dup(0); 70 71 //way 2: 72 //fd = open("/dev/null", O_RDWR); 73 //dup2(fd, 1); 74 //dup2(fd, 2); 75 76 //7. initialize the log file 77 openlog(cmd, LOG_CONS|LOG_PID, LOG_DAEMON); 78 if (fd0 != 0 | fd1 != 1 || fd2 != 2) 79 { 80 syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0 , fd1, fd2); 81 exit(1); 82 } 83 84 pr_ids("grandchild"); 85 }
保证父进程先终止的运行结果:
没有保证父进程先终止的运行结果: