Linux 学习笔记19 信号

Linux 学习笔记19 信号

信号

信号概述

为什么要是使用信号——为了实现进程的有序退出
信号是进程运行过程中,由自身产生或者由进程外部发来的消息。信号是硬件中断的软件模拟(软中断)

  • signal信号处理机制
  •  typedef void (*sighandler_t)(int); //函数指针
     sighandler_t signal(int signum, sighandler_t handler);
    
  • 参数解析:
    signum 表示要捕捉的信号,
    第 2 个参数是个函数指针(回调函数),表示要对该信号进行捕捉的函数,该参数也可以是 SIG_DFL (表示交由系统缺省处理)或 SIG_IGN (表示忽略掉该信号而不做任何处理)。signal 如果调用成功,返回以前该信号的处理函数的地址,否则返回 SIG_ERR。
  • 信号处理机制代码如下:
    signal 例子:
#include <func.h>
  void sigfunc(int signum)//回调函数                                                                          
  {
      printf("sig num%d is coming\n",signum);//显示几号信号
  }
 int main(int argc,char * argv[])
  {
     int ret;
      signal(SIGINT,sigfunc);
      char buf[128]={0};//以下为写一个read,为了使程序阻塞,查看信号的效果
      read(0,buf,sizeof(buf));//用while(1)也可达到同样的效果
      return 0;
  }

执行效果如下:

signal

signal 设定多种信号:

#include <func.h>
  //设定2号信号和3号信号
  //模拟特殊情况
  void sigfunc(int signum)
  {
      printf("%d is coming\n",signum);                                                                        
  }
 int main(int argc,char * argv[])
  {
      signal(SIGINT,sigfunc);
      signal(SIGQUIT,sigfunc);
      char buf[128]={0};
      read(0,buf,sizeof(buf));
      return 0;
  }

执行效果如下:
signal2
signal的特殊情况:

#include <func.h>
  //演示例一:2号信号处理流程会被3号信号打断
  //例二:相同信号不会打断原有信号处理流程,如2号信号不会打断之前2号信号的处理流程
  //例三:发送多次相同信号,只会多响应一次相同信号。
  void sigfunc(int signum)
  {//sigfunc中代码为信号处理流程
      printf("before sleep %d is coming\n",signum);
      sleep(3);
      printf("sig %d is awake\n",signum);                                                                     
  }
 int main(int argc,char * argv[])
  {
      int ret;
      signal(SIGINT,sigfunc);
      signal(SIGQUIT,sigfunc);
      char buf[128]={0};//以下为写一个read,为了使程序阻塞,查看信号的效果
      read(0,buf,sizeof(buf));//用while(1)也可达到同样的效果
      return 0;
  }

特殊情况1:在2号信号执行时,同类型(2号)的信号再发生,无论发生多少次,只会再多执行一次。
special1

特殊情况2:2号信号正在处理时,发生了3号信号,此时会跳转执行3号信号,之后再执行剩下的2号信号
special 2

特殊情况3:程序read正在阻塞,2号信号产生了,此时输入一个回车键,则系统会等待2号信号处理完毕后,再读取回车键
read

SIG_IGN 和 SIG_DFL 举例:

SIG_IGN:

