编写规避竞态条件的mysleep

本文介绍如何使用alarm和pause函数实现自制sleep函数,并讨论了信号处理的重要性。进一步通过sigsuspend函数改进版本,解决竞态条件问题,确保信号的正确处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 编写普通版本mysleep

【基本操作函数】

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct
sigaction *oact);//信号处理函数,作用与signal类似

struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };


#include <unistd.h>
int pause(void);
//pause函数使调⽤用进程挂起直到有信号递达。pause只有出错的返回值,正常调用挂起进程。
//只有出错的返回值,这一点和execv组程序替换函数很像


#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//调⽤用alarm函数可以设定⼀一个闹钟,也就是告诉内核在seconds秒之后给当前进程发
//SIGALRM信号, 该信号的默认处理动作是终⽌止当前进程。这个函数的返回值是0或者是以前
//设定的闹钟时间还余下 的秒数。
//普通版本实现
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>

//利用alarm 和pause  实现 sleep函数

void myhandler(int sig)
{
}

int mysleep(int seconds)
{
      struct sigaction act,oact;
      act.sa_handler=myhandler;
      act.sa_flags=0;
      sigemptyset(&act.sa_mask);

      sigaction(SIGALRM,&act,&oact);
      alarm(seconds);
      pause();

      int ret=alarm(0);
      sigaction(SIGALRM,&oact,NULL);
      return ret;

}

int main()
{
      while(1)
      {
            mysleep(1);
            printf("1 seconds passed\n");
      }

      return 0;
}

补充问题:

1、信号处理函数sig_alrm什么都没⼲干,为什么还要注册它作为SIGALRM的处理函数?不注 册信号处 理函数可以吗?
(SIGALRM会终⽌止进程?)

不可以,信号的递达有三种方式:1.默认(一般是直接终止) 2.忽略 3. 自定义处理(信号捕捉)
如果,不进行捕捉,默认一般会终止进程,达不到函数要实现的内容

2、为什么在mysleep函数返回前要恢复SIGALRM信号原来sigaction(想想不恢复 会怎样?)

不恢复就会导致,下次发生信号SIGALAM后,进程得不到终止。

3.volatile限定符 作用: 防止编译器进行优化,每次必须从内存中读取数据。(保证了数据的一致性)

对于有的变量,编译器会将其优化到寄存器,这样的话,就会两份数据,内存中的数据发生改变,寄存器没有改变就会出现错误。

2. 编写规避竞态条件的mysleep

//规避竞态条件的mysleep
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);

//“解除信号屏蔽”和“挂起等待信号”这两步能合并成⼀一个原⼦子操作
//sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对
//时序要求严格的场合下都应该调⽤用sigsuspend⽽而不是pause。

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


void myhandler(int sig)
{
}

int mysleep(int seconds)
{
      struct sigaction act,oact;
      sigset_t newmask,oldmask,suspmask;

      act.sa_handler=myhandler;
      act.sa_flags=0;
      sigemptyset(&act.sa_mask);
      sigaction(SIGALRM,&act,&oact);

      //block SIGALRM and save current signal mask
      sigemptyset(&newmask);
      sigaddset(&newmask,SIGALRM);
      sigprocmask(SIG_BLOCK,&newmask,&oldmask);//屏蔽信号SIGALRM

      alarm(seconds);

      suspmask=oldmask;
      sigdelset(&suspmask,SIGALRM);//make sure SIGALRM isn't blocked
      sigsuspend(&suspmask);

      int ret=alarm(0);
      sigaction(SIGALRM,&oact,NULL);
      sigprocmask(SIG_SETMASK,&oldmask,NULL);
      return ret;
}

int main()
{
      while(1)
      {
            mysleep(1);
            printf("1 seconds passed\n");
      }

      return 0;
}

3.两者进行对比

系统运⾏行的时序(Timing)并不像我们写程序时所设想的那样。在普通版本中,虽然alarm(nsecs)紧接着的下⼀行就是pause(),但是无法保证pause()⼀一定会在调⽤用alarm(nsecs)之 后的nsecs秒之内被调用。
被切换后,若触发SIGALARM信号,信号会立即递达当该进程切回来的时候,接收不到SIGALRM信号,不会再次接收到该信号,线程一直处于挂起状态,得不到激活。

改进版中,对SIGALRM信号进行了屏蔽,被切出去之后,该信号也不会被递达。因为是原语操作,取消屏蔽字后,pause可以正常接收到了信号,程序正常激活
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值