linux信号

  信号(signal)
   信号是一种软件中断的方式,也是Unix/Linux系统最常用的软件中断方式。
   中断就是中止当前正在执行的代码,转而去执行其他代码。中断分为软件中断和硬件中断。
#include <stdio.h>
#include <signal.h>

void fa(int signo){//信号处理函数,注册后生效
  //以后开发信号处理函数中,代码很复杂
  printf("捕获了信号%d\n",signo);
}
int main(){
  printf("pid=%d\n",getpid());
  signal(SIGINT,fa);//信号2处理方式就是调用fa()
  signal(SIGQUIT,SIG_IGN);//信号3被忽略
  //signal(SIGKILL,fa);//信号9无法忽略和自定义
  while(1);
}



  死循环了,正常的代码就是 死循环
  while(1) printf("zhangfei\n");
  中断死循环 - 信号中断(ctrl+c -> 信号2)

  信号本质就是一个非负整数,Unix系统信号到48,Linux系统信号到64,中间不保证连续。信号为了更好的辨识,都定义了一个宏名称,编程时要求使用宏名称,而不是整数。宏名称都以SIG开头(信号signal)。
  kill 命令用来发送信号。
  kill -9 PID 其实是给进程发9信号,而9信号本身是用来杀死进程的。
  kill -l 可以查看当前系统所有的信号。
  常见信号:
    信号2  SIGINT   ctrl+c
    信号3  SIGQUIT  ctrl+\
    信号9  SIGKILL  杀进程的
   段错误、总线错误、整数除以0都是发送信号终止程序。  
  SIGBUS 对应的值 在不同系统中不同,因此编程时使用 SIGBUS(宏名称) 有更好的通用性
  信号分为可靠信号和不可靠信号,早期的信号都是不可靠信号。
  不可靠信号,Linux系统中,1-31都是不可靠信号,不可靠信号特点不支持排队,因此当多个信号堆积时,会造成信号的丢失。非实时信号。
  可靠信号,Linux系统中,34-64都是可靠信号,可靠信号特点支持排队,不会丢失信号。实时信号。
  信号的处理方式:
   1 默认处理,每个信号都有默认处理,80%的默认处理都是 退出进程。
   2 忽略信号,信号来了不做处理。
   3 自定义信号处理函数,然信号处理时执行程序员的代码。
   4 信号9无法忽略,无法自定义。
   5 信号受用户的限制,当前用户只能给自己的进程发信号,但root可以给所有进程发信号。
  如何设置信号的处理方式? - 信号注册
   signal() - 重点
   sigaction() - 了解
   让某一个信号执行某一个函数(自定义)
   signal(int signum,函数指针)
   参数:signum 就是信号
   函数指针 就是自定义的函数,格式如下:
    void (*fa) (int)
   函数指针也可以用宏做参数:
    SIG_DFL - 采用默认处理
    SIG_IGN - 采用忽略处理
   返回函数指针,出错 返回 SIG_ERR。

  子进程的信号处理和父进程之间的关系?
   fork()创建的子进程完全继承父进程对信号的处理方式,父进程自定义处理函数,子进程也使用相同的处理函数,父进程忽略,子进程也忽略。
  vfork()+execl()创建的子进程部分继承父进程对信号的处理方式,默认和忽略继承,如果父进程自定义处理函数,子进程会改为默认(因为excel()会使用全新的代码区)。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void fa(int signo){
  printf("捕获了信号%d\n",signo);
}
int main(){
  signal(SIGINT,fa); // 信号2自定义
  signal(SIGQUIT,SIG_IGN);//信号3被忽略
  pid_t pid = vfork();
  if(pid == 0){
    printf("pid=%d\n",getpid());
	execl("./proc","proc",NULL);
  }
  printf("父进程结束\n");
  return 0;
}




  发送信号的方式:
   1 键盘发送(部分信号)
    ctrl+c -> 信号2
    ctrl+\ -> 信号3
   2 出错,系统发送(部分信号)
    段错误 -> 信号11
    总线错误 -> 信号7
   3 kill 命令发送(所有信号)
    kill -信号 进程的PID
   4 系统提供了信号发送函数,比如:
    raise() kill() alarm() sigqueue()
   重点是kill()
    raise() 只能给本进程(自己)发信号
    kill() 可以给所有进程发信号,但发送进程得有发送权限(受用户影响)。
   kill -0 进程PID 可以测试是否有发送信号的权限。信号0 是用于测试,本身不做任何的处理。
   
   sleep(int n) - 休眠n秒,但会被信号打断,并返回剩余秒数。
   usleep()也是用于休眠,单位是微秒,1000000微秒 = 1秒。usleep()的手册有问题。
   kill(pid_t pid,int signo)
   参数:signo 就是信号
    pid就是发送给哪些进程,值有4种情况:
    >0 发送给指定的进程(pid对应进程)  
    -1 发送给所有的进程,只要有权限
    0  发送给本组的所有进程
    <-1发送给指定进程组中的所有进程(进程组ID 等于 -pid)
   常用的是 大于0 。
   练习: kill()函数的使用
    用fork()创建一个子进程,然后父进程发信号2给子进程,子进程自定义信号2的处理函数。信号处理函数参考fa()即可。子进程不要退出,父进程发完信号后退出。
    
   alarm()严格来说,不算信号发送函数。
   alarm()通过信号SIGALRM实现闹钟的功能,闹钟就是n秒之后,产生一个信号,完成某项工作。当alarm()设置闹钟时,如果之前有没有完成的闹钟,会替换没有完成的闹钟(之前的闹钟就不会再产生信号了),并返回之前的闹钟的剩余秒数。
  alarm(0)是取消所有闹钟。

 信号集 - 信号组成的集合,本质是个超大的整数,信号集的类型 sigset_t。集合提供的必须函数:
    1 初始化函数
    2 增加元素函数
    3 删除元素函数
    4 查找元素函数
  sigemptyset(sigset_t* set)-清0(无信号)
  sigfillset(sigset_t* set)-置1(满信号)
   新增信号/删除信号
  sigaddset(sigset_t*,int signo)
  sigdelset(sigset_t*,int signo)
  查找信号在不在信号集中
  sigismember(sigset_t*,int signo)
