Linux信号

信号概念

每个信号用一个整形常量宏表示,以SIG开头
定义在头文件
man 7 signal查看信号的默认行为

2号信号 Ctrl+c SIGINT
3号信号 Ctrl+\ SIGQUIT
信号都是异步的

忽略2号信号(命令行效果)

不是真正的忽略,而是把行为换成了\n

    1 #include 
✹   2 void sigfunc(int signum)
    3 {
    4     //printf("%d is coming\n",signum);
    5     printf("\n");
    6 }
    7                                                                                                                               
✹   8 int main(int argc,char*argv[])
    9 {
   10     if(signal(SIGINT,sigfunc)==SIG_ERR)
   11     {
   12         perror("signal\n");
   13     }   
   14     while(1);
   15     
   16     return 0;
   17 } 
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal 
^C
^C
^C

真正的忽略2号信号

设置宏SIG_IGN

    1 #include 
    2 void sigfunc(int signum)
    3 {
    4     printf("%d is coming\n",signum);
    5 }
    6 
✹   7 int main(int argc,char*argv[])
    8 {
    9     if(signal(SIGINT,SIG_IGN)==SIG_ERR)//设置宏SIG_IGN
   10     {
   11         perror("signal\n");
   12     }
   13     while(1);
   14                                                                                                                               
   15     return 0;       
   16 }
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal 
^C^C^C^C^C^C

多个信号的执行行为

  1 #include 
    2 //模拟sigfunc执行很长时间,所以使用sleep
    3 void sigfunc(int signum)
    4 {
    5     printf("befor sleep %d is coming\n",signum);
    6     sleep(3);
    7     printf("after sleep %d is coming\n",signum);             
    8 }
    9 
✹  10 int main(int argc,char*argv[])
   11 {
   12     //注册2号信号
   13     if(signal(SIGINT,sigfunc)==SIG_ERR)
   14     {
   15         perror("signal\n");
   16     }
   17     //注册3号信号
   18     if(signal(SIGQUIT,sigfunc)==SIG_ERR)
   19     {
   20         perror("signal\n");
   21     }
   22     while(1);
   23 
   24     return 0;
   25 }

不同信号到来时,可以打断其他信号的信号处理流程
当前信号到来时,如果正处于当前信号的信号处理流程,不会打断
最多再执行一次
任何信号都具有唤醒功能

//先按Ctrl+c 很快再按Ctrl+\
//效果: 会打断2号信号,进入3号信号的处理,相当于递归的进入另一个信号处理函数
 ✘ wenrou@uDesktop  ~/workplace/LinuxDay10  ./signal    
^Cbefor sleep 2 is coming//不同信号到来
^\befor sleep 3 is coming
after sleep 3 is coming
after sleep 2 is coming
^Cbefor sleep 2 is coming//同一信号到来
^C^C^C^C^C^C^C^Cafter sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming

原理 : 实际相当于一个数组,每个信号占据一个元素,当收到2号信号时,将对应元素位置置为1,处理该信号时,将该位置为0,
信号 2 3 2

^Cbefor sleep 2 is coming
^\befor sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
befor sleep 2 is coming
after sleep 2 is coming

3号信号会将2号信号打断,再按2号信号,此时整体是在2号信号里面,所以后面的2号信号不会将3号信号打断
原理
2号信号到来,将1置为0,转去执行处理
3号信号来,将1置为0,转去执行处理,打断2号处理函数
2号信号再来,置为1,此时,整体再2号信号处理中,不会将3号信号打断
前两个信号 2 3 处理完,再次检测数组,2号仍为1,则再置为0,转去执行2号信号处理函数
信号 2 3 3

^Cbefor sleep 2 is coming//2号

^\befor sleep 3 is coming
^\after sleep 3 is coming//同一信号再来,也在2号信号执行内部
befor sleep 3 is coming
after sleep 3 is coming

after sleep 2 is coming//2号

恢复信号默认处理

signal(SIGINT,SIG_DFL)

在read阻塞等待读取的状态下,也是可以接收信号的


sigaction信号处理机制

sigaction信号处理注册

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
                //signum 是要捕捉的信号
                //act是一个结构体
                //oldact直接用NULL
//sigaction结构体
struct sigaction {
  void     (*sa_handler)(int);
  void     (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t   sa_mask;
  int        sa_flags;
  void     (*sa_restorer)(void);
};

使用sigaction实现signal的效果

    1 #include 
    2 
✹   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("%d is coming\n",signum);
    6 }
    7                                                        
