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