#include <func.h>
  //设定2号信号和3号信号
  //模拟特殊情况
  void sigfunc(int signum)
  {
      printf("%d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      signal(SIGINT,SIG_IGN);//将2号信号忽略
      signal(SIGQUIT,sigfunc);
      char buf[128]={0};
      read(0,buf,sizeof(buf));                                                                                
      return 0;
  }

执行效果如下:
sig ignore

SIG_DFL:

#include <func.h>
  
  void sigfunc(int signum)
  {
      printf("%d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      signal(SIGINT,SIG_IGN);
      printf("start sleep\n");//信号将被忽略
      sleep(10);
      printf("sigfunc awake\n");//此时信号将可以被响应
      signal(SIGINT,SIG_DFL);//SIG_DFL,信号只能被响应一次                                                     
      sleep(2);
      return 0;
  }

执行效果如下:
sig default

  • 信号处理机制的特殊情况
    1注册一个信号处理函数,并且处理完毕一个信号之后,不需要重新注册,才能够捕捉下一个
    信号;
    2、 如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个同类型的信号,这时
    会挨着执行(如:2号信号之后又发生了一个2号信号,就会多执行一次)
    3、 如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号,此时会跳转去执行另一个信号,之后再执行剩下的没有处理完的信号(如:2号信号正在处理时,发生了3号信号,此时会跳转执行3号信号,之后再执行剩下的2号信号)
    4、 如果程序阻塞在一个系统调用(如 read(…))时,发生了一个信号,会先跳转到信号处理函数,等信号处理完毕后,系统调用再返回。(如:程序read正在阻塞,信号2号产生了,此时输入一个回车键,则系统会等待2号信号处理完毕后,再读取回车键)

sigaction信号处理注册

  •  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    
  • 参数解析:
    signum为要捕捉的信号
    act是一个结构体里面包含信号处理函数地址、处理方式等信息
    oldact 是一个传出参数, 为上一次设置的信号行为,通常为 NULL(不需要取出上一次的行为)。
    结构体 struct sigaction的原型为:

  •  struct sigaction {	
     void (*sa_handler)(int); //老类型的信号处理函数指针,因为类似signal,一般不用
     void (*sa_sigaction)(int, siginfo_t *, void *);//新类型的信号处理函数指针,用这个
     sigset_t sa_mask; //将要被阻塞的信号集合
     int sa_flags; //信号处理方式掩码 ( SA_SIGINFO ,具体见下方表格)
     void (*sa_restorer)(void); //保留,不要使用
     };
    

结构体分析:
sa_falgs是一组掩码的合成值,指示信号处理时所应该采取的一些行为

掩码描述
SA_RESETHAND处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,该选项不符合一般的信号处理流程,目前已经不再使用,仅了解即可
SA_NODEFER在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递归地处理。(不常用)无限重入
SA_RESTART如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着从阻塞的系统返回。如果不指定该参数,中断处理完毕之后,read 函数读取失败。
SA_SIGINFO指示结构体的信号处理函数指针是哪个有效,如果 sa_flags 包含该掩码,则 sa_sigaction 指针有效,否则是 sa_handler 指针有效。(常用)
  • sigaction SA_SIGINFO 例子如下
#include <func.h>
  //针对signal五种特殊情况可采用sigaction
  
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("%d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO;//当掩码为SA_SIGINFO时,sigaction生效,否则为结构体中的handler生效              
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      while(1);
      return 0;
  }

执行效果如下:
sigaction_SIGINFO

  • SA_RESETHAND 信号捕捉生效一次后,回归到默认设置。很少用 ,代码如下:
#include <func.h>
  //针对signal五种特殊情况可采用sigaction
  
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("%d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO|SA_RESETHAND;//信号第一次发的时候有效,第二次发时回归到默认                     
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      while(1);
      return 0;
  }

执行效果如下:
sigaction_reset

  • SA_NODEFER 信号不断重入,一个新的信号来了,就打断前一个信号,先执行新的信号,包括自身(即2号也能打断2号信号),代码如下:
 #include <func.h>
  //针对signal五种特殊情况可采用sigaction
  
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("before sleep sig %d is coming\n",signum);
      sleep(3);
      printf("sig %d is awake\n",signum);
  }                                                                                                           
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO|SA_NODEFER;//不断重入
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      while(1);
      return 0;
  }

执行效果如下:
sig_nodefer

  • SA_RESTART 对具有阻塞的接口有影响,如read,scanf,如果不加则不会阻塞 ,处理完毕信号之后直接跳过 read 或者 scanf
    以下为 signal 和 sigaction 对比 代码如下:
  #include <func.h>
  //signal_read
  void sigfunc(int signum)
  {
      printf("sig %d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      signal(SIGINT,sigfunc);
      char buf[128];
      read(0,buf,sizeof(buf));
      printf("buf is %s\n",buf);                                                                              
      return 0;
  }

执行效果如下:
signal_read

#include <func.h>
  //针对signal五种特殊情况可采用sigaction
  
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("%d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO|SA_RESTART;  //测试2
      //act.sa_flags=SA_SIGINFO;//测试1                                                                       
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      char buf[128]={0};
      ret=read(0,buf,sizeof(buf));
      printf("ret=%d,buf=%s\n",ret,buf);
      return 0;
  }

执行效果如下:
sigaction_SA_RESTART

  • sa_mask是是一个包含信号集合(sigset_t)的结构体,该结构体内的信号表示在进行信号处理时,将要被阻塞的信号。(难点)

  •  sigset_t //结构体处理函数
     int sigemptyset(sigset_t *set); //清空信号集合 set
     int sigfillset(sigset_t *set); //将所有信号填充进 set 中,将所有信号置为1
     int sigaddset(sigset_t *set, int signum); //往 set 中添加信号 signum  常用
     int sigdelset(sigset_t *set, int signum); //从 set 中移除信号 signum
     int sigismember(const sigset_t *set, int signum); //判断 signum 是否包含在 set 中(是:返回 1,否:0)  常用
     int sigpending(sigset_t *set); //将被阻塞的信号集合由参数 set 指针返回 与上述函数不同,上述函数是位操作,此函数是系统调用   常用
    

    挂起:信号已经到达,但是没有被响应称之为挂起

  • 实现2号信号不被3号信号打断,先执行完2号信号再执行3号信号的效果——使用 sigset_t 信号集合,代码如下:

#include <func.h>
  //例:如何使用sa_mask
  //实现2号信号处理时,屏蔽3号信号,使3号信号无法打断2号信号。
  //接口 sigaddset
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("before sleep sig %d is coming\n",signum);
      sleep(3);
      printf("sig %d is awake\n",signum);
  }
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO;
      sigaddset(&act.sa_mask,SIGQUIT);//使3号信号被屏蔽
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      sigaction(SIGQUIT,&act,NULL);                                                                           
      while(1);
      return 0;
  }

