uc_day07

一,信号
信号是软件中断
1,中断
中止当前正在执行的程序,转而去处理别的程序代码
分为软件中断和硬件中断
2,信号
一种中断方式,软件中断方式,可以作为进程间通信的方式


常见的信号:段错误  总线错误  浮点数例外
信号是异步的,就是说程序不知道信号会什么时候来。
进程可以处理信号,还可以进程间发送信号,类似进程间通信。


不同的进程是不能相互访问对象的内存空间的
每一个信号都有一个名字,这些名字都是以SIG开头的,信号本质上是一个整数,信号的名字
是这个整数的宏定义


3,信号从哪里来
1)硬件异常:除零,无效的存储空间,这些条件通常由硬件检测到,并将其通知内核,然后内核
为该条件发生时正在运行的进程产生适当的信号
2)软件产生异常信号---调用函数产生
kill  raise alarm等


4,信号的分类
1)不可靠信号,这种信号会丢失。主要特点是不支持排队。
所有不可靠信号都是非实时信号。
2)可靠信号,不会丢失,支持排队


5,进程收到信号如何处理
1)默认处理,80%的情况是退出进程
2)忽略
3)调用信号处理函数


signal.c
#include<stdio.h>
#include<signal.h>
int main(){
  signal(2,SIG_IGN);//忽略2信号
  while(1);
  return 0;
}
------------------------------------------------------------------
#include<stdio.h>
#include<signal.h>
void fa(int signo){
  printf("你发信号%d\n",signo);
}


int main(){
  signal(SIGINT,fa);//注册一个信号处理函数
  while(1);
  return 0;
}


//signal(SIGINT,SIG_DFL);恢复指定信号的默认行为
注:9信号不能忽略,也不能给9信号注册信号处理函数,只能默认处理)
9信号是SIGKILL.
当前用户只能给自己的进程发信号,不能给别的用户的进程发信号。root用户可以给所有用户发信号


二,信号注册函数----signal
给类型起别名
1,用此类型定义一个变量
2,前面加typedef,变量名就是类型的别名
int main(){
   typedef int INT32;
   INT32 x;
   x = 100;


   int a[10];
   sizeof(int[10]);
   typedef int INT10[10];
   INT10 b;
   b[0] = 100;
   b[1] = 200;


   typedef void* (*F)(int*,int);
   F f1 = fa;
}
三,子进程的信号处理
子进程会继承父进程的信号处理方式,直到子进程调用exec函数
子进程调用exec函数后,exec将父进程中设置为捕获的信号恢复为默认处理方式,其余不变


child.c
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>


void fa(int signo){//信号处理函数返回值必须是void,参数int
   printf("捕获到信号%d\n",signo);
}


int main(){
 if(signal(SIGINT,fa)==SIG_ERR){
   perror("signal failed.");
   exit(-1);
 }


 if(signal(SIGQUIT,SIG_IGN) == SIG_ERR){
   perror("signal ignore failed.");
   exit(-1);
 }


 int pid = fork();
 if(pid == -1){
   perror("fork failed."):
   exit(-1);
 }
 if(pid == 0){
   printf("子进程pid=%d\n",getpid());
   while(1);
 }else{
   printf("父进程退出\n");
 }
  return 0;


}
======================================================================
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>


void fa(int signo){//信号处理函数返回值必须是void,参数int
   printf("捕获到信号%d\n",signo);
}


int main(){
 if(signal(SIGINT,fa)==SIG_ERR){
   perror("signal failed.");
   exit(-1);
 }


 if(signal(SIGQUIT,SIG_IGN) == SIG_ERR){
   perror("signal ignore failed.");
   exit(-1);
 }


 int pid = vfork();
 if(pid == -1){
   perror("fork failed."):
   exit(-1);
 }
 if(pid == 0){
   execlp("pro1","pro1",NULL);
 }else{
   printf("父进程退出\n");
 }
  return 0;


}
pro1.c
#include<stdio.h>
#include<unistd.h>
int main(){
  printf("子进程pid=%d\n",getpid());
  while(1);
  return 0;
}
四,发送信号
1,键盘
CTRL+C ==> 给当前进程发送2信号 SIGINT
CTRL+\ ==>发3信号  SIGQUIT
CTRL+Z ==>发20信号 SIGTSTP
2,出错
访问不存在的地址==>发11信号  SIGSEGV
除0==>发8信号 SIGFPE
总线错误==>发7信号 SIGBUS
......
3,命令发送
kill -信号 进程号
4,程序中用函数发送
kill函数
raise函数


