終端、進程組、會話、守護進程(包括創建守護進程)詳解

終端

輸出設備和輸入設備的總稱爲終端。Unix中的terminal是僞終端,輸入和輸出都流經這個terminal。Unix系統中,用戶通過終端登錄系統後得到一個shell進程,這個終端成爲shell進程的控制終端。默認情況下(沒有重定向),每個進程的標準輸入,標準輸出和標準錯誤輸出都指向控制終端。

網絡終端

向XShell這樣的連接到遠程Unix系統的軟件成爲網絡終端,網絡終端工作流程如下圖。tty命令可以查看當前所處的網絡終端號。
在這裏插入圖片描述

進程組

進程組也稱之爲作業,代表一個或多個進程的集合,爲了方便管理多個進程。
進程組生存期:進程組創建到最後一個進程離開(終止或轉移到另一個進程組)。
一個進程可以爲自己或子進程設置進程組ID。
getpgrp函數可以獲取當前進程組ID
getpgid函數可以獲取指定進程的進程組ID
setpgid函數可以改變自己或子進程默認所屬的進程組

會話

進程組的集合成爲會話
創建一個會話必須注意一下五點:
(1)用來創建會話的進程不能是進程組組長(該調用進程是組長進程,則出錯返回),並且調用的進程變爲新會話首領
(2)用來創建會話的進程成爲一個新進程組的組長進程
(3)需要root權限(ubuntu不需要)
(4)新會話丟棄原有的控制終端,該會話沒有控制終端
(5)建立新會話時,先調用fork函數,父進程終止,子進程調用setsid
getsid函數用來獲取進程所屬的會話ID
setsid函數用來創建一個會話,並以自己的ID設置進程組ID,同時也是新會話的ID,即調用了setsid函數的進程,即是新的會長,也是新的組長
創建會話實例


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){
    pid_t pid;

    if(pid = fork() < 0){
        perror("fork error:");
        exit(1);
    }else if(pid == 0){
        printf("child process PID is %d\n",getpid());
        printf("group ID of the child is %d\n",getpgid(0));
        printf("session ID of the child is %d\n",getsid(0));

        sleep(5);
        setsid();//子進程非組長進程,故其可以創建會話

        printf("changed\n");

        printf("child process PID is %d\n",getpid());
        printf("group ID of the child is %d\n",getpgid(0));
        printf("session ID of the child is %d\n",getsid(0));

        exit(0);
    }else{
        sleep(10);//防止子進程變成孤兒進程
    }
    return 0;
}

在這裏插入圖片描述

守護進程

Daemon(精靈)進程,是Linux中的後臺服務進程,通常獨立於控制終端並且週期地執行某任務或等待處理某些發生的事件。一般採用以d結尾的名字,如httpd、vsftpd。
Linux中的一些系統服務進程,沒有控制終端、不能和用戶交互、不受用戶登錄註銷的影響,一直運行着,是守護進程。
創建守護進程最關鍵的一步是調用setsid函數創建一個新的會話,併成爲session leader。
創建守護進程步驟:
(1)創建子進程,父進程退出
(2)在子進程中創建新會話,調用setsid函數,爲了使子進程脫離控制終端而獨立
(3)改變當前目錄爲根目錄,chdir函數,爲了防止佔用可卸載的文件系統,也可以換成其他路徑
(4)重設文件權限掩碼(默認爲0022),umask函數,爲了防止繼承的文件創建屏蔽字拒絕某些權限,增加進程靈活性
(5)關閉文件描述符,0~2這三個文件描述符隨着進程創建而打開,分別指向標準輸入,標準輸出,標準錯誤輸出,由於該會話需要脫離終端,因此不需要這三個文件描述符。實際過程中不是直接close,因爲在使用文件描述符的時候從最小可用的單元找,如果關閉0號,則0號被認爲可用,但是0號一般不被其他程序使用,因此一般會把這三個文件描述符重定向到/dev/null黑洞中。
(6)開始執行守護進程核心工作
(7)守護進程退出處理程序模塊
創建一個守護進程向指定文件中寫入數據實例

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

void printfError(char *s){
    perror(s);
    exit(0);
}
int main(void){
    pid_t pid, sid;
    int ret;
	int i;
    pid = fork(); //第一步
    if(pid < 0){
        printfError("fork error");
    }
    if(pid > 0){
        return 0;
    }

    sid = setsid(); //第二步
    if(sid < 0){
printfError("setsid error");
    }

    ret = chdir("/"); //第三步
    if(ret < 0){
        printfError("chdir error");
    }

    umask(0002); //第四步

	signal(SIGHUP, SIG_IGN);    // 信號處理
    signal(SIGCHLD, SIG_IGN);

    close(STDIN_FILENO);  //第五步
    open("/dev/null",O_RDWR);
    dup2(0,STDOUT_FILENO);
    dup2(0,STDERR_FILENO);

    ret = open("/opt/workspace/linux/log",O_RDWR|O_TRUNC); //第六步
    if(ret == -1){
        printfError("open error");
    }

    pid = fork(); 
     if(pid > 0){
        for (i = 0; i < 60; ++i) {
            write(ret, "Hello World\n", 12);
            sleep(1);
        }
    }
    close(ret);
    return 0;
}

編譯運行,使用命令ps -ajx查看進程信息
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章