时序竞态

什么是时序竞态?将同一个程序执行两次,失常状况下,前后两次执行失去的后果应该是一样的。但因为系统资源竞争的起因,前后两次执行的后果有可能失去不一样的后果,这个景象就是时序竞态

pause函数

函数原型:

int pause(void);

函数作用:

过程调用pause函数时,会造成过程被动挂起(处于阻塞状态,并被动放弃CPU),并且期待信号将其唤醒。

返回值:

咱们晓得,信号的解决形式有三种:1. 默认动作;2. 疏忽解决;3. 捕获。过程收到一个信号后,会先解决响应信号,再唤醒pause函数。于是有上面几种状况:

① 如果信号的默认解决动作是终止过程,则过程将被终止,也就是说一收到信号过程就终止了,pause函数基本就没有机会返回;

② 如果信号的默认解决动作是疏忽,则过程将间接疏忽该信号,相当于没收到这个信号,过程持续处于挂起状态,pause函数不返回;

③ 如果信号的解决动作是捕获,则过程调用完信号处理函数之后,pause返回-1,errno设置为EINTR,示意“被信号中断”。

④ pause收到的信号不能被屏蔽,如果被屏蔽,那么pause就不能被唤醒。

因为alarm函数能够在设定的工夫之后发送SIGALRM信号,pause函数又能够将过程挂起期待信号,则二者联合能够本人写一个sleep函数,如下:

 1#include <unistd.h> 2#include <signal.h> 3#include <stdio.h> 4 5void sig_alrm(int signo) 6{ 7    /* nothing to do */ 8} 910unsigned int mysleep(unsigned int nsecs)11{12    unsigned int unslept;1314    signal(SIGALRM, &sig_alrm);15    unslept = alarm(nsecs); 16    pause();1718    return unslept;19}202122int main(void)23{24    while(1){25        mysleep(2);26        printf("Two seconds passed\n");27    }2829    return 0;30}

时序竞态前导例

在讲时序竞态具体景象之前,咱们先来看一个生存中常见的场景:

想午睡10分钟,于是定了个10分钟的闹钟,心愿10分钟后闹钟将本人叫醒。

失常状况:定好闹钟,午睡,10分钟后闹钟叫醒本人;

异常情况:定好闹钟,躺下睡觉2分钟,被同学叫醒去打球,打了20分钟后回来持续睡觉。但在打球期间,闹钟早就响过了,将不会再唤醒本人。

这个例子与之后要讲的时序竞态有很大的相似之处。

时序竞态问题剖析

咱们再回过头来看下面所写的mysleep程序。这个函数有可能是上面的时序:

  1. SIGALRM默认动作是终止过程,因而咱们要将其捕获,对SIGALRM注册信号处理函数;
  2. 调用alarm(1)函数定时1秒钟;
  3. alarm(1)调用完结,定时器开始计时。就在这时,过程失去CPU,进入就绪态期待CPU(相当于被同学叫醒去打球)。失去CPU的形式有可能是内核调度了优先级更高的过程取代了以后过程,使得以后过程无奈取得CPU;
  4. 咱们晓得,alarm函数如果采纳天然定时法的话,定时器将始终计时,与过程状态无关。于是,1秒后,闹钟定时工夫到,内核向以后过程发送SIGALRM信号。高优先级过程尚未执行结束,以后过程依然无奈取得CPU,持续处于就绪态,信号无奈解决(处于未决状态);
  5. 优先级高的过程执行结束,以后过程取得CPU资源,内核调度回以后过程执行。SIGALRM信号递达,并被过程解决;
  6. 信号处理结束后,返回以后主控流程,并调用pause()函数,挂起期待alarm函数发送的SIGALRM信号将本人唤醒;
  7. 但理论SIGALRM信号曾经处理完毕,pause()函数永远不会等到。

解决时序竞态问题

通过以上时序剖析,咱们能够看出,造成时序竞态的起因就是SIGALRM信号在过程失去CPU的时候就曾经发送过去。为了避免这个景象呈现,咱们能够先将该信号阻塞,将其“抓住”,再在解除阻塞的时候立即调用pause函数挂起期待。这样即便在调用alarm就失去CPU,也能够在过程从新取得CPU时将抓到的SIGALRM信号从新“放进去”,并将之后的pause函数唤醒。

