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. 04 SqlServer

    数据库的注释 –(两个横线) 主键表 外键表 主键,组合主键 SqlServer 使用附加 权限 主文件mdf 日志文件ldf 数据类型 char varchar nchar nvarchar cha ...

  2. HDU 1576 A/B( 逆元水 )

    链接:传送门 思路: 现在给出 n = A % 9973,n = A - A/9973×9973,已知 B|A ,设 A = Bx,可以得到如下形式的式子:Bx + 9973×y = n ,因为gcd ...

  3. python生成器,递归调用

    生成器 什么是生成器:只要在函数体内出现yield关键字,那么再执行函数就不会执行函数代码,会得到一个结果,该结果就是生成器 生成器就是迭代器 yield的功能 yield为我们提供了一种自定义迭代器 ...

  4. Django Rest Framework 简介及 初步使用

    使用Django Rest Framework之前我们要先知道,它是什么,能干什么用? Django Rest Framework 是一个强大且灵活的工具包,用以构建Web API 为什么要使用Res ...

  5. 如何让myeclipse左边选中文件后自动关联右边树

    在左侧项目树的右上角下拉菜单里有link with editor 点击即可

  6. Crontab入门基础

    Crontab入门基础 crontab前言 crontab是Unix和Linux用于设置周期性被执行的指令,是互联网很常用的技术,很多任务都会设置在crontab循环执行,如果不使用crontab,那 ...

  7. STM32 GPIO重映射(转)

    重映射就是将引脚功能重新定义到其他引脚, 例如PA9是USART1-TX默认的 管脚,但是现在PA9用做它用了,那可以将USART1-TX重新映射到PB6,当然这 种映射不是随意的想映射到哪个脚就哪个 ...

  8. 加密中加salt的意思

    所谓加Salt,就是加点“佐料”.当用户首次提供密码时(通常是注册时),由系统自动往这个密码里加一些“Salt值”,这个值是由系统随机生成的,并且只有系统知道.然后再散列.而当用户登录时,系统为用户提 ...

  9. 05006_Linux的jdk、mysql、tomcat安装

    1.软件包下载链接:软件包下载 密码:advk 2.安装JDK (1)查看当前Linux系统是否已经安装java,输入 rpm -qa | grep java : (2)卸载两个openJDK (3) ...

  10. shell 测试命令

    一.使用 test 命令可以对文件.字符串等进行测试,一般配合控制语句使用. 1.字符串测试 test str1 = str2 //测试字符串是否相等 test str1 != str2 //测试字符 ...