执行效果:
sigaddset

  • 信号挂起 sigpending 可用于查看2号信号处理时是否有其他信号发生,代码如下:
#include <func.h>
  //例:如何使用sigpending 信号挂起
 void sigfunc(int signum,siginfo_t *p,void *q) 
  {//第二个参数之后分析,第三个参数基本用不到
  
      printf("before sleep sig %d is coming\n",signum);
      sleep(3);
      sigset_t pend_signal;//挂起信号集
      sigpending(&pend_signal);//将信号集挂起
      if(sigismember(&pend_signal,SIGQUIT))//检测在2号信号处理时,是否有3号信号发生
      {   
          printf("SIGQUIT is pending\n");
      }else
      {   
          printf("SIGQUIT isn't pending\n");
      }   
      printf("sig %d is awake\n",signum);
  }
 int main(int argc,char * argv[])
  {
      struct sigaction act;
      //memset(&act,0,sizeof(act));//memset和bzero效果一样,使结构体初始化为0
      bzero(&act,sizeof(act));
      act.sa_sigaction=sigfunc;//赋函数指针
      act.sa_flags=SA_SIGINFO;
      sigaddset(&act.sa_mask,SIGQUIT);//使3号信号被屏蔽
      int ret;
      ret=sigaction(SIGINT,&act,NULL);
      ERROR_CHECK(ret,-1,"sigaction");
      sigaction(SIGQUIT,&act,NULL);
      while(1);
      return 0;        
   }                                

执行效果如下:

pending

sigprocmask信号阻塞

使用场景:加锁—关键代码—解锁,为了使信号不打断加解锁中的关键代码,让代码先执行再接收信号,需要阻塞信号sigprocmask。需要改造成 阻塞—加锁—代码—解锁—解除阻塞,sigprocmask为全程阻塞,常用于保护关键代码。
概念:sigprocmask 是全程阻塞,在 sigprocmask 中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。

  •  int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    

参数解析:

  • how为以下三者之一:
参数解析
SIG_BLOCK将参数 2 的信号集合添加到进程原有的阻塞信号集合中
SIG_UNBLOCK从进程原有的阻塞信号集合移除参数 2 中包含的信号
SIG_SETMASK重新设置进程的阻塞信号集为参数 2 的信号集
  • *set:想要阻塞的信号集,用 sigset_t 设定。
  • *oldset:原有进程的信号集,一般填NULL。

sigprocmask 代码如下:

#include <func.h>
  //sigprocmask 例子
  //验证无论是否有信号打断,关键代码都会被执行
  //                                                                                                          
 int main(int argc,char * argv[])
  {
      int ret;
      sigset_t mask;//定义一个信号集合
      sigemptyset(&mask);//清空mask信号集
      sigaddset(&mask,SIGINT);//填入想要阻塞的信号
      ret=sigprocmask(SIG_BLOCK,&mask,NULL);//开始阻塞
      ERROR_CHECK(ret,-1,"sigprocmask");
      //以下为想要保护的关键代码
      printf("This is Key programe\n");
      sleep(3);
      ret=sigprocmask(SIG_UNBLOCK,&mask,NULL);//解除阻塞
      ERROR_CHECK(ret,-1,"sigprocmask");
      return 0;
  }

执行效果如下:
sigprocmask

可见,由信号中断时,关键代码不受影响,仍然会执行打印。

