色yeye在线视频观看_亚洲人亚洲精品成人网站_一级毛片免费播放_91精品一区二区中文字幕_一区二区三区日本视频_成人性生交大免费看


Linux守護(hù)進(jìn)程

分享到:
           

    1.守護(hù)進(jìn)程概述

    守護(hù)進(jìn)程,也就是通常所說的daemon進(jìn)程,是Linux中的后臺服務(wù)進(jìn)程。它是一個(gè)生存期較長的進(jìn)程,通常獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。守護(hù)進(jìn)程常常在系統(tǒng)引導(dǎo)載入時(shí)啟動(dòng),在系統(tǒng)關(guān)閉時(shí)終止。Linux有很多系統(tǒng)服務(wù),大多數(shù)服務(wù)都是通過守護(hù)進(jìn)程實(shí)現(xiàn)的。同時(shí),守護(hù)進(jìn)程還能完成許多系統(tǒng)任務(wù),例如,作業(yè)規(guī)劃進(jìn)程crond、打印進(jìn)程lqd等(這里的結(jié)尾字母d就是daemon的意思)。

    由于在Linux中,每一個(gè)系統(tǒng)與用戶進(jìn)行交流的界面稱為終端,每一個(gè)從此終端開始運(yùn)行的進(jìn)程都會(huì)依附于這個(gè)終端,這個(gè)終端稱為這些進(jìn)程的控制終端,當(dāng)控制終端被關(guān)閉時(shí),相應(yīng)的進(jìn)程都會(huì)自動(dòng)關(guān)閉。但是守護(hù)進(jìn)程卻能夠突破這種限制,它從被執(zhí)行開始運(yùn)轉(zhuǎn),直到接收到某種信號或者整個(gè)系統(tǒng)關(guān)閉時(shí)才會(huì)退出。如果想讓某個(gè)進(jìn)程不因?yàn)橛脩簟⒔K端或者其他的變化而受到影響,那么就必須把這個(gè)進(jìn)程變成一個(gè)守護(hù)進(jìn)程。可見,守護(hù)進(jìn)程是非常重要的。

    2.編寫守護(hù)進(jìn)程

    編寫守護(hù)進(jìn)程看似復(fù)雜,但實(shí)際上也是遵循一個(gè)特定的流程,只要將此流程掌握了,就能很方便地編寫出自己的守護(hù)進(jìn)程。下面就分4個(gè)步驟來講解怎樣創(chuàng)建一個(gè)簡單的守護(hù)進(jìn)程。在講解的同時(shí),會(huì)配合介紹與創(chuàng)建守護(hù)進(jìn)程相關(guān)的幾個(gè)系統(tǒng)函數(shù),希望讀者能很好地掌握。

    (1)創(chuàng)建子進(jìn)程,父進(jìn)程退出。這是編寫守護(hù)進(jìn)程的第一步。由于守護(hù)進(jìn)程是脫離控制終端的,因此,完成第一步后就會(huì)在shell終端造成一種程序已經(jīng)運(yùn)行完畢的假象,之后的所有工作都在子進(jìn)程中完成,而用戶在shell終端則可以執(zhí)行其他的命令,從而在形式上做到與控制終端的脫離。

    到這里,有心的讀者可能會(huì)問,父進(jìn)程創(chuàng)建了子進(jìn)程后退出,此時(shí)該子進(jìn)程不就沒有父進(jìn)程了嗎?守護(hù)進(jìn)程中確實(shí)會(huì)出現(xiàn)這么一個(gè)有趣的現(xiàn)象:由于父進(jìn)程已經(jīng)先于子進(jìn)程退出,就會(huì)造成子進(jìn)程沒有父進(jìn)程,從而變成一個(gè)孤兒進(jìn)程。在Linux中,每當(dāng)系統(tǒng)發(fā)現(xiàn)一個(gè)孤兒進(jìn)程時(shí),就會(huì)自動(dòng)由1號進(jìn)程(也就是init進(jìn)程)收養(yǎng)它,這樣,原先的子進(jìn)程就會(huì)變成init進(jìn)程的子進(jìn)程。其關(guān)鍵代碼如下:

    pid = fork();
    if (pid > 0)
    {
        exit(0); /* 父進(jìn)程退出 */
    }

    (2)在子進(jìn)程中創(chuàng)建新會(huì)話。這個(gè)步驟是創(chuàng)建守護(hù)進(jìn)程重要的一步,雖然實(shí)現(xiàn)非常簡單,但意義卻非常重大。在這里使用的是系統(tǒng)函數(shù)setsid(),在具體介紹setsid()之前,讀者首先要了解兩個(gè)概念:進(jìn)程組和會(huì)話期。

    ● 進(jìn)程組。進(jìn)程組是一個(gè)或多個(gè)進(jìn)程的集合。進(jìn)程組由進(jìn)程組ID來唯一標(biāo)識。除了進(jìn)程號(PID)之外,進(jìn)程組ID也是一個(gè)進(jìn)程的必備屬性。

    每個(gè)進(jìn)程組都有一個(gè)組長進(jìn)程,其組長進(jìn)程的進(jìn)程號等于進(jìn)程組ID,且該進(jìn)程ID不會(huì)因組長進(jìn)程的退出而受到影響。

    ● 會(huì)話期。會(huì)話組是一個(gè)或多個(gè)進(jìn)程組的集合。通常,一個(gè)會(huì)話開始于用戶登錄,終止于用戶退出,在此期間該用戶運(yùn)行的所有進(jìn)程都屬于這個(gè)會(huì)話期。進(jìn)程組和會(huì)話期之間的關(guān)系如圖1所示。


