免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 788 | 回复: 0
打印 上一主题 下一主题

APUE2读书笔记(13) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-12-09 13:43 |只看该作者 |倒序浏览

第十三章 守护进程
守护进程(
daemon
)是UNIX系统中的一种特殊进程,它通常以某种特殊用户身份运行,父进程通常是init ,永远不占有控制终端,没有任何与标准输入输出的交互。它在启动成功后将在系统内永久驻留,除非被强行终止。典型的守护进程随系统自举而启动,在系统关闭时终止。
1、设计一个良好的守护进程的一般编程规则
但很多开源程序都有很好的用于守护进程创建的daemonize函数代码可以作为学习参考,例如lighttpd(8)。可以用svn(1)程序下载它的最新开发版本的
代码

svn co svn://svn.debian.org/pkg-lighttpd/lighttpd/trunk

  • 执行fork(2),使父进程退出,一来可以使启动命令正常退出,二来可以使得子进程不是进程组组长,不会占有当前shell,并使子进程变成由init进程接管的孤儿进程。
    if (0 != fork()) exit(0);

  • 通过setsid(2)创建新会话并保证没有控制终端;
    if (-1 == setsid()) exit(0);

  • 再次fork并退出,使得子进程不是该会话首进程,从而保证不能获得tty;
    if (0 != fork()) exit(0);

  • 清umask为0,避免守护进程受到继承的umask的权限的干扰;
    umask(0);

  • 若需要只生成单实例的进程,创建一个固定的pidfile(通常放在/var/run下)并锁住它,如果此文件已存在并被锁定,则认为已经有进程实例。lighttpd尽管也使用了pidfile,但并不实现单实例的daemon。无论是否实现单daemon实例,代码量都不是几行就能完成的,此处略。可参考sysklogd(8)
    源码
    的pidfile.c。

  • 若有安全等考虑,进入工作目录并进行chroot(2)(这里我对原来的代码进行了简化);
    if (-1 == chroot(rootdirp)) {
    log_error_write("chroot failed: ", strerror(errno));
    return -1;
    }

  • chdir(2)到根目录;
    if (0 != chdir("/")) exit(0);

  • 关闭已打开不需要的所有fd(若需要);
    for (i = 0; i
    {
    close(i);
    }

  • 紧接着使标准输入、标准输出、标准出错指向/dev/null,使它们不能使用:
    /* close stdin and stdout, as they are not needed */
    /* move stdin to /dev/null */
    if (-1 != (fd = open("/dev/null", O_RDONLY))) {
    close(STDIN_FILENO);
    dup2(fd, STDIN_FILENO);
    close(fd);
    }
    /* move stdout to /dev/null */
    if (-1 != (fd = open("/dev/null", O_WRONLY))) {
    close(STDOUT_FILENO);
    dup2(fd, STDOUT_FILENO);
    close(fd);
    }
    这一段在4.4BSD-lite的daemon(3)函数
    实现
    中要简洁一些:
    if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
    (void)dup2(fd, STDIN_FILENO);
    (void)dup2(fd, STDOUT_FILENO);
    (void)dup2(fd, STDERR_FILENO);
    if (fd > 2)
    (void)close (fd);
    }

  • 由于守护进程已经没有标准输入输出,若需要监视进程执行情况,应使用syslog(3)机制;
    openlog(daemonstring, LOG_CONS, LOG_DAEMON);
    if (errstring = capture_some_errors())
    {
    syslog(LOG_ERROR, errstring);
    }

  • 若需要,注册对SIGHUP的信号捕捉函数,用于执行重新读取配置文件;

  • 开始daemon例程;
    2、系统日志机制
    产生日志信息的方法有:

  • 通过本地syslog(3) API;

  • 通过远程的syslog服务器(UDP端口514);

  • 使用logger(1)工具;
    使用syslog(3)需要系统守护进程syslogd已经启动,守护进程事实上是通过套接字/dev/log与syslogd进程进行通信的。相关的API包括了
    #include
    void openlog(const char *ident, int option, int facility);
    void syslog(int priorty, const char *format, ...);
    void vsyslog(int priorty, const char *format, va_list ap);
    void closelog(void);
    int setlogmask(int maskpri);
    其中的参数ident为日志消息的前缀字符串,通常为程序名,option为6种选项的逻辑和,facility为消息的功能分类,包括LOG_DAEMON, LOG_KERN, LOG_USER等。而priority是功能分类与严重等级的逻辑和。严重等级按严重程度顺序包括了从LOG_EMERG到LOG_DEBUG共8个等级。syslog函数将把严重性大于等于指定的等级以上的事件记录到对应的日志文件中。format用于vsprintf的格式化字符串,其中的%m格式转换为strerror(errno)的结果。函数setlogmask可以屏蔽掉某些优先等级;
    以下程序将前缀字符串“test log”和PID的日志信息送到标准出错,同时记录到记录LOG_INFO以上等级的日志文件中:
    #include  
    int main(void)
    {
    openlog("test log", LOG_PID | LOG_PERROR, LOG_USER);
    syslog(LOG_INFO, "Log me, man.\n");
    closelog();
    return 0;
    }
    编译运行之,可见/var/log下的文件messages、syslog、user.log都被更新:
    $ cc -o logit logit.c
    $ ./logit
    test log[25226]: Log me, man.
    $ ls -lt /var/log/ | head -5
    total 21M
    -rw-r----- 1 syslog adm 3.7M 2008-11-22 01:08 messages
    -rw-r----- 1 syslog adm 6.4M 2008-11-22 01:08 syslog
    -rw-r----- 1 syslog adm 127K 2008-11-22 01:08 user.log
    -rw-r----- 1 syslog adm 543K 2008-11-21 23:35 debug
    -rw-r--r-- 1 root root 23K 2008-11-21 22:49 Xorg.0.log
    $ tail -1 /var/log/messages
    Nov 22 01:08:46 mjxian test log[25226]: Log me, man.
    $ tail -1 /var/log/syslog
    Nov 22 01:08:46 mjxian test log[25226]: Log me, man.
    $ tail -1 /var/log/user.log
    Nov 22 01:08:46 mjxian test log[25226]: Log me, man.
    3、daemon(3)函数
    daemon(3)函数可以直接使得进程变成一个守护进程。但该函数不存在于POSIX标准中,而是4.4BSD以来BSD家族都提供的一个函数接口。使用GNU的glibc库的Linux也提供了此函数,但Solaris的库不提供。
    #include
    int daemon(int nochdir, int noclose);
    该函数将使进程变成一个守护进程,即脱离控制终端并在后台执行。参数nochdir为0时,进程的工作目录将切换到根目录,否则是当前工作目录;noclose为0时,不使用标准输入、标准输出和标准出错。
    测试程序:
    #include  
    #include  
    #include  
    int main(void)
    {
    printf("Before turn to a daemon process.\n");
    printf("Current directory: %s\n", getcwd(NULL, 1024));
    #ifdef _NOCHROOT_
    daemon(1, 1);
    #else
    daemon(0, 0);
    #endif
    printf("Now I am a daemon process.\n");
    fprintf(stderr, "A message sent to standard error\n");
    openlog("[the daemon cwd log]", LOG_PID | LOG_INFO, LOG_USER);
    syslog(LOG_INFO, "Current directory: %s.\n", getcwd(NULL,1024));
    sleep(360);
    return 0;
    }
    编译并执行:
    $ cc -o daemon daemon.c
    $ ./daemon
    Before turn to a daemon process.
    Current directory: /home/pub/workspace/apuetest
    $ jobs
    $ tail -1 /var/log/messages
    Nov 23 01:05:56 mjxian [the daemon cwd log][21069]: Current directory: /.
    $ ps -o pid,ppid,stat,cmd 21069
    PID PPID STAT CMD
    21069 1 Ss ./daemon
    $ cc -D_NOCHROOT_ -o daemon daemon.c
    $ ./daemon
    Before turn to a daemon process.
    Current directory: /home/pub/workspace/apuetest
    Now I am a daemon process.
    A message sent to standard error
    $ jobs
    $ tail -1 /var/log/messages
    Nov 23 01:20:06 mjxian [the daemon cwd log][22048]: Current directory: /home/pub/workspace/apuetest.
    $ ps -o pid,ppid,stat,cmd 22048
    PID PPID STAT CMD
    22048 1 Ss ./daemon
    可见,调用daemon(0, 0)之后已经不能输出到当前tty,工作目录已经变更到“/”,且父进程变成了init(即PID为1的进程)。而调用daemon(1,1)则可以正常使用标准输入和标准输出,且不会改变工作目录。
    4、守护进程设计的另外一些常见惯例


    • 如果使用锁定文件的方法实现单一实例的守护进程,该文件一般放在/var/run下,文件名为{守护进程名}.pid,同时文件内容为该进程的PID。如/var/run/syslogd.pid;

    • 如果使用配置文件,则配置文件默认放在/etc目录下,名为{守护进程名}.conf;如/etc/syslog.conf;

    • 守护进程的启动和关闭脚本放在/etc/rc.d(4.4BSD及其后代或者追随者的风格,如FreeBSD)或者/etc/init.d(SVR4及其后代或者追随者的风格,如Solaris)中。并链接到通过inittab(5)(Linux的Ubuntu发行版不使用这个方式而用了
      upstart
      包)配置它在某一运行级别中的行为.


    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/82556/showart_1709961.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP