1. 程式人生 > >終端,作業控制與守護程序

終端,作業控制與守護程序

程序組

   概念:一個或多個程序的集合。

   每一個程序除了有一個程序ID外,還屬於一個程序組,同時也只能屬於一個程序組。每個程序組都有一個唯一的程序組ID,且都可以有一個組長程序。一般在程序組中,第一個程序是組長程序。

   為啥要建立程序組呢?為了方便對程序進行管理。假設要完成一個任務,需要同時併發10個程序,當用戶處於某種原因要終止這個任務時,如若沒有程序組,就需要手動的一個一個的去殺死這10個程序,並且嚴格按照進行間的關係順序,否則會打亂程序間的關係,有了程序組,就可以將這10個程序設定一個程序組,他們共有一個組號(pgrp),並且選取一個程序作為組長,(通常選取“輩分”最高的那個,通常該程序的ID就是該程序組的ID

)。現在就可以通過殺死整個程序組來關閉這10個程序。組長程序可以建立程序組,建立該組中的程序,然後終止。只要在某個程序組中一個程序存在,則該組程序就存在,這與組長程序是否終止無關。


作業

   shell分前後臺來控制的是作業或程序組,不是程序。一個前臺作業可以由多個程序組成,一個後臺可以由多個程序組成。

   作業控制shell可以執行一個前臺作業和任意多個後臺作業。  

   1、 與作業控制有關的訊號:

     我們cat為例(把他放在後臺,從終端讀)

  

(1)由於cat需要讀標準輸入(也就是終端輸入),而後臺程序是不能讀終端輸入的,因此核心發SIGTTIN訊號給程序, 該訊號的預設處理動作是使程序停止。

 

    [[email protected] 7-31_class16]$ cat &

    [1] 895

    [[email protected] 7-31_class16]$ //再嗯回車

    

    [1]+  Stopped                 cat

    [[email protected] 7-31_class16]$


       (2)jobs命令:檢視當前前後前後臺有哪些作業

   (3)fg命令:可以將某個作業提至前臺執行:

     a、如果該作業的程序組正在後臺執行則提至前臺執行;

     b、如果該作業處於停止狀態,則給程序組的每個程序發SIGCONT訊號使它繼續行。

     引數%1表示將第1個作業提至前臺執行。

     cat提到前臺執行後,掛起等待終端輸入,當 輸入hello並回車後,cat打印出同樣的一行,然後繼續掛起等待輸入。緊接著, 如果輸入Ctrl-Z則向所有前 臺程序發SIGTSTP 訊號,該訊號的預設動作是使程序停止,cat繼續以後臺作業的形式存在。

    wKiom1eeGCDAiDgdAACD8sRv8ho255.png-wh_50     

        (4)bg命令:可以讓某個停止的作業在後臺繼續執行。也需要給該作業的程序組的每個程序發SIGCONT訊號。cat程序繼續執行,又要讀終端輸入,然而它在後臺不能讀終端輸入,所以又收到SIGTTIN訊號而停止。

    wKioL1eeGG_AEMOOAAAYbjOmneU583.png-wh_50

  2、給一個停止的程序發SIGTERM與SIGKILL訊號的區別:

   (1)kill 命令給一個停止的程序傳送SIGTERM訊號時並不會立即被處理,而是等到合適的時候處理,預設處理動作是終止程序。

    wKioL1eeGM6DcLAHAADbAAhvSB4284.png-wh_50

  (2)SIGKILL訊號既不能被阻塞也不能被忽略 ,也不能用自定義函式捕捉 ,只能按系統的預設動作立刻處理(SIGSTOP 訊號也與此類似)。(這樣保證了不管什麼樣的程都能用 SIGKILL終止或者用SIGSTOP停止, 當系統出現異 常時管理員總是有辦法殺掉有問題的程序或者暫時停掉懷疑有問題的程序。)

    wKiom1eeGTKiwNJPAACbeNGUS8A634.png-wh_50


