zoukankan      html  css  js  c++  java
  • 信号引起的竞态

    竞态是指设备或系统出现不恰当的执行时序,而得到不正确的结果,由于时间片,或其他因素,导致该到达并响应的信号没有被响应,这就是由信号引起的竞态。 

    对于上面,因为alarm5秒后悔触发信号,pause收到信号后,会结束挂起,再次之前先输出hello

    对于加上sleep(6)则输出hello后一直挂起,因为alarm触发信号的时候,正处于睡眠状态,所以pause收不到信号

     1 #include <unistd.h>
     2 #include <signal.h>
     3 #include <stdio.h>
     4 void sig_alrm(int signo)
     5 {//信号处理函数中什么都不做
     6     /* nothing to do */
     7 }
     8 unsigned int mysleep(unsigned int nsecs)
     9 {
    10     //newact用于保存新的信号处理动作,oldact用于保存原有信号处理动作
    11     struct sigaction newact, oldact;
    12     unsigned int unslept;
    13 
    14     newact.sa_handler = sig_alrm;  //设置信号处理函数
    15     sigemptyset(&newact.sa_mask);
    16     newact.sa_flags = 0;
    17     sigaction(SIGALRM, &newact, &oldact);  //设置新sigaction,保存旧sigaction 
    18 
    19     alarm(nsecs);   //设置定时
    20     pause();        //挂起进程
    21 
    22     unslept = alarm(0);
    23     sigaction(SIGALRM, &oldact, NULL);   //将原有sigaction设置回去
    24 
    25     return unslept;  //返回还未睡够的时间
    26 }
    27 int main(void)
    28 {
    29     while(1){
    30         mysleep(2);
    31         printf("Two seconds passed
    ");
    32     }
    33     return 0;
    34 }

    上述程序的执行过程就是: 
      1、先注册SIGALRM信号的处理函数(在信号处理函数中什么都不做),并将原有sigaction保存起来。 
      2、开始定时,挂起进程。 
      3、2秒之后发送SIGALRM信号,进程响应信号,被唤起,进入什么都不做的信号处理函数。 
      4、执行完信号处理函数之后恢复原有sigaction,结束sleep 
      5、打印Two seconds passed

    但是在这里有一个问题,如果在“alarm(nsecs); 设置定时”和 “pause(); 挂起进程”之间由于时间片被用完,导致该进程被调出(这时alarm函数已经被执行,内核已经开始计时),而且在该系统中进程数很多,导致“饥饿现象”,所以该进程很久没有被掉入,假设三秒都没有被掉入(我们设置的定时是2秒),这时在内核中其实在它被掉入前已经倒数完毕并发出SIGALRM信号,所以执行它的信号处理函数(什么都不做),然后它在第三秒后被掉入,继续执行,这时执行的函数是pause(),进程被挂起,但是又由于alarm发出的SIGALRM信号在之前已经被执行,所以pause()会永远等待不到SIGALRM信号的到达,那么该进程会永远的被挂起! 
      以上的情况就是一种由信号引起的竞态,这种情况是不可预知的,而且如若出现后果很严重!

      所以,要改变上述情况,可以使用sigsuspend函数,这个函数有一个特点就是以下的第1步和第2步是一个原子操作,这时,在使用alarm()函数之前先阻塞SIGALRM信号,在执行sigsuspend时解除阻塞(以下的第一步就是该函数的作用),这样就不会引起竞态!就可以解决上述问题了!

    #include <signal.h>
    
    int sigsuspend(const sigset_t *mask)
    函数的执行过程是:
    1.以通过指定mask来临时解除对某个信号的屏蔽,
    2.然后挂起等待,
    3.当被信号唤醒sigsuspend返回时,进程的信号屏蔽字恢复为原来的值
     1 #include <unistd.h>
     2 #include <signal.h>
     3 #include <stdio.h>
     4 
     5 void sig_alrm(int signo)
     6 {
     7     /* do  nothing */
     8 }
     9 
    10 unsigned int mysleep(unsigned int nsecs)
    11 {
    12 
    13     struct sigaction newact, oldact;  //newact用于保存新的信号处理动作,oldact用于保存原有信号处理动作
    14 
    15     //newmask将要添加的阻塞信号集,oldmask原有信号集
    16     //suspmask为sigsuspend所用阻塞信号集
    17     sigset_t newmask, oldmask, suspmask;
    18 
    19     unsigned int unslept;//保存未睡够的时间
    20 
    21     /* 设置SIGALRM信号处理函数,并保存原有信号处理函数 */
    22     newact.sa_handler = sig_alrm;
    23     sigemptyset(&newact.sa_mask);
    24     newact.sa_flags = 0;
    25     sigaction(SIGALRM, &newact, &oldact);
    26 
    27     /* 阻塞SIGALRM信号,并保存当前的阻塞信号集 */
    28     sigemptyset(&newmask);//初始化信号集(全部置0),防止原有垃圾值
    29     sigaddset(&newmask, SIGALRM);//阻塞SIGALRM信号
    30     //注册信号屏蔽字
    31     //设原有的信号屏蔽字为mask,则由于以SIG_BLOCK方式
    32     //首先将原有的保存到oldmask中,现在的阻塞信号字为mask=mask | newmask)
    33     sigprocmask(SIG_BLOCK, &newmask, &oldmask);
    34 
    35     alarm(nsecs);//调用时钟,nsecs秒后向该进程发送SIGALRM信号,程序继续执行
    36 
    37     /* 如果这个时候cpu被抢占>nsecs秒,由于SIGALRM信号被阻塞,所以即使时间到了发送了SIGALRM信号,也不会被处理,也就不会发生时序竞争 */
    38     suspmask = oldmask;//将原有信号集赋给sigsuspend所用的信号集
    39     sigdelset(&suspmask, SIGALRM);//在sigsuspend中不阻塞SIGALRM信号
    40 
    41     //挂起
    42     sigsuspend(&suspmask);
    43     //只要有任何信号被捕捉就会被唤醒继续执行下面的程序
    44     //并将信号集恢复为该进程的信号集:sigprocmask之后的信号集
    45 
    46     // 如果在正常的SIGALRM信号到来之前,接收到其他信号,则计算未睡够的时间
    47     unslept = alarm(0);
    48     sigaction(SIGALRM, &oldact, NULL); //恢复愿来SIGALRM信号的动作
    49     sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复最初信号集
    50     return(unslept);//返回未睡够的时间
    51 }
    52 
    53 int main(void)
    54 {
    55     while(1)
    56     {
    57         mysleep(2);
    58         printf("Two seconds passed
    ");
    59     }
    60     return 0;
    61 }
  • 相关阅读:
    Binary Tree Maximum Path Sum
    ZigZag Conversion
    Longest Common Prefix
    Reverse Linked List II
    Populating Next Right Pointers in Each Node
    Populating Next Right Pointers in Each Node II
    Rotate List
    Path Sum II
    [Leetcode]-- Gray Code
    Subsets II
  • 原文地址:https://www.cnblogs.com/13224ACMer/p/6408173.html
Copyright © 2011-2022 走看看