无涯教程-进程 - 信号(Signals)

信号是对进程的通知,指示事件的发生。信号也称为软件中断,无法预知其发生,因此也称为异步事件。

可以用数字或名称指定信号,通常信号名称以SIG开头。可用信号kill –l(列出信号名称为l)检查可用信号,如下所示-

Signal

无论何时发出信号,都会执行默认操作,忽略信号意味着既不执行默认操作也不处理信号,几乎可以忽略或处理所有信号。不可忽略或无法处理/捕获的信号是SIGSTOP和SIGKILL。

总而言之,对信号执行的操作如下-

  • 默认操作
  • 处理信号
  • 忽略信号

如所讨论的,可以改变默认动作的执行来处理信号,信号处理可以两种方式之一进行,即通过系统调用signal()和sigaction()。

#include <signal.h>typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);

系统调用signal()将在信号产生时调用注册处理程序,如信号传递中所述。处理程序可以是SIG_IGN(忽略信号),SIG_DFL(将信号设置回默认机制)或用户定义的信号处理程序或函数地址之一。

成功的系统调用将返回带有整数参数且没有返回值的函数的地址。如果发生错误,此调用将返回SIG_ERR。

尽管可以使用signal()调用由用户注册的相应信号处理程序,但无法进行微调,例如屏蔽应阻止的信号,修改信号的行为以及其他功能。使用sigaction()系统调用可以做到这一点。

#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

该系统调用用于检查或更改信号动作,如果该动作不为空,则从该动作安装信号信号的新动作。如果oldact不为null,则先前的操作将保存在oldact中。

sigaction结构包含以下字段-

字段1  -  在sa_handler或sa_sigaction中提到的处理程序。

void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);

sa_handler的处理程序基于符号指定要执行的操作,其中SIG_DFL表示默认操作,而SIG_IGN则忽略信号或指向信号处理功能的指针。

sa_sigaction的处理程序将信号编号指定为第一个参数,将siginfo_t结构的指针指定为第二个参数,将用户上下文的指针指定为第三个参数(请检查getcontext()或setcontext()以获取更多信息)。

结构siginfo_t包含信号信息,例如要传送的信号编号,信号值,进程ID,发送进程的实际用户ID等。

字段2   -  要阻止的信号集。

int sa_mask;

此变量指定在执行信号处理程序期间应屏蔽的信号掩码。

字段3  -  特殊标志。

int sa_flags;

该字段指定一组标志,这些标志可修改信号的行为。

字段4  -  还原处理程序。

void (*sa_restorer) (void);

成功时此系统调用返回0,失败时返回-1。

让我们考虑一些示例程序。

首先,让我们从产生异常的示例程序开始,在此程序中,我们尝试执行零除运算,这会使系统生成异常。

/* signal_fpe.c */
#include<stdio.h>int main() {int result;int v1, v2;v1 = 121;v2 = 0;result = v1/v2;printf("Result of Divide by Zero is %d\n", result);return 0;
}

编译和执行步骤

Floating point exception (core dumped)

因此,当我们尝试执行算术运算时,系统生成了带有核心转储的浮点异常,这是信号的默认操作。

现在,让我们使用signal()系统调用来修改代码以处理此特定信号。

