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. 编程规则

  • 在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用。

    1. 调用umask将文件模式创建屏蔽字设置为一个已知值(通常为0)。
    2. 调用fork,然后使父进程exit。因为:第一,如果守护进程是通过shell启动的,这可以让shell认为这条命令已经执行完毕;第二,子进程继承了父进程的进程组ID,但获得一个新的进程ID,这保证了子进程不是一个进程组的组长进程。这是setsid的先决条件。
    3. 调用setsid创建一个新会话。使调用进程:a. 成为新会话的首进程;b. 成为一个新进程组的组长进程;c. 没有控制终端
    4. 将当期工作目录更改为根目录,以免占有某文件系统,使得其不能被卸载。或者,某写守护进程还可能把当前工作目录更改到某个指定位置。
    5. 关闭不再需要的文件描述符。使守护进程不再持有从其父进程继承来的任何文件描述符。可以使用open_max函数或getrlimit函数来判定最高文件描述符的值,并关闭直到该值的所有描述符。
    6. 某些守护进程打开/dev/null使其具有文件描述符0、1、2 。这样,任何一个试图读标准输入、写标准输出或标准错误的库例程都不会产生任何效果。

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

    1. 内核例程可以调用log函数。
    2. 大多数用户进程(守护进程)调用syslog函数来产生日志消息。
    3. 无论一个用户进程是在此主机上,还是在通过TCP/IP网络连接到此主机的其他主机上,都可将日志消息发送到UDP端口514 。
#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系统中,守护进程遵循以下通用惯例:

    1. 若守护进程使用锁文件,那么该文件通常存储在/var/run目录中。守护进程可能需要具有超级用户权限才能在此目录中创建文件。锁文件的名字通常是name.pid。
    2. 若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。配置文件的名字通常是name.conf。
    3. 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一启动的。
    4. 某些守护进程捕捉SIGHUP信号,当它们接收到该信号时,重新读配置文件。

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

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

《Unix环境高级编程》读书笔记 第13章-守护进程的更多相关文章

  1. [置顶] 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  2. APUE读书笔记-第13章-守护进程

    第13章 守护进程 13.1 引言 *守护进程也称精灵进程(daemon)是生存期较长的一种进程.它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNI ...

  3. unix环境高级编程-读书笔记与习题解答-第一篇

    从这周开始逐渐的进入学习状态,每天晚上都会坚持写c程序,并且伴随对这本书的深入,希望能写出更高质量的读书笔记和程序. 本书的第一章,介绍了一些关于unix的基础知识,在这里我不想去讨论linux到底是 ...

  4. unix 环境高级编程-读书笔记与习题解答-第二篇

    第四节 输入与输出 上次的笔记中写到的 open, read, write, lseek 以及close ,都是不带缓存的IO函数,这些函数都使用文件描述符进行工作. 上一篇笔记用到的 read(ST ...

  5. [置顶] 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  6. unix 环境高级编程 读书笔记与习题解答第四篇

    第一章 第六节 第一小节 这一章没有程序设计和API方面的深入学习,而是注重介绍了unix操作系统中的原始数据类型和系统原型函数,错误处理方面的知识. ____unistd.h____ 该文件包含了u ...

  7. unix进程的环境--unix环境高级编程读书笔记

    http://blog.csdn.net/xiaocainiaoshangxiao/article/category/1800937

  8. unix环境高级编程 读书笔记

    1.上班业余时间把书下载下来,第一章读完了,但是程序只能回家运行啦!Fighting!

  9. Unix环境高级编程学习笔记——fcntl

    写这篇文正主要是为了介绍下fcntl,并将我自己在学习过程中的一些理解写下来,不一定那么官方,也有错误,希望指正,共同进步- fcntl: 一个修改一打开文件的性质的函数.基本的格式是 int fcn ...

随机推荐

  1. vue中采用axios发送请求及拦截器

    这几天在使用vue中axios发送get请求的时候很顺手,但是在发送post请求的时候老是在成功的回调函数里边返回参数不存在,当时就纳闷了,经过查阅资料,终于得到了解决方案,在此做一总结: 首先我们在 ...

  2. function——函数声明头的提升和预解析

    函数: 即function语句的集合,就是将多个语句封装到一起: 函数的执行要会自己遍历,遇见函数 a():执行语句,就要移交控制权,函数执行完毕之后,控制权又移交回来了! 函数的参数要罗列在func ...

  3. Linux中设置vim自动在运算符号两边加上空格

    vim中设置自动在=+-之类的运算符号左右两边加上空格.原版的vim不带这个功能,写出的代码例如z=x+y,不美观,很多编译器(如VS)能够自动在符号两边加上空格,如z = x + y,看起来比较美观 ...

  4. 搭建svn服务器(ubuntu)

    ubuntu搭建svn服务器 环境:ubuntu 12.04.5 apt-get install subversion 找个目录作为svn的仓库 mkdir svn svnadmin create s ...

  5. Unity 获取坐标函数 坐标转换函数

    获取世界坐标:transform.position 获取本地坐标:transform.localPosition 获取鼠标坐标:Input.mousePosition 获取手指触摸区域坐标:Input ...

  6. addEventListener()与removeEventListener(),追加事件和删除追加事件

    addEventListener()与removeEventListener()用于追加事件和删除追加.所有的DOM节点中都包含这两种方法,并且它们都接受3个参数:要处理的事件名.作为事件处理程序的函 ...

  7. 根据 thread id 停止一个线程

    出自 https://github.com/Bogdanp/dramatiq/blob/master/dramatiq/middleware/threading.py#L62 thread_id = ...

  8. 如何设置,获取,删除cookie?

    cookie : 存储数据,当用户访问了某个网站(网页)的时候,我们就可以通过cookie来像访问者电脑上存储数据 1.不同的浏览器存放的cookie位置不一样,也是不能通用的 2.cookie的存储 ...

  9. WePy--使用zanUI组件

    因为Wepy 中不能直接引入zanUI组件, 好在还有百度, 参考链接 https://github.com/brucx/wepy-zanui-demo (感谢); 我的做法是将 源码下了下来, 源码 ...

  10. Linux系统_Ubuntu中Hadoop常用命令

    ctrl+alt+t打开终端窗口sudo useradd -m hadoop -s/bin/bash创建新用户ctrl+alt回到自己的笔记本 创建hadoop用户sudo useradd -m ha ...