UNIX环境C语言--信号

一、基本概念
1、中断:中止、暂停当前正在执行的进程,转而去执行其他的任务
  硬中断:来自硬件设备的中断
  软中断:来自其他程序的中断

2、信号:信号是一种软中断,可以把他看作是进程与进程、内核与进程通信的一种方式,它为进程的异步执行,提供了技术支持

3、常见的信号
  SIGINT(2) 终端中断符信号 用户按中断键(Ctrl+C),产生此信号,并送至前台进程组的所有进程 终止
  SIGQUIT(3) 终端退出符信号 用户按退出键(Ctrl+\),产生此信号,并送至前台进程组的所有进程 终止+core
  SIGABRT(6) 异常终止信号 调用abort函数,产生此信号 终止+core
  SIGFPE(8) 算术异常信号 表示一个算术运算异常,例如除以0、浮点溢出等 终止+core
  SIGKILL(9) 终止信号 不能被捕获或忽略。常用于杀死进程 终止
  SIGSEGV(11) 段错误信号 试图访问未分配的内存,或向没有写权限的内存写入数据 终止+core
  SIGALRM(14) 闹钟信号 以alarm函数设置的计时器到期,或以setitimer函数设置的间隔时间到期,均产生此信号 终止
  SIGCHLD(17) 子进程状态改变信号 在一个进程终止或停止时,将此信号发送给其父进程 忽略
  SIGCONT(18) 使停止的进程继续 向处于停止状态的进程发送此信号,令其继续运行 继续/忽略
  SIGSTOP(19) 停止信号 不能被捕获或忽略。停止一个进程 停止进程
  SIGTSTP(20) 终端停止符信号 用户按停止键(Ctrl+Z),产生此信号,并送至前台进程组的所有进程 停止进程

4、不可靠的信号(非实时)
  a、编号小于SIGRGMI(34)的信号都是不可靠的,这些信号是建立在早期的信号机制上的,一个事件的发生可能会产生多次信号
  b、不可靠信号不支持排除,在接收信号的时候可能会丢失,如果一个信号发送给进程多次,它可能只接收到一次,其它的可能就丢失了
  c、进程在处理这种信号的时候,哪怕设置的信号处理函数,当信号处理函数执行完毕后,会再次恢复成默认的信号处理方式

5、可靠信号(实时)
  a、位于[SIGRGMI(34),SIGRTMAX(64)]区间的都是可靠信号
  b、可靠信号支持排队,不会丢失
  c、无论是可靠信号还是不可靠信号都是通过:kill、signal、sigqueue、sigaction进行处理

6、信号的来源
  硬件来源:
    键盘:Ctrl + c、Ctrl + \、Ctrl + z
    驱动:硬件设备被激活、使用、失效
    内存:非法访问内存
  软件来源:
    命令:kill、killall
    函数:kill/raise/alarm/setitimer/

7、信号的处理方式
  a、忽略
  b、终止
  c、终止+core
    core dump 把内存的使用情况仍出来
    core文件是一种二进制文件,需要一些高度工具才能解析出来(gdb)
      1、gcc -g core.c -> 生成带调试信息的可执行文件
      2、运行可执行文件
      3、gdb ./a.out core 程序会停止在产生错误的位置
      ubuntu默认不产生core文件,需要使用命令设置:ulimit -c unlimited
  d、捕获并处理

二、信号的捕获并处理 //signal.c
  #include <signal.h>

  typedef void (*sighandler_t)(int);

  sighandler_t signal(int signum, sighandler_t handler);
  功能:向内核注册一个信号处理函数
    signum:信号的编号,可以直接写数字,也可以使用系统提供的宏
    handler:信号的处理方式
      函数指针
      SIG_IGN 忽略信号
      SIG_DFL 恢复信号默认的处理方式
      返回值:是之前信号的处理方式
      函数指针、SIG_IGN、SIG_DFl、SIG_ERR

    1、在有些系统中向内核注册的信号处理函数只执行一次(在执行前就被恢复成默认的处理方式),如果想持续处理信号,可以在每次的处理函数结束时再次注册
    2、SIGKILL、SIGSTOP不能被捕获也不能被忽略di
    3、普通用户只能给自己的进程发信号,root用户可以给任意进程发送信号

三、子进程的信号处理
  1、通过fork创建的子进程会继承父进程的信号处理方式 //fork_signal.c
  2、通过vfork+exec创建的子进程不会继承父进程的信号处理方式,会恢复成默认的


