Linux守护进程编写指南 (注:文章为转载)

*:first-child {
margin-top: 0 !important;
}

body>*:last-child {
margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}

h1 {
font-size: 28px;
color: #000;
}

h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}

h3 {
font-size: 18px;
}

h4 {
font-size: 16px;
}

h5 {
font-size: 14px;
}

h6 {
color: #777;
font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* TABLES
=============================================================================*/

table th {
font-weight: bold;
}

table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}

table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}

table tr:nth-child(2n) {
background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

Linux守护进程编写指南

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。 守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器 httpd等。同时,守护进程完成许多系统任 务。比如,作业规划进程crond,打印进程lpd等。

守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则 并不一致。这需要读者注意,照搬 某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。 下面将全面介绍Linux下守护进程的编程要点并给出详细实例。

1.守护进程及其特性

守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次,守护进程必须与其运行前的环境隔离开来。 这些环境包括未关闭的 文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的 父进程(特别是shell)中继承下来的。最后,守 护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本 /etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端 (通常是shell)执行。

总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。因此,编写守护进程实际上是把一个普通进程按照上述的守护 进程的特性改造成为守护进程。如果读者对进程有比较深入的认识就更容易理解和编程了。

2.守护进程的编程要点

前面讲过,不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样,区别在于具体的实现细节不同。 这个原则就是要满足守护 进程的特性。同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点如下:

2.1.创建子进程,以便脱离控制终端,在后台运行。

为避免挂起控制终端,需将Daemon放入后台执行。方法是在进程中调用fork()创建子进程,然后使父进程终止,让Daemon在子进程中后台执行。

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //子进程
        //...
    }

    return 0;
}

2.2.脱离控制终端,登录会话和进程组

有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的 进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。

控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上, 调用setsid()使进程成为会话组长:

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //子进程
        //...
        setsid();
    }

    return 0;
}

说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长 和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

2.3.禁止进程重新打开控制终端

现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端。

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //第一子进程,肯定不是会话首进程,保证后续调用setsid()成功
        //...
        setsid();   //创建新会话
        pid = fork();
        if (0 < pid) {
            exit(-1);   //创建第二子进程失败
        } else if (0 > pid) {
            exit(0);    //第一子进程,退出
        } else {
            //第二子进程,肯定不是会话首进程,保证不能打开控制终端
            //...
        }
    }

    return 0;
}

2.4.关闭打开的文件描述符

进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的 错误。按如下方法关闭它们:

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //第一子进程,肯定不是会话首进程,保证后续调用setsid()成功
        //...
        setsid();   //创建新会话
        pid = fork();
        if (0 < pid) {
            exit(-1);   //创建第二子进程失败
        } else if (0 > pid) {
            exit(0);    //第一子进程,退出
        } else {
            //第二子进程,肯定不是会话首进程,保证不能打开控制终端
            //...
            //关闭父进程打开的文件描述符
            for (i=0; i < MAX_FD; ++i) {
                close(i);
            }

        }
    }

    return 0;
}

2.5.改变当前工作目录

进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如 "/tmp"

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //第一子进程,肯定不是会话首进程,保证后续调用setsid()成功
        //...
        setsid();   //创建新会话
        pid = fork();
        if (0 < pid) {
            exit(-1);   //创建第二子进程失败
        } else if (0 > pid) {
            exit(0);    //第一子进程,退出
        } else {
            //第二子进程,肯定不是会话首进程,保证不能打开控制终端
            //...
            //关闭父进程打开的文件描述符
            for (i=0; i < MAX_FD; ++i) {
                close(i);
            }
            //改变工作目录
            chdir("/");
        }
    }

    return 0;
}

2.6.重设文件创建掩模

进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //第一子进程,肯定不是会话首进程,保证后续调用setsid()成功
        //...
        setsid();   //创建新会话
        pid = fork();
        if (0 < pid) {
            exit(-1);   //创建第二子进程失败
        } else if (0 > pid) {
            exit(0);    //第一子进程,退出
        } else {
            //第二子进程,肯定不是会话首进程,保证不能打开控制终端
            //...
            //关闭父进程打开的文件描述符
            for (i=0; i < MAX_FD; ++i) {
                close(i);
            }
            //改变工作目录
            chdir("/");
            //重设文件创建掩码
            umask(0);
        }
    }

    return 0;
}

2.7.处理SIGCHLD信号

处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为 僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。

