linux 系统编程 -- 信号及信号处理(四)

概念

信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。

信号的来源:
①硬件方式:按键会产生信号,比如ctrl+c组合键,产生一个SIGINT信号;硬件异常信号,比如除数为0,无效存储访问,由cpu通知内核,内核生成相应信号。

②软件方式: 用户在终端下调用kill命令向进程发送任意信号;进程调用kill或sigqueue函数发送信号;当某种条件已经具备时发送信号,如settimer定时器超时产生SIGALRM信号。

信号的种类:
使用kill -l 命令系统支持的全部信号。
在这里插入图片描述
①SIGHUP:用户退出shell,由该shell启动的所有进程将收到这个信号,默认动作为终止进程。

②SIGINT:用户按下ctrl+c组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程。

③SIGQUIT:用户按下ctrl+\组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程并产生core文件。

④SIGILL:cpu检测到某进程执行了非法指令,默认动作为终止进程并产生core文件。

⑤SIGTRAP:由断点指令或其他trap指令产生,默认动作为终止进程并产生core文件。

⑥SIGABRT:调用abort函数产生该信号,,默认动作为终止进程并产生core文件。

⑦SIGBUS:非法访问内存地址,包括内存地址对齐出错,默认动作为终止进程并产生core文件。

⑧SIGFPE:在发生致命的算术运算时产生,不仅包括浮点运算错误,还包括溢出及除数为0等错误,默认动作为终止进程并产生core文件。

⑨SIGKILL:无条件终止进程,本信号不能被忽略、处理和阻塞。默认动作为终止进程,它向系统管理员提供了一种可以杀死任何进程的方法。

⑩SIGUSR1:用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

⑪SIGSEGV: 指示进程进行了无效的内存访问。默认动作为终止进程并产生core文件。

⑫SIGUSR2:另一个用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

⑬SIGPIPE:向一个没有读端的管道写数据,默认动作为终止进程。

⑭SIGALRM:定时器超时,超时的时间由系统调用alarm设置,默认动作为终止进程。

⑮SIGTERM:程序结束信号,与SIGKILL不同,这个信号可以被阻塞和处理。通常要求程序正常退出,执行shell命令kill时,缺省产生这个信号。默认动作为终止进程。

⑯SIGSTKFLT :该信号仅仅在Linux中有定义,出现在早期的Linux系统中,其原意是用于数字协处理器的堆栈错误,该信号在内核中并不会被产生,但是为了兼容性被保留下来了。

⑰SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略该信号。

⑱SIGCONT:让一个暂停的进程继续执行。

⑲SIGSTOP:停止进程的执行,注意它和SIGTERM以及SIGINT的区别。该进程还未结束,只是暂停执行,本信号不能被忽略、阻塞或处理。默认动作为暂停进程。

⑳SIGTSTP:停止进程的执行,按下ctrl+z组合键产生,本信号可以被忽略或处理。默认动作为暂停进程。

从34) SIGRTMIN到64 )SIGRTMAX为linux的实时信号,它们没有固定含义,或者说由用户自由使用,注意linux线程使用了前3个实时信号。
所有的实时信号默认动作为终止进程。

可靠与优先级

从1号SIGHUP信号到31号SIGSYS之间的信号都是继承自UNIX系统,是不可靠信号。
linux系统根据posix.4标准定义了SIGRTMIN(33号)至SIGRTMANX(64号)之间的信号,是可靠信号,也称实时信号。
在linux系统中,信号的可靠性是指信号是否会丢失,或者说信号是否支持排队。

当信号产生后,内核会在进程表中设置某种形式的标志,当内核设置了这个标志,我们说内核向一个进程递送了一个信号,在信号产生和递送之间的时间间隔,称为信号未决。

被递送多次的信号(即信号在未决队列里排队)称为可靠信号,只被递送一次的称为不可靠信号。

如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核按照发送的顺序来递送;如果存在多个实时未决信号,内核按照信号的编号越小越先递送;如果即存在不可靠信号,又存在可靠信号,内核优先递送不可靠信号。

进程对信号的响应