#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>


void child(int signo){
  printf("进程%d捕获到信号%d\n",getpid(),signo);
}
int main(){
  int pid = fork();
  if(pid == 0){
    signal(50,child);
    while(1);
  }
  printf("父进程给子进程发送50信号\n");
  kill(pid,50);
  return 0;
}


pause函数:等待信号到来,信号处理完后返回-1
sleep函数:可以被任何信号打断,当信号处理完后,不会再继续sleep,而是直接返回睡眠剩下几秒
raise函数:自己给自己发信号
alarm函数:自己给自己发信号。当有多个alarm的时候,那么前面的alarm未执行的时候,
那么后年的alarm有效,前面的无效,后面的执行返回值是前面还剩余的时间是多少
sigqueue函数


五,信号集和信号屏蔽
信号集:一个变量,可以同时表示多个信号
int sigemptyset//清空
int sigfillset//添加


#include<stdio.h>
#incldue<signal.h>
int main(){
  printf("sizeof(sigset_t)=%d\n",sizeof(sigset_t));
  sigset_t set;//set是信号集变量
  sigempty(&set);
  sigfillset(&set):
  sigaddset(&set,SIGINT);
  sigaddset(&set,SIGQUIT);
  sigaddset(&set,SIGALARM);
  
  sigdelset(&set,SIGINT);
  int res = sigismember(&set,SIGQUIT);
  return 0;
}


信号屏蔽字
#include<signal.h>
int sigprocmask(int how,const sigset_t* set,sigset* oldset);
how:SIG_BLOCK:把指定的信号添加到原来的里面中
SIG_UNBLOCK:在原来的基础上减去新添加的
SIG_SETMASK:不管以前的,按照新加的
set:
oldset:
每个进程都会有一个信号屏蔽字,它规定了当前那些信号可以递送,那些信号需要阻塞。
当程序执行敏感任务时(比如数据库更新),不希望外界信号中断程序的运行,在这个阶段
并不是简单地忽略信号,而是阻塞这些信号,当进程处理完关键任务后,还会处理这些信号
在信号处理函数中,当前的信号总是加入信号屏蔽字中


信号屏蔽字的类型是信号集。默认情况下,信号屏蔽字中不包括任何信号
sigmask.c
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<stdlib.h>


void sig_fun(int signo){
  printf("捕获到信号%d\n",signo);
}
int main(){
   signal(SIGINT,sig_fun);
   signal(SIGQUIT,sig_fun);
   printf("程序执行普通代码,试试发信号2,3\n");
   sleep(10);
   printf("程序开始执行关键代码,屏蔽掉一些信\n");
   //准备信号集变量,放入要屏蔽的信号
   sigset_t set,old;
   sigemptyset(&set);
   sigaddset(&set,SIGINT);
   sigaddset(&set,SIGQUIT);
  //设置新的信号屏蔽字
   if(sigpromask(SIG_SETMASK,&set,&old)==-1){
     perror("设置信号屏蔽字失败"),exit(-1);
   }
   //执行关键代码
   sleep(6);
   printf("程序睡足6秒时间,关键代码执行完\n");
   //恢复原先的信号屏蔽字
   sigpromask(SIG_SETMASK,&old,NULL);
   while(1);
   return 0;
}


sigpending:可以获取到被屏蔽掉的信号
注:被屏蔽掉的信号如果是不可靠,发送多次,可能只处理一个,因为前面的
丢弃了,如果是可靠信号,发送几个处理几个


六,sigaction
功能和signal相同的,用来注册一个信号处理方式的
int sigaction(int signum,const struct sigaction* act,struct sigaction* oldact);


C语言中,结构体中不能有函数,但是可以有函数指针


sigaction.c
#include<stdio.h>
#include<signal.h>


void fa(int signo){
  printf("捕获到信号%d\n",signo);
  sleep(5);
}
int main(){
   struct sigaction action = {};
   action .sa_handler = fa;
   sigaction(SIGINT,&action,NULL);
   pause();
   return 0;
}
七,发送信号,特点发送信号的同时,还可以带值
sigsequeue
八,Linux为每个进程维护3个计时器,分别是真实计时器,虚拟计时器和实用计时器
真实计时器计算的是程序的运行时间 SIGALARM
虚拟计时器SIGVTALRM
实用计时器SIGPROF
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值