时序竞态
- 产生原因
- 改进
- 总结
产生原因
#include <cstdio>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>void catch_sigalrm(int signo)
{printf("pause sucess 11111\n");
}unsigned int mysleep(unsigned int seconds)
{struct sigaction act, oldact;sigset_t new1,old;act.sa_flags = 0;act.sa_handler = catch_sigalrm;sigemptyset(&act.sa_mask);int ret=sigaction(SIGALRM, &act, &oldact);if (ret == -1){perror("sigaction error:");exit(1);}sigemptyset(&new1);sigemptyset(&old);sigaddset(&new1,SIGALRM);sigaddset(&old, SIGALRM);sigprocmask(SIG_BLOCK,&new1, NULL);sigprocmask(SIG_UNBLOCK, &old, NULL);alarm(seconds);ret=pause();//主动挂起 等信号if (ret == -1 && errno== EINTR){printf("pause sucess \n");}ret=alarm(0);sigaction(SIGALRM,&oldact,NULL);return ret;
}int main()
{mysleep(3);printf("----------------\n");return 0;
}
如果进程在执行完alarm函数后,突然失去CPU,被阻塞等待(这是有可能的,进程在执行过程中,若非原子操作,都有可能随时失去CPU),如果失去CPU的时间大于了执行完,则此时在执行pause函数前,信号已经到了,因此会先处理信号(软中断,而不是先执行pause函数),在信号处理完后,再去执行pause函数,此时进程会被永远挂起,不会被唤醒,因为SIGALRM信号已经被处理了。
时序竞态:即由于进程之间执行的顺序不同,导致同一个进程多次运行后产生了不同结果的现象。如上述sleep函数,有时执行结果是正确的,有时却会导致进程永远被挂起,因此这就是一个时序竞态问题。因此需要重新对该函数进行改进。
改进
代码如下(示例):
#include <cstdio>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>void doaction(int signo)
{printf("---------%d 信号-------\n",signo);
}int mysleep(int second)
{int unslept;struct sigaction cur, old;sigset_t n_ew,o_ld,supemask;//安装信号cur.sa_flags = 0;sigemptyset(&cur.sa_mask);cur.sa_handler = doaction;sigaction(SIGALRM,&cur, &old);//设置阻塞信号集,阻塞SIGALRM信号sigemptyset(&n_ew);sigaddset(&n_ew,SIGALRM);sigprocmask(SIG_BLOCK,&n_ew, &o_ld);//定时alarm(second);//构建一个临时的sigsuspend有效阻塞信号集supemask = o_ld;sigemptyset(&supemask);sigsuspend(&supemask);//supemask未阻塞信号挂起unslept = alarm(0);//恢复之前状态sigaction(SIGALRM,&old,NULL);sigprocmask(SIG_SETMASK,&o_ld,NULL);return unslept;//int ret=pause();//if (ret == -1 && errno == EINTR)//{// printf("pause success\n");//}}int main()
{mysleep(3);}
使用sigsuspend函数,sigsuspend具有“原子操作“,这样就能够避免。
总结
- 竞态条件,跟系统负载有很紧密的关系,体现出信号的不可靠性。系统负载越严重,信号不可靠性越强。
- 不可靠由其实现原理所致。信号是通过软件方式实现(跟内核调度高度依赖,延时性强),每次系统调用结束后,或中断处理处理结束后,需通过扫描PCB中的未决信号集,来判断是否应处理某个信号。当系统负载过重时,会出现时序混乱。
- 这种意外情况只能在编写程序过程中,提早预见,主动规避,而无法通过gdb程序调试等其他手段弥补。且由于该错误不具规律性,后期捕捉和重现十分困难。