Linux 信号处理

2021/10/13 7:14:52

本文主要是介绍Linux 信号处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

信号处理:

基本概念:

    1.中断

        当进程接收到信息后中止当前正在执行的进程,转而去执行其他任务,等其他任务完成后再返回执行原任务,这种执行模式称为中断模式

            可分为:硬件中断和软件中断

    2.信号

        是一种软件中断,由操作系统发出,进程接收后会执行相应的操作

    3.常见的信号

        kill -l 显示出所有的信号

    SIGINT(2)   Ctrl+c          终止

    SIGQUIT(3)  Ctrl+\          终止+core 

    SIGFPE(8)   除以0           终止+core 

    SIGKILL(9)  终止信号        终止

    SIGSEGV(11) 非法访问内存    终止+core 

    4.不可靠信号和可靠信号

        建立在早期信号处理机制上的信号(1~31),是不可靠信号

        不支持排队,可能会丢失信号,同一个信号如果产生多次,进程可能只处理一次

        建立在新的信号处理机制上的信号(34~64),是可靠信号

        支持排队,不会丢失

    5.信号来源

        硬件异常:除0、无效的内存访问、未定义的指令、总线错误

        软件异常:一般通过命令、函数产生的信号

    6.信号的处理方式

        1、忽略

        2、终止进程

        3、终止并产生core文件

        4、捕获并处理信号(在信号来之前,向内核注册一个信号函数,当信号发生后,系统会自动执行注册函数)

信号捕获:

    #include <signal.h>

    typedef void (*sighandler_t)(int);

    功能:信号处理函数的格式,必须遵循

    sighandler_t signal(int signum, sighandler_t handler);

    功能:向内核注册一个绑定某个信号的信号处理函数

    signum:信号编号

    handler:函数指针

        除了直接给函数名外,还可以给以下参数:

            SIG_IGN 忽略

            SIG_DFL 按默认方式处理

    返回值:

        之前的信号处理方式    

    注意:

        1、一个函数可以同时绑定多个信号

        2、个别系统通过signal注册的函数只能执行一次,如果想要持续有效,可以在信号处理函数中再重新注册一次

        3、信号处理完后会返回产生信号的代码处继续执行,如果我们捕获并处理了段错误、算术异常等信号,可能会产生死循环,因此正确地处理段错误、算术异常应该是备份数据并结束进程

        4、子进程会继承父进程的信号处理方式

    练习2:测试一下 1-31 哪些信号不能被捕获处理

        kill函数

        9 19号信号不能被捕获也不能忽略

信号的发送:

    键盘:

        Ctrl+c

        Ctrl+\

        Ctrl+z 暂停\挂起  命令fg唤醒

    错误:

        除0

        非法内存访问

        硬件错误、总线错误

    命令:

        kill -信号 进程号

        killall -信号 进程名 

            可以向同名的多个进程发送同一个信号

    函数:

        int kill(pid_t pid, int sig);

        功能:向指定进程发送指定信号

        pid:进程号

        sig:信号编号

        int raise(int sig);

        功能:向进程自己发送信号sig

        void abort(void);

        功能:向自己发送SIGABRT信号

        unsigned int alarm(unsigned int seconds);

        功能:让内核在seconds秒后向调用者发送SIGALRM信号

        返回值:上一次alarm设置的剩余秒数

        注意:如果再次调用时,会覆盖之前的设置,按照新的设置定时,因此不会产生多次闹钟信号


 

进程休眠信号:

    int pause(void);

    功能:让调用者进入休眠状态,直到进程遇到信号

    返回值:要么一直休眠不返回,要么返回-1

    相当于没有时间限制的sleep

    unsigned int sleep(unsigned int seconds);

    功能:让调用者进入休眠指定的秒数,当遇到信号时会提前返回

    返回值:剩余没睡的秒数

信号集与信号屏蔽

    信号集:是一种数据类型,可以存储多个信号

            sihset_t 128位数,每一位代表了一个信号

    相关函数:

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

            功能:测试在信号集中是否存在某个信号

            返回值:

                0   不存在

                1   存在

                -1  信号非法

    信号阻塞:

        当程序执行一些特殊的操作时,是不适合处理信号的,此时可以让内核先屏蔽信号,等操作执行结束后再继续发送处理信号

        当信号发生时,内核会在其维护的信号表中为进程设置一个与改信号对应的标志位,该过程称为“递送”

        从信号产生到最终完成发送时有一个时间间隔,处于该间隔的信号状态称为“未决”

        信号阻塞就是让信号一直处于未决状态,暂停发送,当屏蔽解除后,信号可以继续发送

        每个进程都有一个信号集专门用于存储需要屏蔽的信号

    

        int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

        功能:设置要屏蔽的信号,这些屏蔽的信号存储再进程内部的信号集中

        how:信号屏蔽的方式

            SIG_BLOCK       把set中的信号添加到要屏蔽的信号集中

            SIG_UNBLOCK     从信号集中删除set中的信号

            SIG_SETMASK     用set替换原来的屏蔽信号集中的信号

        set:准备好的信号集  输入型参数

        oldset:获取旧的屏蔽信号集   输出型参数 不接可以给NULL

定时器:

    #include <sys/time.h>

       int getitimer(int which, struct itimerval *curr_value);

       功能:获取当前的定时方案

       which:选择一个计时器

            ITIMER_REAL         真实计时器      程序总的执行时间            SIGALRM

            ITIMER_VIRTUAL      虚拟计时器      用户态的执行时间            SIGALRM

            ITIMER_PROF         实际计时器      用户态+内核态的执行时间     SIGPROF

            真实计时器=实际计时器+休眠时间+切换时间

        


 

       int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);

        功能:设置一个新的定时方案

    

                struct itimerval {

                    struct timeval it_interval; /* 每次时钟信号产生的间隔时间 */

                    struct timeval it_value;    /* 第一次时钟信号产生的时间 */

                };

                struct timeval {

                    time_t      tv_sec;         /*  秒 */

                    suseconds_t tv_usec;        /* 微秒 */

                };

        注意:如果tv_usec的值超过一秒以上,方案会失败



这篇关于Linux 信号处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程