1、信号

首先信号我们要和信号量区分开来,虽然两者都是操作系统进程通信的方式。可以简单的理解,信号是用来通知进程发生了什么需要做什么,信号量一般是用作进程同步(pv操作)

2、常见信号量

(以下数字标号代表信号再bitmap中的位置)

SIGINT 可能使我们最常用的信号之一。一般在我们想进程中断,键盘输入Ctrl + C 即可实现,这是一个进程终止信号。

SIGQUIT程序异常退出信号和2 类似, 输入Ctrl + \ 实现

SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。

11SIGSEGV试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.和SIGILL一样,程序出BUG时,我们经常见到。

17 SIGCHLD 子进程结束时, 父进程会收到这个信号。如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。

 这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。后面会详细介绍。

19 SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略. GDB中就使用了该信号。

20 SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.

3、信号常见操作

我们从进程说起,在进程的控制块中(PCB)有两个表一个叫做信号未决表,一个叫做信号阻塞表(都是64位图存储)。内核首先根据信号阻塞表中屏蔽状态字判断是否需要阻塞,如果该信号被设为为了阻塞的,那么信号未决表中对应 状态字(pending)相应位制成1;若该信号阻塞解除,信号未

决状态字(pending)相应位制成0;表示信号此时可以抵达了,也就是可以接收该信号了。其实由这个地方我们可以看到,同一个时刻,如果一个信号为阻塞了,那么无论你到来了多少次,在解除阻塞的时候进程只会处理一次。

备注: 阻塞意味着,信号到了,我暂时不处理,放着就是。 信号忽略,进程看到了,但是什么都不会做。

由此可见,对于信号而言,要么直接处理,要么一会处理(也有可能一直不处理), 要么压根就不会处理。

我们看下系统中内置的API接口:

int sigemptyset(sigset_t *set);//将信号集清空,共64bits
int sigfillset(sigset_t *set);//将信号集置1
int sigaddset(sigset_t *set, int signum);//将signum对应的位置为1
int sigdelset(sigset_t *set, int signum);//将signum对应的位置为0
int sigismember(const sigset_t *set, int signum);//判断signum是否在该信号集合中,如果集合中该位为1,则返回1,表示位于在集合中 // 读取更改屏蔽状态字的API函数
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
/*
参数how有下面三种取值:
SIG_BLOCK: 将参数set指向的信号集中设置的信号添加到现在的屏蔽状态字中,设置为阻塞;
SIG_UNBLOCK:将参数set指向的信号集中设置的信号添加到现在的屏蔽状态字中,设置为非阻塞, 也就是解除阻塞;
SIG_SETMASK:将参数set指向的信号集直接覆盖现在的屏蔽状态字的值;
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
若成功则为0,若出错则为-1 */
#include <signal.h>
#include <unistd.h> using namespace std; void Handle(int sig) {
printf("sig = %d\n", sig);
abort();
} void Prints(sigset_t* set) {
for (int i = 1; i <= 31; ++i) {
if (sigismember(set, i)) {
printf("1");
} else {
printf("0");
}
}
printf("\n"); } int main(){
signal(SIGINT, Handle);
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
sigprocmask(SIG_BLOCK, &set, NULL);
// signal(SIGINT, SIG_IGN); //忽略信号操作。
int i = 5;
while(i) {
i--;
sigset_t p;
sigpending(&p); // 获得当前信号未决表的数据。
Prints(&p);
sleep(2);
}
sigprocmask(SIG_UNBLOCK, &set, NULL);
//sigprocmask(SIG_UNBLOCK, &set, NULL);
//sigset_t p;
//sigpending(&p);
//Prints(&p); printf("-1----------------------");
printf("-2----------------------");
printf("-3----------------------");
printf("-4----------------------"); return 0;
}

在上面代码中,signal(SIGINT, Handle),我们自己注册了处理信号的函数。先展示结果:

0000000000000000000000000000000

0000000000000000000000000000000

^C0100000000000000000000000000000

0100000000000000000000000000000

0100000000000000000000000000000

sig = 2

简单解释一下,按下ctrl + c 给进程传入了中断的信号,在map中位置为2的地方置为了1,因为我们阻塞了信号,所以他不做处理。

等在循环结束以后我们解除了屏蔽,系统调用了我们注册的函数,进行了信号处理,在处理函数中我们调用了abort,所以剩下的代码并没有执行。

如果我们把代码中的注释打开再编译运行,发现是下面的结果:

0000000000000000000000000000000

0000000000000000000000000000000

^C0100000000000000000000000000000

0100000000000000000000000000000

0100000000000000000000000000000

0000000000000000000000000000000

-1-----------------------2-----------------------3-----------------------4----------------------

可以看到,SIGINT信号,到的时候,不是先忽略,是先判定阻塞,放在了未决信号表中。

然后等信号解阻塞以后,可以看到,进程忽略了该信号。不做处理。

4、SIGCHLD信号

这个信号,我们上面解释说明过。子进程退出时,会给父进程发送这个命令,让父进程回收资源。如果父进程不做处理,那么就成了子进程就成了我们说的僵尸进程,造成内存泄漏(敲黑板,这玩意也会造成内存泄漏)。

如果子进程退出的时候,父进程早就没了,那么回收资源的工作交给init进程。一般情况下,如果父进程,不关心子进程的资源回收,也不期待从儿子那边获得什么,可以对这个信号进行忽略,像上面代码中那样操作即可

signal(SIGCHLD, SIG_IGN)。这样也不会产生僵尸进程,子进程退出的时候,发个信号给父进程。父进程处理方式是忽略,子进程就自己退出了。

当然也可以自己创建处理函数,处理该信号。

在引入代码之前我们先说两个函数:wait 和 waitpid

头文件sys/wait.h

pid_t wait(int *status);

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样: pid = wait(NULL);

pid_t waitpid(pid_t pid, int *status, int options);

参数:

status:如果不是空,会把状态信息写到它指向的位置,与wait一样

options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

The value of options is an OR of zero or more of the following con-

stants:

WNOHANG return immediately if no child has exited.

wait与waitpid区别:

在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。
waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。
实际上wait函数是waitpid函数的一个特例。waitpid(-1, &status, 0);

了解这些以后我们看代码例子:

#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h> void waitchildren(int signo) { int status;
wait(&status);
//while(1)waitpid(-1, &status, WNOHANG); //注释3
//while(1)wait(&status) //注释4 } int main() { int i;
pid_t pid; //signal(SIGCHLD, waitchildren); //注释1
//signal(SIGCHLD, SIG_IGN); //注释2 for(i=0; i<100; i++) {
pid = fork();
if(pid == 0)
break;
} if(pid>0) {
printf("press Enter to exit...");
getchar();
} return 0;
}

如果 我们编译运行,发现在不退出的情况下,重新开一个终端运行top命令,可以看到zombie字段为100.即是100个僵尸进程。

如果打开注释1 发现还是有僵尸进程,但是数量不为100,同一时刻,很多子进程退出,但是可惜只处理一部分。

关闭注释1打开注释2.发现一个僵尸进程都没了。

如果只打开注释3 你也会发现一个僵尸进程都没了。

如果你只打开注释4,你会发现程序只处理了一个,而且卡死了。

wait 我们可以称其为同步接口,而waitpid为异步接口。

再看下面这段代码:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream> int count = 0;
void waitchildren(int signo) {
count++;
std::cout << count << "------------" << std::endl;
int status;
wait(&status); } int main() { int i;
pid_t pid; signal(SIGCHLD, waitchildren);
//signal(SIGCHLD, SIG_IGN);
sigset_t set;
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, NULL);
for(i=0; i<100; i++) {
pid = fork();
if(pid == 0)
break;
}
if(pid>0) {
printf("press Enter to exit...");
getchar();
sigprocmask(SIG_UNBLOCK, &set, NULL);
} return 0;
}

这段程序你会发现,只有一个子进程发送的信号,被捕获处理了。

以上就是全部内容,欢迎各位大佬,纠错指正。

