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)——信号处理初步学习的更多相关文章

  1. UNIX基础知识--<<UNIX 环境编程>>读书笔记

    1 shell程序就是位于应用软件与系统调用之间的程序   每个用户登录系统,系统就会为用户分配shell (用户的登录的口令文件 在  /etc/passwd 2 ls filename  运行原理 ...

  2. UNIX环境编程学习——反思认识

     学习情况: 有关UNIX系统环境编程的学习时间用来非常长的时间.可是感觉效果还是不是太好,在中间经过了期末考试.用来非常长的时间用来学习专业课.就将该过程的学习放到了一边上,放假以后又回家造成了 ...

  3. Ext.Net学习笔记23:Ext.Net TabPanel用法详解

    Ext.Net学习笔记23:Ext.Net TabPanel用法详解 上面的图片中给出了TabPanel的一个效果图,我们来看一下代码: <ext:TabPanel runat="se ...

  4. [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  5. 【神经网络与深度学习】学习笔记:AlexNet&Imagenet学习笔记

    学习笔记:AlexNet&Imagenet学习笔记 ImageNet(http://www.image-net.org)是李菲菲组的图像库,和WordNet 可以结合使用 (毕业于Caltec ...

  6. Vue学习笔记-Vue.js-2.X 学习(四)===>脚手架Vue-CLI(基本工作和创建)

    (五) 脚手架Vue-CLI 一 Vue-CLI前提(nodejs和webpack) 二  Vue学习-nodejs按装配置,Node.js 就是运行在服务端的 JavaScript. 1. 去nod ...

  7. Vue学习笔记-Vue.js-2.X 学习(一)===>基本知识学习

    一  使用环境: windows 7 64位操作系统 二  IDE:VSCode/PyCharm 三  Vue.js官网: https://cn.vuejs.org/ 四  下载安装引用 方式1:直接 ...

  8. [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明-Babel)

    五  Vue学习-vue-cli脚手架学习(创建只选一个选项:Babel) 1. 项目目录说明 node_modules : 包管理文件夹 public : 静态资源 src : 源代码 gitign ...

  10. Vue学习笔记-Vue.js-2.X 学习(五)===>脚手架Vue-CLI(PyCharm)

    Vue项目在pycharm中配置 退出运行: ctrl+c Vue学习笔记-Vue.js-2.X 学习(六)===>脚手架Vue-CLI(项目说明)

随机推荐

  1. spring batch中MyBatisPagingItemReader分页使用介绍

    假如是mysql的话,SQL语句 <![CDATA[select * from ( SELECT so.* FROM t_tm_sales_order so where so.last_modi ...

  2. 网络构建入门技术(2)——IP子网划分

    说明(2017-5-10 10:54:31): 1. 为什么要子网划分? 子网划分就是,网络位变长,主机位变短的过程.实际上就是将一个大网络,划分成多个小网络的过程. 目的就是为了解决IP地址不够用的 ...

  3. ios系统中各种设置项的url链接

    ios系统中各种设置项的url链接 在代码中调用如下代码:NSURL*url=[NSURL URLWithString:@"prefs:root=WIFI"];[[UIApplic ...

  4. 基于jquery横向手风琴效果

    基于jquery横向手风琴效果是一款基于jquery实现的左右滑动手风琴图片轮播切换特效.效果图如下: 在线预览   源码下载 效果图如下: <div class="flash&quo ...

  5. [转]启动Tomcat提示:指定的服务未安装

    原文地址:http://blog.csdn.net/yilip/article/details/8066246 新下载的Tomcat7.0 解压缩完了运行tomcat7.exe屏幕一闪就没了 运行to ...

  6. C语言 · 集合运算

    算法训练 集合运算   时间限制:1.0s   内存限制:512.0MB    问题描述 给出两个整数集合A.B,求出他们的交集.并集以及B在A中的余集. 输入格式 第一行为一个整数n,表示集合A中的 ...

  7. 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 ...

  8. 【jquery】ajax 请求成功后新开窗口被拦截解决方法

    问题: 前面开发项目时碰到一个问题,ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是很可惜被浏览器给拦截了,怎么解决这个问题呢? 分析: 浏览器之所以 ...

  9. Graph-BFS-图的广度优先遍历

    #include <iostream> #include <queue> using namespace std; /* 5 5 1 2 1 3 1 5 2 4 3 5 1 2 ...

  10. const_cast的应用

    对于const变量,我们不能修改它的值,这是这个限定符最直接的表现.但是我们就是想违背它的限定希望修改其内容怎么办呢?于是我们可以使用const_cast转换符是用来移除变量的const限定符.con ...