1.信号的概念

在这里要给出一个信号的准确概念感觉很困难,可以这么说,信号就是进程之间或者内核与进程间异步通信的一种机制,有点类似于中断的性质。在  linux  系统中有  31  种信号,每一种信号都以  SIG  三个字母开头,例如  SIGABRT  是夭折信号,就是调用  abort  函数产生的信号,SIGALRM  是调用  alarm  函数定时时间溢出后产生的信号。

对每一个信号,系统有三种处理方式:

1.忽略这种信号,就是对这种信号不做任何处理。但是,对于  SIGKILL  和  SIGSTOP  信号,却不能忽略。因为这两种信号,通常被操作系统用来终止失去控制的进程。
2.捕捉该信号。当一个进程收到该信号时,就调用相应的信号处理函数,来对该信号做出相应的动作。
3.按系统默认方式处理信号,一般的默认动作是终止程序。

2.signal函数

当需要捕捉某种信号时,需要用到  signal  函数来注册处理该信号的函数。这个  signal  函数比较特殊,它的返回值是一个函数指针,这个函数指针指向之前处理该信号的函数。它有两个参数,第一个参数用来指定信号,第二个参数用来指定处理该信号的函数。它的函数原型如下:

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

举一个简单的  signal  的使用例子如下:

#include <signal.h>
#include <stdio.h>static void sig_usr(int signo);int main(void)
{if(signal(SIGUSR1,sig_usr)==SIG_ERR){printf("signal(SIGUSR1) error\n");return -1;}if(signal(SIGUSR2,sig_usr)==SIG_ERR){printf("signal(SIGUSR2) error\n");return -1;}for(;;){pause();}return 0;
}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);}
}

将程序编译为  a.out  之后,用下面的命令在后台运行:

./a.out &

在终端上输出程序的PID如下:

[1] 6212

我们输入如下命令:

kill -SIGUSR1 6212

可以看到程序输出

received SIGUSR1

3.可再入函数

在信号处理的过程中会产生一些问题,例如,当在  main  函数中正在调用  malloc  函数在堆上动态分配内存空间的时候,产生了一个信号,需要去执行这个信号的信号处理函数。而在这个信号处理函数中,也需要调用  malloc  函数,这时候就有可能发生问题。因为  malloc  为它分配的存储区保存一个链接表,而如果在main函数处理这张链接表时,在信号处理函数调用了  malloc,就使进程遭到了破坏。

再举一个例子,如果在  main  函数中,刚刚调用完  getpwnam  函数,这时候来了一个信号,需要去执行信号处理函数,在这个函数中也许要调用  getpwnam  函数,这就造成了  main  函数调用  getpwnam  得到的信息丢失。

通过上面的两个例子产生的问题,linux  规定了一个可再入函数表,这个表中列举了在信号处理函数中可以调用的函数。当然这张表中肯定没有包括  malloc  函数和  getpwnam  函数,这些函数有另一个名字,叫不可再入函数。

就算对于可再入函数,我们知道每一个进程只有一个  errno ,如果在  main  函数中设定了 errno  的值以后,如果在信号处理程序中调用的某个可再入函数也可能会修改了  errno  的值。所以要求我们在信号处理程序前保存现场,在信号处理程序要结束时,恢复现场。

4.kill和raise以及几个术语

kill函数用来向指定的进程或者进程组发送信号,raise函数用来向进程自己发送信号。进程只能向和它所有者相同的进程发送信号,当然超级用户进程可以向各个进程发送信号。

信号的产生:当造成信号的事件发生时,为进程产生一个信号。

当产生了信号以后,内核会在进程表中设置某种形式的标记,这个过程称为信号递送。

在信号产生和信号递送这个时间,称为信号未决。

5.alarm函数和pause函数

alarm  函数用来定时一段时间,当定时时间溢出时,会产生  SIGALRM  信号。pause  函数用来使进程挂起,直到捕捉到一个信号,并且等待执行完信号处理函数之后,pause  函数才会返回  -1  。它们的函数原型如下:

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

alarm  函数的参数单位是秒,它的返回值是上一次调用  alarm  时剩余的时间。当参数  seconds  为  0  时,如果上次的定时时间还未到,则取消上次的定时,这是一个很常用的功能。

alarm  和  pause  函数可以用来实现sleep函数,如下:

#include <signal.h>
#include <stdio.h>static void sig_alrm(int signo)
{return;
}unsigned int sleep1(unsigned int nsecs)
{if(signal(SIGALRM,sig_alrm)==SIG_ERR){printf("signal(SIGALRM) error\n");return nsecs;}alarm(nsecs);pause();return (alarm(0));
}int main(void)
{printf("before sleep1\n");sleep1(2);printf("after sleep1\n");return 0;
}

