信号分类

不可靠信号

Linux信号机制基本上是从UNIX系统中继承过来的。早期UNIX系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是:

1.进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。

2.因此导致, 早期UNIX下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。

Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux下的不可靠信号问题主要指的是信号可能丢失。

可靠信号

随着时间的发展,实践证明,有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种UNIX版本分别在这方面进行了研究,力图实现"可靠信号"。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号(SIGRTMIN ~ SIGRTMAX),并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()。

sigaction和signal函数都是调用内核服务do_signal函数;[内核服务函数,应用程序无法调用该函数]

早期UNIX系统只定义了31种信号,而Linux 3.x支持64种信号,编号1-64(SIGRTMIN=34,SIGRTMAX=64),将来可能进一步增加,这需要得到内核的支持。 前31种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL+C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于可靠信号。这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,可用于应用进程。

非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

信号API-信号发送(1)

1.kill

int kill(pid_t pid, int signo); 

kill既可以向自身发送信号,也可以向其他进程发送信号

signo参数组合情况解释

pid>0 将信号sig发给pid进程

pid=0 将信号sig发给同组进程

pid=-1 将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身)

pid<-1 将信号sig发送给进程组是pid(绝对值)的每一个进程

//示例
void onSignalAction(int signalNumber)
{switch(signalNumber){case SIGUSR1:cout << "SIGUSR1 = " << signalNumber << endl;break;default:cout << "Other Signal ..." << endl;break;}
}int main()
{if (signal(SIGUSR1,onSignalAction)== SIG_ERR){perror("signal error");return -1;}pid_t pid = fork();if (pid == -1){perror("fork error");return -1;}else if (pid == 0){/**向父进程发送信号pid_t ppid = getppid();kill(ppid,SIGUSR1);*//**向同组所有进程发送信号,子进程也会收到该信号kill(0,SIGUSR1);*///向本组所有进程发送信号,作用同上
//getpgrp()函数获取进程组pidpid_t pgid = getpgrp();killpg(pgid,SIGUSR1);exit(0);}int sleepTime = 3;while (sleepTime > 0){write(STDOUT_FILENO,"Parent start Sleep...\n",sizeof("Parent start Sleep...\n"));sleepTime = sleep(sleepTime);write(STDOUT_FILENO,"Parent return from Sleep...\n",sizeof("Parent return from Sleep...\n"));}return 0;
}

注意:如果在fork之前安装信号,则子进程可以继承信号。

Sleep遇上signal,子进程向父进程发送信号,sleep函数的几点说明

1)sleep函数作用,让进程睡眠。

2)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码

3)sleep函数的返回值,是剩余的秒数

Man手册显示:

RETURN VALUE

Zero if the requested time has elapsed, or the number of  seconds  left to sleep,

if the call was interrupted by a signal handler.

//示例:sleep遇上signal
void onSignalAction(int signalNumber)
{switch(signalNumber){case SIGINT:cout << "SIGINT = " << signalNumber << endl;break;default:cout << "Other Signal ..." << endl;break;}
}int main()
{if (signal(SIGINT,onSignalAction)== SIG_ERR){perror("signal error");return -1;}cout << "Main Start Sleeping..." << endl;int returnValue = sleep(100); //可中断睡眠cout << "Main End Sleeping... returnValue = " << returnValue << endl;return 0;
}
//示例:sleep加强
int main()
{
//...同上
cout << "Main Start Sleeping..." << endl;
//sleep加强版^^int sleepTime = 20;do{sleepTime = sleep(sleepTime);
cout << "continue..." << endl;}while (sleepTime > 0);cout << "Main End Sleeping... sleepTime = " << sleepTime << endl;return 0;
}

2.raise

int raise(int sig);

给自己发送信号。raise(sig)等价于kill(getpid(), sig);

3.killpg

int killpg(int pgrp, int sig);

给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig);

4.sigqueue

int sigqueue(pid_t pid, int sig, const union sigval value);

给进程发送信号,支持排队,可以附带信息。

信号API-pause

int pause(void);

将进程置为可中断睡眠状态。然后它调用内核函数schedule(),使Linux进程调度器找到另一个进程来运行。

pause使调用者进程挂起,直到一个信号被捕获

//示例
int main()
{if (signal(SIGINT,handler)== SIG_ERR)err_exit("signal error");while(true){pause();cout << "pause return..." << endl;}
}

信号API-信号发送(2)

unsigned int alarm(unsigned int seconds);

alarm函数,设置一个闹钟延迟发送SIGALRM信号(告诉Linux内核n秒中以后,发送SIGALRM信号);

手册描述-DESCRIPTION

alarm() arranges for a SIGALRM signal to be delivered to the process in seconds seconds.

If seconds is zero, no new alarm() is scheduled.

In any event any previously set alarm() is cancelled.

