Linux学习:信号

Linux学习:信号_第1张图片

一,信号的概念

1,信号的本质

信号是linux里面的一种向目标进程发送消息的机制。本质上就是软件模拟中断的行为

2,面对信号进程需要做到的事情

1,认识信号

 进程在信号产生之前就已经知道了各种信号是如何处理的,进程通过中断信号表和一张函数指针数组表来实现这个功能。

Linux学习:信号_第2张图片

每个进程都会维护一张中断向量表和一张函数指针数组的表,这样便能让进程认识信号。

2,处理信号

进程能够处理信号,但是处理信号不一定是立即处理的。而是在合适的时刻处理。处理的方式有三种:1,默认处理方式  2,忽略  3,自定义处理

 3,信号的产生和进程是异步的

进程并不知道信号何时产生。

 4,进程能够保存信号

二,信号的产生

前置知识

1,进程的运行方式有两种:前台运行  后台运行。

2,在./“可执行文件名”后面加上一个&便能让前台进程变成后台进程。

如:

Linux学习:信号_第3张图片

3,后台进程可以有多个,但前台进程只有一个。

4,在同一个窗口下,jobs可以查看后台进程的个数。fg+后台进程编号可以将后台进程提到前台。

5,在linux操作系统中有一个函数能够捕捉信号,这个函数就是:

Linux学习:信号_第4张图片

 1,键盘产生

1,可以通过键盘发送ctrl+c指令来终止前台进程。

2,ctrl+z可以用来暂停前台进程——>将前台进程变为后台进程,状态时stop。

问题:os如何得知键盘上输入了什么?

先来看图:

Linux学习:信号_第5张图片

在这里其实使用了一个中断技术,键盘与cpu之间是有连接的。但按下相应的按键时cpu里面就会产生一个中断号,这个中断号会去调用中断向量表里面的函数指针方法,然后便会执行相应的函数对进程进行处理。

2,通过系统调用

 系统调用主要调用这三个函数:

kill():这个函数也是kill命令底层调用的函数。

raise():这个系统调用函数实现的便是一个进程自己杀掉自己的系统调用。

//abort():abort()不是系统调用而是一个函数。(这里只是介绍一下)

3,硬件条件

硬件条件产生进程终止的情况便是:异常

比如除零错误,空指针等等。

引发异常后,计算机处理的方式如下:

1,cpu内的寄存器(如status寄存器)将错误标记下来。

2,cpu检测到错误后,不再执行剩下的代码。

3,cpu通知os.

4,os向进程发信号,进程根据信号来执行相应的函数代码来终止进程或者其他处理方式。

5,如果进程没有被终止,cpu再次向os发送通知,os再次发送信号。

异常出现是硬件条件,与语言无关,与编译器无关。

4,软件条件

软件条件与硬件条件的区分便是:信号是由软件产生的还是硬件产生的?

硬件产生:cpu内的寄存器等。

软件产生:引用计数,函数等。

三,信号的处理

0,前置知识

内核态:os在工作时的状态,权限更大,能访问进程地址空间上的绝大部分空间。

用户态:普通用户的状态。

状态转化:通过系统调用用户态可以转化为内核态。

转化实际:cpu上的cs寄存器在1与3两个数值的转化。

1,处理时机

信号的处理时机大部分都是在内核态转换为用户态时处理的。

2,处理方式

默认处理    忽略    自定义处理。

四,信号的捕捉

1,signal(int signum,void(*fun)(int))

这个函数可以通过捕捉信号来进行对信号的自定义处理。

示例代码:

 void sigcb(int signal)
 {
   cout<<"signal:"<

 以上代码可以对信号2进行捕捉,捕捉后执行sigbc函数的代码。

2,sigaction(int signum,struct_sigaction* act,struct_sigaction* oact)

该函数也可以实现signal函数的功能,但它的功能更加强大,能够实现一次性屏蔽多个信号屏蔽一个信号集。

使用如下:

void sigbc(int signal)
{
  cout << "pid:" << getpid() << "捕捉到信号" << signal << endl;
}

int main()
{
  struct sigaction act, oact;//创建两个结构体
  act.sa_handler = sigbc;//将自定义方法引入
  sigaction(2, &act, &oact); // 捕捉一个信号

  //屏蔽多个信号
  sigisemptyset(&act.sa_mask);
  sigaddset(&act.sa_mask, 1);
  sigaddset(&act.sa_mask, 2);
  sigaddset(&act.sa_mask, 3);
  sigaddset(&act.sa_mask, 4);
  sigaddset(&act.sa_mask, 5);
  sigaddset(&act.sa_mask, 6);
  sigaction(2, &act, &oact);

  return 0;
}

五,信号的其他问题

1,可重入函数

当多个执行流进入函数但不会造成函数错误的函数则为可重入函数。反之则为不可重入函数。

不可重入函数的特点:

调用了 malloc free, 因为 malloc 也是用全局链表来管理堆的。
调用了标准 I/O 库函数。标准 I/O 库的很多实现都以不可重入的方式使用全局数据结构。

2,volataile关键字

volatile关键字的作用是取消优化,因为优化后的代码可能在变量取值时会直接在寄存器里面取而不再内存里面取导致错误。

3,SIGCHLD

这是一个子进程退出时向父进程发送的信号。子进程也可以根据信号来退出。

你可能感兴趣的:(Linux系统编程,学习,linux)