一.信号
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:所有进程
案例:
使用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
#include
#include
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
#include
#include
main()
{
int i;
for(i=0;i<10;i++){
kill(4095,45);
}
}
#include
#include
#include
#include
#include
#include 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);
}