当信号发生时,用户可以要求进程以下列3种方式之一来做出响应:
① 捕捉信号:对于要捕捉的信号,可以为其制定信号处理函数,信号发生时改函数自动被调用

② 忽略信号:大多数信号都可使用这种方式处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略与捕捉。

③按照系统默认方式处理:即终止进程。

在这里插入图片描述
signal函数捕捉并处理信号。
typedef void (*sighandler_t)(int); sighandler_t是一个函数指针型,它指向的函数有一个int参数,返回值是void。

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


void handler_sigint(int signo)
{
  printf("recv SIGINT %d\n",signo);
}

int main()
{
  signal(SIGINT, handler_sigint);
  while(1)
  ;
  
  return 0;
}

在这里插入图片描述
按下ctrl+c组合键,产生信号SIGINT,被程序捕捉并处理。最后按下ctrl+\组合键发送SIGQUIT信号,由于程序没有处理该信号,按照默认方式处理–即结束进程。

信号的发送

信号的发送主要由函数kill、raise、sigqueue、alarm、setitimer以及abort来完成。
在这里插入图片描述

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

① kill第一个参数是进程号,第二个参数是信号编号。
函数执行成功返回0,错误返回-1.
只有具有root权限的进程才能向其他任意进程发送信号,非root权限的进程只能向属于同一个组或同一个用户的进程发送信号。

② raise函数是ANSI C而非POSIX标准定义的。
在这里插入图片描述
在单线程中,它等价于 kill(getpid(), sig);

③sigqueue 函数是一个比较新的发送信号函数,它支持信号带有参数。与函数sigaction配合使用,因此这个额外的信号带有参数就能完成数据的传递。

④alarm 函数用来设置定时器,定时器超时会产生SIGALRM信号给调用进程。
在这里插入图片描述
参数代表设定的秒数,经过seconds秒后,内核将给调用该函数的进程发送SIGALRM信号。如果seconds为0,则不再发送SIGALRM信号。
最新一次调用alarm函数将取消之前一次的设定。
注意alarm只设定为发送一次信号,如果要多次发送,就要对alarm进行多次调用。

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

void send_ip()
{
  printf("send a icmp echo request panckt\n");
}

void recv_ip()
{
  printf("recv a ip\n");
  while(1)
    ;
}
void handler_sigalarm(int signo)
{
//信号处理程序再次设定定时器
  send_ip();
  alarm(2);
}

int main()
{
  //安装信号处理
  signal(SIGALRM, handler_sigalarm);
  //触发信号给本进程
  raise(SIGALRM);
  recv_ip();
  return 0;
}

如果没有raise的话,第一次就无法触发,在信号处理程序中反复设定定时器,以获得多次发送效果。
在这里插入图片描述
执行结果来看,当程序运行到 raise(SIGALRM)时,已经触发了handler_sigalarm,所以输出send a icmp echo request panckt,之后再执行
recv_ip。

⑤setitimer函数 也是用来设置定时器,它的功能更强大。
在这里插入图片描述
第一个参数which制定使用哪一个定时器,种类有ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROF。

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

void handler_sigtime(int signo)
{
  switch (signo)
  {
    case SIGALRM:
      printf("recv SIGALRM\n");
      break;
    case SIGPROF:
      printf("recv STGPROF\n");
      break;
    default:
      break;
  }
}

int main()
{
  struct itimerval value;
//相关信号共用一个处理函数
  signal(SIGALRM, handler_sigtime);
  signal(SIGPROF, handler_sigtime);
  
  //第一次1秒触发信号
  value.it_value.tv_sec=1;
  value.it_value.tv_usec=0;
 //第二次开始每5秒触发信号
  value.it_interval.tv_sec=5;
  value.it_interval.tv_usec=0;
  
//设置定时器
  setitimer(ITIMER_REAL,&value,NULL);
  setitimer(ITIMER_PROF,&value,NULL);
  while(1)
    ;
  return 0;
}
版权声明:本文为dark_tone原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dark_tone/article/details/89465710

智能推荐

Reflect反射的基础知识

写个父类: 写个子类: 利用反射获得该子类中的属性,方法,构造,父类及接口: 运行结果:...

spring cloud netflix (07) 服务的消费者(feign)