✹   8 int main(int argc,char*argv[])
    9 {
   10     struct sigaction act;
   11     //将act结构体清空
   12     bzero(&act,sizeof(act));
   13     act.sa_flags=SA_SIGINFO;
   14     act.sa_sigaction=sigfunc;
   15     int ret;
   16     ret=sigaction(SIGINT,&act,NULL);
   17     ERROR_CHECK(ret,-1,"sigaction");
   18     while(1);
   19 
   20     return 0;
   21 }
 ✘ wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming

使用sigaction捕捉多个信号执行

    1 #include 
    2                
✹   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {              
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);  
    7     printf("after sleep %d is coming\n",signum);
    8 }              
    9                
✹  10 int main(int argc,char*argv[])
   11 {              
   12     struct sigaction act;
   13     //将act结构体清空  
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO;
   16     act.sa_sigaction=sigfunc;
   17     int ret;           
   18     //注册2号信号      
   19     ret=sigaction(SIGINT,&act,NULL);
   20     ERROR_CHECK(ret,-1,"sigaction");
   21     //注册3号辛哈      
   22     ret=sigaction(SIGQUIT,&act,NULL);
   23     ERROR_CHECK(ret,-1,"sigaction");                         
   24     while(1);
   25     
   26     return 0;
   27 }  

信号 2 3 2

//与signal效果一样
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^\before sleep 3 is coming
^Cafter sleep 3 is coming
after sleep 2 is coming
before sleep 2 is coming
after sleep 2 is coming

sa_flags

设置为 SA_RESETHAND

处理完毕要捕捉的信号之后,自动撤销信号处理函数的注册
信号处理行为只有一次生效

 12     struct sigaction act;
   13     //将act结构体清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO|SA_RESETHAND;
   16     act.sa_sigaction=sigfunc;                          

设置为 SA_NODEFER

信号不会再丢失,信号不断的打断,会打断自己

 12     struct sigaction act;
   13     //将act结构体清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO|SA_NODEFER;                      
   16     act.sa_sigaction=sigfunc;

真 · 递归调用
信号不会丢失

 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
^Cbefore sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming
after sleep 2 is coming

设置为 SA_RESTART

当发生信号时,正阻塞在某系统调用,设置此参数,立即进入信号的处理,处理完毕之后,继续处理该阻塞

    1 #include 
    2           
✹   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {         
    5     printf("%d is coming\n",signum);
    6 }         
    7           
✹   8 int main(int argc,char*argv[])
    9 {         
   10     struct sigaction act;
   11     //将act结构体清空
   12     bzero(&act,sizeof(act));
   13     act.sa_flags=SA_SIGINFO|SA_RESTART;
   14     act.sa_sigaction=sigfunc;
   15     int ret;
   16     //注册2号信号
   17     ret=sigaction(SIGINT,&act,NULL);
   18     ERROR_CHECK(ret,-1,"sigaction");
   19     //卡在读取标准输入                                                                                                        
   20     char buf[128];
   21     read(STDIN_FILENO,buf,sizeof(buf));
   22     printf("buf is %s\n",buf);
   23     return 0;              
   24 } 
✘ wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction
^C2 is coming
^C2 is coming
^C2 is coming
^C2 is coming
nihao
buf is nihao

sigaction结构体成员 sigset_t sa_mask 将要被阻塞的信号集合

//系统提供对sigset_t结构体的接口
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
   1 #include 
    2 
✹   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);
    7     printf("after sleep %d is coming\n",signum);
    8 }
    9 
✹  10 int main(int argc,char*argv[])
   11 {
   12     struct sigaction act;
   13     //将act结构体清空
   14     bzero(&act,sizeof(act));
   15     act.sa_flags=SA_SIGINFO;
   16     act.sa_sigaction=sigfunc;
   17     //我们将3号信号填入sa_mask
   18     //1.清空sa_mask集合
   19     sigemptyset(&act.sa_mask);
   20     //2.把3号信号填入阻塞集合
   21     sigaddset(&act.sa_mask,SIGQUIT);
   22     int ret;
   23     ret=sigaction(SIGINT,&act,NULL);
   24     ERROR_CHECK(ret,-1,"sigaction");
   25     ret=sigaction(SIGQUIT,&act,NULL);
   26     ERROR_CHECK(ret,-1,"sigaction");
   27     while(1);                                                                                                                 
   28     return 0;
   29 }


3号信号不会将2号信号打断

 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
^\^\^\after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming

取出当前内核中挂起的信号

必须写在当前正在执行的信号处理函数内,否则,当前信号处理完毕,阻塞信号得到响应边查不到

✹   3 void sigfunc(int signum,siginfo_t *p,void *p1)
    4 {
    5     printf("before sleep %d is coming\n",signum);
    6     sleep(3);
    7     sigset_t pend;//存储挂起的信号集合
    8     sigpending(&pend);//从内核中把挂起的信号取出
    9     if(sigismember(&pend,SIGQUIT))
   10     {
   11         printf("SIGQUIT is pending\n");
   12            
          }                                                                                                         
   13     printf("after sleep %d is coming\n",signum);
   14 } 
 wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigaction 
