进程篇——守护进程

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《Linux网络编程》、《linux系统编程》、《Unix环境高级编程》P408

什么是守护进程?

  守护进程又叫精灵进程,是一种生存期较长的进程,用于执行日常事务活动。
  常在系统自举时启动,仅在系统关闭时才终止,不与任何控制终端相关联(tty = ?),故在后台运行。
  它们一般以root用户运行或者其他特殊的用户(例如 apache和postfix),并处理一些系统级的任务。习惯上守护进程的名字通常以d结尾(就像crond 和 sshd),但这不是必须的,甚至不是通用的。

名字来源
  来源于麦克斯韦妖(Maxwell’s demon),它是物理学家JamesMaxwell在1867年进行的一个思想实验。Daemon这个词也是希腊神话中的鬼怪,它存在与人类和神之间,拥有占卜的能力。与Judeo-Christian神话中的daemon不同,希腊神话中的daemon不是邪恶的。实际上,神话中的daemon是神的助手,做一些奥林匹斯山的居民自己不愿做的事——很像Unix中的守护进程,完成前台用户不愿做的事。

unix系统中一些守护进程

  1.init进程 = 系统守护进程,负责启动各执行层次特定系统服务。(这些服务在各自拥有守护进程帮助下实现)
  2.keventd守护进程 ,内核中运行计划执行的函数提供进程上下文。
  3.kapmd守护进程,对高级电源管理提供支持。
  4.kswapd守护进程 = 页面调出守护进程,将脏页面以低速写到磁盘上是的这些页面在需要时仍可回收使用。
  5.bdflush和kupdated守护进程,将高速缓存中的数据冲洗到磁盘上。
  6.inetd守护进程,侦听系统网络接口,获取网络服务进程的请求。

守护进程的两个基本要求:

  ①必须是init进程的子进程。
  ②不与任何控制终端相关联。

守护进程的编程规则:

  1.调用umask()将文本模式创建屏蔽字设置为0。
     由继承得来的文件模式创建屏蔽字可能会拒绝设置某些文件。
  2.调用fork()创建新的进程
    该进程会是将来的守护进程。
  3.父进程中调用exit()
    保证祖父进程(守护进程的祖父进程)确认父进程已结束。还保证了父进程不再继续运行,守护进程不是进程组的组长进程。最后一点是顺利完成以下步骤的前提。
  4.调用setsid()使得守护进程有一个新进程组和新会话,两者都把它作为首进程
    保证它不会与控制终端相关联(因为进程刚刚创建了新的会话,同时也就不会为其关联一个控制终端)。
  5.调用chdir()将当前工作目录改为根目录。
    因为前面调用fork()创建新进程,它所继承的当前工作目录可能在文件系统中任何地方。而守护进程通常在系统启动时运行,同时不希望一些随机目录保持打开状态,也就阻止了管理员卸载守护进程工作目录所在的那个文件系统。
  6.关闭所有的文件描述符。
    不需要继承任何打开的文件描述符,对于无法确认的文件描述符,让它们继续处于打开状态。
    可使用open_max()getrlimit()判定最高文件描述符值,并关闭直到该值的所有描述符。
  7.某些守护进程打开/dev/null使其具有文件描述符0、1、2
    这样,任何一个试图读标准输入,写标准输出或标准出错的库示例不会产生任何效果。因为守护进程并不与终端设备相关联,故不能在终端设备上显示其输出,也无处从交互式用户处接收输入。

初始化守护进程的代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>

int main(int argc, char *argv[])
{
    /* STEP 0:变量声明 */
    int i;
    pid_t pid;
    struct rlimit rl;
    /* STEP 1 :清除文件模式创建屏蔽字为设置为0 */
    umask(0);

    /* STEP 2 :创建新进程 */
    pid = fork();
    if ( pid  < 0)                                          //创建出错
        fprintf(stderr, "can't fork");

   /* STEP 3 :结束父进程 */
    if (pid > 0)                                            //父进程
        return -1;

    /* STEP 4 :创建新会话 */
    if (setsid() == -1) 
        return -1;

    /* STEP 5 :将当前的工作目录更改为根目录,这样我们就不会阻止文件系统被卸载 */
    if (chdir("/") < 0)
        fprintf(stderr, "can't change directory to / ");

     /* STEP 6 :关闭所有的文件描述符 */
    if (getrlimit(RLIMIT_NOFILE,&rl) < 0)                   //获取文件描述符的最大数量
        fprintf(stderr, "can't get file limit");
    if (rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for (i = 0; i < rl.rlim_max; i++)
        close(i);

    /* STEP 7 :文件描述符0、1和2附加到/dev/null*/
    open("/dev/null", O_RDWR);                               //stdin 
    dup(0);                                                  //stdout 
    dup(0);                                                  //stderror 

    /* do its daemon thing.. .  */
    return 0;
}

  许多Unix系统在它们的C函数库中提供了daemon()函数来完成这些工作,将繁琐的工作简化了很多:

#include <unistd.h>
int daemon (int nochdir,int noclose) ;

  若nochdir非零,就不会将工作目录改为根目录。
  若noclose非零,就不关闭所有打开的文件描述符。
  若父进程设置了守护进程的这些属性,那么这些参数是很有用的。通常都会把这些参数设为0。
   成功时,返回0。失败时返回-1。errno设置为fork()或者setsid()的错误代码之一。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值