Linux编程:信号

2022/8/26 5:23:16

本文主要是介绍Linux编程:信号,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1.信号概念

  信号是软件中断,很多比较重要的应用程序都需要信号处理。信号是一种进程之间或者内核与进程间异步通信的一种机制,例如:用户在终端键入中断键,会通过信号机制停止一个程序。

2.信号的共性

  • 简单
  • 不能携带大量信息
  • 满足某个特设条件才发送

3.信号的机制

  A给B发送信号,B 收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。
  信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。

  每个进程收到的所有信号,都是由内核负责发送,内核处理

4.与信号相关的事件和状态

  产生信号:

  • 按键产生:如:Ctrl+c、Ctrl+z、Ctrl+\
  • 系统调用产生:如:kill、raise、abort
  • 软件条件产生:如:定时器 alarm
  • 硬件异常产生:如:非法访问内存(段错误)、除 0(浮点数例外)、内存对齐出错(总线错误)
  • 命令产生:如:kill命令

  递达:递送并且到达进程。

  未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。

  信号的处理方式:

  • 执行默认动作(每一个信号都有默认的处理事件)
  • 忽略
  • 捕捉(调用户处理函数 )

  linux内核的进程控制块PCB是一个结构体,包含了信号相关信息,主要指阻塞信号集和未决信号集。

  阻塞信号集:将某系信号加入集合,对他们设置屏蔽,当屏蔽x信号后,在收到该信号,该信号的处理将推后(解除屏蔽后)

  未决信号集:信号产生,未决信号集中该信号位立刻反转1,表示信号处于未决状态。当信号被处理对应位反转为0.这一刻往往非长短暂。

5.信号四要素

  在信号使用之前,要确定其四要素

  编号、名称、事件、默认处理动作

6.信号的产生

  • 终端按键产生信号
  • 硬件异常产生信号
  • kill函数/命令产生信号
    • 函数原型
      1 int kill(pid_t pid, int sig);  //成功:0;失败:-1,设置errno
      2 //sig:不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致
      3 //pid > 0: 发送信号给指定的进程
      4 //pid = 0: 发送信号给 与调用 kill 函数进程属于同一进程组的所有进程。
      5 //pid < 0: 取|pid|发给对应进程组。
      6 //pid = -1:发送给进程有权限发送的系统中所有进程

       

    • kill命令产生信号:kill -SIGKILL pid
  • 软件条件产生信号
    • alarm函数 : 设置定时清。指定seconds后,内核会给当前进程发送SIGALRM信号,进程收到该信号,默认动作中止,每个进程都有且只有一个唯一定时器。
    • 1 unsigned int alarm(unsigned int seconds);    //返回 0 或剩余的秒数,无失败。
    • 使用time命令查看程序执行的时间 
      • time ./test
      • 实际执行时间=系统时间+用户时间+等待时间
      • time ./test > out
      • 结论:程序运行的瓶颈在于IO,优化程序,首选优化IO 
    • setitimer函数原型:设置定时器。可替代alarm函数。精度微秒,可以实现周期定时
    • 1 int setitimer (int which, const struct itimerval *new_value, struct itimerval *old_value);    //成功:0;失败:-1,设置errno
      //which:指定定时方式(①自然定时:ITIMER_REAL)(②虚拟空间计时(用户空间)ITIMER_VIRTUAL)(③运行时计时(用户+内核):ITIMER_PROF)
      //参数二:结构体设置间隔时间和单次时间
      //参数三:返回旧闹钟的剩余时间(传出参数)
      //it_value:结构体内部的参数:第一次触发信号的时间
      //it_interval:结构体内部的参数:用来设定两次定时任务之间间隔的时间(第二次和第一次 or 第三次和第二次之间的时间间隔)

 7.信号集操作函数+原理(重)

    控制原理:内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字mask可以影响未决信号集。而我们可以再应用程序中自定义set来改变mask。以达到屏蔽指定信号的目的。(重点) 

  

 

    sigprocmask函数

      作用:用来屏蔽信号、解除屏蔽也是用该函数。其本质,读取或修改进程的信号屏蔽字(PCB中)

      

1 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);   //成功:0;失败:-1,设置 errno
2 //set:传入参数,是一个位图,set 中哪位置 1,就表示当前进程屏蔽哪个信号。
3 //oldset:传出参数,保存旧的信号屏蔽集
4 //how 参数取值: 假设当前的信号屏蔽字为 mask
5 //SIG_BLOCK: 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号。
6 //SIG_UNBLOCK: 该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集的补集的交集。set包含了我们希望解除阻塞的信号.
7 //SIG_SETMASK: 自定义set替换mask(mask表示系统自带的阻塞信号集)

    sigpending函数

      作用:读取当前进程的未决信号集

      

1 int sigpending(sigset_t *set);    //set 传出参数。 返回值:成功:0;失败:-1,设置 errno

8.信号捕捉

signal函数

  作用:注册一个信号捕捉函数(注册而非创建)

  原型:

1 sighandler_t signal(int signum, sighandler_t handler);
2 typedef void (*sighandler_t)(int);//函数指针类型 需要注意的就是需要传入一个整形参数 不管用不用

sigaction函数

  作用:修改信号处理动作(通常在Linux用来注册一个信号的捕捉函数)

  原型:

1 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
2 //act: 传入参数、新的处理方式
3 //oldact:  传出参数,旧的处理方式  

 9.信号捕捉特性(重)

  • 进程正常运行时,默认PCB中有一个信号屏蔽字,假定为✳,它决定了进程自动屏蔽那些信号。当注册了某个信号捕捉函数,捕捉到该信号后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由✳来指定。而是用sa_mask来指定。调用完信号处理函数,在恢复为✳。
  • xxx信号捕捉函数执行期间,xxx信号自动被屏蔽
  • 阻塞的常规信号不支持排队、产生多次只记录一次。

10.内核实现信号捕捉过程

  

 

 11.SIGCHLD信号

  产生条件:

      子进程中止时

      子进程接收到SIGSTOP信号停止时

      子进程处在停止态,接收到SIGCONT后唤醒时

  借助该信号回收进程:

    子进程结束运行,其父进程会收到 SIGCHLD 信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。



这篇关于Linux编程:信号的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程