圖1 進(jìn)程組和會(huì)話期之間的關(guān)系

    接下來就可以具體介紹setsid()的相關(guān)內(nèi)容。

    ① setsid()函數(shù)的作用。setsid()函數(shù)用于創(chuàng)建一個(gè)新的會(huì)話組,并擔(dān)任該會(huì)話組的組長。調(diào)用setsid()有以下3個(gè)作用:
    ● 讓進(jìn)程擺脫原會(huì)話的控制。
    ● 讓進(jìn)程擺脫原進(jìn)程組的控制。
    ● 讓進(jìn)程擺脫原控制終端的控制。

    那么,在創(chuàng)建守護(hù)進(jìn)程時(shí)為什么要調(diào)用setsid()函數(shù)呢?讀者可以回憶一下創(chuàng)建守護(hù)進(jìn)程的第一步,在那里調(diào)用了fork()函數(shù)來創(chuàng)建子進(jìn)程再令父進(jìn)程退出。由于在調(diào)用fork()函數(shù)時(shí),子進(jìn)程全盤復(fù)制了父進(jìn)程的會(huì)話期、進(jìn)程組和控制終端等,雖然父進(jìn)程退出了,但原先的會(huì)話期、進(jìn)程組和控制終端等并沒有改變,因此,還不是真正意義上的獨(dú)立。而setsid()函數(shù)能夠使進(jìn)程完全獨(dú)立出來,從而脫離所有其他進(jìn)程的控制。

    ② setsid()函數(shù)格式。表1列出了setsid()函數(shù)的語法要點(diǎn)。

表1 setsid()函數(shù)語法要點(diǎn)

