《Unix环境高级编程》读书笔记 第13章-守护进程,守护进程


1. 引言

  • 守护进程是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。它们没有控制终端,在后台运行。
  • 本章说明守护进程结构、如何编写守护进程程序、守护进程如何报告出错情况。

2. 守护进程的特征

  • 基于BSD的系统下执行:ps -axj

    -a 显示由其他用户所拥有的进程的状态;-x 显示没有控制终端的进程状态;-j 显示与作业有关的信息

  • 基于System V的系统下执行:ps -efj
  • Linux下执行以上两个命令输出一致
  • 常见的守护进程:
  • kswapd,内存换页守护进程。
  • flush守护进程在可用内存达到设置的最小阀值时将脏页面冲洗至磁盘。
  • sync_supers守护进程定期将文件系统元数据冲洗至磁盘。
  • jbd守护进程帮助实现了ext4文件系统中的日志功能。
  • rpcbind守护进程提供了将远程过程调用程序号映射为网络端口号的服务。
  • rsyslogd守护进程可以被由管理员启用的将系统消息记入日志的任何程序使用。
  • inetd守护进程。超级因特网服务进程。
  • crond守护进程在定期安排的日期和时间执行命令。
  • atd守护进程,允许用户在指定的时间执行任务,但是每个任务只执行一次。
  • sshd守护进程提供了安全的远程登录和执行设施。
  • cupsd守护进程,打印假脱机进程,处理对系统提出的各个打印请求。

3. 编程规则

  • 在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用。
    
    
#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);
  }
}

4. 出错记录

    

  • 有以下3种产生日志消息的方法:
    
    
#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

5. 单实例守护进程

  • 为了正常运作,某些守护进程会实现为,在任一时刻只运行该守护进程的一个副本。
  • 文件和记录锁机制提供了一种保证一个守护进程只有一个副本在运行的方法。
#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); }

6. 守护进程的惯例

  • 在Unix系统中,守护进程遵循以下通用惯例:
    
    

7. 客户进程-服务器进程模型

  • 守护进程常常用作服务器进程。
  • 一般而言,服务器进程等待客户进程与其联系,提出某种类型的服务要求。
  • 在服务器进程中调用fork然后exec另一个程序来项客户进程提供服务是很常见的。服务器进程通常管理着多个文件描述符:通信端点、配置文件、日志文件和类似的文件。为保证安全,可设置所有对于被执行程序不需要的文件描述符的执行关闭标志close-on-exec。

    原创文章,转载请声明出处:http://www.cnblogs.com/DayByDay/p/3948402.html

对于守护进程与启动程序脚本的教,

1、守护进程就是用来在执行不需要人干预的任务的。一般Linux需要在/etc/init.d/也可能是/etc/rc.d/init.d/下放启动脚本,然后在/etc/rcX.d/也可能是/etc/rc.d/rcX.d里做之前放在init.d里的脚本软连接,X是启动级别。2、首先你写的程序应该是一个在没有接收到结束信号时应该是无限循环,不会自动结束。至于脚本,其实你直接在/etc/rc.local里加上一句 /path/to/youapp &就可以开机自动启动了。
 

错误提示


相关内容