sigprocmask 和 sigpending ,查看在全程阻塞的过程中是否有信号发生,代码如下:

#include <func.h>                                                                                           
  //sigprocmask 例子
  //利用sigpending查看在全程阻塞的过程中是否有信号发生
 int main(int argc,char * argv[])
  {
      int ret;
      sigset_t mask;//定义一个信号集合
      sigemptyset(&mask);//清空mask信号集
      sigaddset(&mask,SIGINT);//填入想要阻塞的信号
      ret=sigprocmask(SIG_BLOCK,&mask,NULL);//开始阻塞
      ERROR_CHECK(ret,-1,"sigprocmask");
      //以下为想要保护的关键代码
      printf("This is Key programe\n");
      sleep(3);
      sigset_t pend;
      sigemptyset(&pend);
      sigpending(&pend);
      if(sigismember(&pend,SIGINT))
      {   
          printf("SIGINT is pending\n");
      }else
      {   
          printf("SIGINT isn't pending\n");
      }   
      ret=sigprocmask(SIG_UNBLOCK,&mask,NULL);//解除阻塞
      ERROR_CHECK(ret,-1,"sigprocmask");
      return 0;
  }

执行效果如下:
sigpend和sigprocmask

kill信号

  •  int kill(pid_t pid,int sig);//如 kill(pid,SIGINT)
    
  • 参数解析
    pid<0 将信号发送给进程组 ID 等于 pid 绝对值的所有进程,
    pid=0 将信号发送给与此进程同一个进程组的所有进程
    pid>0 将信号发给 ID 为 pid 的进程
    pid=-1 将信号发送给该进程有权限发送的系统里的所有进程,如果是 root 就会发送给所有进程
    sig为想要发送的信号
  • kill 举例:
#include <func.h>

int main(int argc,char * argv[])
{
    ARGS_CHECK(argc,2);
    pid_t pid=atoi(argv[1]);
    int ret=kill(pid,SIGINT);
    ERROR_CHECK(ret,-1,"kill");                                                                               
    return 0;
}
  • 子进程自身使自己终止与pid=0时的代码如下:
#include <func.h>
  
 int main(int argc,char * argv[])
  {
      if(!fork())
      {
         int ret=kill(getpid(),SIGINT);//子进程使自身终止 kill杀死自己时不会产生Z进程 
         //int ret=kill(0,SIGINT);//pid=0时,父子进程都将终止,属于同一进程组                                                  
         while(1);
      }else
      {
          wait(NULL);
          while(1);
      }
      return 0;
  }

执行效果如下:
fork_kill

  • pid<0举例:
#include <func.h>
  
 int main(int argc,char * argv[])
  {
      if(!fork())
      {
          while(1);                                                                                           
      }else                      
      {                          
          wait(NULL);            
          while(1);              
      }                          
      return 0;                  
  }                              
      

执行效果如下:
![kill_pid](https://img-blog.csdnimg.cn/20200410214619904.png在这里插入图片描述

-13655 代表 pid<0 会删除进程组 ID 为13655 的所有进程。

  • pid=-1 举例:
  •  kill -2 -1 //注意最好不要用 root 删除,否则操作系统会注销所有进程 
    

sleep函数

  • sleep实现原理:sleep内部是由信号处理机制进行处理的,sleep采用两个接口实现
  •  usigned int alarm(unsigned int seconds); // 告诉自身的进程再seconds秒后自动产生一个SIGALRM(14号)的信号
    
  •  int pause(void); //将自身进程挂起,直到信号发生时,才从pause返回
    
  •  kill -l // 查看所有信号的命令
    
  •  man 7 signal //查看信号默认行为
    
  • alarm代码如下:
#include <func.h>
  //演示 alram 是怎么运作的
  void sigfunc(int signum)
  {
      printf("sig %d is coming\n",signum);
  }
 int main(int argc,char * argv[])
  {
      alarm(3);
      char buf[128]={0};
      read(0,buf,sizeof(buf));                                                                                
      return 0;
  }

alarm执行效果:
alarm

  • pause 的实现:
#include <func.h>
  //pause 的实现                                                                                              
 int main(int argc,char * argv[])
  {
      pause();
      return 0;
  }

执行效果如下:
pause
可见,pause 将进程放入睡眠队列中。

  • sleep 函数的实现:
#include <func.h>
  //sleep 的实现
 void sigfunc(int signum)
  {
  }
 int main(int argc,char * argv[])
  {
      //signal(SIGALRM,SIG_IGN);//测试忽略(SIG_IGN)的时候能不能让进程唤醒——不能
      signal(SIGALRM,sigfunc);//空函数,使sleep函数实现
      alarm(3);//3秒后将进程唤醒                                                                              
      pause();
      return 0;
  }

时钟处理

Linux为每个进程维护3个计时器,分别是真实计时器、虚拟计时器和实用计时器

真实计时器计算的是程序运行的实际时间
实用计时器程序处于R状态(运行)时所消耗的时间之和,即统计进程对时间片消耗的累计值
虚拟计时器计算的是程序运行在用户态所消耗的时间

  •  int getitimer(int which, struct itimerval *curr_value);//获取计时器的设置,不常用
    
  •  int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);//设置计时器(常用)
    