四、信号的发送
  1、键盘
    Ctrl+c 终端中断信号
    Ctrl+z 终端暂停信号,fg命令再次开启
    Ctrl+\ 终端退出信号
  2、错误产生的信号
    除0
    非法内存访问
    硬件总线
  3、命令产生的信号
    kill -信号 进程号
    killall -信号 程序名(杀死所有同名的进程)
  4、函数产生的信号
    int kill(pid_t pid, int sig);
    功能:向指定的进程发送信号
      pid:与waitpid一样
      sig:信号码
        0表示空信号,不会向进程发送信号,但是会测试是否能向pid发送信号,这样可以检测一个进程是否存在,返回-1表示进程不存在,errno为ESRCH
      返回值:-1,说明进程不存在

    int raise(int sig);
    功能:向自己发送信号

五、pause
  #include <unistd.h>
  int pause(void);
  功能:休眠

1、进程调用了pause函数后会入睡眠状态,直到有信号把它叫醒(不被忽略的信号)
2、当信号来临后,先执行信号处理函数,信号处理函数结束后pause再返回
3、pause函数要么不返回(一直睡眠),要么返回-1,并且修改errno的值
4、从功能上来讲它相当于没有时间限制的sleep函数

六、sleep
  #include <unistd.h>
  unsigned int sleep(unsigned int seconds);
  功能:使用调用的进程睡眠seconds秒
1、调用sleep的进程如果没有睡眠足够的秒数,除非收到信号才会返回
2、sleep的返回值是0,或剩余睡眠的妙数
3、相当于有时间限制的pause

  int usleep(useconds_t usec);
  功能:睡眠usec微秒
  1秒=1000毫秒=1000000微秒
  它是一种更精确的睡眠函数

七、alarm
  #include <unistd.h>
  unsigned int alarm(unsigned int seconds);
  功能:定时一个闹钟信号
1、让内核向调用它的进程,在secongds秒后发送一个时钟信号SIGALRM信号
2、SIGALRM信号的默认处理方式是直接退出


八、信号集和信号屏蔽
1、信号集:
多个信号的集合,sigset_t
由128个二进制位组成,每个二进制位表示一个信号
#include <signal.h>
int sigemptyset(sigset_t *set);
功能:清空信号集

int sigfillset(sigset_t *set);
功能:填满信号集

int sigaddset(sigset_t *set, int signum);
功能:从信号集中添加信号

int sigdelset(sigset_t *set, int signum);
功能:从信号集中删除信号

int sigismember(const sigset_t *set, int signum);
功能:测试信号集中是否有某个信号
返回值:有返回1,没有返回0,失败返回-1

2、屏蔽信号集中的信号
每个进程都有一个信号掩码(signal mask),它就是一个信号集,里面包含了进程所屏蔽的信号
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:设置进程的信号掩码(信号屏蔽码)
how:修改信号掩码的方式
SIG_BLOCK:向信号掩码中添加信号
SIG_UNBLOCK:从信号掩码中删除信号
SIG_SETMASK:用新的信号集替旧的信号掩码
newset:新添加、删除、替换的信号集,也可以为空
oldset:获取旧的信号掩码
当newset为空时,就是在备份信号掩码

当进程执行一些敏感操作时不希望被打扰(原子操作),此时需要屏蔽信号
屏蔽信号的目的不是为了不接收信号,而是延迟接收,当处理完要做的事情后,应该把屏蔽的信号还原
当信号屏蔽时发生的信号会记录一次,这个信号设置为末决状态,当信号屏蔽结束后,会再发送一次
不可靠信号在信号屏蔽期间无论信号发送多少次,信号解除屏蔽后,只发送一次
可靠信号在信号屏蔽期间发生的信号会排队记录,在信号接触屏蔽后逐个处理
在执行信号处理函数时,会默认把当前处理的信号屏蔽掉,执行完成后再恢复

int sigpending(sigset_t *set);
功能:获取未决状态的信号

九、信号处理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 *); //信号处理函数指针 需要使用sigqueue发送信号
sigset_t sa_mask; //信号屏蔽码
int sa_flags;
SA_NOCLDSTOP:忽略SIGCHLD
SA_NODEFER/SA_NOMASK:处理时不屏蔽信号
SA_RESETHAND:处理完信号后,恢复默认处理方式
SA_RESTART:当信号处理函数中断的系统调用,则重启
SA_SIGINFO:用sa_sigaction处理信号
void (*sa_restorer)(void);//保留
};

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


猜你喜欢

转载自www.cnblogs.com/qsz805611492/p/9367877.html
今日推荐