#include <stdio.h>
#include <signal.h>

void fa(int signo){
  printf("捕获到了信号%d\n",signo);
}
int main(){
  signal(SIGINT,fa); signal(SIGQUIT,fa);
  signal(50,fa);
  printf("pid=%d\n",getpid());
  printf("执行普通代码,没有屏蔽信号\n");
  sleep(15);
  printf("执行关键代码,开始信号屏蔽\n");
  sigset_t set,old;//set屏蔽,old解除
  sigemptyset(&set); sigaddset(&set,50); 
  sigaddset(&set,2); sigaddset(&set,3);
  sigprocmask(SIG_SETMASK,&set,&old);//开始屏蔽
  sleep(15); printf("屏蔽被解除\n");
  sigprocmask(SIG_SETMASK,&old,0);//解除屏蔽
}//50是可靠信号,所以不丢失;2 3是不可靠信号 会丢失



  一个二进制位代表一个信号的有无,二进制的倒数第n位 代表信号n。
  信号集应用 - 信号屏蔽
   信号无法确定 什么时候来,程序员 控制不了 信号 什么时候来。
   当程序在执行关键代码时,如果信号来了可能会造成严重的后果,信号没法控制不要来,可以采用信号屏蔽技术让信号来,但不做处理,等解除屏蔽后再处理。
   信号屏蔽时,信号放入 信号集中再屏蔽。
   sigprocmask()函数用于屏蔽信号
   int sigprocmask(int how,
    sigset_t* new,sigset_t* old)
   参数:new 是 需要屏蔽的信号所在信号集
    old是传出参数,会把之前的屏蔽带出来
    当信号屏蔽解除时,再把old放回去即可
    how有三个宏:
    SIG_BLOCK: A B C + C D E->A B C D E
    SIG_UNBLOCK:A B C - C D E->A B
    SIG_SETMASK:A B D = C D E->C D E
    一般使用SIG_SETMASK。
   返回 -1 代表错误。

  在信号屏蔽时,如果来的是可靠信号,发送几次,解除屏蔽后处理几次;如果来的是不可靠信号,发送几次,都只处理一次。相同的不可靠信号在屏蔽期间 会丢失。

  sigaction()函数是signal()的增强版,因此sigaction()过于复杂,Unix/Linux系统把它的主体功能抽取出来,做了signal()函数。
  sigaction()比signal()强在 它能额外的传参,还能拿到信号更多的信息。
  基于信号的计时器(定时器)
   Linux系统对每个进程都支持3种计时器:
    真实计时器、虚拟计时器、实用计时器
   真实计时器最常用,存储程序运行的真实总时间。
  计时器就是M秒之后每隔N秒就产生一个信号,完成某种功能。计时器有一个开始时间,会产生第一个信号;还有一个间隔时间,第一个信号之后的每个信号都是间隔时间决定。
  计时器相关函数:
   setitimer()、getitimer()
   setitimer(int which,struct itimerval*
    time,struct itimerval* oldtime)
   which 选 ITIMER_REAL,代表真实计时器
   time包括开始时间和间隔时间
   oldtime 基本不用,给NULL即可

#include <stdio.h>
#include <sys/time.h>
#include <signal.h>

void fa(int signo){
  printf("每隔1.1秒执行一次\n");
}
int main(){
  signal(SIGALRM,fa);
  struct itimerval timer;
  //间隔时间
  timer.it_interval.tv_sec = 1;//秒数
  timer.it_interval.tv_usec = 100000;//微秒
  //开始时间
  timer.it_value.tv_sec = 5;
  timer.it_value.tv_usec = 0;
  setitimer(ITIMER_REAL,&timer,0);//启动计时器
  while(1);
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值