关于 信号signal的知识铺垫 点这里

信号由三种处理方式:

  1. 忽略
  2. 执行该信号的默认处理动作
  3. 捕捉信号

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个自定义函数,这称为捕捉信号

进程收到一个信号后不会被立即处理,而是在恰当时机进行处理!即内核态返回用户态之前 !

但是由于信号处理函数的代码在用户空间,所以这增加了内核处理信号捕捉的复杂度。

内核实现信号捕捉的步骤:

  1. 用户为某信号注册一个信号处理函数sighandler。
  2. 当前正在执行主程序,这时候因为中断、异常或系统调用进入内核态。
  3. 在处理完异常要返回用户态的主程序之前,检查到有信号未处理,并发现该信号需要按照用户自定义的函数来处理。
  4. 内核决定返回用户态执行sighandler函数,而不是恢复main函数的上下文继续执行!(sighandler和main函数使用的是不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程)
  5. sighandler函数返回后,执行特殊的系统调用sigreturn从用户态回到内核态
  6. 检查是否还有其它信号需要递达,如果没有 则返回用户态并恢复主程序的上下文信息继续执行。

signal

给某一个进程的某一个信号(标号为signum)注册一个相应的处理函数,即对该信号的默认处理动作进行修改,修改为handler函数指向的方式;

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

signal函数接受两个参数:一个整型的信号编号,以及一个指向用户定义的信号处理函数的指针。  

此外,signal函数的返回值是一个指向调用用户定义信号处理函数的指针。

sigaction

sigaction函数可以读取和修改与指定信号相关联的处理动作。

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction
{
void (*sa_handler)(int); //信号处理方式
void (*sa_sigaction)(int, siginfo_t *, void *); //实时信号的处理方式 暂不讨论
sigset_t sa_mask; //额外屏蔽的信号
int sa_flags;
void (*sa_restorer)(void);
};

signum是指定信号的编号。

处理方式:

  1. 若act指针非空,则根据act结构体中的信号处理函数来修改该信号的处理动作。
  2. 若oact指针非 空,则通过oact传出该信号原来的处理动作。
  3. 现将原来的处理动作备份到oact里,然后根据act修改该信号的处理动作。

(注:后两个参数都是输入输出型参数!)

将sa_handler三种可选方式:

  1. 赋值为常数SIG_IGN传给sigaction表示忽略信号;
  2. 赋值为常数SIG_DFL表示执行系统默认动作;
  3. 赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。

(注:这是一个回调函数,不是被main函数调用,而是被系统所调用)

  当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。

pause

pause函数使调用进程挂起直到有信号递达!

#include <unistd.h>
int pause(void);

处理方式: 

  • 如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;
  • 如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;
  • 如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR。

所以pause只有出错的返回值(类似exec函数家族)。错误码EINTR表示“被信号中断”。

举个栗子

  1. 定义一个闹钟,约定times秒后,内核向该进程发送一个SIGALRM信号;
  2. 调用pause函数将进程挂起,内核切换到别的进程运行;
  3. times秒后,内核向该进程发送SIGALRM信号,发现其处理动作是一个自定义函数,于是切回用户态执行该自定义处理函数;
  4. 进入sig_alrm函数时SIGALRM信号被自动屏蔽,从sig_alrm函数返回时SIGALRM信号自动解除屏蔽。然后自动执行特殊的系统调用sigreturn再次进入内核,之后再返回用户态继续执行进程的主控制流程(main函数调用的mytest函数)。

  5. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理 动作。

/*************************************************************************
> File Name: Pause.c
> Author:Lynn-Zhang
> Mail: iynu17@yeah.net
> Created Time: Sun 14 Aug 2016 12:27:03 PM CST
************************************************************************/ #include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_alarm(int signum)
{
printf("I am a custom handler!\n");
}
void mysleep(unsigned int times)
{
//注册两个信号处理动作
struct sigaction new,old;
new.sa_handler=sig_alarm; //信号处理函数
sigemptyset(&new.sa_mask);//不屏蔽任何信号屏蔽字
new.sa_flags=0; //对SIGALRM 信号的默认处理动作修改为自定义处理动作
sigaction(SIGALRM,&new,&old);
alarm(times);
pause(); //挂起等待
alarm(1);
sleep(2);
alarm(0); //取消闹钟
//恢复SIGALRM 信号到默认处理动作
sigaction(SIGALRM,&old,NULL);
alarm(1);
sleep(2);
}
int main()
{
while(1)
{
mysleep(2);
printf("many seconds passed\n");
printf("###################\n");
}
return 0;
}

    

