course:nctu-高等unix程式設計:chapter13
目錄表
Daemon Processes
0x00 Outline
- Introduction
- Daemon conventions
- Client-Server model
- Coding rules
- Error logging
- Single-instance daemon
0x01 Introduction
- daemon 一般會在開啟時就啟動,直到關機才終止
- daemon 沒有 controlling terminal,在 background 執行
- 可以使用
ps ajx
查看,有 [ ] 包圍的是 kernel process
0x02 Daemon conventions
- 如果 daemon 有使用 lock file,通常會存放在 /var/run/xxx.pid
- 如果 daemon 支援 configuration options,則他的設定檔通常放在 /etc 底下
- daemon 也可以在 command line 啟動,但通常在開機時就會被 etc/rc* or /etc/init.d/* 執行
- 如果 daemon 有設定檔,通常 daemon 只會在開始執行時讀取一次,有些 daemon 則會抓取 SIGHUP signal,並重新讀取 config
0x03 Client-Server model
- 一般 daemon process 會用在 server 上
- server daemon 會等待 client 連線
- server 和 client 間的 communication channel 可以是 one- way 也可以是 two-way
0x04 Coding rules
- 使用 umask 設置 daemon 若建立檔案時所要求的正確權限
- 呼叫 fork 然後 exit,一來不會 block shell,二來不會成為 process leader,如此才能成為 session leader
- 呼叫 setsid,如此如此能成為一個新的 session leader 和 process leader,且沒有 controlling terminal
- 改變 current working directory
- 關閉不使用的 file descriptors,並將 file descriptors 0,1,2 重導到 /dev/null,忽略 stdin,stdout,stderr 對 daemon 的影響
- 設定 logfile 或 system log 處理 daemon output
#include "apue.h" #include <syslog.h> #include <fcntl.h> #include <sys/resource.h> void daemonize(const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; /* * Clear file creation mask. */ umask(0); /* * Get maximum number of file descriptors. */ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) err_quit("%s: can't get file limit", cmd); /* * Become a session leader to lose controlling TTY. */ if ((pid = fork()) < 0) err_quit("%s: can't fork", cmd); else if (pid != 0) /* parent */ exit(0); setsid(); /* * Ensure future opens won't allocate controlling TTYs. */ 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", cmd); if ((pid = fork()) < 0) err_quit("%s: can't fork", cmd); else if (pid != 0) /* parent */ exit(0); /* * Change the current working directory to the root so * we won't prevent file systems from being unmounted. */ if (chdir("/") < 0) err_quit("%s: can't change directory to /", cmd); /* * Close all open file descriptors. */ if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024; for (i = 0; i < rl.rlim_max; i++) close(i); /* * Attach file descriptors 0, 1, and 2 to /dev/null. */ fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); /* * Initialize the log file. */ 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); } }
0x05 Error Logging
- 因為 daemon 沒有controlling terminal,因此無法寫 error message 到 stdout
- daemon 不該輸出訊息到 console
- 系統管理者會更偏好有中央通一管理所有 daemon 的 log
- BSD 有 syslog,而 Linux 則有 rsyslogd(reliable and extended syslogd)
#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); /* Returns: previous log priority mask value */
- call openlog 是可選用而非必需,在第一次呼叫 syslog 時 openlog 會自動被呼叫
- call closelog 也是可選用的,他僅僅簡單關閉 file descriptor
- openlog option 有一些處理 syslog 的選項可用
- LOG_CONS
- LOG_NDELAY
- LOG_NOWAIT
- LOG_ODELAY
- LOG_PERROR
- LOG_PID
- facility 則可指定一些 log 的來源
- LOG_AUTH
- LOG_AUTHPRIV
- LOG_CRON
- LOG_DAEMON
- LOG_FTP
- LOG_KERN
- LOG_LPR
- LOG_MAIL
- LOG_NEWS
- LOG_SYSLOG
- LOG_USER
- LOG_UUCP
- LOG_LOCAL0 ~ LOG_LOCAL7
- syslog 的 priority 則可指定紀錄 log 的等級,以下由高到低排列
- LOG_EMERG
- LOG_ALERT
- LOG_CRIT
- LOG_ERR
- LOG_WARNING
- LOG_NOTICE
- LOG_INFO
- LOG_DEBUG
Example:
openlog("lpd", LOG_PID, LOG_LPR); syslog(LOG_ERR, "open error for %s: %m", filename);
- %m 用來印出 errno 對應的 error string
0x06 Single-Instance Daemons
- 有些 daemon 會設計成一次只允許一隻 process 執行,這時候一般會使用到 lockfile,這部分在 Advanced I/O 會有更詳細的說明
#include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <syslog.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) extern int lockfile(int); int already_running(void) { int fd; char buf[16]; fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE); if (fd < 0) { syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno)); exit(1); } if (lockfile(fd) < 0) { if (errno == EACCES || errno == EAGAIN) { close(fd); return(1); } syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno)); exit(1); } ftruncate(fd, 0); sprintf(buf, "%ld", (long)getpid()); write(fd, buf, strlen(buf)+1); return(0); }
course/nctu-高等unix程式設計/chapter13.txt · 上一次變更: 由 127.0.0.1