Linux常见信号介绍的更多相关文章

  1. linux常见目录介绍

    /bin:/usr/bin: 可执行二进制文件目录,如常用命令ls.cat /boot: 放置linux启动时用到的一些文件,建议分区的时候独立分区 /dev: 存在linux系统下的设备文件,访问该 ...

  2. Linux 信号介绍

    是内容受限时的一种异步通信机制 首先是用来通信的 是异步的 本质上是 int 型的数字编号,早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31 ...

  3. Linux性能工具介绍

    l  Linux性能工具介绍 p  CPU高 p  磁盘I/O p  网络 p  内存 p  应用程序跟踪 l  操作系统与应用程序的关系比喻为“唇亡齿寒”一点不为过 l  应用程序的性能问题/功能问 ...

  4. Linux 启动参数介绍

    Linux 启动参数介绍 取自2.6.18 kernel Documentation/i386/boot.txt 文件中介绍 vga= 这里的不是一个整数(在C语言表示法中,应是十进制,八进制或者十六 ...

  5. Linux实战教学笔记07:Linux系统目录结构介绍

    第七节 Linux系统目录结构介绍 标签(空格分隔):Linux实战教学笔记 第1章 前言 windows目录结构 C:\windows D:\Program Files E:\你懂的\精品 F:\你 ...

  6. ReactiveCocoa常见操作方法介绍/MVVM架构思想

      1.ReactiveCocoa常见操作方法介绍. 1.1 ReactiveCocoa操作须知 所有的信号(RACSignal)都可以进行操作处理,因为所有操作方法都定义在RACStream.h中, ...

  7. Linux core 文件介绍

    Linux core 文件介绍 http://www.cnblogs.com/dongzhiquan/archive/2012/01/20/2328355.html 1. core文件的简单介绍在一个 ...

  8. 嵌入式Linux开发教程:Linux常见命令(上篇)

    摘要:这是对周立功编著的<嵌入式Linux开发教程>的第7期连载.本期刊载内容有关LinuxLinux常见命令中的导航命令.目录命令和文件命令.下一期将连载网络操作命令.安装卸载文件系统等 ...

  9. Linux系统启动过程介绍

    Linux系统启动过程介绍 学习操作系统有必要了解一下系统的启动过程,这样在面对各种系统故障的时候能快速定位解决问题,下面以Centos来分析linux系统的启动过程. 1.BIOS自检:当开机的时候 ...

随机推荐

  1. 一款好用的数据血缘关系在线工具--SQLFlow

      l  数据血缘关系(data lineage) 数据血缘属于数据治理中的一个概念,是在数据溯源的过程中找到相关数据之间的联系,它是一个逻辑概念.数据治理中经常提到血缘分析,血缘分析是保证数据融合的 ...

  2. 阿里云RDS数据库到期实例被清除,别急着哭(阿里没有删库跑路),或许还有一线生机

    阿里资源到期未续费,数据保存期限: ECS实例的保存期是15天. Redis实例的保存期是7天. RDS实例的保存期也是7天. 过期当天会收到一条短信: [阿里云]尊敬的用户:您的RDS实例(实例ID ...

  3. Java集合详解(一):全面理解Java集合

    概述 Java所有集合类都在java.util包下,支持并发的集合在java.util.concurrent(juc)包下. 集合与数组区别: 数组大小是固定的,集合大小可以根据使用情况进行动态扩容. ...

  4. 从零搭建springboot服务01-初始搭建、内嵌swagger

    愿历尽千帆,归来仍是少年 1.基础springBoot框架 编辑工具:IDEA.jdk1.8.tomcat8.maven3.3.9 编码格式:UTF-8 参考文献:https://www.cnblog ...

  5. springboot+Thymeleaf+layui 实现分页

    layui分页插件 引入相关的js和css layui:css <link rel="stylesheet" th:href="@{layui/css/layui. ...

  6. [笔记] 《我的第一本c++书》

    函数 优秀函数的五个要点 函数的返回值:直接返回和间接返回(指针) 在函数的入口处对参数有效性进行检验:if语句,断言(assert) 如果函数有返回值,不可返回一个指向函数体内局部对象的指针或引用 ...

  7. 在linux下把jsp文件 打包war格式(centos7)

    在linux下把jsp文件 打成war包 chmod -R 777 /data/jdk8u242-b08/bincd /data/TongWeb61712/deployment/TestCase//d ...

  8. 使用autotools工具用configure、make、make install编译安装linux工程的详细步骤

    使用autotools工具用configure.make.make install编译安装linux工程的详细步骤 转载tmxkwzy 最后发布于2016-11-24 10:20:15 阅读数 324 ...

  9. Sed常用功能个人整理

    Sed常用功能个人整理 AsdilFibrizo关注 2019.06.24 10:23:41字数 240阅读 15 Sed对1G以下的数据效率很高这里介绍一些个人在工作中遇到的sed问题 1.查找字段 ...

  10. rsync+inotify实现全网自动化数据备份

    第1章 环境配置 实例1-1 服务器及IP主机名规划 已知 4 台服务器主机名主机对应信息见下表: 服务器说明 外网 IP(NAT) 内网 IP(NAT)  主机名 web服务器 10.0.0.7/2 ...