所需頭文件 #include <sys/types.h>
#include <unistd.h>
函數(shù)原型 pid_t setsid(void)
函數(shù)返回值 成功:該進(jìn)程組ID
出錯(cuò):-1

    (3)改變當(dāng)前目錄為根目錄。這一步也是必要的步驟。使用fork()創(chuàng)建的子進(jìn)程繼承了父進(jìn)程的當(dāng)前工作目錄。由于在進(jìn)程運(yùn)行過程中,當(dāng)前目錄所在的文件系統(tǒng)(如“/mnt/usb”等)是不能卸載的,這對以后的使用會(huì)造成諸多的麻煩(如系統(tǒng)由于某種原因要進(jìn)入單用戶模式)。因此,通常的做法是讓“/”作為守護(hù)進(jìn)程的當(dāng)前工作目錄,這樣就可以避免上述問題。當(dāng)然,如有特殊需要,也可以把當(dāng)前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函數(shù)是chdir()。

    (4)重設(shè)文件權(quán)限掩碼。文件權(quán)限掩碼是指屏蔽掉文件權(quán)限中的對應(yīng)位。例如,有一個(gè)文件權(quán)限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執(zhí)行權(quán)限。由于使用fork()函數(shù)新建的子進(jìn)程繼承了父進(jìn)程的文件權(quán)限掩碼,這就給該子進(jìn)程使用文件帶來了諸多的麻煩。因此,把文件權(quán)限掩碼設(shè)置為0,可以大大增強(qiáng)該守護(hù)進(jìn)程的靈活性。設(shè)置文件權(quán)限掩碼的函數(shù)是umask()。在這里,通常的使用方法為umask(0)。

    (5)關(guān)閉文件描述符。同文件權(quán)限掩碼一樣,用fork()函數(shù)新建的子進(jìn)程會(huì)從父進(jìn)程那里繼承一些已經(jīng)打開的文件。這些被打開的文件可能永遠(yuǎn)不會(huì)被守護(hù)進(jìn)程讀或?qū)懀鼈円粯酉南到y(tǒng)資源,而且可能導(dǎo)致所在的文件系統(tǒng)無法被卸載。

    在上面的第(2)步之后,守護(hù)進(jìn)程已經(jīng)與所屬的控制終端失去了聯(lián)系,因此,從終端輸入的字符不可能達(dá)到守護(hù)進(jìn)程,守護(hù)進(jìn)程中用常規(guī)方法(如printf())輸出的字符也不可能在終端上顯示出來。所以,文件描述符為0、1和2的3個(gè)文件(常說的輸入、輸出和報(bào)錯(cuò)這3個(gè)文件)已經(jīng)失去了存在的價(jià)值,也應(yīng)被關(guān)閉。通常按如下方式關(guān)閉文件描述符:

    for(i = 0; i < MAXFILE; i++)
    {
        close(i);
    }

    這樣,一個(gè)簡單的守護(hù)進(jìn)程就建立起來了。創(chuàng)建守護(hù)進(jìn)程的流程圖如圖2所示。


圖2 創(chuàng)建守護(hù)進(jìn)程流程圖

    下面是實(shí)現(xiàn)守護(hù)進(jìn)程的一個(gè)完整實(shí)例,該實(shí)例首先按照以上的創(chuàng)建流程建立了一個(gè)守護(hù)進(jìn)程,然后讓該守護(hù)進(jìn)程每隔10s向日志文件/tmp/daemon.log寫入一句話。

    /* daemon.c 創(chuàng)建守護(hù)進(jìn)程實(shí)例 */
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<sys/wait.h>

    int main()
    {
        pid_t pid;
        int i, fd;
        char *buf = "This is a Daemon\n";

        pid = fork(); /* 第一步 */
        if (pid < 0)
        {
            printf("Error fork\n");
            exit(1);
        }
        else if (pid > 0)
        {
            exit(0); /* 父進(jìn)程退出 */
        }

        setsid(); /* 第二步 */
        chdir("/"); /* 第三步 */
        umask(0); /* 第四步 */
        for(i = 0; i < getdtablesize(); i++) /* 第五步 */
        {
            close(i);
        }

        /* 這時(shí)創(chuàng)建完守護(hù)進(jìn)程,以下開始正式進(jìn)入守護(hù)進(jìn)程工作 */
        while(1)
        {
            if ((fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0)
            {
                printf("Open file error\n");
                exit(1);
            }
            write(fd, buf, strlen(buf) + 1);
            close(fd);
            sleep(10);
        }
        exit(0);
    }

    將該程序下載到開發(fā)板上,可以看到該程序每隔10s就會(huì)在對應(yīng)的文件中輸入相關(guān)內(nèi)容,并且使用ps可以看到該進(jìn)程在后臺運(yùn)行,結(jié)果如下:

    $ tail -f /tmp/daemon.log
    This is a Daemon
    This is a Daemon
    This is a Daemon
    This is a Daemon
    …
    $ ps -ef|grep daemon
    76        root        1272    S    ./daemon
    85        root        1520    S    grep daemon

    3.守護(hù)進(jìn)程的出錯(cuò)處理

    讀者在前面編寫守護(hù)進(jìn)程的具體調(diào)試過程中會(huì)發(fā)現(xiàn),由于守護(hù)進(jìn)程完全脫離了控制終端,因此,不能像其他普通進(jìn)程一樣將錯(cuò)誤信息輸出到控制終端來通知程序員,即使使用gdb也無法正常調(diào)試。那么,守護(hù)進(jìn)程的進(jìn)程要如何調(diào)試呢?一種通用的辦法是使用syslog服務(wù),將程序中的出錯(cuò)信息輸入到系統(tǒng)日志文件中(如“/var/log/messages”),從而可以直觀地看到程序的問題所在(“/var/log/message”系統(tǒng)日志文件只能由擁有root權(quán)限的超級用戶查看。在不同Linux發(fā)行版本中,系統(tǒng)日志文件路徑全名可能有所不同,例如,可能是“/var/log/syslog”)。

    syslog是Linux中的系統(tǒng)日志管理服務(wù),通過守護(hù)進(jìn)程syslogd來維護(hù)。該守護(hù)進(jìn)程在啟動(dòng)時(shí)會(huì)讀一個(gè)配置文件“/etc/syslog.conf”,該文件決定了不同種類的消息會(huì)發(fā)送到何處。例如,緊急消息可被送到系統(tǒng)管理員并在控制臺上顯示,而警告消息則可被記錄到一個(gè)文件中。

    該機(jī)制提供了3個(gè)syslog相關(guān)函數(shù),分別為openlog()、syslog()和closelog(),下面就分別介紹這3個(gè)函數(shù)。

    1)syslog相關(guān)函數(shù)說明

    通常,openlog()函數(shù)用于打開系統(tǒng)日志服務(wù)的一個(gè)鏈接;syslog()函數(shù)用于向日志文件中寫入消息,在這里可以規(guī)定消息的優(yōu)先級、消息輸出格式等;closelog()函數(shù)用于關(guān)閉系統(tǒng)日志服務(wù)的鏈接。

    2)syslog相關(guān)函數(shù)格式

    表2列出了openlog()函數(shù)的語法要點(diǎn)。

