7/*********************************************************************** * * 开发守护进程 * **********************************************************************/ #include #include #include #include #include #include #include #include #include // use getrlimit#include // use sigaction#include // 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;}