作業與程序組的區別:

  如果一個作業中的某個程序又建立了一個子程序,該子程序不屬於作業。一旦作業執行結束,shell就把它提到前臺,如果原來前臺程序還存在,它自動變為後臺程序組。


會話

  概念:一個或多個程序組的集合,但只能有一個前臺程序組。

   控制程序:建立與控制終端連線的會話首程序。每個會話都有一個會話首領(leader)即建立會話的程序。

   sys_setsid()呼叫能建立一個會話。

   注意:只有當前程序不是程序組的組長時,才能建立一個新的會話。

   一次會話中應該包括:一個控制程序,一個前臺程序和任意多個後臺程序。


終端

   控制終端:會話的領頭程序開啟一個終端,之後,該終端就成為該會話的控制終端。一個會話只能有一個控制終端。

   程序屬於一個程序組,程序組屬於一個會話,會話可能有也可能沒有控制終端。一般而言,當用戶在某個終端上登入時,一個新的會話就開始了。程序組由組中的領頭程序標識,領頭程序的程序識別符號就是程序組的組識別符號。類似地,每個會話也對應有一個領頭程序

   同一會話中的程序通過該會話的領頭程序和一個終端相連,該終端作為這個會話的控制終端。一個會話只能有一個控制終端,而一個控制終端只能控制一個會話。使用者通過控制終端,可以向該控制終端所控制的會話中的程序傳送鍵盤訊號。

   當我們開啟多個終端視窗時,實際上就建立了多個終端會話。每個會話都會有自己的前臺工作和後臺工作


檢視終端裝置

測試程式碼:

#include<stdio.h>

#include<unistd.h>

int main()

{

       printf("fd :%d->%s\n",0,ttyname(0));

       printf("fd :%d->%s\n",1,ttyname(1));

       printf("fd :%d->%s\n",2,ttyname(2));

       return 0;

}


終端1執行結果:

[[email protected] 7-31_class16]$ ./a.out

fd :0->/dev/pts/2

fd :1->/dev/pts/2

fd :2->/dev/pts/2

終端2執行結果:

[[email protected] 7-31_class16]$ ./a.out

fd :0->/dev/pts/0

fd :1->/dev/pts/0

fd :2->/dev/pts/0

[[email protected] 7-31_class16]$

終端3執行結果:

[[email protected] 7-31_class16]$ ./a.out

fd :0->/dev/pts/3

fd :1->/dev/pts/3

fd :2->/dev/pts/3

[[email protected] 7-31_class16]$


守護程序(精靈程序)

  是執行在後臺的一種特殊程序,獨立於控制終端並且週期性的執行某種任務或等待處理某些發生的事件。守護程序是一種很有用的程序,Linux的大多數伺服器就是用守護程序實現的。 守護程序完成許多系統任務。大多數守護程序以d結尾,凡是中括號括起來的都是核心執行緒。

root         2  0.0  0.0      0     0 ?        S    Jul27   0:00 [kthreadd]

root        17  0.0  0.0      0     0 ?        S    Jul27   0:00 [kacpid]

root        22  0.0  0.0      0     0 ?        S    Jul27   0:00 [ksuspend_usbd]

root        23  0.0  0.0      0     0 ?        S    Jul27   0:04 [khubd]

root        24  0.0  0.0      0     0 ?        S    Jul27   0:00 [kseriod]

root        28  0.0  0.0      0     0 ?        S    Jul27   0:00 [khungtaskd]

root        30  0.0  0.0      0     0 ?        SN   Jul27   0:00 [ksmd]

root        38  0.0  0.0      0     0 ?        S    Jul27   0:00 [pciehpd]

root        40  0.0  0.0      0     0 ?        S    Jul27   0:00 [kpsmoused]

root        72  0.0  0.0      0     0 ?        S    Jul27   0:00 [kstriped]

root      1103  0.0  0.0      0     0 ?        S    Jul27   0:00 [kauditd]

