大龙的博客

常用链接

统计

最新评论

Linux C语言 信号及信号的操作

一.信号
 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); 
}

posted on 2011-11-05 05:06 大龙 阅读(729) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理