IT培训机构|91免费精品视频|专注编程培训|91免费精品|软件开发培训_91免费国产视频_华清远见教育


Linux守護進程

分享到:
           

    1.守護進程概述

    守護進程,也就是通常所說的daemon進程,是Linux中的后臺服務進程。它是一個生存期較長的進程,通常獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。守護進程常常在系統引導載入時啟動,在系統關閉時終止。Linux有很多系統服務,大多數服務都是通過守護進程實現的。同時,守護進程還能完成許多系統任務,例如,作業規劃進程crond、打印進程lqd等(這里的結尾字母d就是daemon的意思)。

    由于在Linux中,每一個系統與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附于這個終端,這個終端稱為這些進程的控制終端,當控制終端被關閉時,相應的進程都會自動關閉。但是守護進程卻能夠突破這種限制,它從被執行開始運轉,直到接收到某種信號或者整個系統關閉時才會退出。如果想讓某個進程不因為用戶、終端或者其他的變化而受到影響,那么就必須把這個進程變成一個守護進程。可見,守護進程是非常重要的。

    2.編寫守護進程

    編寫守護進程看似復雜,但實際上也是遵循一個特定的流程,只要將此流程掌握了,就能很方便地編寫出自己的守護進程。下面就分4個步驟來講解怎樣創建一個簡單的守護進程。在講解的同時,會配合介紹與創建守護進程相關的幾個系統函數,希望讀者能很好地掌握。

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

    到這里,有心的讀者可能會問,父進程創建了子進程后退出,此時該子進程不就沒有父進程了嗎?守護進程中確實會出現這么一個有趣的現象:由于父進程已經先于子進程退出,就會造成子進程沒有父進程,從而變成一個孤兒進程。在Linux中,每當系統發現一個孤兒進程時,就會自動由1號進程(也就是init進程)收養它,這樣,原先的子進程就會變成init進程的子進程。其關鍵代碼如下:

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

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

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

    每個進程組都有一個組長進程,其組長進程的進程號等于進程組ID,且該進程ID不會因組長進程的退出而受到影響。

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


圖1 進程組和會話期之間的關系

    接下來就可以具體介紹setsid()的相關內容。

    ① setsid()函數的作用。setsid()函數用于創建一個新的會話組,并擔任該會話組的組長。調用setsid()有以下3個作用:
    ● 讓進程擺脫原會話的控制。
    ● 讓進程擺脫原進程組的控制。
    ● 讓進程擺脫原控制終端的控制。

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

    ② setsid()函數格式。表1列出了setsid()函數的語法要點。

表1 setsid()函數語法要點

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

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

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

    (5)關閉文件描述符。同文件權限掩碼一樣,用fork()函數新建的子進程會從父進程那里繼承一些已經打開的文件。這些被打開的文件可能永遠不會被守護進程讀或寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法被卸載。

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

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

    這樣,一個簡單的守護進程就建立起來了。創建守護進程的流程圖如圖2所示。


圖2 創建守護進程流程圖

    下面是實現守護進程的一個完整實例,該實例首先按照以上的創建流程建立了一個守護進程,然后讓該守護進程每隔10s向日志文件/tmp/daemon.log寫入一句話。

    /* daemon.c 創建守護進程實例 */
    #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); /* 父進程退出 */
        }

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

        /* 這時創建完守護進程,以下開始正式進入守護進程工作 */
        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);
    }

    將該程序下載到開發板上,可以看到該程序每隔10s就會在對應的文件中輸入相關內容,并且使用ps可以看到該進程在后臺運行,結果如下:

    $ 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.守護進程的出錯處理

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

    syslog是Linux中的系統日志管理服務,通過守護進程syslogd來維護。該守護進程在啟動時會讀一個配置文件“/etc/syslog.conf”,該文件決定了不同種類的消息會發送到何處。例如,緊急消息可被送到系統管理員并在控制臺上顯示,而警告消息則可被記錄到一個文件中。

    該機制提供了3個syslog相關函數,分別為openlog()、syslog()和closelog(),下面就分別介紹這3個函數。

    1)syslog相關函數說明

    通常,openlog()函數用于打開系統日志服務的一個鏈接;syslog()函數用于向日志文件中寫入消息,在這里可以規定消息的優先級、消息輸出格式等;closelog()函數用于關閉系統日志服務的鏈接。

    2)syslog相關函數格式

    表2列出了openlog()函數的語法要點。

表2 openlog()函數語法要點

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

    表3列出了syslog()函數的語法要點。

表3 syslog()函數語法要點

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

    表3.4列出了closelog()函數的語法要點。

表3.4 closelog()函數語法要點

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

    3)使用實例

    這里將上一個示例程序用syslog服務進行重寫,其中有區別的地方用加粗的字體表示,源代碼如下:

    /* syslog_daemon.c利用syslog服務的守護進程實例 */
    #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); /* 父進程退出 */
        }

        /* 打開系統日志服務,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);
        }

        /* 這時創建完守護進程,以下開始正式進入守護進程工作 */
        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);
    }

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

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

    本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》

   熱點鏈接:

   1、Linux下多進程編程之exec函數語法及使用實例
   2、Linux下多進程編程之fork()函數語法
   3、Linux下多進程編程之fork()函數說明
   4、Linux下進程的內存結構
   5、Linux下進程的創建、執行和終止

更多新聞>> 