^Cbefore sleep 2 is coming
after sleep 2 is coming

^Cbefore sleep 2 is coming //2号处理过程中发送3号信号 3号信号挂起
^\SIGQUIT is pending
after sleep 2 is coming
before sleep 3 is coming
after sleep 3 is coming

关键代码若不希望被打断,实现保护代码,借助 sigprocmask信号阻塞(全程阻塞)

sigprocmask信号阻塞

    1 #include 
    2 
✹   3 int main(int argc,char*argv[])
    4 {
    5     sigset_t block;
    6     sigemptyset(&block);
    7     sigaddset(&block,SIGINT);
    8     sigprocmask(SIG_BLOCK,&block,NULL);
    9     sleep(10);//重要代码                                                              
   10     return 0; 
   11 }     
wenrou@uDesktop  ~/workplace/LinuxDay10  ./sigprocmask 
^C^C^C^C^C%                                                     

接触阻塞sigprocmask(SIG_UNBLOCK,&block,NULL)

kill信号发送函数

  5     //kill(pid,sig)
  6     //pid 为要接收信号的进程pid 可以通过getpid获取
  7     //sig 为要发送的信号
  8     ARGS_CHECK(argc,2);
  9     pid_t pid=atoi(argv[1]);
 10     //SIGINT 即为Ctrl+c                                                                
 11     int ret=kill(pid,SIGINT);
 12     ERROR_CHECK(ret,-1,"kill");
    //发送SIGQUIT信号把自己杀死
    kill(getpid(),SIGQUIT);

计时器与信号

睡眠函数

sleep();//以秒为单位
usleep();//以微秒为单位

sleep函数内部是用信号机制处理的
alarm(seconds);
seconds秒后自动产生一个SIGALRM信号

计时统计

    1 #include 
    2 
    3 int main(int argc,char*argv[])
    4 {
    5     struct timeval start,end;                                                                                                 
    6     gettimeofday(&start,NULL);
    7     for(int i=0;i<100000000;i++);
    8     gettimeofday(&end,NULL);
    9     printf("use time=%ld\n",(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
   10                                                                                
   11     return 0;                                                                  
   12 }  

时钟处理

真实计时器 : 直观感受到的时间,包括read阻塞
虚拟计时器 :
实用计时器 : 实际运行在内核态和用户态,不包括read阻塞

使用真实计时器

    1 #include 
    2 
?   3 void sigfunc(int signum)
    4 {
    5     //获取当前时间并打印
    6     time_t t;
    7     time(&t);
    8     printf("%s\n",ctime(&t));
    9 }
   10 
?  11 int main(int argc,char*argv[])
   12 {
   13     signal(SIGALRM,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始间隔
   17     t.it_interval.tv_sec=2;//重复间隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_REAL,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     while(1);
   22     return 0;                                             
   23 }

真实计时器,程序执行是直观感受到的时间,阻塞也计算在内

使用实用计时器

    1 #include 
    2     
✹   3 void sigfunc(int signum)
    4 {   
    5     //获取当前时间并打印
    6     time_t t;
    7     time(&t);
    8     printf("%s\n",ctime(&t));
    9 }   
   10     
✹  11 int main(int argc,char*argv[])
   12 {   
   13     signal(SIGPROF,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始间隔
   17     t.it_interval.tv_sec=2;//重复间隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_PROF,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     char buf[128]={0};
   22     read(STDIN_FILENO,buf,sizeof(buf));
   23     printf("%s\n",buf);
   24     while(1);                                             
   25     return 0;
   26 }
 ✘ wenrou@uDesktop  ~/workplace/LinuxDay10  ./setitimer_real
Wed Jan 27 21:33:30 2021
//实用计时器,不统计read阻塞
hello world
hello world
//直到不阻塞才继续统计
Wed Jan 27 21:33:39 2021

Wed Jan 27 21:33:41 2021

Wed Jan 27 21:33:43 2021

^C

使用虚拟计时器

   13     signal(SIGVTALRM,sigfunc);
   14     struct itimerval t;
   15     bzero(&t,sizeof(t));
   16     t.it_value.tv_sec=3;//初始间隔
   17     t.it_interval.tv_sec=2;//重复间隔
   18     sigfunc(0);
   19     int ret=setitimer(ITIMER_VIRTUAL,&t,NULL);
   20     ERROR_CHECK(ret,-1,"setitimer");
   21     sleep(5);
   22     while(1);                                             
   23     return 0;

你可能感兴趣的:(Linux信号)