表2 openlog()函數(shù)語法要點(diǎn)

所需頭文件 #include <syslog.h>
函數(shù)原型 void openlog (char *ident, int option , int facility)
函數(shù)傳入值 ident 要向每個(gè)消息加入的字符串,通常為程序的名稱
option LOG_CONS:如果消息無法送到系統(tǒng)日志服務(wù),則直接輸出到系統(tǒng)控制終端
LOG_NDELAY:立即打開系統(tǒng)日志服務(wù)的鏈接。在正常情況下,直接發(fā)送到第一條消息時(shí)才打開鏈接
LOG_PERROR:將消息也同時(shí)送到stderr上
LOG_PID:在每條消息中包含進(jìn)程的PID
facility:
指定程序發(fā)送的消息類型
LOG_AUTHPRIV:安全/授權(quán)信息
LOG_CRON:時(shí)間守護(hù)進(jìn)程(cron及at)
LOG_DAEMON:其他系統(tǒng)守護(hù)進(jìn)程
LOG_KERN:內(nèi)核信息
LOG_LOCAL[0~7]:保留
LOG_LPR:行打印機(jī)子系統(tǒng)
LOG_MAIL:郵件子系統(tǒng)
LOG_NEWS:新聞子系統(tǒng)
LOG_SYSLOG:syslogd內(nèi)部所產(chǎn)生的信息
LOG_USER:一般使用者等級信息
LOG_UUCP:UUCP子系統(tǒng)

    表3列出了syslog()函數(shù)的語法要點(diǎn)。

表3 syslog()函數(shù)語法要點(diǎn)

所需頭文件 #include <syslog.h>
函數(shù)原型 void syslog(int priority, char *format, ...)
函數(shù)傳入值 priority:
指定消息的重要性
LOG_EMERG:系統(tǒng)無法使用
LOG_ALERT:需要立即采取措施
LOG_CRIT:有重要情況發(fā)生
LOG_ERR:有錯(cuò)誤發(fā)生
函數(shù)傳入值 priority:
指定消息的重要性
LOG_WARNING:有警告發(fā)生
LOG_NOTICE:正常情況,但也是重要情況
LOG_INFO:信息消息
LOG_DEBUG:調(diào)試信息
format 以字符串指針的形式表示輸出的格式,類似于printf中的格式

    表3.4列出了closelog()函數(shù)的語法要點(diǎn)。

表3.4 closelog()函數(shù)語法要點(diǎn)

