• 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);


  • 相关阅读:
    Linux下磁盘监控及系统版本-CPU-内存等查看
    Linux目录结构详解
    Linux常用命令
    SecureCRT或XShell软件
    JMeter打开脚本失败 如何解决?
    JMeter常见错误解决方法
    Linux安装 火速入门
    浅谈我对持续集成的理解
    操作Frame和IFrame中页面元素
    弹出对话框的处理
  • 原文地址:https://www.cnblogs.com/qsz805611492/p/9367877.html
Copyright © 2020-2023  润新知