专栏首页Zaqdt_ACMLinux时序竞态问题(sleep函数的实现)

Linux时序竞态问题(sleep函数的实现)

       时序竞态是指同样的程序,多次调用运行的结果不同,这是由于争夺系统资源所造成的。比如说我们要使用alarm和pause函数来实现一个sleep的功能,那么由于alarm函数的实现过程并不是一个原子操作,那么随时可能被中断。比如说alarm了1秒,在这个过程中,进程失去了CPU,然后当该进程再次获得CPU的时候可能这个时间已经大于1秒了,那么对于alarm来说就已经发出了SIGALRM信号。此时往下继续调用pause函数的话,它会一直都收不到alarm发来的信号,所以导致进程的永久挂起。

       为了解决这个问题,引用了sigsuspend函数。sigsuspend用于在接收到某个信号之前,临时用mask替换进程的信号掩码,并暂停进程执行,直到收到信号为止。也就是说,进程执行到sigsuspend时,sigsuspend并不会立刻返回,进程处于TASK_INTERRUPTIBLE状态并立刻放弃CPU,等待UNBLOCK(mask之外的)信号的唤醒。进程在接收到UNBLOCK(mask之外)信号后,调用处理函数,然后还原信号集,sigsuspend返回,进程恢复执行。下面通过使用alarm和sigsuspend函数来实现sleep函数,代码中有详细的注释来解释说明:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>

void sig_alarm(int num){     // 空函数仅用来捕捉信号

}

unsigned int mysleep(unsigned int n_seconds){
        struct sigaction n_sig, o_sig;     // 一个用来设定,一个用来备份
        sigset_t nsigmask, osigmask, tmpsigmask;   // 用来设定,备份,替换

        /*初始化*/
        n_sig.sa_handler = sig_alarm;       // 设定信号的捕捉动作
        n_sig.sa_flags = 0;
        sigemptyset(&n_sig.sa_mask);
        sigaction(SIGALRM, &n_sig, &o_sig); // 设定SIGALRM的处理行为

        /*对SIGALRM信号设置阻塞,防止在挂起前出现递达态*/
        sigemptyset(&nsigmask);
        sigaddset(&nsigmask, SIGALRM);
        sigprocmask(SIG_BLOCK, &nsigmask, &osigmask);   // 设置新mask,备份旧mask

        alarm(n_seconds);           // 设置n秒的闹钟

        /*对原有的mask取消SIGALRM的阻塞*/
        tmpsigmask = osigmask;
        sigdelset(&tmpsigmask, SIGALRM);
        sigsuspend(&tmpsigmask);    // 挂起等待没有被阻塞的信号

        sigprocmask(SIG_SETMASK, &osigmask, NULL);   // 还原mask
        sigaction(SIGALRM, &o_sig, NULL);            // 还原处理行为
        unsigned int unsletp = alarm(0);             // 获取剩余的睡眠时间
       /*因为如果没有睡眠够n_seconds就收到了一个唤醒信号,那么再次调用alarm会返回剩余的时间*/
        return unsletp;
}

int main(void)
{
        int ans = mysleep(5);
        printf("%d\n", ans);
        return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 牛客寒假算法基础集训营3 B. 处女座的比赛资格(DAG上拓扑排序)

    题目链接:https://ac.nowcoder.com/acm/contest/329/B

    Ch_Zaqdt
  • Codeforces Round #503 (by SIS, Div. 2) C. Elections(思维)

    题目链接:https://codeforces.com/contest/1020/problem/C

    Ch_Zaqdt
  • C++多线程的三种创建方式

           C++11的新特性std::thread的创建方式主要有:函数指针,类或结构体,lambda表达式。下面将会附上这三种创建线程方式的code。

    Ch_Zaqdt
  • svg-captcha的巨大漏洞发现日志

    可能有部分nodejs开发者因为安装图形库很麻烦,都用svg-captcha来生成图形验证码

    黒之染
  • D3.js-柱形图

    data()的工作过程: data()能将数组各项分别绑定到选择集的各元素上,并且能指定绑定的规则。当数组长度与元素数量不一致时,data()也能够处理。当...

    奋飛
  • 简单审计-菠菜源码黑吃黑

    洛米唯熊
  • Python+django网页设计入门(6):文件上传与数据导入

    1、在网站项目的templates文件夹中创建文件uploadQuestions.html,内容如下:

    Python小屋屋主
  • Swift专题讲解二十三——高级运算符 原

            除了前边博客中介绍的基本运算符外,Swift中还支持更多高级运算符,也支持开发者进行运算符的自定义。Swift中的算符运算符有一个特点,其不会产...

    珲少
  • 百闻不如一码!手把手教你用Python搭一个Transformer

    与基于RNN的方法相比,Transformer 不需要循环,主要是由Attention 机制组成,因而可以充分利用python的高效线性代数函数库,大量节省训练...

    一墨编程学习
  • Qt 打开文件的默认路径 QFileDialog::getOpenFileName()

    为了说明QFileDialog::getOpenFileName()函数的用法,还是先把函数签名放在这里:

    bear_fish

扫码关注云+社区

领取腾讯云代金券