主站蜘蛛池模板: 石家庄德迈机电设备有限公司| 津成电线电缆价格,天津津成线缆,津成线缆销售电话,天津津成线缆批发电话-天津市津成电线电缆有限公司 | 污泥处理设备|污泥固化固液分离压泥机压滤机厂家-山东冠诚开山贸易 | 土壤养分测定仪-土壤养分速测仪-生产厂家-托普云农土壤仪器站 | 深圳喜运达物流官网_东南亚物流|海运|专线|海外仓_泰国菲律宾马来西亚海运|物流专线|海外仓 | 雾度计-雾度仪-透光率测试仪-3nh品牌雾度仪生产厂家 | 门禁控制器-自动道闸-停车场系统-车位引导系统-车牌识别系统-楼宇对讲-可视门铃-门禁一卡通-河北京鹏电子科技有限公司 | 立式/卧式/潜水/液下/螺杆/耐磨/渣浆泵|泥浆泵|离心泵,厂家 - 河北聚盛泵业制造有限公司 | 上海钧尚电器有限公司 - Faulhaber电机 AMETEK pittman电机 AMETEK ROTRON军用航空风机 Exlar电动缸 MAE电机 MCG电机 CP电动工具 马头工具 AMCI驱动器 直流电机 减速箱 直流伺服电机,无刷电机,直线电机 直流防爆电机 防爆电机 汽车助力转向电机 EPS电机 faulhaber motor faulhaber gearbox NANOTEC电机 ELWOOD电机 PHYTRON电机 EXLAR伺服电动缸 高力矩、高性能直流电机,音圈电机,风机,直流风机,航空风机 | 呼吸家官网|肺功能检测仪生产厂家|国产肺功能仪知名品牌|肺功能检测仪|肺功能测试仪|婴幼儿肺功能仪|弥散残气肺功能仪|肺功能测试系统|广州红象医疗科技有限公司|便携式肺功能仪|大肺功能仪|呼吸康复一体机|儿童肺功能仪|肺活量计|医用简易肺功能仪|呼吸康复系统|肺功能仪|弥散肺功能仪(大肺)|便携式肺功能检测仪|肺康复|呼吸肌力测定肺功能仪|肺功能测定仪|呼吸神经肌肉刺激仪|便携式肺功能 | 郑州建网站,郑州做网站,郑州网站建设,郑州网站制作,郑州高端定制网站,郑州APP开发 | 智能试剂柜-疾控|高校实验室|医院药品智能试剂管理柜-北京晶品赛思 | 内蒙古慧申设计顾问有限公司-官网 | 压滤机专用泵|柱塞泥浆泵|咸阳华星泵业有限公司 | 银泰洁净--净化工程总承包,20年精耕细作,专为净化而来_银泰洁净--净化工程总承包,20年精耕细作,专为净化而来 | 上海办公室租赁-写字楼出租、创意产业园区厂房招商、孵化器众创联合办公空间出租网 | 中国家居资讯网-家居建材-知名十大品牌-著名品牌资讯网 | 吸管包装机_塑料杯包装机_纸杯包装机_吸管挤出机_温州恩博机械有限公司 | 专注全球电机测试-电机测试系统-AIP艾普【官网】 | 石墨热场|PECVD石墨舟|碳碳框|燃料电池双极板|半导体石墨|光纤用石墨模具|石墨电极加工|石墨制品|上海弘竣新能源材料有限公司 | 南昌利驰科技有限公司| 沈阳资质代办_代办建筑资质「快」-【华廷元】沈阳代办公司首页- 沈阳机电一体化电热锅炉_沈阳蓄热式电锅炉_沈阳壁挂式电锅炉【沈阳远鹏电热供水设备工程安装有限公司】 | 无锡大型数控龙门铣,喷涂加工,回火抛丸加工,精密不锈钢焊接机床身机床底座制造加工-无锡美高帝机械有限公司 | 随州市恒利达包装制造有限公司| 叶片泵,液压电机,油泵电机,变量叶片泵,油压电机-广东俊泰液压科技有限公司 | 小程序定制,小程序开发,北京小程序公司,网站建设,网站制作,北京网站建设,北京网站制作 | 荣事达电动洗地机_全自动工业洗地车_扫地机_清洁设备工厂 | 环链电动葫芦_钢丝绳电动葫芦_电动葫芦厂家_上海沪工起重机械有限公司 | 南山荔枝,深圳南荔农业荔枝园自销-质保优放心选购 | 四川方法数码科技有限公司| 长沙变频器维修,变频器维修,ABB变频器维修,西门子变频器维修,施耐德变频器维修,伺服驱动器维修,工业机器人维修,20年专业工控电气维修,长沙文铖电气设备有限公司_长沙文铖电气设备有限公司 | 和部长一起去出差旅全程,构建和谐上下级关系-蜜桃视频 | 网带烘干机-不锈钢网带-乙型网带-不锈钢链板-网带输送机-宁津县天惠机械制造有限公司 | 五金冲压件生产厂家_加工五金拉伸件-沧州浚鼎机械制造有限公司-沧州浚鼎机械制造有限公司 | 热熔胶涂布机-青岛三人行机械有限公司| 天空彩票天下彩,天空彩天空彩票免费资料,天空彩票与你同行开奖,天下彩正版资料大全 | 合金锤头_破碎机锤头_耐磨锤头_巩义市东辰铸造 高耐磨合金锤头厂家 | 组合包装箱,折叠包装箱,烟台木箱,烟台包装箱-烟台顺达包装有限责任公司 | 压力蒸汽灭菌器_脉动真空灭菌器_环氧乙烷灭菌器_等离子灭菌器_广州市科洋 | 卫生间隔断-东莞卫生间隔断-东莞卫生间隔断厂家-公共卫生间隔断-东莞市康丰家居建材有限公司 | 婴童人才网-孕婴童、母婴行业人才招聘求职系统|