前言 完整知识点:spring cloud netflix 系列技术栈 Feign (同步通信 HTTP通信) feign是基于接口完成服务与服务之间的通信的 搭建Feign服务 项目结构 项目搭建 pom.xml application类 application.yml 使用feign完成服务与服务之间的通信 feign是基于接口完成服务与服务之间的通信的...

AtCoder Beginner Contest 174 E.Logs

AtCoder Beginner Contest 174 E.Logs 题目链接 到最后才发现是二分,菜菜的我/(ㄒoㄒ)/~~ 我们直接二分 [1,max{a[i]}][1,max\lbrace a[i]\rbrace][1,max{a[i]}] 即可,对每一个 midmidmid,每个数 a[i]a[i]a[i] 只需要切 a[i]−1mid\frac{a[i]-1}{mid}mi...

小程序基础与实战案例

小程序开发工具与基础 小程序开发准备: 申请小程序账号( appid ) 下载并安装微信开发者工具 具体步骤如下: 先进入 微信公众平台 ,下拉页面,把鼠标悬浮在小程序图标上 然后点击 小程序开发文档 照着里面给的步骤,就可以申请到小程序账号了。 然后就可以下载 开发者工具 了 下载完打开后的界面就是这个样子 下面让我们来新建一个小程序开发项目: 在AppID输入自己刚刚注册的AppID就可以,或...

VMware centOS7 下通过minikube部署Kubernetes

1、环境准备: VMware CentOS-7-x86_64 CPU:2*2core 内存:8G 宿主机和虚拟机需网络互通,虚拟机外网访问正常 Centos发行版版本查看:cat /etc/centos-release root用户操作 2、禁用swap分区 Kubernetes 1.8开始要求关闭系统的Swap,可暂时关闭或永久禁用, 使用 $ free -m 确认swap是否为开启状态 $ s...

猜你喜欢

逻辑回归与scikit-learn

欢迎关注本人的微信公众号AI_Engine LogisticRegression 算法原理 一句话概括:逻辑回归假设数据服从伯努利分布,通过极大化似然函数(损失函数)的方法,运用梯度下降或其他优化算法来求解参数,来达到将数据二分类的目的。 定义:逻辑回归(Logistic Regression)是一种用于解决二分类(0 or 1)问题的机器学习方法,用于估计某种事物的可能性(不是概率)。比如某用户...

指针OR数组?用他们来表达字符串又有何不同?

cocowy的编程之旅 在学习C语言的过程中我们经常可以看到或者听到这样一句话:数组其实等价于指针,例如: 在这里可以轻松的看出输出后他们的值相等,其实在计算机内存里面,p为本地变量,有着他自己的作用域。而指针变量q保存着这个数组的首地址,通过*号指向这个地址保存的变量值。 然而我们再看一个例子: 这个时候计算机报错,这是为什么呢? 其实原因很简单,指针说指向的这个字符串的地址是位于计算机代码段地...

广度搜索

广度搜索的基本使用方法 广度搜索不同于深度搜索,是一种一步一步进行的过程,每一个点只记录一遍。需要用到队列记录每一步可以走到的位置,找到目标位置输出步数即可。 用到的知识:结构体、队列 如图 首先我们需要定义一个结构体来存储每个遍历到的点和步数 广搜不会用到递归,所以可以直接在主函数里写,这里需要定义一个结构体队列 初始化队列并将起始点入列 遍历 完整代码...

NIO Socket 编程实现tcp通信入门(二)

1、NIO简介 NIO面向通道和缓冲区进行工作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。可以双向传输数据,是同步非阻塞式IO。NIO还引入了选择器机制,从而实现了一个选择器监听多个底层通道,减少了线程并发数。用NIO实现socket的Tcp通信需要掌握下面三个知识点: Buffer 缓冲区 Channel 通道 Selector 选择器   2、java.nio.Buff...

[字节码系列]ObjectWeb ASM构建Method Monitor

      在前面的篇章中,我们看到Java Instrutment的强大能力,本篇,我们将介绍如何使用ObjectWeb ASM的字节码增强能力构建Method Monitor       1.什么是ObjectWeb ASM      ObjectWeb ...