定义一个闹钟并挂起等待,收到信号后执行自定义处理动作,在没有恢复默认处理动作前,收到SIGALRM信号都会按照其自定义处理函数来处理。恢复自定义处理动作之后收到SIGALRM信号则执行其默认处理动作即终止进程!

Linux下捕捉信号的更多相关文章

  1. linux 下信号处理命令trap && linux下各种信号的意义

    1.用途说明 trap是一个shell内建命令,它用来在脚本中指定信号如何处理.比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出 ...

  2. linux信号Linux下Signal信号太详细了,终于找到了

    linux信号Linux下Signal信号太详细了,终于找到了 http://www.cppblog.com/sleepwom/archive/2010/12/27/137564.html

  3. Linux 改进捕捉信号机制(sigaction,sigqueue)

    sigaction函数 sigaction函数的功能是用于改变进程接收到特定信号后的行为. int sigaction(int signum, const struct sigaction *act, ...

  4. linux下 signal信号机制的透彻分析与各种实例讲解

    转自:http://blog.sina.com.cn/s/blog_636a55070101vs2d.html 转自:http://blog.csdn.net/tiany524/article/det ...

  5. Linux下signal信号汇总

    SIGHUP /* Hangup (POSIX). */ 终止进程 终端线路挂断 SIGINT /* Interrupt (ANSI). */ 终止进程 中断进程 Ctrl+C SIGQUIT /* ...

  6. Linux下的信号机制

    2017-04-06 之前在看LinuxThreads线程模型的时候,看到该模型是通过信号实现线程间的同步,当时没有多想,直接当做信号量了,现在想起来真是汗颜……后来想想并不是那么回事,于是,就有了今 ...

  7. Linux下异常信号

    我们介绍一些标准信号的名称以及它们代表的事件.每一个信号名称是一个代表正整数的宏,但是你不要试图去推测宏代表的具体数值,而是直接使用名称.这是因为这个数值会随不同的系统或同样系统的不同版本而不同,但是 ...

  8. Linux下的信号详解

    文章链接:https://blog.csdn.net/qq_38646470/article/details/80257512

  9. Windows与Linux下进程间通信技术比较

    一般我们写的程序都是以单个进程的方式来运行的,比较少涉及到多进程.特别是在windows下,因为Windows是按照线程来分配CPU时间片的,线程是最小的调度单位,所以在Windows下更多的用到多线 ...

随机推荐

  1. 【Python】GUI 练习1--利率计算器

    import sys from PyQt4.QtCore import * from PyQt4.QtGui import * class Form(QDialog): def __init__(se ...

  2. VC++ 判断你的窗口是否置顶TopMost

    大家可能已经知道,使你的窗口置顶(TopMost)或者总是最前(Always on Top)的方法:  C++ Code  12345   // Make topmost , SWP_NOMOVE | ...

  3. Mysql数据库存储是乱码问题(或者在查询时无法加载数据)

    在连接数据库时添加一行代码即可解决:?useUnicode=true&characterEncoding=utf8 截图如下:

  4. Toxophily-数论以及二分三分

    G - Toxophily Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submi ...

  5. 第三课补充01——set类型 sorted类型命令操作详解,redis管道及事务

    1. set类型的命令操作: (1)sadd命令:向key指定的set集合添加成员 ##sadd命令:是设置set集合类型的数据,sadd  <key> <mumber> [& ...

  6. document.cookie = 'wcookie_date=' + wv + ';max-age=60'

    js cookie生命周期

  7. Visualizing mathematical functions by generating custom meshes using FireMonkey(很美)

    Abstract: This article discusses how you can generate your own 3-dimensional mesh for visualizing ma ...

  8. git "Could not read from remote repository.Please make&n

    git "Could not read from remote repository.Please make sure you have the correct access rights. ...

  9. Linux使用SecureCRT上传下载

    操作远程 Linux 系统,很多时候选用 SecureCRT 软件,在 SecureCRT 环境下,使用 lrzsz 工具可以很方便的完成文件的上传下载. 这里使用的 Ubuntu Linux 安装: ...

  10. 序列化组件之生成hypermedialink

    一  生成hypermedialink(极少数) 组件 class BooksSerializer(serializers.ModelSerializer): name = serializers.C ...