/* signal_fpe_handler.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>void handler_dividebyzero(int signum);int main() {int result;int v1, v2;void (*sigHandlerReturn)(int);sigHandlerReturn = signal(SIGFPE, handler_dividebyzero);if (sigHandlerReturn == SIG_ERR) {perror("Signal Error: ");return 1;}v1 = 121;v2 = 0;result = v1/v2;printf("Result of Divide by Zero is %d\n", result);return 0;
}void handler_dividebyzero(int signum) {if (signum == SIGFPE) {printf("Received SIGFPE, Divide by Zero Exception\n");exit (0);} elseprintf("Received %d Signal\n", signum);return;
}

编译和执行步骤

Received SIGFPE, Divide by Zero Exception

如所讨论的,信号是由系统生成的,或者用户也可以通过编程方式生成信号,如果要以编程方式生成信号,请使用库函数raise()。

要继续执行已停止的进程,请将SIGCONT发送到该特定进程。您还可以发出fg(前景)或bg(背景)命令以继续执行,在这里,这些命令只会重新开始最后一个进程的执行。如果停止了多个进程,则仅恢复最后一个进程。如果要恢复以前停止的进程,请恢复作业(使用fg/bg)以及作业号。

以下程序用于通过raise()函数引发信号SIGSTOP。用户按下CTRL + Z(Control + Z)键也可以生成信号SIGSTOP。发出该信号后,程序将停止执行。发送信号(SIGCONT)以继续执行。

在以下示例中,我们将使用命令fg恢复停止的进程。

/* signal_raising.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>int main() {printf("Testing SIGSTOP\n");raise(SIGSTOP);return 0;
}

编译和执行步骤

Testing SIGSTOP
[1]+ Stopped ./a.out
./a.out

现在,通过从另一个终端发出SIGCONT,增强先前的程序以继续执行已停止的进程。

/* signal_stop_continue.c */
#include<stdio.h>
#include<signal.h>
#include <sys/types.h>
#include <unistd.h>void handler_sigtstp(int signum);int main() {pid_t pid;printf("Testing SIGSTOP\n");pid = getpid();printf("Open 另一个码头 and issue following command\n");printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d\n", pid, pid, pid);raise(SIGSTOP);printf("Received signal SIGCONT\n");return 0;
}

编译和执行步骤

Testing SIGSTOP
Open 另一个码头 and issue following command
kill -SIGCONT 30379 or kill -CONT 30379 or kill -18 30379
[1]+ Stopped ./a.outReceived signal SIGCONT
[1]+ Done ./a.out

在另一个终端

kill -SIGCONT 30379

到目前为止,我们已经看到了处理系统生成的信号的程序。现在,让我们看看通进程序(使用raise()函数或通过kill命令)生成的信号。该程序生成信号SIGTSTP(终端停止),其默认操作是停止执行。但是,由于我们现在正在处理信号而不是默认操作,因此它将到达已定义的处理程序。在这种情况下,我们只是打印消息并退出。

