1.基本概念
中斷:
中斷是系統對於異步事件的響應
中斷信號
中斷源
現場信息
中斷處理程序
中斷向量表
異步事件的響應:進程執行代碼的過程中可以隨時被打斷,然后去執行異常處理程序
生活中的中斷和計算機系統中的中斷
1) 無中斷生活場景
張三看書,廚房燒水
2)有中斷的生活場景
張三看書,設置鬧鍾,廚房燒水。
鬧鍾發出中斷信號,張三把書合好(第20頁),去廚房把開水事情處理好,張三重新打開20頁進行閱讀。
3)計算機系統的中斷場景
中斷源發出中斷信號,CPU判斷中斷是否屏蔽屏蔽、保護現場,cpu執行中斷處理程序,cpu恢復現場,繼續原來的任務。
4)中斷的其他概念
中斷向量表保存了中斷處理程序的入口地址。
中斷個數固定,操作系統啟動時初始化中斷向量表。
中斷有優先級(有人敲門,有人打電話,有優先級)
中斷可以屏蔽(張三可以屏蔽電話)。
中斷分類:
硬件中斷(外部中斷)
外部中斷是指由外部設備通過硬件請求的方式產生的中斷,也稱為硬件中斷
軟件中斷(內部中斷)
內部中斷是由CPU運行程序錯誤或執行內部程序調用引起的一種中斷,也稱為軟件中斷。
x86平台INT指令 ARM軟中斷指令SWI
信號概念:
信號是UNIX系統響應某些狀況而產生的事件,進程在接收到信號時會采取相應的行動。
信號是因為某些錯誤條件而產生的,比如內存段沖突、浮點處理器錯誤或者非法指令等
信號是在軟件層次上對中斷的一種模擬,所以通常把它稱為是軟中斷
信號和中斷的區別:
信號與中斷的相似點:
(1)采用了相同的異步通信方式;
(2)當檢測出有信號或中斷請求時,都暫停正在執行的程序而轉去執行相應的處理程序;
(3)都在處理完畢后返回到原來的斷點;
(4)對信號或中斷都可進行屏蔽。
信號與中斷的區別:
(1)中斷有優先級,而信號沒有優先級,所有的信號都是平等的;
(2)信號處理程序是在用戶態下運行的,而中斷處理程序是在核心態下運行;
(3)中斷響應是及時的,而信號響應通常都有較大的時間延遲。
2.信號名稱及常用信號解釋
kill –l 可以查看linux內核支持的信號
Man 7 signal 查看信號的默認動作、信號的含義
信號名稱 描述
SIGABRT 進程停止運行 6
SIGALRM 警告鍾
SIGFPE 算述運算例外
SIGHUP 系統掛斷
SIGILL 非法指令
SIGINT 終端中斷 2
SIGKILL 停止進程(此信號不能被忽略或捕獲)
SIGPIPE 向沒有讀者的管道寫入數據
SIGSEGV 無效內存段訪問
SIGQUIT 終端退出3
SIGTERM 終止
SIGUSR1 用戶定義信號1
SIGUSR2 用戶定義信號2
SIGCHLD 子進程已經停止或退出
SIGCONT 如果被停止則繼續執行
SIGSTOP 停止執行
SIGTSTP 終端停止信號
SIGTOUT 后台進程請求進行寫操作
SIGTTIN 后台進程請求進行讀操作
3.信號處理
進程對信號的三種響應
1)忽略信號
不采取任何操作、有兩個信號不能被忽略:SIGKILL(9號信號)和SIGSTOP。
注意:為什么進程不能忽略SIGKILL、SIGSTOP信號。(如果應用程序可以忽略這2個信號,系統管理無法殺死、暫停進程,無法對系統進行管理。)。SIGKILL(9號信號)和SIGSTOP信號是不能被捕獲的。
2)捕獲並處理信號
內核中斷正在執行的代碼,轉去執行先前注冊過的處理程序。
3)執行默認操作
默認操作通常是終止進程,這取決於被發送的信號。
注意:信號的默認操作:通過 man 7 signal 進程查看
4.signal信號安裝函數
1)signal函數作用
(1):站在應用程序的角度,注冊一個信號處理函數。
(2):忽略信號、設置信號默認處理信號的安裝和恢復
typedef void (*__sighandler_t) (int);
#define SIG_ERR ((__sighandler_t) -1)
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)
2)函數原型
__sighandler_t signal(intsignum, __sighandler_t handler);
參數:
1)signal是一個帶signum和handler兩個參數的函數,准備捕捉或屏蔽的信號由參數signum給出,接收到指定信號時將要調用的函數由handler給出
2)handler這個函數必須有一個int類型的參數(即接收到的信號代碼),它本身的類型是void
3)handler也可以是下面兩個特殊值:
SIG_IGN 屏蔽該信號
SIG_DFL 恢復默認行為
5.信號發送
1)kill函數
kill基本用法:
發送信號的函數有kill和raise
區別:kill既可以向自身發送信號,也可以向其他進程發送信號;
raise函數向進程自身發送信號。
intkill(pid_tpid, intsiq)
int raise(intsigno)
參數組合情況解釋:
kill(pid_tpid, intsiq)
pid>0 將信號sig發給pid進程
pid=0 將信號sig發給同組進程
pid=-1 將信號sig發送給所有進程,調用者進程有權限發送的每一個進程(除了1號進程之外,還有它自身)
pid<-1 將信號sig發送給進程組是pid(絕對值)的每一個進程
注意:如果在fork之前安裝信號,則子進程可以繼承信號。
sleep函數幾點說明:
(1)sleep函數作用,讓進程睡眠。
(2)能被信號打斷,然后處理信號函數以后,就不再睡眠了。直接向下執行代碼
(3)sleep函數的返回值,是剩余的秒數
2)raise函數
raise給自己發送信號。raise(sig)等價於kill(getpid(), sig);
killpg給進程組發送信號。killpg(pgrp, sig)等價於kill(-pgrp, sig);
sigqueue給進程發送信號,支持排隊,可以附帶額外數據信息。
3)alarm函數
unsigned int alarm(unsigned int seconds);
alarm函數,設置一個鬧鍾延遲發送信號告訴linux內核n秒中以后,發送SIGALRM信號
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void myhandle(int num) 7 { 8 printf("recv signal id num : %d \n", num); 9 alarm(1); 10 } 11 int main() 12 { 13 printf("main ....begin\n"); 14 if (signal(SIGALRM, myhandle) == SIG_ERR) 15 { 16 perror("func signal err\n"); 17 return 0; 18 } 19 alarm(1); 20 while(1) 21 { 22 pause(); 23 printf("pause return\n"); 24 } 25 return 0; 26 }
6.信號的阻塞和未達
信號在內核中的表示:
執行信號的處理動作稱為信號遞達(Delivery),信號從產生到遞達之間的狀態,稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。
注意:阻塞和忽略是不同,只要信號被阻塞就不會遞達,而忽略是在遞達之后可選的一種處理動作。信號在內核中的表示可以看作是這樣的:
說明1)PCB進程控制塊中結構體中有信號屏蔽狀態字(block),信號未決狀態字(pending)還有是否忽略標志;
說明2)信號屏蔽狀態字(block),1代表阻塞、0代表不阻塞;信號未決狀態字(pending)的1代表未決(表示有未達信號),0代表信號可以抵達了;
說明3)向進程發送SIGINT,內核首先判斷信號屏蔽狀態字是否阻塞,信號未決狀態字(pending相應位制成1;若阻塞解除,信號未決狀態字(pending)相應位制成0;表示信號可以抵達了。
說明4)block狀態字、pending狀態字 64bit;//socket select
說明5)block狀態字用戶可以讀寫,pending狀態字用戶只能讀;這是信號設計機制。
信號集操作函數(狀態字表示)
intsigemptyset(sigset_t *set);把信號集清空64bit/8=8個字節
intsigfillset(sigset_t *set);把信號集置成1
intsigaddset(sigset_t *set, intsigno);根據signo,把信號集中的對應bit置成1
intsigdelset(sigset_t *set, intsigno);根據signo,把信號集中的對應bit置成0
intsigismember(constsigset_t *set, intsigno);//判斷signo是否在信號集中
sigprocmask讀取或更改進程的信號屏蔽狀態字(block)
int sigprocmask(int how, constsigset_t *set, sigset_t *oset);
返回值:若成功則為0,若出錯則為-1
如果oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出。如果set是非空指針,則更改進程的信號屏蔽字,參數how指示如何更改。如果oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset里,然后根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字為mask,下表說明了how參數的可選值。
SIG_BLOCK ,講信號集set添加到進程block狀態字中。
sigpending獲取信號未決狀態字(pending)信息:int sigpending(sigset_t *set);
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void handler(int sig) 7 { 8 if (sig == SIGINT) 9 printf("recv a sig=%d\n", sig); 10 else if (sig == SIGQUIT) 11 { 12 sigset_t uset; 13 sigemptyset(&uset); 14 sigaddset(&uset, SIGINT); 15 //ctr + \ 用來觸發 SIGINT 信號 16 //解除阻塞 17 sigprocmask(SIG_UNBLOCK, &uset, NULL); 18 } 19 } 20 21 void printsigset(sigset_t *set) 22 { 23 int i; 24 for (i=1; i<NSIG; ++i) 25 { 26 if (sigismember(set, i)) 27 putchar('1'); 28 else 29 putchar('0'); 30 } 31 printf("\n"); 32 } 33 // 連續的按ctrl+c鍵盤,雖然發送了多個SIGINT信號,但是因為信號是不穩定的,只保留了一個。 34 //不支持排隊 35 int main(int argc, char *argv[]) 36 { 37 sigset_t pset; //用來打印的信號集 38 sigset_t bset; //用來設置阻塞的信號集 39 40 sigemptyset(&bset); 41 sigaddset(&bset, SIGINT); 42 43 if (signal(SIGINT, handler) == SIG_ERR) 44 ERR_EXIT("signal error"); 45 46 if (signal(SIGQUIT, handler) == SIG_ERR) 47 ERR_EXIT("signal error"); 48 49 //讀取或更改進程的信號屏蔽字這里用來阻塞ctrl+c信號 50 //ctrl+c信號被設置成阻塞,即使用戶按下ctl+c鍵盤,也不會抵達 51 sigprocmask(SIG_BLOCK, &bset, NULL); 52 53 for (;;) 54 { 55 //獲取未決字信息 56 sigpending(&pset); 57 58 //打印信號未決sigset_t字 59 printsigset(&pset); 60 sleep(1); 61 } 62 return 0; 63 }
7.sigaction函數
包含頭文件<signal.h>
功能:sigaction函數用於改變進程接收到特定信號后的行為。
原型:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
參數:
該函數的第一個參數為信號的值,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號(為這兩個信號定義自己的處理函數,將導致信號安裝錯誤)
第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對特定信號的處理,可以為空,進程會以缺省方式對信號處理
第三個參數oldact指向的對象用來保存原來對相應信號的處理,可指定oldact為NULL。
返回值:函數成功返回0,失敗返回-1
sigaction結構體:
第二個參數最為重要,其中包含了對指定信號的處理、信號所傳遞的信息、信號處理函數執行過程中應屏蔽掉哪些函數等等
struct sigaction {
void (*sa_handler)(int); //信號處理程序不接受額外數據
void (*sa_sigaction)(int, siginfo_t *, void *); //信號處理程序能接受額外數據,和sigqueue配合使用
sigset_t sa_mask; //
int sa_flags; //影響信號的行為SA_SIGINFO表示能接受數據
void (*sa_restorer)(void); //廢棄
};
注意:回調函數句柄sa_handler、sa_sigaction只能任選其一。
sigaction的函數注冊信號,基本用法代碼:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 void handler(int sig) 7 { 8 printf("recv a sig=%d\n", sig); 9 } 10 11 __sighandler_t my_signal(int sig, __sighandler_t handler) 12 { 13 struct sigaction act; 14 struct sigaction oldact; 15 act.sa_handler = handler; 16 sigemptyset(&act.sa_mask); 17 act.sa_flags = 0; 18 19 if (sigaction(sig, &act, &oldact) < 0) 20 return SIG_ERR; 21 22 return oldact.sa_handler; 23 } 24 25 int main(int argc, char *argv[]) 26 { 27 struct sigaction act; 28 sigset_t sa_mask; 29 30 act.sa_handler = handler; 31 act.sa_flags = 0; 32 sigemptyset(&act.sa_mask); 33 34 //測試信號安裝函數 35 sigaction(SIGINT, &act, NULL); 36 37 //模擬signal函數 38 //my_signal(SIGINT, handler); 39 40 for (;;) 41 { 42 pause(); 43 } 44 return 0; 45 }
如上圖修改對應地方編譯后,都可以實現效果
測試sigaction結構體第三個參數sigset_tsa_mask的作用,代碼如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 #include<errno.h> 6 /* 7 struct sigaction { 8 void (*sa_handler)(int); 9 void (*sa_sigaction)(int, siginfo_t *, void *); 10 sigset_t sa_mask; 11 int sa_flags; 12 void (*sa_restorer)(void); 13 } */ 14 //測試sigaction結構體第三個參數sigset_t sa_mask的作用 15 //作用sigaddset(&act.sa_mask, SIGQUIT); 加入到sa_mask中的信號,被阻塞(信號處理函數執行的過程中被阻塞)。 16 //注意:SIGQUIT信號最終還會抵達 17 void handler(int sig); 18 int main(int argc, char *argv[]) 19 { 20 struct sigaction act; 21 act.sa_handler = handler; 22 23 sigemptyset(&act.sa_mask); 24 sigaddset(&act.sa_mask, SIGQUIT); 25 act.sa_flags = 0; 26 27 if (sigaction(SIGINT, &act, NULL) < 0) 28 { 29 perror("sigaction error"); 30 } 31 32 for (;;) 33 { 34 pause(); 35 } 36 return 0; 37 } 38 39 void handler(int sig) 40 { 41 printf("recv a sig=%d 信號處理函數執行的時候,阻塞sa_mask中的信號\n", sig); 42 sleep(5); 43 }
結論:sigaddset(&act.sa_mask, SIGQUIT) 加入到sa_mask中的信號,被阻塞(信號處理函數執行的過程中被阻塞),SIGQUIT信號最終還會抵達;
8.sigqueue函數
功能:新的發送信號系統調用,主要是針對實時信號提出的支持信號帶有參數,與函數sigaction()配合使用。
注意:和kill函數相比int kill(pid_tpid, intsiq)多了參數
原型:int sigqueue(pid_t pid, int sig, const union sigval value);
參數:
sigqueue的第1個參數是指定接收信號的進程id,第2個參數確定即將發送的信號,第3個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。
union sigval {
int sival_int;
void *sival_ptr;
};
返回值:成功返回0,失敗返回-1
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。
9.其他介紹
三種睡眠函數:
unsigned int sleep(unsigned int seconds);秒
若被中斷打斷,返回剩余時間
int usleep(useconds_tusec);微妙
若被中斷打斷,返回剩余時間
int nanosleep(const struct timespec *req, struct timespec *rem);納秒時間
要睡眠的時間req;剩余睡眠時間,如果要中斷,通過rem返回過來。
三種時間結構:
time_t秒
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */微妙
};
struct timespec {納秒
time_ttv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
setitime函數:間隙性產生
包含頭文件<sys/time.h>
功能setitimer()比alarm功能強大,支持3種類型的定時器
原型:int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
參數:
第一個參數which指定定時器類型
第二個參數是結構itimerval的一個實例,結構itimerval形式
第三個參數可不做處理。
返回值:成功返回0失敗返回-1
ITIMER_REAL:經過指定的時間后,內核將發送SIGALRM信號給本進程
ITIMER_VIRTUAL :程序在用戶空間執行指定的時間后,內核將發送SIGVTALRM信號給本進程
ITIMER_PROF :進程在內核空間中執行時,時間計數會減少,通常與ITIMER_VIRTUAL共用,代表進程在用戶空間與內核空間中運行指定時間后,內核將發送SIGPROF信號給本進程.