一.信號
1.信號的作用
1.1.控制進程
1.2.實現簡單的多任務
1.3.進程間交換數據
2.什么是信號
2.1.信號是一個整數
kill -l
2.2.信號是軟中斷(模擬中斷)
中斷就是在進行某個過程,隨時停下來,并進行其他過程,當其他過程結束后回到原來過程的現象。
2.3.信號的工作原理
信號源(操作系統,硬件,用戶程序)發出信號
系統接收到信號(信號),系統查找信號的注冊表,并找到該進程(信號目的地)對應的信號處理函數(中斷函數),停止原進程的實現,執行信號處理函數,執行完畢,繼續原進程的執行。
信號與信號處理函數注冊必須缺省哪個進程。
a.信號源(系統進程,用戶進程)
b.信號目的地(進程)
c.信號(有特殊含義的整數)
d.信號處理函數(函數void(*)(int))
2.4.信號分類
不可靠信號1-31(非實時信號)
可靠信號34-64(實時信號)
2.5.信號的編程模型:
發送信號:kill函數;或者其他鍵盤操作。
信號處理函數:void(*)(int);
注冊信號:signal函數
案例:
體會信號SIGINT=2。
處理信號2
結論:
1.信號如果用戶不處理,系統會缺省處理
系統的缺省處理大部分情況,是輸出信號含義,并終止進程。
信號有缺省處理,也有忽略處理
SIG_IGN 特殊的信號處理函數-忽略信號1
(void(*)(int))1
SIG_DFL 特殊的信號處理函數-缺省處理0
(void(*)(int))0
2.sleep,pause函數被信號中斷后,不會再繼續,而是返回.
函數類型:
可重入函數:
中斷后可繼續執行的函數
不可重入函數:
中斷后函數不繼續執行,而是返回結束。
信號中斷會導致什么安全問題?
多線程本身就是數據不安全的。
2.6.發送信號
int kill(pid_t pid,//信號的目的地進程
int sig);//信號
參數1:
>0:進程ID
=0:同一個進程組的所有進程
-1:所有進程
<-1:進程組為|pid|的所有進程
案例:
使用kill發送信號
結論:
1.在獨立兩個進程之間,使用信號通信需要知道另外一個進程ID。
2.1-31之間的信號是不可靠。
34-64信號是可靠的。
2.7.案例:使用信號控制進程。
使用信號修改搖獎程序。
2.8.案例:使用信號實現簡單的多任務。
2.8.1.簡易版本的信號發送函數
int raise(int sec) 向本進程發送信號
=int kill(getpid(),int sec);
2.8.2.準備工作:定時器信號
alarm 延時定時器
向系統注冊,在指定的時間秒后發送一次SIGALRM信號。
setitimer 間隔定時器
向系統注冊,在指定的時間微秒后每隔一個間隔時間向進程發送信號SIGALRM。
int setitimer(int which,//定時器計時方法
const struct itimerval *val,//延時與間隔時間
struct itimerval *oldval);//返回上次設置值
struct itimerval
{
struct timeval it_interval;//間隔時間
struct timeval it_value;//延時時間
}
struct timeval
{
long tv_sec;//秒
long tv_usec;//微秒
}
定時器計時方法:
ITIMER_REAL 真實時間
SIGALRM
ITIMER_VIRTUAL 進程實際運行時間
SIGVTALRM
ITIMER_PROF 進程時間+內核時間
SIGPROF
案例1:使用alarm設置系統的信號發送參數案例2:使用setitimer設置間隔定時時間
結論:想馬上發送信號,不要把it_value設置為0秒0微秒,而是設置為0秒1微秒。
2.8.3.使用定時器實現與時間有關的子任務
案例:
顯示7位隨機數與時間。
方法:
一個進程+定時器信號。
2.9.信號的操作
2.9.1.信號屏蔽
使用signal把某個信號忽略。只能對單個的信號處理,處理多個信號,需要反復調用signal函數
a.背景:
信號屏蔽的意義。
保護一段代碼不受信號的中斷影響。
等代碼執行結束再處理信號。[信號不丟失,但被系統延遲處理]
b.信號屏蔽函數
int sigprocmask(int how,//信號操作方式
const sigset_t *sigs,//操作的信號集合
sigset_t *oldsigs);//返回原來的信號集合
信號的操作方式:
SIG_BLOCK 屏蔽信號
SIG_UNBLOCK 解除信號屏蔽
SIG_SETMASK 修改屏蔽信號
sigset_t數據集合的操作:
清空信號集合sigemptyset
添加信號到集合sigaddset
刪除集合中的某個信號sigdelset
判定某個信號是否在集合中sigismember
把所有信號添加到信號集合sigfillset
編程模型:
定義信號集合sigset_t sigs;
初始化信號集合empty add del fill
設置屏蔽信號sigprocmask SIG_BLOCK
解除屏蔽信號sigprocmask SIG_UNBLOCK
判定某個信號是否在集合中ismember
SIGSTOP SIGKILL
2.9.2.查詢被屏蔽的信號是否發生
背景:
在被屏蔽代碼中,怎么知道被屏蔽的信號已經發生?
函數:
int sigpending(sigset_t *sigs);//返回發生的信號,而且該信號被屏蔽。
問題:
信號屏蔽的時候,信號發送,信號不被處理,解除信號屏蔽,信號才被處理。
在信號屏蔽中,能否處理信號?
答案:能
問題:怎么處理?
答案:解除屏蔽,然后再屏蔽。
問題:
信號屏蔽與解除屏蔽瞬間,其他信號是否會發生,導致程序解除。
信號處理函數在執行中,是否會被信號影響?肯定影響(本身信號不影響,被其他信號影響。)
2.9.3.防止信號處理函數執行中被信號影響。
a.sigsuspend函數設置屏蔽信號
該函數的作用:等待信號發生,當信號發生,并且設置新的屏蔽信號,執行信號處理函數,執行完畢,解除信號屏蔽,恢復原來的信號屏蔽,函數返回。
int sigsuspend(const sigset_t*);
等待信號:任意信號。
在等待過程中,不受參數設置的信號集合中的信號影響。
當信號發生,處理信號,同樣不受參數設置的信號影響。
信號處理結束,sigsuspend恢復原來的信號屏蔽,并且sigsuspend返回。
sigsuspend該函數等待信號,沒有信號發生,該函數阻塞。信號發生,則處理后返回。
結論:
1.sigsuspend 解除原來的所有信號屏蔽
2.沒有信號發生,sigsuspend阻塞。
3.sigsuspend返回前,會恢復解除屏蔽的信號
4.在解除原來的信號屏蔽,設置新的信號屏蔽。
5.函數結束前,解除新的信號屏蔽
int sigsuspend(sigset_t *sig)
{
1.sigprocmask(SIG_UNBLOCK,*s,0);
2.sigprocmask(SIG_BLOCK,*sig,0);
3.等待信號發生pause
4.處理信號
5.sigprocmask(SIG_UNBLOCK,*sig,0);
6.sigprocmask(SIG_BLOCK,*s,0);
7.return 0;
}
sigsuspend未必一定要在sigprocmask環境下工作
案例:
利用sigsuspend控制進程.
作業:
1.實現搖獎。
要求:時間顯示使用SIALRM信號。
控制:使用sigsuspend控制隨機數進程。
注意:
使用信號控制進程.強烈建議:
sigsuspend + signal + kill
不推薦
while(1);+signal+kill
pause/sleep+signal+kill
sigsuspend有兩個作用:
定點信號處理。(屏蔽信號切換具備原子性)
控制進程。
b.高級信號發送與處理函數處理信號。
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handle(int s){
printf("信號發生.....\n");
}
main()
{
signal(45,handle);
/*printf("%d:%d\n",SIG_IGN,SIG_DFL);*/
/*signal(SIGINT,SIG_IGN);*/
while(1){
/*printf("Hello信號!\n");*/
/*sleep(1);*/
}
}
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
main()
{
int i;
for(i=0;i<10;i++){
kill(4095,45);
}
}
#include <curses.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <math.h>
#include <time.h>
int isstop=0;
void handle(){
isstop=isstop==1?0:1;
}
main()
{
/*變量區*/
int i;
sigset_t si;
WINDOW *wtime,*wcode;
pid_t pid_time,pid_code;
pid_t pid_sub;
initscr();
sigfillset(&si);
sigdelset(&si,34);
noecho();
curs_set(0);
wtime=derwin(stdscr,3,10,0,COLS-10);
wcode=derwin(stdscr,3,9,(LINES-3)/2,(COLS-9)/2);
keypad(stdscr,TRUE);
keypad(wtime,TRUE);
keypad(wcode,TRUE);
box(wtime,0,0);
box(wcode,0,0);
refresh();
wrefresh(wtime);
wrefresh(wcode);
for(i=0;i<2;i++){
if(pid_sub=fork())
{
if(i==0){
pid_time=pid_sub;
}
if(i==1){
pid_code=pid_sub;
}
continue;
}
else
{
if(i==0){
/*子進程1:時間顯示*/
time_t tt;
struct tm *t;
while(1){
tt=time(0);
t=localtime(&tt);
mvwprintw(wtime,1,1,"%02d:%02d:%02d",
t->tm_hour,t->tm_min,t->tm_sec);
refresh();
wrefresh(wcode);
wrefresh(wtime);
sleep(1);
}
exit(0);
}
if(i==1){
/*子進程2:隨機數顯示*/
int num;
signal(34,handle);
while(1){
if(isstop){
sigsuspend(&si);
}
num=rand()%10000000;
mvwprintw(wcode,1,1,"%07u",num);
refresh();
wrefresh(wtime);
wrefresh(wcode);
usleep(10000);
}
exit(0);
}
}
}
/*主進程:鍵盤輸入*/
int ch;
while(1){
ch=getch();
if(ch=='\n'){
/*停止隨機數進程*/
/*發送信號:可靠信號>=34*/
kill(pid_code,34);
}
/*退出整個任務*/
if(ch=='x' || ch=='X'){
/*通知子進程結束*/
sched_yield();
break;
}
}
wait(0);
wait(0);
delwin(wtime);
delwin(wcode);
endwin();
exit(0);
}