/* signal_raising_handling.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>void handler_sigtstp(int signum);int main() {void (*sigHandlerReturn)(int);sigHandlerReturn = signal(SIGTSTP, handler_sigtstp);if (sigHandlerReturn == SIG_ERR) {perror("Signal Error: ");return 1;}printf("Testing SIGTSTP\n");raise(SIGTSTP);return 0;
}void handler_sigtstp(int signum) {if (signum == SIGTSTP) {printf("Received SIGTSTP\n");exit(0);}elseprintf("Received %d Signal\n", signum);return;
}

编译和执行步骤

Testing SIGTSTP
Received SIGTSTP

我们已经看到了执行默认操作或处理信号的实例。现在,该忽略信号了。在这里,在此示例程序中,我们通过SIG_IGN注册了要忽略的信号SIGTSTP,然后提高了信号SIGTSTP(终端停止)。当信号SIGTSTP产生时,将被忽略。

/* signal_raising_ignoring.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>void handler_sigtstp(int signum);int main() {void (*sigHandlerReturn)(int);sigHandlerReturn=signal(SIGTSTP, SIG_IGN);if (sigHandlerReturn == SIG_ERR) {perror("Signal Error: ");return 1;}printf("Testing SIGTSTP\n");raise(SIGTSTP);printf("Signal SIGTSTP is ignored\n");return 0;
}

编译和执行步骤

Testing SIGTSTP
Signal SIGTSTP is ignored

到目前为止,我们已经观察到我们有一个信号处理程序来处理一个信号。我们可以有一个处理程序来处理多个信号吗?答案是肯定的。让我们考虑一个程序。

以下程序执行以下操作-

步骤1  -   注册一个处理程序(handleSignals)以捕获或处理信号SIGINT(CTRL + C)或SIGQUIT(CTRL +\)

步骤2  -   如果用户生成信号SIGQUIT(通过终止命令或使用CTRL +\的键盘控制),则处理程序仅将消息打印为返回。

步骤3  -   如果用户首次生成信号SIGINT(通过kill命令或使用CTRL + C的键盘控制),则它将修改该信号以从下一次执行默认操作(使用SIG_DFL)。

步骤4  -   如果用户第二次生成信号SIGINT,它将执行默认操作,即终止程序。

/* Filename: sigHandler.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>void handleSignals(int signum);int main(void) {void (*sigHandlerInterrupt)(int);void (*sigHandlerQuit)(int);void (*sigHandlerReturn)(int);sigHandlerInterrupt = sigHandlerQuit = handleSignals;sigHandlerReturn = signal(SIGINT, sigHandlerInterrupt);if (sigHandlerReturn == SIG_ERR) {perror("signal error: ");return 1;}sigHandlerReturn = signal(SIGQUIT, sigHandlerQuit);if (sigHandlerReturn == SIG_ERR) {perror("signal error: ");return 1;}while (1) {printf("\nTo terminate this program, perform the following:\n");printf("1. Open another terminal\n");printf("2. Issue command: kill %d or issue CTRL+C 2 times (second time it terminates)\n", getpid());sleep(10);}return 0;
}void handleSignals(int signum) {switch(signum) {case SIGINT:printf("\nYou pressed CTRL+C\n");printf("Now reverting SIGINT signal to default action\n");signal(SIGINT, SIG_DFL);break;case SIGQUIT:printf("\nYou pressed CTRL+\\\n");break;default:printf("\nReceived signal number %d\n", signum);break;}return;
}

编译和执行步骤

To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default actionTo terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+2 times (second time it terminates)
^\You pressed CTRL+\
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 120
Terminated

另一个终端

kill 71

第二种方法

我们知道要处理信号,我们有两个系统调用,即signal()或sigaction()。到目前为止,我们已经看到了signal()系统调用,现在是时候进行sigaction()系统调用了。让我们修改上述程序以使用sigaction()如下执行-

/* Filename: sigHandlerSigAction.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>void handleSignals(int signum);int main(void) {void (*sigHandlerReturn)(int);struct sigaction mysigaction;mysigaction.sa_handler = handleSignals;sigemptyset(&mysigaction.sa_mask);mysigaction.sa_flags = 0;sigaction(SIGINT, &mysigaction, NULL);if (mysigaction.sa_handler == SIG_ERR) {perror("signal error: ");return 1;}mysigaction.sa_handler = handleSignals;sigemptyset(&mysigaction.sa_mask);mysigaction.sa_flags = 0;sigaction(SIGQUIT, &mysigaction, NULL);if (mysigaction.sa_handler == SIG_ERR) {perror("signal error: ");return 1;}while (-1) {printf("\nTo terminate this program, perform either of the following:\n");printf("1. Open another terminal and issue command: kill %d\n", getpid());printf("2. Issue CTRL+C 2 times (second time it terminates)\n");sleep(10);}return 0;
}void handleSignals(int signum) {switch(signum) {case SIGINT:printf("\nYou have entered CTRL+C\n");printf("Now reverting SIGINT signal to perform default action\n");signal(SIGINT, SIG_DFL);break;case SIGQUIT:printf("\nYou have entered CTRL+\\\n");break;default:printf("\nReceived signal number %d\n", signum);break;}return;
}

让我们看看编译和执行进程。在执行进程中,让我们两次查看CTRL + C问题,其余检查/方式(如上所述)也可以尝试该程序。

编译和执行步骤

To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
You have entered CTRL+C
Now reverting SIGINT signal to perform default action
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C

进程 - 信号(Signals) - 无涯教程网无涯教程网提供信号是对进程的通知,指示事件的发生。信号也称为软件中断,无法预知其发生,因此也称...https://www.learnfk.com/process/inter-process-communication-signals.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/53223.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

骨传导耳机和普通耳机哪个危害大?一文读懂骨传导耳机!

作为一个5年重度运动爱好者&#xff0c;常年跑步、爬山、骑行&#xff0c;入手过的各类耳机超30款&#xff0c;用真实体验告诉大家&#xff0c;骨传导耳机和普通耳机哪个危害大&#xff01; 首先大家要知道的是&#xff0c;不管什么类型的耳机&#xff0c;如说说音量过大&…

SpringBoot生成和解析二维码完整工具类分享(提供Gitee源码)

前言&#xff1a;在日常的开发工作当中可能需要实现一个二维码小功能&#xff0c;我参考了网上很多关于SpringBoot生成二维码的教程&#xff0c;最终还是自己封装了一套完整生成二维码的工具类&#xff0c;可以支持基础的黑白二维码、带颜色的二维码、带Logo的二维码、带颜色和…

Spring Cloud Alibaba-Sentinel-Sentinel入门

1 什么是Sentinel Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel 具有以下特征: 丰富的应用场景&#xff1a;Sentinel 承接了阿里…

Linux 虚拟机常用命令

一、文件/文件夹管理 1. ls命令 就是 list 的缩写&#xff0c;通过 ls 命令不仅可以查看 linux 文件夹包含的文件&#xff0c;而且可以查看文件权限(包括目录、文件夹、文件权限)查看目录信息等等。 ls -a 列出目录所有文件&#xff0c;包含以.开始的隐藏文件ls -A 列出除.…

【深度学习】实验02 鸢尾花数据集分析

文章目录 鸢尾花数据集分析决策树K-means 鸢尾花数据集分析 决策树 # 导入机器学习相关库 from sklearn import datasets from sklearn import treeimport matplotlib.pyplot as plt import numpy as np# Iris数据集是常用的分类实验数据集&#xff0c; # 由Fisher, 1936收集…

JetBrains 2023.2全新发布!IDEA、PyCharm等支持AI辅助

日前JetBrains官方正式宣布旗下IDE系列今年第二个重要版本——v2023.2全新发布&#xff0c;涵盖了 IntelliJ IDEA、PyCharm、WebStorm等一众知名产品&#xff0c;接下来我们一起详细了解一下他们的更新重点吧~ IntelliJ IDEA v2023.2——引入AI辅助开发 IntelliJ IDEA 2023.2…

websocket 接收消息无法获取用户id

1.遇到问题 公司项目是基于ruoyi 框架快速搭建开发&#xff0c;使用多线程搜索查询&#xff0c;所以以用户区分任务&#xff0c;保证可以搜索任务和取消搜索&#xff0c;所以我这需要获得用户id&#xff0c;使用 SecurityUtils 共工工具类从请求头获取token&#xff0c;然后解…

动捕设备助力打造沉浸式虚拟现实体验

在纪录片《超时空寻找》中&#xff0c;借助了实时动捕设备&#xff0c;基于三维数字人技术进行老战士与历史场景还原&#xff0c;让抗美援朝老战士可以通过虚拟现实技术&#xff0c;跨越时空与战友实现隔空对话。 随着动捕设备的不断发展&#xff0c;虚拟现实技术越来越成熟&a…

WGS84地球坐标系,GCJ02火星坐标系,BD09百度坐标系简介与转换 资料收集

野火 ATGM332D简介 高性能、低功耗 GPS、北斗双模定位模块 STM32 GPS定位_为了维护世界和平_的博客-CSDN博客 秉火多功能调试助手上位机开源&#xff01;共六款软件&#xff0c;学到你吐... , - 电脑上位机 - 野火电子论坛 - Powered by Discuz! https://www.firebbs.cn/for…

M1 Pro 利用docker 搭建pytho2的开发环境,以vscode连接开发为例

使用 M1 Pro &#xff08;不支持python2的安装&#xff09;开发&#xff0c;需要使用 Python 2.7 的环境&#xff0c;在使用 pyenv 安装 Python 2 时遇到了各种奇怪的问题。最终&#xff0c;我决定使用 Docker 搭建开发环境&#xff0c;并使用 VS Code 连接到本地容器。以下是详…

Docker【部署 04】Docker Compose下载安装及实例Milvus Docker compose(CPU)使用说明分享

Docker Compose 下载安装使用说明 1.Compose说明1.1 Overview of installing Docker Compose1.2 Installation scenarios1.2.1 Scenario one: Install Docker Desktop1.2.2 Scenario two: Install the Compose plugin1.2.3 Scenario three: Install the Compose standalone 2.C…

Python爬虫快速入门指南

引言&#xff1a; 网络爬虫是一种自动化程序&#xff0c;可以在互联网上搜集和提取数据。Python作为一种功能强大且易学的编程语言&#xff0c;成为了许多爬虫开发者的首选。本文将为你提供一个关于Python爬虫的快速入门指南&#xff0c;包括基本概念、工具和实际案例。 第一…

Conda命令整理-自用版

Conda用法整理-自用版 Conda介绍1、环境操作1.1 创建环境1.2 激活环境1.3 导出环境1.4 导入环境1.5 关闭环境1.6 删除环境 2、包操作2.1 安装软件包2.2 安装指定包的指定版本 参考资料 Conda介绍 Conda是一个开源的包管理系统和环境管理器&#xff0c;用于在不同的计算环境中安…

Unity 应用消息中心-MessageCenter

Ps&#xff1a;主要解决耦合问题&#xff0c;把脚本之间的联系通过不同消息类型事件形式进行贯通 1.MessageCenter主脚本 2.DelegateEvent消息类型脚本 3.MC_Default_Data具体接收类脚本 using System; using System.Collections; using System.Collections.Generic; using …

macbook m1 docker中使用go

已经有一个centos8的镜像&#xff0c;本来打算在centos8中安装go 安装方法&#xff1a; # 1.下载go的安装包 mkdir install && cd install # 任意创建个文件夹 wget https://go.dev/dl/go1.20.2.linux-amd64.tar.gz# 2. 解压 tar -C xzf go1.20.2.linux-amd64.tar.g…

ThreadPoolExecutor的参数keepAliveTime的作用

直接断点进去&#xff1a; Test public void testKeepAliveTime3() {//生存时间 - 针对救急线程ThreadPoolExecutor executor new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, new SynchronousQueue<>()); }public ThreadPoolExecutor(int corePoolSize,int maxim…

layui表格事件分析实例

在 layui 的表格组件中&#xff0c;区分表头事件和行内事件是通过事件类型&#xff08;toolbar 和 tool&#xff09;以及 lay-filter 值来实现的。 我们有一个表格&#xff0c;其中有一个工具栏按钮和操作按钮。我们将使用 layui 的 table 组件来处理这些事件。 HTML 结构&…

vue2+element-ui 实现下拉框滚动加载

一、自定义滚动指令。 VUE.directive( el-select-loadmore: { bind(el, binding) { const SELECTWRAP_DOM el.querySelector(.el-select-dropdown .el-select-dropdown__wrap) SELECTWRAP_DOM.addEventListener(scroll, function () { /*…

算法训练营第三十六天(8.26)| 动态规划Part07:完全背包

Leecode 139.单词拆分 题目地址&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目类型&#xff1a;完全背包 class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {// 完全背包问题// dp[i]代表从0…

汇总区间java解决方案

问题&#xff1a; 给定一个 无重复元素 的 有序 整数数组 nums 。 返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说&#xff0c;nums 的每个元素都恰好被某个区间范围所覆盖&#xff0c;并且不存在属于某个范围但不属于 nums 的数字 x 。 列表中的每个区…