UNIX环境编程学习笔记(23)——信号处理初步学习
lienhua34
2014-10-29
1 信号的概念
维基百科中关于信号的描述是这样的:
在计算机科学中,信号(英语:Signals)是 Unix、类 Unix 以及其他 POSIX 兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
关于这段描述,我们可以从中学习到下面几点关于信号的知识,
1. 信号是什么:UNIX 进程间的一种异步通讯机制。
2. 信号的作用:提醒目标进程某事件的发生,并中断了目标进程正常的控制流程。
3. 进程对信号的处理:目标进程可以执行信号的默认处理函数,或者执行进程自定义的信号处理程序。
每个信号都有一个名字, 这些名字都以三个字符 SIG 开头。 例如,SIGABRT是夭折信号, 当进程调用 abort 函数时产生这种信号。SIGALRM 是闹钟信号,当由 alarm 函数设置的计时器超时后产生此信号。在 UNIX 系统中,这些信号都定义在头文件 <signal.h> 中,并且都是以一个正整数来表示(信号编号)。通过在 shell 中运行命令 kill -l 可以查看当前系统所执行的所有信号。
UNIX 系统规定了内核可以对信号执行以下三种处理行为,
1. 忽略此信号。有两个信号 SIGKILL 和 SIGSTOP 不可忽略,这两个信号提供给超级用户终止或停止进程的可靠方法。
2. 执行系统默认动作。大多数信号的默认动作是终止进程。
3. 捕获信号,执行用户自定义的处理函数。
2 设置信号处理程序
UNIX 系统提供了 signal 函数来设置信号的处理程序,
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
返回值:若成功则返回信号以前的处理配置,若出错则返回SIG_ERR
其中,参数 func 的值可以是,
• 常 量SIG_IGN, 向 内 核 表 示 忽 略 此 信 号 (有 两 个 信 号 SIGKILL 和SIGSTOP 不可忽略,调用 signal 函数会报错)。
• 常量SIG_DFL,表示执行信号的系统默认动作。
• 一个函数指针,表示信号发生时执行该函数进行处理,这个函数称为信号处理程序。信号处理函数接收一个 int 类型的信号值参数,无返回值。
signal 信号的返回值也是一个函数指针,指向该信号之前的信号处理程序。
下面我们来看一个例子,
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> static void sig_usr(int); int
main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("can't catch SIGUSR1: %s\n", strerror(errno));
exit(-);
}
if (signal(SIGUSR2, sig_usr) == SIG_ERR) {
printf("can't catch SIGUSR2: %s\n", strerror(errno));
exit(-);
}
for (;;) {
pause();
}
exit();
} static void
sig_usr(int signo)
{
if (signo == SIGUSR1) {
printf("received SIGUSR1\n");
} else if (signo == SIGUSR2) {
printf("received SIGUSR2\n");
} else {
printf("received signal: %d\n", signo);
}
}
signaldemo.c
上面 signaldemo.c 程序为信号 SIGUSR1 和 SIGUSR2 设置了信号处理程序sig_usr。然后在主流程 main 中循环调用 pause 等待信号,pause函数使调用进程挂起直至捕捉到一个信号,
#include <unistd.h>
int pause(void);
返回值:-1,并将errno设置为EINTR
只有执行了一个信号处理程序并从中返回,pause 函数才返回。
编译 signaldemo.c 程序,生成并执行 signaldemo 文件,
lienhua34:demo$ gcc -o signaldemo signaldemo.c
lienhua34:demo$ ./signaldemo &
[]
lienhua34:demo$ kill -USR1
lienhua34:demo$ received SIGUSR1
kill -USR2
lienhua34:demo$ received SIGUSR2
kill
其中 kill -USR1 3033 和 kill -USR2 3033 分别表示向进程 ID 为 3033 的进程发送信号 SIGUSR1 和 SIGUSR2;而 kill 3033 表示向进程 3033 发送信号 SIGTERM,该信号会终止进程。
说明一下信号 SIGUSR1 和 SIGUSR2,这是 UNIX 定义的两个用户定义信号。一个 UNIX 系统支持的信号类型是固定,用户不能够新增加信号类型。如果用户希望通过信号的机制来实现自己的功能,则可以通过自定义信号 SIGUSR1 或 SIGUSR2 的信号处理程序来实现自己想要的功能。例如维基百科中举的几个例子,
许 多程 序使 用 SIGUSR1 在线 程 和 进 程间 进行 同 步,例 如在Linux 2.0 中的 LinuxThreads 线程库(已被废弃,为 NPTL 所代替)。其它的程序,例如 dd 的某些版本,会在收到该信号时输出当前状态(Mac OS X 的 dd 实现会在收到 USR1 时暂停)。USR1 亦通常被用来告知应用程序重载配置文件;例如,向Apache HTTP 服务器发送一个 USR1 信号将导致以下步骤的发生:停止接受新的连接,等待当前连接停止,重新载入配置文件,重新打开日志文件,重启服务器,从而实现相对平滑的不关机的更改。
3 向进程发送信号
UNIX 系统提供了两个函数 kill 和 raise 来产生信号。kill 函数将信号发送给指定的进程或进程组。raise 函数则允许进程向自身发送信号。
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
两个函数返回值:若成功则返回0,若出错则返回-1
调用 raise(signo) 等价于 kill(getpid(), signo)。
kill 函数的 pid 参数有 4 种不同情况:
(1)pid>0 将信号发送给进程 ID 为 pid 的进程。
(2)pid==0 将信号发送给与发送进程属于同一个进程组的所有进程,而且发送进程具有向这些进程发送信号的权限。
(3)pid<0 将信号发送给其进程组 ID 等于 pid 的绝对值的所有进程,而且发送进程具有向这些进程发送信号的权限。
(4)pid==-1 将信号发送给发送进程有权限向他们发送信号的系统上的所有进程。
上面有两点需要说明一下:(1)“所有进程”不包括系统进程集;(2)发送进程是超级用户,或者发送进程的实际或有效用户 ID 等于接收进程的实际或有效用户 ID 才具有发送信号权限。下面来看个例子,
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <unistd.h> static void sig_usr(int); int
main(void)
{
pid_t pid; if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("can't catch SIGUSR1: %s\n", strerror(errno));
exit(-);
}
if (signal(SIGUSR2, sig_usr) == SIG_ERR) {
printf("can't catch SIGUSR2: %s\n", strerror(errno));
exit(-);
} if ((pid = fork()) < ) {
printf("fork error: %s\n", strerror(errno));
exit(-);
}
else if (pid == ) {
pause();
exit();
} printf("process %d creates a child process %d\n", getpid(), pid); sleep();
kill(pid, SIGUSR1);
waitpid(pid, NULL);
raise(SIGUSR2); exit();
} static void
sig_usr(int signo)
{
if (signo == SIGUSR1) {
printf("process %d received SIGUSR1\n", getpid());
} else if (signo == SIGUSR2) {
printf("process %d received SIGUSR2\n", getpid());
} else {
printf("process %d received signal: %d\n", getpid(), signo);
}
}
killdemo.c
上面的 killdemo.c 程序文件中,父进程为信号 SIGUSR1 和 SIGUSR2设置了信号处理程序sig_usr。然后调用 fork 创建一个子进程,子进程的信号 SIGUSR1 和 SIGUSR2 的信号处理程序继承父进程的,同样是sig_usr(除非子进程调用了 exec 函数,子进程中这两个信号的信号处理动作才会设置为系统默认)。父进程调用 kill 函数向子进程发送信号 SIGUSR1,而调用 raise 函数向自身发送信号 SIGUSR2. 编译该程序,生成并执行 killdemo文件,
lienhua34:demo$ ./killdemo
process creates a child process
process received SIGUSR2
process received SIGUSR1
通过上面的运行结果,可以看到父进程 3261 创建了子进程 3262,父进程 3261 捕获了信号 SIGUSR2,子进程捕获了信号 SIGUSR1。实际运行结果与预期结果相符合。
(done)
UNIX环境编程学习笔记(23)——信号处理初步学习的更多相关文章
- UNIX基础知识--<<UNIX 环境编程>>读书笔记
1 shell程序就是位于应用软件与系统调用之间的程序 每个用户登录系统,系统就会为用户分配shell (用户的登录的口令文件 在 /etc/passwd 2 ls filename 运行原理 ...
- UNIX环境编程学习——反思认识
学习情况: 有关UNIX系统环境编程的学习时间用来非常长的时间.可是感觉效果还是不是太好,在中间经过了期末考试.用来非常长的时间用来学习专业课.就将该过程的学习放到了一边上,放假以后又回家造成了 ...
- Ext.Net学习笔记23:Ext.Net TabPanel用法详解
Ext.Net学习笔记23:Ext.Net TabPanel用法详解 上面的图片中给出了TabPanel的一个效果图,我们来看一下代码: <ext:TabPanel runat="se ...
- [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- 【神经网络与深度学习】学习笔记:AlexNet&Imagenet学习笔记
学习笔记:AlexNet&Imagenet学习笔记 ImageNet(http://www.image-net.org)是李菲菲组的图像库,和WordNet 可以结合使用 (毕业于Caltec ...
- Vue学习笔记-Vue.js-2.X 学习(四)===>脚手架Vue-CLI(基本工作和创建)
(五) 脚手架Vue-CLI 一 Vue-CLI前提(nodejs和webpack) 二 Vue学习-nodejs按装配置,Node.js 就是运行在服务端的 JavaScript. 1. 去nod ...
- Vue学习笔记-Vue.js-2.X 学习(一)===>基本知识学习
一 使用环境: windows 7 64位操作系统 二 IDE:VSCode/PyCharm 三 Vue.js官网: https://cn.vuejs.org/ 四 下载安装引用 方式1:直接 ...
- [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明-Babel)
五 Vue学习-vue-cli脚手架学习(创建只选一个选项:Babel) 1. 项目目录说明 node_modules : 包管理文件夹 public : 静态资源 src : 源代码 gitign ...
- Vue学习笔记-Vue.js-2.X 学习(五)===>脚手架Vue-CLI(PyCharm)
Vue项目在pycharm中配置 退出运行: ctrl+c Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明)
随机推荐
- spring batch中MyBatisPagingItemReader分页使用介绍
假如是mysql的话,SQL语句 <![CDATA[select * from ( SELECT so.* FROM t_tm_sales_order so where so.last_modi ...
- 网络构建入门技术(2)——IP子网划分
说明(2017-5-10 10:54:31): 1. 为什么要子网划分? 子网划分就是,网络位变长,主机位变短的过程.实际上就是将一个大网络,划分成多个小网络的过程. 目的就是为了解决IP地址不够用的 ...
- ios系统中各种设置项的url链接
ios系统中各种设置项的url链接 在代码中调用如下代码:NSURL*url=[NSURL URLWithString:@"prefs:root=WIFI"];[[UIApplic ...
- 基于jquery横向手风琴效果
基于jquery横向手风琴效果是一款基于jquery实现的左右滑动手风琴图片轮播切换特效.效果图如下: 在线预览 源码下载 效果图如下: <div class="flash&quo ...
- [转]启动Tomcat提示:指定的服务未安装
原文地址:http://blog.csdn.net/yilip/article/details/8066246 新下载的Tomcat7.0 解压缩完了运行tomcat7.exe屏幕一闪就没了 运行to ...
- C语言 · 集合运算
算法训练 集合运算 时间限制:1.0s 内存限制:512.0MB 问题描述 给出两个整数集合A.B,求出他们的交集.并集以及B在A中的余集. 输入格式 第一行为一个整数n,表示集合A中的 ...
- TED Notes 1 (What leads to success)
1. the first thing is passion, do it for love, not for money2. if you do it for love, the money come ...
- 【jquery】ajax 请求成功后新开窗口被拦截解决方法
问题: 前面开发项目时碰到一个问题,ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是很可惜被浏览器给拦截了,怎么解决这个问题呢? 分析: 浏览器之所以 ...
- Graph-BFS-图的广度优先遍历
#include <iostream> #include <queue> using namespace std; /* 5 5 1 2 1 3 1 5 2 4 3 5 1 2 ...
- const_cast的应用
对于const变量,我们不能修改它的值,这是这个限定符最直接的表现.但是我们就是想违背它的限定希望修改其内容怎么办呢?于是我们可以使用const_cast转换符是用来移除变量的const限定符.con ...