【总结】linux使用-守护进程

Posted on Posted in 计算机2,003 views

一、概念

    1、控制终端

        每一个从此终端开始运行的进程都会依附于这个终端,这个终端被称为这些进程的控制终端,当控制终端被关闭的时候,相应的进程都会自动关闭。

    2、守护进程

        在linux/unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。它脱离终端的目的:

            避免进程在运行的过程中的信息在任何终端中显示;

            进程也不会被任何终端所产生的终端信息所打断;

        它从被执行的时候开始运转,直到整个系统关闭才退出。

    3、常见linux守护进程列表

amd:自动安装NFS(网络文件系统)守侯进程
apmd:高级电源治理
Arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和ip地址对数据库
Autofs:自动安装治理进程automount,与NFS相关,依靠于NIS
Bootparamd:引导参数服务器,为LAN上的无盘工作站提供引导所需的相关信息
crond:linux下的计划任务
Dhcpd:启动一个DHCP(动态IP地址分配)服务器
Gated:网关路由守候进程,使用动态的OSPF路由选择协议
Httpd:WEB服务器
Inetd:支持多种网络服务的核心守候程序
Innd:Usenet新闻服务器
Linuxconf:答应使用本地WEB服务器作为用户接口来配置机器
Lpd:打印服务器
Mars-nwe:mars-nwe文件和用于Novell的打印服务器
Mcserv:Midnight命令文件服务器
named:DNS服务器
netfs:安装NFS、Samba和NetWare网络文件系统
network:激活已配置网络接口的脚本程序
nfs:打开NFS服务
nscd:nscd(Name Switch Cache daemon)服务器,用于NIS的一个支持服务,它高速缓存用户口令和组成成员关系
portmap:RPC portmap治理器,与inetd类似,它治理基于RPC服务的连接
postgresql:一种SQL数据库服务器
routed:路由守候进程,使用动态RIP路由选择协议
rstatd:一个为LAN上的其它机器收集和提供系统信息的守候程序
ruserd:远程用户定位服务,这是一个基于RPC的服务,它提供关于当前记录到LAN上一个机器日志中的用户信息
rwalld:激活rpc.rwall服务进程,这是一项基于RPC的服务,答应用户给每个注册到LAN机器上的其他终端写消息
rwhod:激活rwhod服务进程,它支持LAN的rwho和ruptime服务
sendmail:邮件服务器sendmail
smb:Samba文件共享/打印服务
snmpd:本地简单网络治理候进程
squid:激活代理服务器squid
syslog:一个让系统引导时起动syslog和klogd系统日志守候进程的脚本
xfs:X Window字型服务器,为本地和远程X服务器提供字型集
xntpd:网络时间服务器
ypbind:为NIS(网络信息系统)客户机激活ypbind服务进程
yppasswdd:NIS口令服务器
ypserv:NIS主服务器
gpm:管鼠标的
identd:AUTH服务,在提供用户信息方面与finger类似

    4、守护进程信息通过ps –a无法查看到,需要用到–x参数,当使用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程。

    5、后台进程和守护进程

        基本上任何一个程序都可以后台运行,但守护进程是具有特殊要求的程序,比如要脱离自己的父进程,成为自己的会话组长等,这些要在代码中显式地写出来。换句话说,守护进程肯定是后台进程,但反之不成立。守护进程顾名思义,主要用于一些长期运行,守护着自己的职责(监听端口,监听服务等)。我们的系统下就有很多守护进程。其他可参考:http://blog.evanxia.com/2016/05/622

    6、进程组

        每个进程也属于一个进程组;

        每个进程主都有一个进程组号,该号等于该进程组组长的PID号;

        一个进程只能为它自己或子进程设置进程组ID号;

    7、会话期

        会话期(session)是一个或多个进程组的集合。setsid()函数可以建立一个对话期,如果调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。此进程变成该对话期的首进程,此进程变成一个新进程组的组长进程。此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联被解除。如果该进程是一个进程组的组长,此函数返回错误。

二、守护进程实现方式

2.1、在后台运行

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

if(pid=fork())  exit(0);//是父进程,结束父进程,子进程继续

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

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

在第1点的基础上,调用setsid()使进程成为会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。 

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

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

if(pid=fork())  exit(0);//结束第一子进程,第二子进程继续(二不再是会话组长)

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

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

for(i=0;i 
    close(i);

2.5、改变当前工作目录

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

2.6、重设文件创建掩模 

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

2.7、处理SIGCHLD信号 

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

signal(SIGCHLD,SIG_IGN);

三、实现代码

3.1、代码

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
//#include <sys/types.h>
//#include <signal.h>
 
void InitDeamon() {
    int pid = 0;
    int i = 0;
    /*1-在后台运行*/
    if (pid=fork() ) {
        exit(0);            // 父进程退出,
    } else if (pid < 0) {
        exit(1);            // 创建进程失败
    } else {                // 是第一子进程,后台继续执行
    /*2-脱离控制终端、登录会话和进程组*/
        setsid();           // 进程成为新的会话组长和进程组长,脱离终端
    /*3-禁止进程重新打开控制终端*/
        if (pid=fork() ) {
            exit(0);        // 结束第一子进程 ,l防止进程重新打开控制终端
        } else if (pid< 0) {
            exit(1);        // 创建进程失败
        } else {            //是第二子进程,继续运行,但不再是会话组长 
    /*4-关闭打开的文件描述符*/
            for (i=0;i< NOFILE;++i) { 
                close(i);   //关闭打开的文件描述符
            }
    /*5-改变当前工作目录*/
            chdir("/tmp");//改变工作目录到/tmp 
    /*6-重设文件创建掩模*/
            umask(0);//重设文件创建掩模
            return; 
        }
    }
}

int main(void)
{
    time_t t;
    int fd;
    InitDeamon();
    while(1){
        fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);
        if(fd == -1) {
            perror("open error");
            exit(EXIT_FAILURE);
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd,buf,strlen(buf));
        close(fd);
        sleep(60);
    }
    return 0;
}

3.2、测试

    1、注释掉InitDeamon后编译,在一个ssh终端1运行:

        图片1.png

    在另一个ssh终端3里面ps查看:

        图片2.png

    关掉ssh终端1,然后在终端3里面查看:

        图片3.png

    查看当前文件夹下的daemon.log

        图片4.png

    2、加上InitDeamon后编译,在一个ssh终端1运行:

        图片5.png

    在另一个ssh终端3里面ps查看:【注?表示后台进程】

        图片6.png

    关掉ssh终端1,然后在终端3里面查看:

        图片7.png

    查看tmp文件夹下的daemon.log

        图片8.png

四、daemon函数

    1、定义:

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

    2、参数:

        nochdir:=0将当前目录更改至“/”

        noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”

    3、返回值:

        成功:0

        失败:-1

五、参考

1、http://blog.evanxia.com/2016/05/622

2、http://blog.csdn.net/dlutbrucezhang/article/details/8656680

3、http://www.cnblogs.com/mickole/p/3188321.html


转载标明出处:https://blog.evanxia.com/2016/08/957