实验五 实现mysleep函数
实验描述
函数名字和原型:
unsigned int mysleep(unsigned int);
该函数的功能要求与UNIX的sleep函数一样。
要求:
1、使用alarm函数实现定时。
2、必须正确处理mysleep函数中的闹钟与调用者可能设置的闹钟之间的关系。例如,如何解决不同的信号处理函数的保存和恢 复?如何处理调用者设置的闹钟比mysleep函数中的闹钟早响的问题?如何处理调用进程屏蔽SIGALRM信号?
3、不允许出现任何竟态条件(时间窗口)。
4、总之,mysleep的实现细节应当对调用者透明,也就是说, 不论在实现mysleep函数时是否使用了alarm函数,对调用者是 否以及如何使用alarm函数均不应有任何影响。
主要原理
1、以书中10-21 sleep的可靠实现为蓝本,引入与以前设置的闹钟的交互作用
2、使用flag判断是否存在前置闹钟,存在则返回剩余值,不存在则返回零(保存SIGALARM信号处理程序的地址,并在返回前复位它)
实验代码:mysleep.c
#include "apue.h"
static void sig_alrm(int signo)
{
}
unsigned int mysleep(unsigned int nsecs)
{
structsigaction newact, oldact;
sigset_t newmask, oldmask, suspmask, blockedmask;
intissuspend = 0;/* SIGALRM是否被阻塞 */
intisblocked = 0;/* 是否存在未决的SIGALRM */
unsigned int unslept, slept;
unsigned int oldalarm;
oldalarm = alarm(0);
/* 保存以前的SIGALRM配置,并设置成我们需要的配置 */
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
/* 处理以前的SIGALRM */
/* 判断SIGALRM是否被阻塞 */
sigprocmask(SIG_BLOCK, NULL, &oldmask);
if(sigismember(&oldmask, SIGALRM)) {
/*SIGALRM被阻塞 */
issuspend = 1;
}
/* 判断是否存在未决的SIGALRM */
if(sigpending(&blockedmask) < 0) {
err_sys("sigpending error");
}
if(sigismember(&blockedmask, SIGALRM)) {
/*存在未决的SIGALRM */
suspmask = oldmask;
isblocked = 1;
sigdelset(&suspmask, SIGALRM);
sigsuspend(&suspmask);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
}
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
#ifdefDEBUG
printf("DEBUG - oldalarm = %u\n", oldalarm);
#endif
if((oldalarm != 0) && (oldalarm < nsecs) && (issuspend == 0)){
/*以前剩余的闹钟值比睡眠时间少且没有被阻塞 */
alarm(oldalarm);
}
else {
/*以前剩余的闹钟值比睡眠时间多或者被阻塞 */
alarm(nsecs);
}
suspmask = oldmask;
sigdelset(&suspmask, SIGALRM);
sigsuspend(&suspmask);
unslept = alarm(0);
sigaction(SIGALRM,&oldact, NULL);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
/* 计算实际睡眠时间 */
if((oldalarm != 0) && (oldalarm < nsecs) && (issuspend == 0)){
/*以前的闹钟值比睡眠时间少 */
slept = oldalarm - unslept;
}
else {
/* 以前的闹钟值比睡眠时间多 */
slept = nsecs - unslept;
}
if(isblocked == 1) {
kill(getpid(), SIGALRM);
}
if(slept < oldalarm) {
/*以前的闹钟值比实际睡眠时间多 */
alarm(oldalarm - slept);
return nsecs - slept;
}
else {
if(oldalarm != 0) {
kill(getpid(), SIGALRM);
}
return nsecs - slept;
}
return0; /* 出错 */
}
四.实验结果
源程序名:mysleep.c
可执行程序名:mysleeep
编译方法:gcc mysleep.c testsleep.c error2e.c –o mysleep (注:testsleep.c 文件由老师提供,但为了方便附上这一文件)
结束方法:ctrl+c
运行过程:
1)编译
2)运行
五 实验小结
通过此次实验学习可以得知:
sigset_t信号集类型,含有各种类型的信号;sigaction结构出现在sigaction函数中,sigaction函数的功能是检查或修改与指定信号相关联的处理动作
intsigaction(int signo,const struct sigaction *restrict act,const struct sigaction*restrict oact);
若act指针非空,则要修改其动作。若oact指针非空,则系统经由oact指针返回该信号的上一个动作
alarm(0)的功能是取消前一个设置的闹钟,并且其返回值是上一个alarm的余留值
_sleep函数过程
1.设置newact,对应于SIGALRM的动作
2.设置newmask,这个信号量集用来屏蔽SIGALRM
3.alarm(nsecs)
4. sigsuspend(&suspmask);进程挂起,直到捕捉到一个信号量(任意信号量),返回
5.unslept=alarm(0)
6.return(unslept)
mysleep函数过程
1.oldAlarm=alarm(0);取消以前设置的alarm,并记录下以前alarm的时间
2.sigpending(&pendingSigset);取出被阻塞的进程
3.检查是否有SIGALRM信号,如果有,则释放掉,忽略
4.case1: alarm(0),sleep(5)
_sleep(nsecs);直接睡5秒
5.case2:alarm(5),sleep(10)
睡5秒后发出一个SIGALRM信号,sleep被打断
6.case3:alarm(10),sleep(5)
先睡5秒,然后在10s的时候发出SIGALRM信号
7.case4:如果之前有被pending的信号,在sleep之后raise
附件:testsleep.c
#include <signal.h>
#include <stddef.h>
#include <time.h>
#include "apue.h"
static char *testpos;
static unsigned int smallsecs = 5;
static unsigned int bigsecs = 10;
static void sig_alrm(int signo)
{
printf("%s: SIGALRM is caught\n", testpos);
return;
}
static void sig_usr1(int signo)
{
printf("%s: SIGUSR1 is caught\n", testpos);
return;
}
static void loopwait(unsigned int nsecs)
{
time_t start;
start = time(NULL);
while( time(NULL) < (start + nsecs) );
}
void testsleep(unsigned int oldsecs, unsigned int nsecs)
{
pid_t pid;
testpos = "in testsleep, 1st pass";
alarm(oldsecs);
printf("%s: unslept is %u seconds\n", testpos, mysleep(nsecs) );
loopwait(bigsecs);
testpos = "in testsleep, 2nd pass";
if( (pid = fork() ) < 0 )
err_sys("fork error");
if( pid == 0) {
loopwait(2);
kill(getppid(), SIGUSR1);
exit(0);
}
else {
alarm(oldsecs);
printf("%s: unslept is %u seconds\n", testpos, mysleep(nsecs) );
if( wait(NULL) != pid )
err_sys("wait error");
}
}
main()
{
sigset_t alrmmask, oldmask;
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGUSR1, sig_usr1);
signal(SIGALRM, sig_alrm);
sigemptyset(&alrmmask);
sigaddset(&alrmmask, SIGALRM);
printf("set alarm to 0 seconds and sleep 5 seconds\n");
testsleep(0, 5);
testpos = "out of testsleep";
printf("************* 1st sleep test finished *************\n\n");
printf("set alarm to %u seconds and sleep %u seconds\n", smallsecs, bigsecs);
testsleep(smallsecs, bigsecs);
testpos = "out of testsleep";
loopwait(bigsecs);
printf("************* 2nd sleep test finished *************\n\n");
printf("set alarm to %u seconds and sleep %u seconds\n", bigsecs, smallsecs);
testsleep(bigsecs, smallsecs);
testpos = "out of testsleep";
loopwait(bigsecs);
printf("************* 3rd sleep test finished *************\n\n");
printf("Now SIGALRM is blocked\n");
sigprocmask(SIG_BLOCK, &alrmmask, &oldmask);
printf("set alarm to %u seconds and sleep %u seconds\n", smallsecs, bigsecs);
testsleep(smallsecs, bigsecs);
testpos = "out of testsleep";
printf("Now SIGALRM is unblocked\n");
sigprocmask(SIG_SETMASK, &oldmask, NULL);
loopwait(bigsecs);
printf("************* 4th sleep test finished *************\n\n");
printf("Now SIGALRM is blocked\n");
sigprocmask(SIG_BLOCK, &alrmmask, &oldmask);
printf("set alarm to %u seconds and sleep %u seconds\n", bigsecs, smallsecs);
testsleep(bigsecs, smallsecs);
testpos = "out of testsleep";
printf("Now SIGALRM is unblocked\n");
sigprocmask(SIG_SETMASK, &oldmask, NULL);
loopwait(bigsecs);
printf("************* 5th sleep test finished *************\n\n");
printf("Now SIGALRM is blocked and a SIGALRM is pending\n");
sigprocmask(SIG_BLOCK, &alrmmask, &oldmask);
kill(getpid(), SIGALRM);
printf("set alarm to 0 seconds and sleep 5 seconds\n");
testsleep(0, 5);
testpos = "out of testsleep";
printf("Now SIGALRM is unblocked\n");
sigprocmask(SIG_SETMASK, &oldmask, NULL);
printf("************* 6th sleep test finished *************\n\n");
}