守護進程

在Linux或者Unix操做系統中在系統引導的時候會開啓不少服務,這些服務就叫作守護進程。linux

守護進程,也就是一般說的Daemon進程,是Linux中的後臺服務進程。sql

它是一個生存期較長的進程,一般獨立於控制終端而且週期性地執行某種任務或等待處理某些發生的事件。shell

守護進程經常在系統引導裝入時啓動,在系統關閉時終止。數據庫

Linux系統有不少守護進程,大多數服務都是經過守護進程實現的,如Internet服務器inetd,Web服務器httpd等;緩存

同時,守護進程還能完成許多系統任務,例如,做業規劃進程crond、打印進程lqd等(這裏的結尾字母d就是Daemon的意思)。服務器

 

因爲在Linux中,每個系統與用戶進行交流的界面稱爲終端,每個今後終端開始運行的進程都會依附於這個終端,這個終端就稱爲這些進程的控制終端。網絡

當控制終端被關閉時,相應的進程都會自動關閉。可是守護進程卻可以突破這種限制,它從被執行開始運轉,直到整個系統關閉時才退出。session

若是想讓某個進程不由於用戶或終端或其餘地變化而受到影響,那麼就必須把這個進程變成一個守護進程。socket

 

守護進程也可能從某個終端由用戶在shell提示符下鍵入命令行啓動,這樣的守護進程必須親自脫離與控制終端的關聯,ide

從而避免與做業控制、終端會話管理、終端產生信號等發生任何不指望的交互,也可避免在後臺運行的守護進程非預期地輸出到終端。

 

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服務器做爲 用戶接口來配置機器
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 系統日誌守候進程的腳本 (514/udp syslog UNIX 系統日誌服務)
xfs:X Window字型服務器,爲本地和遠程X服務器提供字型集
xntpd:網絡時間服務器
ypbind:爲NIS(網絡信息系統)客戶機激活ypbind服務進程
yppasswdd:NIS口令服務器
ypserv:NIS主服務器
gpm:管鼠標的
identd:AUTH服務,在提供用戶信息方面與finger相似
 

#include <syslog.h>

void syslog(int priority, const char *message, ...);   // 記錄至系統日誌

第一個參數priority是級別(level)和設施(facility)二者的組合;

第二個參數message相似printf的格式串。

日誌消息的level:

日誌消息的facility:

例如rename函數調用意外失敗時,守護進程能夠執行如下調用:

syslog(LOG_INFO | LOG_LOCAL2, "rename(%s, %s): %m", file1, file2);

level和facility的目的在於,容許在/etc/syslog.conf文件中統一配置來自同一個給定設施的全部消息,

或者統一配置具備相同級別的全部消息。舉例來講,該配置文件可能含有如下兩行:

kern.*               /dev/console

local7.debug      /var/log/cisco.log

這兩行指定全部內核消息登記到控制檯,來自local7設施的全部debug消息添加到文件/var/log/cisco.log的末尾。

當syslog被應用進程首次調用時,它建立一個Unix域數據報套接字,而後調用connect鏈接到由syslogd守護進程建立的

Unix域數據報套接字的衆所周知路徑名(如/var/run/log)。這個套接字一直保持打開,指導進程終止爲止。

做爲替換,進程也能夠調用openlog和closelog。

#include <syslog.h>

void openlog(const char *ident, int options, int facility);  // 可在首次調用syslog前調用

void closelog(void);  // 可在應用進程再也不須要發送日誌消息時調用

參數ident是一個由syslog冠於每一個日誌消息前的字符串,其值一般爲程序名。

參數options由一個或多個常值的邏輯或構成。

openlog被調用時,一般並不當即建立Unix域套接字;相反,該套接字直到首次調用syslog時纔打開。

LOG_NDELAY選項迫使該套接字在openlog被調用時就建立。

 

日誌消息也能夠由logger命令產生,可用在shell腳本中以向syslogd發送消息。

 

Linux建立守護進程的步驟:

1)建立子進程,父進程退出

2)在子進程中建立新會話

3)改變當前目錄爲根目錄

4)重設文件權限掩碼

5)關閉文件描述符

6)忽略SIGCHLD信號

7)用日誌系統記錄出錯信息

8)守護進程退出處理

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#define MAXFILE 65535
void sigterm_handler(int arg);
volatile sig_atomic_t _running = 1;

