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