Linux 信号之mysleep

来源:互联网 发布:linux yum安装ant 编辑:程序博客网 时间:2024/06/05 01:03

一、    用alarm和pause实现sleep(3)函数,称为mysleep。

 

1. main函数调用mysleep函数,后者调用sigaction注册了SIGALRM信号的处理函数sig_alrm。

2. 调用alarm(seconds)设定闹钟。

3. 调用pause等待,内核切换到别的进程运行。

4. seconds秒之后,闹钟超时,内核发SIGALRM给这个进程。

5. 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数 是sig_alrm。

6. 切换到用户态执行sig_alrm函数,进入sig_alrm函数时SIGALRM信号被自动屏蔽, 从sig_alrm函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入 内核,再返回用户态继续执行进程的主控制流程(main函数调用的mysleep函数)。

7. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理 动作。

              #include <stdio.h>

#include <signal.h>

#include <unistd.h>

 

void sig_alarm(int signo)//信号处理函数

{}

 

int mysleep(int seconds)

{

structsigaction act, oact;

act.sa_handler= sig_alarm;

sigemptyset(&act.sa_mask);

act.sa_flags= 0;

sigaction(SIGALRM,&act, &oact);//注册信号处理函数

 

alarm(seconds);//设置闹钟

pause();

int_time = alarm(0);//清空闹钟

sigaction(SIGALRM,&oact,NULL);//恢复默认信号处理动作

return_time;

}

 

int main()

{

while(1)

{

        printf("iam sleeping !\n");

        mysleep(3);

 

}

return0;

}

运行结果:每3秒打印一条语句


缺陷:在闹钟设置之后,响应之前被切出去,再过3秒再切回来,就将永远收不到信号,进程将被永远挂起。

思考问题:

1、           信号处理函数sig_alrm什么都没⼲干,为什么还要注册它作为SIGALRM的处理函数?不注册信号处 理函数可以吗?

信号处理机制:

用函数signal注册一个信号捕捉函数。原型为:


#include 
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
 
signal 的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数,该参数也可以是SIG_DEF(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉该信号而不做任何处理)。signal如果调用成功,返回以前该信号的处理函数的地址,否则返回 SIG_ERR。


sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个参数,表示信号值。

示例:

1)、  捕捉终端CTRL+c产生的SIGINT信号:
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
 
void SignHandler(int iSignNo)
{
    printf("Capture sign no:%d/n",iSignNo); 
}
 
int main()
{
    signal(SIGINT,SignHandler); 
    while(true) 
        sleep(1); 
    return 0; 
}
该程序运行起来以后,通过按CTRL+c将不再终止程序的运行。应为CTRL+c产生的SIGINT信号已经由进程中注册的SignHandler函数捕捉了。该程序可以通过Ctrl+/终止,因为组合键Ctrl+/能够产生SIGQUIT信号,而该信号的捕捉函数尚未在程序中注册。

 

2)、  忽略掉终端CTRL+c产生的SIGINT信号:
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
 
int main()
{
    signal(SIGINT,SIG_IGN); 
    while(true) 
        sleep(1); 
    return 0; 
}
该程序运行起来以后,将CTRL+C产生的SIGINT信号忽略掉了,所以CTRL+C将不再能是该进程终止,要终止该进程,可以向进程发送SIGQUIT信号,即组合键CTRL+/
 
3)、  接受信号的默认处理,接受默认处理就相当于没有写信号处理程序:
 
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
 
int main()
{
    signal(SIGINT,DEF); 
    while(true) 
        sleep(1); 
    return 0; 
}

2、为什么在mysleep函数返回前要恢复SIGALRM信号原来的sigaction?

 

        当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。

 

 

 

二、  sigsuspend实现mysleep 函数:

 

对于第一个版本的mysleep , 出现这个问题的根本原因是系统运行的时序(Timing)并不像我写程序时所设想的那样。虽然alarm(nsecs)紧接着的下⼀行就是pause(),但是⽆无法保证pause()⼀一定会在调用alarm(nsecs)之后的nsecs秒之内被调⽤用。由于异步事件在任何时候都有可能发⽣生(这⾥的异步事件指出现更高优 先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这叫做竞态条件 (Race Condition)。

 

sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的场合下都应该调⽤用sigsuspend而不是pause。

 

#include <signal.h>

int sigsuspend(const sigset_t *sigmask);

 

和pause⼀一样,sigsuspend没有成功返回值,只有执⾏行了⼀一个信号处理函数之后sigsuspend才返回,返回值为-1,errno设置为EINTR。

 

调⽤用sigsuspend时,进程的信号屏蔽字由sigmask参数指定,可以通过指定sigmask来临时解除 对某 个信号的屏蔽,然后挂起等待,当sigsuspend返回时,进程的信号屏蔽字恢复为原来的值,如果原来对该信号是屏蔽的,从sigsuspend返回后仍然是屏蔽的。

 

以下⽤用sigsuspend重新实现mysleep函数:

1、         屏蔽SIGALARM信号

2、         alarm(seconds);

3、         解除对SIGALARM信号的屏蔽。

4、         挂起等待pause();

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

 

void sig_alarm(int signo)

{}

 

int mysleep(int seconds)

{

       structsigaction act, oact;

       sigset_tnewmask, oldmask, suspmask;

//设置信号处理函数,保存以前的信息

       unsignedint unslept;

       act.sa_handler= sig_alarm;

       sigemptyset(&act.sa_mask);

       act.sa_flags= 0;

       sigaction(SIGALRM,&act, &oact);

   //阻塞信号,保存当前的信号屏蔽字

       sigemptyset(&newmask);

       sigaddset(&newmask,SIGALRM);

       sigprocmask(SIG_BLOCK,&newmask, &oldmask);

       //屏蔽SIGALRM

       alarm(seconds);

       suspmask= oldmask;

       sigdelset(&suspmask,SIGALRM);

       sigsuspend(&suspmask);

//解除屏蔽,挂起等待//SIGALRM信号递达后,sigsuspend返回,自动恢复原来的屏蔽字,自动恢复原来的屏蔽字,即再次屏蔽SIGMASK

int _time =alarm(0);

       sigaction(SIGALRM,&act, NULL);

//恢复默认的信号处理动作

       sigprocmask(SIG_SETMASK,&oldmask, NULL);

      //重置信号屏蔽字,再次解除对SIGALRM的屏蔽。

       return_time;

}

 

int main()

{

       while(1)

       {

              mysleep(5);

              printf("iam sleeping !\n");

       }

       return0;

}

 

运行结果:每隔五秒响应动作,打印语句。

1 0
原创粉丝点击