setitimer参数解析:

  • which 指定哪个计时器,可选项为: ITIMER_REAL(真实计时器)、ITIMER_VIRTUAL(虚拟计时器)d、ITIMER_PROF(实用计时器)

  • new_value 为一结构体的传入参数,指定该计时器的初始间隔时间重复间隔时间

  • old_value 为一结构体传出参数,用于传出以前的计时器时间设置。
    结构体struct itimerval 如下:

  •  	struct itimerval {
            struct timeval it_interval; /* Interval for periodic timer */ //重复间隔
            struct timeval it_value;    /* Time until next expiration */  //初始间隔
        };
        struct timeval {
            time_t      tv_sec;         /* seconds */    //秒
            suseconds_t tv_usec;        /* microseconds */   //微秒
        };
    
  • 真实计时器 ITIMER_REAL 代码如下:

#include <func.h>
  //使用 ITIMER_REAL
 void sigfunc(int signum)
  {
      //打印当前时间
      time_t now;
      now=time(NULL);
      printf("%s\n",ctime(&now));
  }
 int main(int argc,char * argv[])
  {
      signal(SIGALRM,sigfunc);
      sigfunc(0);//打印时间
      struct itimerval t;
      bzero(&t,sizeof(t));
      t.it_value.tv_sec=2;//初始间隔为2s
      t.it_interval.tv_sec=3;//重复间隔为3s
      int ret;
      ret=setitimer(ITIMER_REAL,&t,NULL);
      ERROR_CHECK(ret,-1,"setitimer");
      char buf[128];
      read(0,buf,sizeof(buf));//read使程序阻塞                                                                
      return 0;
  }

执行效果:
无论进程处于R 或者 S 真实计时器都在计时
真实计时器

  • 实用计时器 ITIMER_PROF ,代码如下:
#include <func.h>
  //使用 ITIMER_REAL
 void sigfunc(int signum)
  {
      //打印当前时间
      time_t now;
      now=time(NULL);
      printf("%s\n",ctime(&now));
  }
 int main(int argc,char * argv[])
  {
      signal(SIGPROF,sigfunc);//使用信号SIGPROF
      sigfunc(0);//打印时间
      struct itimerval t;
      bzero(&t,sizeof(t));
      t.it_value.tv_sec=2;//初始间隔为2s
      t.it_interval.tv_sec=3;//重复间隔为3s
      int ret;
      ret=setitimer(ITIMER_PROF,&t,NULL);//实用计时器
      ERROR_CHECK(ret,-1,"setitimer");
      char buf[128];
      read(0,buf,sizeof(buf));//read使程序阻塞
      //sleep(5);//例2,验证系统调用sleep不算在实用计时器计算范围内
      while(1);//在用户态运行                                                                                    
      return 0;
  }

执行效果:
实用计时器

  • 虚拟计时器 ITIMER_VIRTUAL 代码如下:
#include <func.h>
  //使用 ITIMER_VIRTURAL
 void sigfunc(int signum)
  {
      //打印当前时间
      time_t now;
      now=time(NULL);
      printf("%s\n",ctime(&now));
  }
 int main(int argc,char * argv[])
  {
      signal(SIGVTALRM,sigfunc);//使用SIGVTALRM信号
      sigfunc(0);//打印时间
      struct itimerval t;
      bzero(&t,sizeof(t));
      t.it_value.tv_sec=2;//初始间隔为2s
      t.it_interval.tv_sec=3;//重复间隔为3s
      int ret;
      ret=setitimer(ITIMER_VIRTUAL,&t,NULL);//使用ITIMER_VIRTUAL
      ERROR_CHECK(ret,-1,"setitimer");
      sleep(5);        
      while(1);//用户态运行                                                                           
      return 0;
  }

执行效果如下:

虚拟计时器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值