root      2001  0.0  0.0      0     0 ?        S<   Jul27   0:00 [krfcommd]


守護程序的特性:

   1、後臺執行(最重要的)

    2、守護程序必須與其執行前的環境隔離開來。

    3、啟動方式有其特殊之處:可以在linux系統啟動時從啟動指令碼/etc/rc.d中啟動,可以在作業規劃程序cround啟動。還可以由使用者終端(通常是shell)執行。


後臺程序與守護程序的區別

  1、後臺程序與特定終端關聯,與會話緊密相聯。

  2、守護程序是後臺程序的一種,與終端無關


建立守護程序

   1、呼叫umask將檔案模式建立遮蔽欄位設定為0

   2、呼叫fork函式,父程序退出。

    原因:1)如果該守護程序是作為一條簡單的shell命令啟動的,那麼父程序終止使得shell認為該命令已經執行完畢。

        2)保證子程序不是一個 程序組的組長程序。

   3、呼叫setsid建立一個新會話。

      setsid會導致:

        1)呼叫程序成為新會話的首程序。

        2)呼叫程序成為一個程序組的組長程序 。

        3)呼叫程序沒有控制終端。(再次fork一次,保證 daemon程序,之後不會開啟tty裝置)      

   4、將當前工作目錄更改為根目錄。

   5、關閉不在需要的檔案描述符。

   6、 其他:忽略SIGCHLD訊號。


建立守護程序程式碼:

測試1:

#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include<signal.h>
#include <fcntl.h>

void my_daemon()
{
    umask(0);// 設定檔案掩碼為0

    pid_t id = fork();
    if (id == 0)
    {
        // child
        setsid();//  設定 新會話

        chdir("/");// 更換 目錄

        close(0);
        close(1);
        close(2);

        signal(SIGCHLD,SIG_IGN);// 註冊子程序退出忽略訊號
    }
    else
    {
        sleep(14);
        exit(0);// 終止父程序
    }

    close(0);// 關閉標準輸入
    int fd0 = open("dev/null", O_RDWR);// 重定向所有標準輸出、 錯誤到/dev/null
    dup2(fd0, 1);
    dup2(fd0, 2);
}
int main()
{
    my_daemon();
    while(1);

}


測試2;

#include<stdio.h>

#include<stdlib.h>

#include<signal.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/stat.h>

void create_daemon(void)

{

    int i;

    int fd0;

    pid_t pid;

    struct sigaction sa;

    umask(0);//設定檔案掩碼為0

    if(pid = fork() < 0){

    }else if(pid != 0){

        exit(0);//第一次fork()終止父程序

    }

    setsid();//設定新會話

    sa.sa_handler = SIG_IGN;

    sigemptyset(&sa.sa_mask);

    sa.sa_flags = 0;

    if(sigaction(SIGCHLD,&sa,NULL) < 0){//註冊子程序退出忽略訊號

        return ;

    }

    if(pid = fork() < 0){//為何要fork兩次?->再次fork,終止子程序,保證孫子程序不是話首程序,從而保證後續不會再和其他終端關聯

        printf("fork error !\n");

        return ;

    }else if(pid != 0){

    exit(0);

    }

    if(chdir("/")<0){//更改工作目錄到根

        printf("child dir error\n");

        return ;

    }

    close(0);

    fd0 = open("/dev/null",O_RDWR);//關閉標準輸入,重定向所有標準(輸入輸出錯誤)到/dev/null

    dup2(fd0,1);

    dup2(fd0,2);

}

int main()

{

    create_daemon();

    while(1)

    {

        sleep(1);

    }

    return 0;

}

執行監視:

    wKiom1eeG0ehYqZaAABKGoO4fpg693.png-wh_50

關閉標準輸入,重定向所有標準(輸入輸出錯誤)到/dev/null

    wKioL1eeG1fBLtM2AAAypRdpyKI759.png-wh_50


本文出自 “緣去即成幻” 部落格,請務必保留此出處http://liu153.blog.51cto.com/10820414/1832671