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可以正常接收到了信号,程序正常激活