int main()
{
    pid_t pc, pid;
    int i, fd, len, flag = 1;
    char *buf = "this is a Dameon\n";
    len = strlen(buf);
    pc = fork();                  //第一步
    if(pc < 0){
        printf("error fork\n");
        exit(1);
    }
    else if(pc > 0)
        exit(0);

    pid = setsid();               //第二步
    if (pid < 0)
        perror("setsid error");

    chdir("/");                   //第三步
    umask(0);                     //第四步
    for(i = 0; i < MAXFILE; i++)        //第五步
        close(i);

    signal(SIGTERM, sigterm_handler);
    while(_running){
        if( flag == 1 && (fd=open("/tmp/daemon.log", O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0){
            perror("open");
            flag=0;
            exit(1);
        }

        write(fd, buf, len);
        close(fd);
        usleep(10 * 1000); //10毫秒
    }
}

void sigterm_handler(int arg)
{
    _running = 0;
}                

 

#include "unp.h"
#include <syslog.h>
#define MAXFD 64

extern int daemon_proc;

int daemon_init(const char *pname, int facility)
{
    int i;
    pid_t pid; 
    
    if ( (pid = fork()) < 0)
        return -1;
    else if (pid)
        exit(0);

    if (setsid() < 0)
        return -1;
    
    signal(SIGHUP, SIG_IGN);
    if ( (pid = fork()) < 0)
        return -1;
    else if (pid)
        exit(0);

    daemon_proc = 1;    // 把全局變量daemon_proc置爲非0值,告知它們改成調用syslog,以取代fprintf到標準錯誤輸出。
    
    chdir("/");    // 把工做目錄改到根目錄,守護進程多是在某個任意的文件系統中啓動,若仍在其中,該文件系統就沒法拆卸,除非採用潛在破壞性的強制措施。

    for (i = 0; i < MAXFD; i++)
        close(i);    // 關閉本守護進程從執行它的進程(一般是一個shell)繼承來的全部打開的描述符

    open("/dev/null", O_RDONLY);   // 將stdin、stdout和stderr重定向到/dev/null
    open("/dev/null", O_RDWR);
    open("/dev/null", O_RDWR);

    openlog(pname, LOG_PID, facility);   // 使用syslogd處理錯誤,第一個參數來自調用者,一般爲程序名字(argv[0])。
// 第二個參數指定把進程ID加到每一個日誌消息中。
// 第三個參數由調用者指定。
return 0; }

首次調用fork終止父進程。本進程是從前臺做爲一個shell命令啓動的,當父進程終止時shell認爲該命令已執行完畢。這樣子進程就自動在後臺運行。

另外,子進程繼承了父進程的進程組ID,不過它有本身的進程ID,這就保證子進程不是一個進程組的頭進程,則該接着調用setsid。

 

setsid用於建立一個新的會話(session)。當前進程變爲新會話的會話頭進程以及新進程組的進程組頭進程,從而再也不有控制終端。

 

忽略SIGHUP信號並再次調用fork。再次調用fork目的是確保本守護進程未來即便打開一個終端設備,也不會自動得到控制終端。(確保新的子進程再也不是一個會話頭進程)

這裏必須忽略SIGHUP信號,由於當會話頭進程(即首次fork產生的子進程)終止時,其會話中的全部進程(即再次fork產生的子進程)都收到SIGHUP信號。 

 

一個 session可能會有一個session首進程,而一個session首進程可能會有一個控制終端。
一個 進程組可能會有一個進程組首進程。進程組首進程的進程ID與該進程組ID相等。
系統對SIGHUP信號(停止信號)的默認處理是終止收到該信號的進程。因此若程序中沒有捕捉該信號,當收到該信號時,進程就會退出。
 
/dev/null(空設備)是一個特殊的設備文件,它丟棄一切寫入其中的數據(但報告寫入操做成功),read系統調用讀取它則會當即獲得一個EOF。
它可看做黑洞(black hole),空設備一般被用於丟棄不須要的輸出流,或做爲用於輸入流的空文件。這些操做一般由重定向完成。
 
一個程序做爲守護進程運行,就得避免調用諸如printf和fprintf之類函數,可改用err_msg()
 
int main(int argc, char **argv)
{
    int listenfd, connfd;
    socklen_t addrlen, len;
    struct sockaddr_in servaddr;
    char buff[MAXLINE];
    pid_t pid;
    time_t ticks;

    daemon_init(argv[0], 0);

    if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        cout<<"Error No:"<<errno<<endl;
        cout<<"socket error!"<<endl;
        exit(0);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(30000);
    
    if (bind(listenfd, (SA*)&servaddr, sizeof(servaddr)) < 0){
        cout<<"bind error!"<<endl;
        exit(0);
    }

    if (listen(listenfd, LISTENQ) < 0){
        cout<<"listen error!"<<endl;
        exit(0);
    }

    for ( ; ; ){
        connfd = accept(listenfd, (SA*)NULL, NULL);
        if ( (pid = fork()) == 0){
            close(listenfd);
            
            ticks = time(NULL);
            bzero(buff, sizeof(buff));

            sprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
            write(connfd, buff, strlen(buff));
            close(connfd);
            //str_echo(connfd);
            exit(0);
        }
        close(connfd);
    }

    return 0;
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息