Linux多程序開發III
阿新 • • 發佈:2021-08-09
1.訊號,事件發生時對程序的通知機制,有時稱為軟體中斷,是在軟體層次上對中斷機制的一種模擬,是一種非同步通訊方式。傳送程序的訊號一般來源於核心。
引發核心為程序產生訊號的事件:
5.常用訊號。
- 對於前臺程序,使用者可以通過鍵盤輸入特殊的中斷字元來給它傳送訊號。
- 硬體發生異常,即硬體檢測到一個錯誤條件並通知核心,再由核心傳送相應訊號給相關程序。
- 系統狀態變化,如alarm定時器到期,程序執行的CPU時間超限等。
- 執行kill命令或呼叫kill函式。
- 讓程序知道發生了特定的事情。
- 強迫程序執行它自己程式碼中的訊號處理程式。
- 簡單
- 不能攜帶大量資訊
- 滿足特定條件才傳送
- 優先順序比較高
kill -l
SIGIN <Ctrl + C>組合鍵,使用者終端向正在執行的由此終端啟動的程式傳送訊號 終止程序 SIGQUIT <Ctrl + \>組合鍵,使用者終端向正在執行的由此終端啟動的程式傳送訊號 終止程序 SIGKILL 無條件終止程序,不能被忽略,處理和阻塞 終止程序,可以殺死任何程序 SIGSEGV 知識程序進行了無效記憶體訪問(段錯誤) 終止程序併產生core檔案 SIGPIPE 破裂管道,向一個沒有讀端的管道寫資料 終止程序 SIGCHILD 子程序結束時,父程序會收到該訊號 忽略該訊號 SIGCONT 如果程序已停止,則使其繼續執行 繼續/忽略 SIGSTOP 停止程序的執行。訊號不能被忽略,處理和阻塞 終止程序
6.訊號的5中預設處理動作。
- Term 終止程序
- Ign 當前程序忽略該訊號
- Core 終止程序並生成一個core檔案
- stop 暫停當前程序
- Cont 繼續執行當前被暫停的程序
- 產生
- 未決
- 遞達
ulimit -a // 檢視core檔案大小 ulimit -c 1024 // 修改core檔案大小 (gdb) core-file core // 在gdb除錯下檢視core檔案中的錯誤
10.訊號相關的函式。
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); - 作用:給某個程序pid傳送某個訊號 - pid: 需要傳送訊號的程序,>0 傳送給指定程序;=0 傳送給當前程序組;=-1 傳送給每一個有許可權接收該訊號的程序;<-1 pid=某個程序組的ID取反 - sig: 需要傳送訊號的編號或巨集值,0表示不傳送任何訊號 int raise(int sig); - 作用:給當前的程序傳送訊號 - sig: 要傳送的訊號 - 返回值:成功返回0,失敗返回非0 - kill(getpid(), sig); void abort(void); - 作用:傳送SIGABRT訊號給當前程序,殺死當前程序 - kill(getpid(), SIGABRT); #include <unistd.h> unsigned int alarm(unsigned int seconds); - 作用:設定定時器,函式呼叫,開始倒計時,當倒計時為0時,函式會給當前的程序傳送一個訊號:SIGALARM - seconds: 倒計時的時長,單位:秒。如果引數為0,定時器無效(不進行倒計時,不傳送訊號)。取消一個定時器,通過alarm(0)。 - 返回值:之前沒有定時器返回0,之前有定時器返回之前的定時器剩餘的時間 -SIGALARM:預設終止當前程序,每一個程序都有且只有唯一一個定時器。alarm(100) -> 該函式是非阻塞的 定時器,與程序的狀態無關(自然定時法),無論程序處於什麼狀態,alarm都會計時。 int setitimer(int which, const struct itimerval *new_val, struct itimerval *old_val); - 作用:設定定時器。可以代替alarm函式,精讀微秒,可以實現週期性定時 - which: 定時器漆以什麼時間計時,ITIMER_REAL 真實時間,時間到達,傳送SIGALRM;ITIMER_VIRTUAL 使用者時間,時間到達,傳送SIGVTALRM;ITMIER_PROF 以該程序在使用者態和核心態所消耗的時間計算, 時間到達,傳送SIGPROF; - new_val: 設定定時器的屬性 - old_val: 上一次定時的時間引數,一般不使用指定為NULL - 返回值:成功返回0,失敗返回-1並設定errno struct itimerval { // 定時器 struct timeval it_interval; // 週期時間 struct timeval it_value; // 第一次延遲多長時間執行定時器 }; struct timeval { // 時間 time_t tv_sec; // 秒數 suseconds_t tv_usec; // 微秒 };
11.執行程式的時間 實際的時間 =核心時間 +使用者時間 +消耗的時間 進行檔案IO操作的時候比較浪費時間 12.訊號捕捉。訊號捕捉要註冊在訊號之前。
#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); - 作用:設定某個訊號的捕捉行為 - signum: 要捕捉的訊號 - handler: 捕捉到的訊號要如何處理,SIG_IGN:忽略訊號;SIG_DFL:使用訊號預設的行為;回撥函式:這個函式是系統呼叫,程式設計師只負責寫,捕捉到訊號後如何處理訊號 - 返回值:成功返回上一次註冊的訊號處理函式的地址,第一次呼叫返回NULL;失敗返回SIG_ERR,並設定錯誤號 - 回撥函式:程式設計師提前實現,函式型別根據需求,看函式指標定義;當訊號產生時,由核心呼叫;函式指標是實現回撥的手段,函式實現後,將函式名放到函式指標的位置 int sigaction(int signum, const struct sigaction *act, struct sigaction *odlact); - 作用:檢查或者改變訊號的處理。訊號捕捉 - signum: 需要捕捉的訊號的編號或者巨集值 - act: 捕捉到訊號之後的處理動作 - oldact: 上一次對訊號捕捉相關的設定,一般設定為NULL - 返回值:成功返回0,石板返回-1 struct sigaction { void (*sa_handler)(int); // 函式指標,指向的函式是訊號捕捉到之後的處理函式 void (*sa_sigaction)(int, siginfo_t *, void *); // 不常用 sigset_t sa_mask; // 臨時阻塞訊號集,訊號捕捉函式執行過程中臨時阻塞某些訊號 int sa_flags; // 使用哪一個訊號處理對捕捉到的訊號進行處理,0表示使用sa_handler,SA_SIGINFO表示使用sa_sigaction void (*sa_restorer)(void); // 被廢棄了 };13.訊號集。多個訊號課使用一個稱之為訊號集的資料結構表示,系統資料型別為 sigset_t。PCB中有兩個訊號集。
- 阻塞訊號集, 訊號的阻塞是讓系統暫時保留訊號留待以後傳送。由於另外有辦法讓系統忽略訊號,所以一般情況下訊號的阻塞只是暫時的,只是為了防止訊號打斷敏感操作。
- 未決訊號集,訊號的“未決”是一種狀態,指的是從訊號的產生到訊號被處理前的一段時間。
- 這兩個訊號集都是內二使用點陣圖機制實現的,無法直接進行操作。需要藉助訊號集操作函式來對這兩個訊號集修改。
- 使用者通過鍵盤 Ctrl +C,產生2號訊號SIGINT(訊號被建立)
- 訊號產生但是沒有被處理(未決)
- 在核心中將所有沒有被處理的訊號儲存在一個集合中(未決訊號集)
- SIGINT訊號狀態被儲存在第二盒標誌位上
- 這個標誌位的值為0,表示訊號不是未決
- 這個標誌位的值為1,表示訊號處於未決
- 這個未決狀態的訊號,需要被處理,處理之前需要和另一個訊號集(阻塞訊號集),進行比較:
- 阻塞訊號集預設不阻塞任何訊號集
- 如果想要阻塞某些訊號需要使用者呼叫系統API
- 在處理的時候和阻塞訊號集中的標誌位進行查詢,看是不是對該訊號設定了阻塞:
- 如果沒有阻塞,這個訊號被處理
- 如果阻塞了,這個訊號就繼續處於未決狀態,直到阻塞解除,這個訊號就被處理
// 以下訊號集相關的函式都是對自定義的訊號集進行操作。 #include <signal.h> int sigemptyset(sigset_t *set); - 作用:清空訊號集中的資料,將訊號集中的所有標誌位置為0 - set: 傳出引數,需要操作的訊號集 - 返回值:成功返回0,失敗返回-1並設定errno int sigfillset(sigset_t *set); - 作用:將訊號集中的所有標誌位置為1 - set: 傳出引數,需要操作的訊號集 - 返回值:成功返回0,失敗返回-1並設定errno int sigaddset(sigset_t *set, int signum); - 作用:設定訊號集中的某一個訊號對應的標註為1,表示阻塞該訊號 - set: 傳出引數,需要操作的訊號集 - signum: 需要設定阻塞的訊號 - 返回值:成功返回0,失敗返回-1並設定errno int sigdelset(sigset_t *set, int signum); - 作用:設定訊號集中的某一個訊號對應的標註為0,表示不阻塞該訊號 - set: 傳出引數,需要操作的訊號集 - signum: 需要設定不阻塞的訊號 - 返回值:成功返回0,失敗返回-1並設定errno int sigismember(const sigset_t *set, int signum); - 作用:判斷某個訊號是否屬於訊號集set - set: 需要操作的訊號集 - signum: 需要判斷的訊號 - 返回值:屬於返回1,不屬於返回0,呼叫失敗返回-1並設定errno
15.處理系統訊號集。(只能改變核心阻塞訊號集,不能改變未決訊號集)
int sigprocmask(int how, cosnt sigset_t *set, sigset_t *oldset); - 作用:將自定義訊號集中的資料設定到核心中(設定阻塞,解除阻塞,替換) - how: 如何對核心阻塞訊號集進行處理, - SIG_BLOCK 將使用者設定的阻塞訊號集新增到核心中,核心原來的資料不變,即:mask | set;(假設核心中預設阻塞訊號集為mask) - SIG_UNBLOCK 根據使用者設定的資料,對核心中的資料進行解除阻塞,即:mask &= ~set - SIG_SETMASK 覆蓋核心中原來的值 - set: 已經初始化好的使用者自定義的訊號集 - old_set: 儲存設定之前的核心中的阻塞訊號集的狀態,可以使NULL - 返回值:成功返回0,失敗返回-1並設定錯誤號:EFAULT、EINVAL int sigpending(sigset_t *set); - 作用:獲取核心中的未決訊號集 - set: 傳出引數,儲存的是核心中的未決訊號集中的資訊16.以下三種條件都會給父程序傳送SIGCHILD訊號,父程序預設忽略該訊號。SIGCHILD訊號產生的條件:
- 子程序終止
- 子程序接收到SIGSTOP訊號停止時
- 子程序處在停止態,接收到SIGCONT後喚醒
sigset_t set; sigemptyset(&set); sigeaddset(&set, SIGCHLD); sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞 sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