歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

linux系統編程之進程(五) 終端、作業控制與守護進程

一、終端的概念

在UNIX系統中,用戶通過終端登錄系統後得到一個Shell進程,這個終端成為Shell進程的控制終端(Controlling Terminal),控制終端是保存在PCB中的信息,而我們知道fork會復制PCB中的信息,因此由Shell進程啟動的其它進程的控制終端也是這個終端。默認情況下(沒有重定向),每個進程的標准輸入、標准輸出和標准錯誤輸出都指向控制終端,進程從標准輸入讀也就是讀用戶的鍵盤輸入,進程往標准輸出或標准錯誤輸出寫也就是輸出到顯示器上。在控制終端輸入一些特殊的控制鍵可以給前台進程發信號,例如Ctrl-C表示SIGINT,Ctrl-\表示SIGQUIT。

每個進程都可以通過一個特殊的設備文件/dev/tty訪問它的控制終端。事實上每個終端設備都對應一個不同的設備文件,/dev/tty提供了一個通用的接口,一個進程要訪問它的控制終端既可以通過/dev/tty也可以通過該終端設備所對應的設備文件來訪問。ttyname函數可以由文件描述符查出對應的文件名,該文件描述符必須指向一個終端設備而不能是任意文件。在linux上的命令tty 也可以查看到當前的終端。

比如我們在圖形界面下打開一個終端可能是/dev/pts/0, 第二個可能是/dev/pts/1 ...(網絡終端)

而切換到字符界面下可能是/dev/tty1 ...(虛擬終端)

二、作業控制

事實上,Shell分前後台來控制的不是進程而是作業(Job)或者進程組(Process Group)。一個前台作業可以由多個進程組成,一個後台作業也可以由多個進程組成,Shell可以同時運行一個前台作業和任意多個後台作業,這稱為作業控制(JobControl)。例如用以下命令啟動5個進程(這個例子出自APUE):

$ proc1 | proc2 &

$ proc3 | proc4 | proc5

其中proc1和proc2屬於同一個後台進程組,proc3、proc4、proc5屬於同一個前台進程組,Shell進程本身屬於一個單獨的進程組。這些進程組的控制終端相同,它們屬於同一個Session,一個Session與一個控制終端相關。當用戶在控制終端輸入特殊的控制鍵(例如Ctrl-C)時,內核會發送相應的信號(例如SIGINT)給前台進程組的所有進程。各進程、進程組、Session的關系如下圖所示。

在上面的例子中,proc3、proc4、proc5被Shell放到同一個前台進程組,其中有一個進程是該進程組的Leader,Shell調用wait等待它們運行結束。一旦它們全部運行結束,Shell就調用tcsetpgrp函數將自己提到前台繼續接受命令。但是注意,如果proc3、proc4、proc5中的某個進程又fork出子進程,子進程也屬於同一進程組,但是Shell並不知道子進程的存在,也不會調用wait等待它結束。換句話說,proc3 | proc4 | proc5是Shell的作業,而這個子進程不是,這是作業和進程組在概念上的區別。一旦作業運行結束,Shell就把自己提到前台,如果原來的前台進程組還存在(如果這個子進程還沒終止),則它自動變成後台進程,被init進程接管。

三、守護進程

守護進程是在後台運行不受控端控制的進程,通常情況下守護進程在系統啟動時自動運行,用戶關閉終端窗口或注銷也不會影響守護進程的運行,只能kill掉。守護進程的名稱通常以d結尾,比如sshd、xinetd、crond等

我們用ps axj命令查看系統中的進程,凡是TPGID(前台進程組ID)一欄寫著-1的都是沒有控制終端的進程,或者TTY一欄為?的,也就是守護進程。

四、創建守護進程步驟

調用fork(),創建新進程,它會是將來的守護進程

在父進程中調用exit,保證子進程不是進程組組長

調用setsid創建新的會話期

將當前目錄改為根目錄

將標准輸入、標准輸出、標准錯誤重定向到/dev/null

成功調用setsid函數的結果是:

創建一個新的Session,當前進程成為Session Leader,當前進程的id就是Session的id。

創建一個新的進程組,當前進程成為進程組的Leader,當前進程的id就是進程組的id。

如果當前進程原本有一個控制終端,則它失去這個控制終端,成為一個沒有控制終端的進程。

示例程序:

/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int setup_daemon(int, int);
/* 守護進程一直在後台運行且無控制終端 */
int main(int argc, char *argv[])
{
// daemon(0, 0)
setup_daemon(0, 0);
printf("test ... \n"); // 無輸出
for(;;) ;
return 0;
}
int setup_daemon(int nochdir, int noclose)
{
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid > 0)
exit(EXIT_SUCCESS);
/* 調用setsid的進程不能為進程組組長,故fork之後將父進程退出 */
setsid(); // 子進程調用後生成一個新的會話期
if (nochdir == 0)
chdir("/"); //更改當前目錄為根目錄
if (noclose == 0)
{
int i;
for (i = 0; i < 3; ++i)
close(i);
open("/dev/null", O_RDWR); // 將標准輸入,標准輸出等都重定向到/dev/null
dup(0);
dup(0);
}
return 0;
}

執行程序再ps axj 一下:

simba@ubuntu:~/Documents/code/linux_programming/APUE/process$ ./daemon

simba@ubuntu:~/Documents/code/linux_programming/APUE/process$ ps axj

PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND

...........................................................................................................................

1  7678  7678  7678 ?           -1 Rs    1000   0:03 ./daemon

可以看出守護進程的ID也是進程組的ID,也是會話期的ID,此外這個會話期沒有前台進程組。

五、使用daemon函數實現守護進程

功能:創建一個守護進程

原型:int daemon(int nochdir, int noclose);

參數:

nochdir:=0將當前目錄更改至“/”

noclose:=0將標准輸入、標准輸出、標准錯誤重定向至“/dev/null”

Copyright © Linux教程網 All Rights Reserved