int main(int argc, char* argv[])
{
    pid_t pid = fork();
    if(0 > pid) {
        exit(-1);   //创建第一子进程失败
    } else if (0 < pid) {
        exit(0);    //父进程,退出
    } else {
        //第一子进程,肯定不是会话首进程,保证后续调用setsid()成功
        //...
        setsid();   //创建新会话
        pid = fork();
        if (0 < pid) {
            exit(-1);   //创建第二子进程失败
        } else if (0 > pid) {
            exit(0);    //第一子进程,退出
        } else {
            //第二子进程,肯定不是会话首进程,保证不能打开控制终端
            //...
            //关闭父进程打开的文件描述符
            for (i=0; i < MAX_FD; ++i) {
                close(i);
            }
            //改变工作目录
            chdir("/");
            //重设文件创建掩码
            umask(0);
            //忽略子进程终止信号
            signal(SIGCHLD, SIG_IGN);
        }
    }

    return 0;
}

这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

时间: 2024-10-24 02:47:05

Linux守护进程编写指南 (注:文章为转载)的相关文章

linux C守护进程编写

linux编程-守护进程编写 守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待 处理某些发生的事件.守护进程是一种很有用的进程. Linux的大多数服务器就是用守护进程实现的.比如,Internet服务器inetd,Web服务器httpd等. 同时,守护进程完成许多系统任务.比如,作业规划进程crond,打印进程lpd等. 守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同, 造成不同 Unix环境下守护进程的编程规则并不一

笔记整理--Linux守护进程

Linux多进程开发(三)进程创建之守护进程的学习 - _Liang_Happy_Life__Dream - 51CTO技术博客 - Google Chrome (2013/10/11 16:48:27) Linux多进程开发(三)进程创建之守护进程的学习 2013-07-04 17:25:35 标签:守护进程 daemon Linux多进程开发 系统编程 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://liam2199.bl

C#开发Linux守护进程

C#开发Linux守护进程 Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多进程,父子进程文件描述符共享,父子进程通讯.控制等方面,是实现Linux大型服务的基础技术之一. 去年我也曾写了一篇关于守护进程的帖子,名字叫<.NET跨平台实践:用C#开发Linux守护进程>,这篇文章的的确确实现了一个Daemon,不过,它有一个弱点,不能运行多

.NET跨平台实践:再谈用C#开发Linux守护进程

Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多子进程,父子进程,父子进程通讯与控制等方面,是实现Linux大型服务的基础技术之一. 去年我也曾写了一篇关于守护进程的帖子,名字叫<.NET跨平台实践:用C#开发Linux守护进程>,这篇文章的的确确实现了一个Daemon,不过,它有一个弱点,不能运行多线程! 这篇帖子的目的就是进一步完善,让

.NET跨平台实践:用C#开发Linux守护进程

Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为Linux开发与控制台无关的,需要在后台长时间不间断运行的“服务程序”,Daemon技术是非常重要的. Daemon程序一般用c/c++开发.不过,我今天要讲的,不是怎么用c/c++开发daemon,而是用C#! 一,创建Daemon程序: 用VS新建一个控制台项目,假设名称是MyDaemon,输入下

Linux守护进程的编程实现

Linux 守护进程的编程方法 守护进程(Daemon)是执行在后台的一种特殊进程.它独立于控制终端而且周期性地执行某种任务或等待处理某些发生的事件.守护进程是一种非常实用的进程.Linux的大多数server就是用守护进程实现的.比方,Internetserverinetd,Webserverhttpd等.同一时候,守护进程完毕很多系统任务.比方,作业规划进程crond,打印进程lpd等. 守护进程的编程本身并不复杂,复杂的是各种版本号的Unix的实现机制不尽同样,造成不同Unix环境下守护进

[Linux] 守护进程和守护线程

对于JAVA而言,一般一个应用程序只有一个进程——JVM.除非在代码里面另外派生或者开启了新进程. 而线程,当然是由进程开启的.当开启该线程的进程离开时,线程也就不复存在了. 所以,对于JAVA而言,线程是完全可以由自己的API自由产生.例如new Thread().但是进程就不同,必须通过调用OS的API,如Runtime.getRuntime.exec(). 所以说,进程是OS级别的概念. 守护线程和用户线程的区别: 二者其实基本上是一样的.唯一的区别在于JVM何时离开. 用户线程:当存在任

转:linux守护进程的启动方法

Linux 守护进程的启动方法 作者: 阮一峰 日期: 2016年2月28日 "守护进程"(daemon)就是一直在后台运行的进程(daemon). 本文介绍如何将一个 Web 应用,启动为守护进程. 一.问题的由来 Web应用写好后,下一件事就是启动,让它一直在后台运行. 这并不容易.举例来说,下面是一个最简单的Node应用server.js,只有6行. var http = require('http'); http.createServer(function(req, res)

linux守护进程

#include <iostream>#include <unistd.h>//#include "curl/curl.h"#include "app_curl.h"#include "youtube_package.h"#include "CAutoMail.h"#include <fcntl.h>#include <signal.h>#include <unistd.h