//alarm 递归调用
void onSignalAction(int signalNumber)
{switch(signalNumber){case SIGALRM:cout << "SIGALRM = " << signalNumber << endl;alarm(1);  //继续调用onSignalActionbreak;default:cout << "Other Signal ..." << endl;break;}
}int main()
{if (signal(SIGALRM,onSignalAction)== SIG_ERR){perror("signal error");return -1;}alarm(1);while(true){pause();cout << "pause returned..." << endl;}return 0;
}

可重入/不可重入函数

所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可重入函数在信号处理函数中被视为不安全函数。

为了增强程序的稳定性,在信号处理函数中应使用可重入函数。

//不可重入函数示例
struct Teacher
{int a;int b;int c;int d;
};Teacher g_teacher;
void onSigAlarm(int signo)
{printf("%d %d", g_teacher.a, g_teacher.b);printf(" %d %d\n", g_teacher.c, g_teacher.d);alarm(1);
}int main()
{if (signal(SIGALRM,onSigAlarm)== SIG_ERR)err_exit("signal error");Teacher zero = {0, 0, 0, 0};Teacher ones = {1, 1, 1, 1};alarm(1);g_teacher = zero;while(true){g_teacher = zero;g_teacher = ones;}
}

输出结果演示:

原因分析:

可以将语句g_teacher = zero分解为:

g_teacher.a = zero.a;

g_teacher.b = zero.b;

g_teacher.c = zero.c;

g_teacher.d = zero.d;

因此, 在这四条语句执行的中间, 如果此时SIGALRM信号到达(中断到达), 则g_teacher中的一些数据会是新值, 而有些却是以前留下的脏值, 究其原始则是g_teacher = zero不是原子操作, 而信号处理函数onSigAlarm却又访问了全局变量g_teacher.

如果将两条printf封装成一个函数

void unsafe_function()
{printf("%d %d", g_teacher.a, g_teacher.b);printf(" %d %d\n", g_teacher.c, g_teacher.d);
}

然后在onSigAlarm中调用, 则unsafe_function函数就成了不可重入函数(其实printf就是不可重入函数), 因此, 在信号响应函数中, 尽量不要调用不可重入函数;

不可重入函数

满足下列条件的函数多数是不可重入的:

(1)使用静态数据结构,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;

(2)函数实现时,调用了malloc()或者free()函数;

(3)实现时使用了标准I/O函数

附-man 7 signal可以查看那些函数是可重入和不可重入的.

Linux信号实践(2) --信号分类相关推荐

  1. Linux信号实践(4) --可靠信号

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

  2. Linux信号实践(1) --Linux信号编程概述

    中断 中断是系统对于异步事件的响应, 进程执行代码的过程中可以随时被打断,然后去执行异常处理程序; 计算机系统的中断场景:中断源发出中断信号 -> CPU判断中断是否屏蔽屏蔽以及保护现场 -&g ...

  3. Linux信号实践(3) --信号内核表示

    信号在内核中的表示 执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未 ...

  4. Linux信号实践(5) --时间与定时器

    三种不同精度的睡眠 1.sleep #include <unistd.h> unsigned int sleep(unsigned int seconds); RETURN VALUE Z ...

  5. linux信号(signal) 机制分析

    1       信号本质 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件.在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的. ...

  6. 非常好的一篇对linux信号(signal)的解析

    [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执 ...

  7. Linux 信号学习

    Linux 信号学习 信号量的基本概念 信号产生的条件 信号如何被处理 信号的异步特质 信号的分类 可靠信号/不可靠信号 实时信号/非实时信号 常见信号与默认行为 信号处理 `signal()` 函数 ...

  8. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever  sailing_9806@163.com 转载请注明 http://blog.csdn.net/sailor_8318/archive/2008 ...

  9. 【Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever sailing_9806@163.com转载请注明 http://blog.csdn.net/sailor_8318/archive/2008/0 ...

最新文章

  1. Activity has leaked window that was originally added
  2. spring aop设计模式_Spring框架中设计模式的运用
  3. Go语言命令行工具介绍-3
  4. docker~aspnetcore2.0镜像缺少libgdiplus问题
  5. MSRCRGIMP(基于GIMP版本的多尺度Retinex)
  6. Git 将本地项目上传到Github
  7. python简易版猜单词游戏_Python实现简单的猜单词小游戏
  8. 基于OpenGL编写一个简易的2D渲染框架-11 重构渲染器-Renderer
  9. 圆周率π前百万位,完整版显示
  10. ACM/ICPC World Finals 2012 B Curvy Little Bottles
  11. usb扫描枪驱动下载 wince_常用扫描枪驱动
  12. python josn数据解析
  13. html优秀作品展示,31个漂亮的作品展示网页设计
  14. OC5128欧创芯原装芯片,开关降压型恒流驱动芯片
  15. 笔记本电脑触摸板手势命令
  16. winhex常用快捷键
  17. JS - 4 - 数组 Array - API(slice、splice、shift、)
  18. lio linux工具,ISCSI (简体中文)/LIO (简体中文)
  19. 2011大纽约区域赛试题 Decoding EDSAC Data 解题报告
  20. SpringBoot Zxing _ Java 生成二维码(可内嵌图片)

热门文章

  1. 55 - I. 二叉树的深度
  2. USACO-Section1.3 Milking Cows (区间问题)
  3. office卸载工具、安装工具
  4. CTF中Crypty入门必看(密码类,密码学)
  5. python 中的 del 使用方法
  6. 2. OD-爆破exe验证程序
  7. ORB-SLAM介绍(无源码版本)
  8. C/C++:Windows编程—Windows RPC 传递自定义数据类型、自定义数据类型数组、指针数组
  9. opencart卸载语言包要记得在后台进行设置否则会出错
  10. SybaseASE系统表的应用