但在解除阻塞与pause期待挂起信号之间,还是有可能失去CPU,除非将这两个步骤做成一个“原子操作”。Linux零碎提供的sigsuspend函数就具备这个性能。所以,在时序要求比拟严格的场合下都应该应用sigsuspend函数,而非pause函数。

函数原型:

int sigsuspend(const sigset_t *mask);

函数作用:

挂起期待信号;

函数参数:

mask,传入参数,sigsuspend函数调用期间,过程信号屏蔽字由参数mask指定。

具体用法:可将某个信号(如SIGALRM)从长期信号屏蔽字mask中删除,也就是在调用sigsuspend函数时对该信号解除屏蔽,而后挂起期待信号。但咱们此时曾经扭转了过程的信号屏蔽字,所以调用完sigsuspend函数之后,应将过程的信号屏蔽字复原原样。

 1#include <unistd.h> 2#include <signal.h> 3#include <stdio.h> 4 5void sig_alrm(int signo) 6{ 7    /* nothing to do */ 8} 910unsigned int mysleep(unsigned int nsecs)11{12    struct sigaction newact, oldact;13    sigset_t newmask, oldmask, suspmask;14    unsigned int unslept;1516    //1.为SIGALRM设置捕获函数,一个空函数17    newact.sa_handler = sig_alrm;18    sigemptyset(&newact.sa_mask);19    newact.sa_flags = 0;20    sigaction(SIGALRM, &newact, &oldact);2122    //2.设置阻塞信号集,阻塞SIGALRM信号23    sigemptyset(&newmask);24    sigaddset(&newmask, SIGALRM);25   sigprocmask(SIG_BLOCK, &newmask, &oldmask);   //信号屏蔽字 mask2627    //3.定时n秒,到时后能够产生SIGALRM信号28    alarm(nsecs);2930    /*4.结构一个调用sigsuspend长期无效的阻塞信号集,31     *  在长期阻塞信号集里解除SIGALRM的阻塞*/32    suspmask = oldmask;33    sigdelset(&suspmask, SIGALRM);3435    /*5.sigsuspend调用期间,采纳长期阻塞信号集suspmask替换原有阻塞信号集36     *  这个信号集中不蕴含SIGALRM信号,同时挂起期待,37     *  当sigsuspend被信号唤醒返回时,复原原有的阻塞信号集*/38    sigsuspend(&suspmask); 3940    unslept = alarm(0);41    //6.复原SIGALRM原有的解决动作,响应后面正文142    sigaction(SIGALRM, &oldact, NULL);4344    //7.解除对SIGALRM的阻塞,响应后面正文245    sigprocmask(SIG_SETMASK, &oldmask, NULL);4647    return(unslept);48}4950int main(void)51{52    while(1){53        mysleep(2);54        printf("Two seconds passed\n");55    }5657    return 0;58}

可重入函数/不可重入函数

一个函数在被调用执行期间尚未调用完结的时候,因为某种时序,该函数又被反复调用,这种状况称为「重入」。如果从信号处理程序返回,则继续执行过程断点处的失常指令序列,从从新复原到断点从新执行的过程中,函数所依赖的环境没有产生扭转,就说这个函数是可重入的,反之就是不可重入的。

如果要将函数做成可重入函数,则函数内不能含有全局变量及static变量,也不能应用malloc、free。

更多精彩内容,请关注公众号良许Linux,公众内回复1024可收费取得5T技术材料,包含:Linux,C/C++,Python,树莓派,嵌入式,Java,人工智能,等等。公众号内回复进群,邀请您进高手如云技术交换群。


最初,最近很多小伙伴找我要Linux学习路线图,于是我依据本人的教训,利用业余时间熬夜肝了一个月,整顿了一份电子书。无论你是面试还是自我晋升,置信都会对你有帮忙!

收费送给大家,只求大家金指给我点个赞!

电子书 | Linux开发学习路线图

也心愿有小伙伴能退出我,把这份电子书做得更完满!

有播种?心愿老铁们来个三连击,给更多的人看到这篇文章

举荐浏览:

  • 干货 | 程序员进阶架构师必备资源免费送
  • 神器 | 反对搜寻的资源网站