所需頭文件 #include <syslog.h>
函數(shù)原型 void closelog(void)

    3)使用實(shí)例

    這里將上一個(gè)示例程序用syslog服務(wù)進(jìn)行重寫,其中有區(qū)別的地方用加粗的字體表示,源代碼如下:

    /* syslog_daemon.c利用syslog服務(wù)的守護(hù)進(jìn)程實(shí)例 */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <syslog.h>

    int main()
    {
        pid_t pid, sid;
        int i, fd;
        char *buf = "This is a Daemon\n";

        pid = fork(); /* 第一步 */
        if (pid < 0)
        {
            printf("Error fork\n");
            exit(1);
        }
        else if (pid > 0)
        {
            exit(0); /* 父進(jìn)程退出 */
        }

        /* 打開系統(tǒng)日志服務(wù),openlog */
        openlog("daemon_syslog", LOG_PID, LOG_DAEMON);
        if ((sid = setsid()) < 0) /* 第二步 */
        {
            syslog(LOG_ERR, "%s\n", "setsid");
            exit(1);
        }

        if ((sid = chdir("/")) < 0) /* 第三步 */
        {
            syslog(LOG_ERR, "%s\n", "chdir");
            exit(1);
        }

        umask(0); /* 第四步 */
        for(i = 0; i < getdtablesize(); i++) /* 第五步 */
        {
            close(i);
        }

        /* 這時(shí)創(chuàng)建完守護(hù)進(jìn)程,以下開始正式進(jìn)入守護(hù)進(jìn)程工作 */
        while(1)
        {
            if ((fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0)
            {
                syslog(LOG_ERR, "open");
                exit(1);
            }

            write(fd, buf, strlen(buf) + 1);
            close(fd);
            sleep(10);
        }

        closelog();
        exit(0);
    }

    讀者可以嘗試用普通用戶的身份執(zhí)行此程序。由于這里的open()函數(shù)必須具有root權(quán)限,因此,syslog會(huì)將錯(cuò)誤信息寫入到系統(tǒng)日志文件(如“/var/log/messages”)中,結(jié)果如下:

    Jan 30 18:20:08 localhost daemon_syslog[612]: open

    本文選自華清遠(yuǎn)見嵌入式培訓(xùn)教材《從實(shí)踐中學(xué)嵌入式Linux應(yīng)用程序開發(fā)》

   熱點(diǎn)鏈接:

   1、Linux下多進(jìn)程編程之exec函數(shù)語法及使用實(shí)例
   2、Linux下多進(jìn)程編程之fork()函數(shù)語法
   3、Linux下多進(jìn)程編程之fork()函數(shù)說明
   4、Linux下進(jìn)程的內(nèi)存結(jié)構(gòu)
   5、Linux下進(jìn)程的創(chuàng)建、執(zhí)行和終止

更多新聞>> 

主站蜘蛛池模板: 97亚洲熟妇自偷自拍另类图片 | 玩弄放荡人妇系列AV在线网站 | 成人一区av | 中文字幕无码精品亚洲35 | 欧美日本DVD一幕无码 | 国产www| 国产无遮挡又黄又爽奶头 | 日韩欧美亚洲综合久久影院d3 | 国产一二三四在线 | 免费观看的无遮挡AV | 中国美女一区二区三区 | 中文字幕日韩精品一区二区三区 | 又粗又黄a级裸片 | 久久久久成人片免费观看 | 性中国妓女毛茸茸视频 | 99久久无色码中文字幕人妻蜜柚 | 无套中出丰满人妻无码 | 无遮挡高潮国产免费观看 | 国产美女被遭强高潮免费网站 | a片疯狂做爰全过的视频 | 日本道二区免费v | 精品亚洲成A人在线观看青青 | 免费看又黄又爽又猛的视频 | 久久综合久中文字幕青草 | av色综合久久天堂av色综合 | 国产精品久久午夜夜伦鲁鲁 | 亚洲VA中文字幕无码久久 | 亚洲av永久无码精品一百度影院 | 内地性生生活影视大全 | 国产AV永久无码精品网站 | 野花在线观看免费视频 | 11孩岁女被A片黑人黑与白 | 中文字幕一线产区和二线区的区别 | 又摸又添下面添奶头视频 | 精品一区二区三区无码免费视频 | 日本人成网站18禁止久久影院 | 欧美私人家庭影院 | 99久久免费国产精品 | 日韩乱码人妻无码中文视频 | 和邻居少妇愉情中文字幕 | 九色中文|