一、如何讓程式在背景運作
在之前的章節中,如果要運作程式,在指令提示行下輸入程式名後回車,程式被執行,然後等待程式運作完成,在程式運作的過程中,也可以用Ctrl+c中止它。
在實際開發中,我們需要讓程式在背景運作,沒有界面,沒有使用者輸入資料,例如socket服務端程式book250。
如果想讓程式在背景運作,有兩種方法。
1、加“&”符号
如果想讓程式在背景運作,執行程式的時候,指令的最後面加“&”符号。
如:
./book250 &
程式就在背景運作了。

在背景運作的程式,用Ctrl+c無法中斷,并且就算終端退出了,程式仍在背景運作。
如果終端退出了,背景運作的程式将由系統托管。
在第一張圖中,book250的父程序是12178,第二張圖中,book250的父程序是1。
為了不影響接下來的學習,用killall book250指令讓book250程式退出。
2、采用fork
另一種方法是采用fork,主程式執行fork,生成一個子程序,然後父程序退出,留下子程序繼續運作,子程序将由系統托管。
在book250的main函數後增加以下代碼:
if (fork()>0) return 0;
重新編譯後執行book250,運作效果如下:
上圖中,20752是fork後的子程序,它的父程序号是1,是系統程序(親爹沒了,天地日月為父)。
3、如何讓中止背景運作中程式
問題來了,程式在背景運作了,離開了終端控制,用Ctrl+c上也無法中止,那怎麼讓它停下來呢?暫時用一個笨方法,殺了它。
殺程式有兩個方法:
1)killall 程式名
killall book250
執行效果
2)先用“ps -ef|grep 程式名”找到程式的程序編号,然後用“kill 程序編号”。
二、signal信号
signal信号是Linux程式設計中非常重要的部分,接下來将詳細介紹信号的基本概念、實作和使用,和與信号的幾個系統調用(庫函數)。
signal信号是程序之間互相傳遞消息的一種方法,信号全稱為軟中斷信号,也有人稱作軟中斷,從它的命名可以看出,它的實質和使用很象中斷。信号是程序控制的一部分。
1、信号的基本概念
軟中斷信号(signal,又簡稱為信号)用來通知程序發生了事件。程序之間可以通過調用kill庫函數發送軟中斷信号。Linux核心也可能給程序發送信号,通知程序發生了某個事件(例如記憶體越界)。
注意,信号隻是用來通知某程序發生了什麼事件,無法給程序傳遞任何資料,程序對信号的處理方法有三種:
1)第一種方法是,忽略某個信号,對該信号不做任何處理,就象未發生過一樣。
2)第二種是設定中斷的處理函數,收到信号後,由該函數來處理。
3)第三種方法是,對該信号的處理采用系統的預設操作,大部分的信号的預設操作是終止程序。
2、信号的類型
發出信号的原因很多,這裡按發出信号的原因簡單分類,以了解各種信号:
信号名 | 信号值 | 預設處 理動作 | 發出信号的原因 |
---|---|---|---|
SIGHUP | 1 | A | 終端挂起或者控制程序終止 |
SIGINT | 2 | 鍵盤中斷Ctrl+c | |
SIGQUIT | 3 | C | 鍵盤的退出鍵被按下 |
SIGILL | 4 | 非法指令 | |
SIGABRT | 6 | 由abort(3)發出的退出指令 | |
SIGFPE | 8 | 浮點異常 | |
SIGKILL | 9 | AEF | 采用kill -9 程序編号 強制殺死程式。 |
SIGSEGV | 11 | 無效的記憶體引用 | |
SIGPIPE | 13 | 管道破裂:寫一個沒有讀端口的管道 | |
SIGALRM | 14 | 由alarm(2)發出的信号 | |
SIGTERM | 15 | 采用“kill 程序編号”或“killall 程式名”通知程式。 | |
SIGUSR1 | 30,10,16 | 使用者自定義信号1 | |
SIGUSR2 | 31,12,17 | 使用者自定義信号2 | |
SIGCHLD | 20,17,18 | B | 子程序結束信号 |
SIGCONT | 19,18,25 | 程序繼續(曾被停止的程序) | |
SIGSTOP | 17,19,23 | DEF | 終止程序 |
SIGTSTP | 18,20,24 | D | 控制終端(tty)上按下停止鍵 |
SIGTTIN | 21,21,26 | 背景程序企圖從控制終端讀 | |
SIGTTOU | 22,22,27 | 背景程序企圖從控制終端寫 |
處理動作一項中的字母含義如下
A 預設的動作是終止程序。
B 預設的動作是忽略此信号,将該信号丢棄,不做處理。
C 預設的動作是終止程序并進行核心映像轉儲(core dump),核心映像轉儲是指将程序資料在記憶體的映像和程序在核心結構中的部分内容以一定格式轉儲到檔案系統,并且程序退出執行,這樣做的好處是為程式員
提供了友善,使得他們可以得到程序當時執行時的資料值,允許他們确定轉儲的原因,并且可以調試他們的程式。
D 預設的動作是停止程序,進入停止狀況以後還能重新進行下去。
E 信号不能被捕獲。
F 信号不能被忽略。
3、signal庫函數
signal庫函數可以設定程式對信号的處理方式。
函數聲明:
sighandler_t signal(int signum, sighandler_t handler);
參數signum表示信号的編号。
參數handler表示信号的處理方式,有三種情況:
1)SIG_IGN:忽略參數signum所指的信号。
2)一個自定義的處理信号的函數,信号的編号為這個自定義函數的參數。
3)SIG_DFL:恢複參數signum所指信号的處理方法為預設值。
程式員不關心signal的傳回值。
4、信号有什麼用
服務程式運作在背景,如果想讓中止它,強行殺掉不是個好辦法,因為程式被殺的時候,程式突然死亡,沒有釋放資源,會影響系統的穩定,用Ctrl+c中止與殺程式是相同的效果。
如果能向背景程式發送一個信号,背景程式收到這個信号後,調用一個函數,在函數中編寫釋放資源的代碼,程式就可以有計劃的退出,安全而體面。
信号還可以用于網絡服務程式抓包等,這是較複雜的應用場景,暫時不介紹。
5、信号應用示例
在實際開發中,在main函數開始的位置,程式員會先屏蔽掉全部的信号。
for (int ii=0;ii<100;ii++) signal(ii,SIG_IGN);
這麼做的目的是不希望程式被幹擾。然後,再設定程式員關心的信号的處理函數。
程式員關心的信号有三個:SIGINT、SIGTERM和SIGKILL。
程式在運作的程序中,如果按Ctrl+c,将向程式發出SIGINT信号,信号編号是2。
采用“kill 程序編号”或“killall 程式名”向程式發出的是SIGTERM信号,編号是15。
采用“kill -9 程序編号”向程式發出的是SIGKILL信号,編号是9,此信号不能被忽略,也無法捕獲,程式将突然死亡。
是以,程式員隻要設定SIGINT和SIGTERM兩個信号的處理函數就可以了,這兩個信号可以使用同一個處理函數,函數的代碼是釋放資源。
示例(book257.cpp)
/*
* 程式名:book257.cpp,此程式用于示範用信号通知背景服務程式退出。
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void EXIT(int sig)
{
printf("收到了信号%d,程式退出。\n",sig);
// 在這裡添加釋放資源的代碼
exit(0); // 程式退出。
}
int main()
{
for (int ii=0;ii<100;ii++) signal(ii,SIG_IGN); // 屏蔽全部的信号
signal(SIGINT,EXIT); signal(SIGTERM,EXIT); // 設定SIGINT和SIGTERM的處理函數
while (1) // 一個死循環
{
sleep(10);
}
}
運作效果
不管是用Ctrl+c還是kill,程式都能體面的退出。
三、發送信号
Linux作業系統提供了kill指令向程式發送信号,C語言也提供了kill庫函數,用于在程式中向其它程序或者線程發送信号。
int kill(pid_t pid, int sig);
kill函數将參數sig指定的信号給參數pid 指定的程序。
參數pid 有幾種情況:
1)pid\>0 将信号傳給程序号為pid 的程序。
2)pid=0将信号傳給和目前程序相同程序組的所有程序,常用于父程序給子程序發送信号,注意,發送信号者程序也會收到自己發出的信号。
3)pid=-1将信号廣播傳送給系統内所有的程序,例如系統關機時,會向所有的登入視窗廣播關機資訊。
sig:準備發送的信号代碼,假如其值為零則沒有任何信号送出,但是系統會執行錯誤檢查,通常會利用sig值為零來檢驗某個程序是否仍在運作。
傳回值說明: 成功執行時,傳回0;失敗傳回-1,errno被設為以下的某個值。
EINVAL:指定的信号碼無效(參數 sig 不合法)。
EPERM:權限不夠無法傳送信号給指定程序。
ESRCH:參數 pid 所指定的程序或程序組不存在。
四、課後作業
本章節的重點是介紹信号的應用場景,屬于概念性的知識,代碼其實很簡單,隻要各位了解了信号原理和應用就可以了。