在这个实现的函数中存在着一个竞争条件,当执行完  alarm(nsecs)  之后,可能还没有执行  pause  时,定时时间已经到了。这时执行  sig_alrm  信号处理函数,之后再执行  pause,如果系统中再没有信号产生,这就会使进程一直挂起。虽然这种情况极少发生,但是这总是一个  bug,当它发生错误时,会很难查找。可以用  setjmp  和  longjmp  来消除这个潜在的bug:

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>static jmp_buf env_buf;static void sig_alrm(int signo)
{longjmp(env_buf,1);
}unsigned int sleep2(unsigned int nsecs)
{if(signal(SIGALRM,sig_alrm)==SIG_ERR){printf("signal(SIGALRM) error\n");return nsecs;}if(setjmp(env_buf)==0){alarm(nsecs);pause();}return (alarm(0));
}int main(void)
{printf("before sleep1\n");sleep2(2);printf("after sleep1\n");return 0;
}

这样,即使在还没有执行  pause  时,时间就已经溢出了,也不会发上上面的情况。alarm  函数还用来为会阻塞的操作定时,防止它们进入永久阻塞的状态。这个功能类似于看门狗的功能,主要防止程序跑飞。

linux信号(一)--unix环境高级编程读书笔记相关推荐

  1. 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  2. linux信号(二)--unix环境高级编程读书笔记

    1.信号集 在linux中,可以用一个称为信号集的数据类型  sigset_t,来表示所有的被阻塞信号的一个集合.对这个集合的操作函数有: #include <signal.h>int s ...

  3. linux进程控制(一)--unix环境高级编程读书笔记

    1.进程PID和特殊的3个进程 每一个进程在系统中都有一个唯一的标识,这个标识叫做进程标识符,或者叫  PID(process identity).我们可以通过调用  getpid  函数来获取一个进 ...

  4. linux系统数据文件和信息--unix环境高级编程读书笔记

    linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...

  5. unix进程的环境--unix环境高级编程读书笔记

    1.进程的启动 进程总是从   main   函数开始执行的,main函数的函数原型如下: int main(int argc,char* argv[]); 当内核启动  c   程序时,使用一个   ...

  6. 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  7. 高级IO(一)--UNIX环境高级编程读书笔记

    在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...

  8. 标准IO库--unix环境高级编程读书笔记

    标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...

  9. APUE Unix环境高级编程读书笔记

    .. 转载于:https://www.cnblogs.com/solitrarychen/p/5407536.html

最新文章

  1. jstat -gcutil 输出结果分析_JVM故障分析
  2. ios绘图基本图形之线条
  3. 恭喜我的同事黄玉奇入选开放原子开源基金会TOC
  4. 【Android UI设计与开发】7.底部菜单栏(四)PopupWindow 实现显示仿腾讯新闻底部弹出菜单...
  5. Python连接MySQL及一系列相关操作
  6. POJ - 1384 Piggy-Bank(完全背包)
  7. python nodemcu_python开发nodemcu2(跑马灯实验)
  8. html支持1080p,支持1080p全高清 实战英特尔WiDi 2.0
  9. 测试面试题 用例设计题目回答思路
  10. 高一计算机课期中考试总结反思,高一期中考试总结反思1300字
  11. 阿里云服务器防火墙的问题
  12. 售前笔记(三)——PPT方案
  13. 打开U盘 提示 服务器无法运行,win10打开u盘提示“系统资源不足 无法完成请求的服务”怎么办...
  14. MFC: DeviceIoControl 通过API访问设备驱动程序
  15. 银行板块行情发令枪已打响12月7日天弘中证银行ETF发售1天
  16. 管你 JDK 还是 Linux,我 Netty 稳坐钓鱼台
  17. C++ libco 介绍与应用
  18. 视觉惯性里程计 VIO
  19. 《洋娃娃和小熊跳舞》吉他简谱
  20. 智能小车之PWM脉冲控制小车调速

热门文章

  1. WPF--ContextMenu绑定命令的一个问题
  2. [转载] Python中filter筛选函数匿名参数问题
  3. [转载] 快速入门(完整):Python实例100个(基于最新Python3.7版本)
  4. CF EDU - E. Lomsat gelral 树上启发式合并
  5. python进阶之装饰器之5把装饰器作用到类和静态方法上
  6. CRC 冗余校验计算
  7. Moment.js 一款JS时间封装库
  8. 第3章 控制程序流程(1)
  9. WIZnet推出串口转以太网模块WIZ550S2E
  10. Fastreport 分组多列排序问题