daemon_init的实现
引子
守护进程(Daemon)
是一种长期执行特定任务的进程,在编写某些程序(例如Web服务器)时经常使用到守护进程。在Linux
中,需要根据一些约定将普通进程转换为守护进程。每次我们想要使用守护进程时都要进行繁琐的转换,很不方便。所以博主就实现了一个名为daemon_init
的函数,把这些转换步骤都“包装”起来。当我们想要将一个普通进程转换为守护进程时,只需要调用这个函数就行了。
普通进程转换成守护进程的步骤
- 执行
fork()
,父进程退出,子进程继续执行。
让Shell以为程序已经执行完成。
确保子进程不是进程组的首进程。
- 子进程调用
setsid()
开启一个新会话并释放它与控制终端的所有关联关系。
- 再执行一次
fork()
,确保子进程不会成为会话组长。
- 清除进程的
umask
。
- 修改进程的当前目录为
根目录
,防止文件系统无法卸载。
- 关闭父进程的所有打开的文件描述符。
- 将文件描述符
0
,1
和2
指向/dev/null
。
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #ifndef DAEMON_H #define DAEMON_H
#define DAEMON_RESERVE_UMASK 1 #define DAEMON_NO_CHDIR 2 #define DAEMON_NO_CLOSE_FILES 4 #define DAEMON_NO_REOPEN_STD_FDS 8
extern int daemon_init(int flags);
#endif
|
之所以要有一个flags
参数,是因为对转换过程进行控制。例如,有些守护进程不需要关闭所有文件描述符,则flags就可以设置为DAEMON_NO_CLOSE_FILES
。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| int daemon_init(int flags) { switch (fork()) { case -1: return -1; case 0: break; default: exit(EXIT_SUCCESS); } if (setsid() == -1) { return -1; } switch (fork()) { case -1: return -1; case 0: break; default: exit(EXIT_SUCCESS); } if (!(flags & DAEMON_RESERVE_UMASK)) { umask(0); }
if (!(flags & DAEMON_NO_CHDIR)) { if (chdir("/")) { return -1; } }
if (!(flags & DAEMON_NO_CLOSE_FILES)) { int max_fd = sysconf(_SC_OPEN_MAX); if (max_fd == -1) { max_fd = DAEMON_MAX_FD; } for (int fd = 0; fd < max_fd; fd++) { close(fd); } }
if (!(flags & DAEMON_NO_REOPEN_STD_FDS)) { if (open("/dev/null", O_RDONLY) != STDIN_FILENO) { return -1; } if (open("/dev/null", O_WRONLY) != STDOUT_FILENO) { return -1; } if (open("/dev/null", O_WRONLY) != STDERR_FILENO) { return -1; } }
return 0; }
|
参考文献
- 《Linux/UNIX系统编程手册》
- 《UNIX环境高级编程》
- Linux高级编程-守护进程