• 信号发送与处理-上


    问题

    按下 Ctrl + C 后,命令行中的前台进程会被终止。为什么???

    什么是信号?

    信号是一种 "软件中断",用来处理异步事件

    • 内核发送信号到某个进程,通知进程事件的发生
    • 事件可能来自硬件,可能来自用户输入,可能来自除零错误

    信号是一种类型的进程间通信方式 (一个进程向另一个进程发送信号)

    • A 进程发生事件 T,向 B 进程发送信号,B 进程执行动作响应事件
    • 进程可以对接收到的不同信号进行不同响应动作 (信号 => 处理)

    信号的分类

    硬件异常

    • 内核检测到硬件错误,发送相应信号给相关进程

    终端信号

    • 在终端输入 "特殊字符" 等价于向前台进程组发送相应信号

    软件信号

    • 在软件层面 (进程代码中) 触发的信号 (发送给自身或其他进程)

    硬件异常信号

    终端相关信号

    SIGINT ( Ctrl + C )

    • 程序终止信号,用于通知前台进程组终止进程

    SIGQUIT ( Ctrl + \ )

    • 与 SIGINT 类似,进程收到该信号退出时可产生 coredump 文件

    SIGTSTP ( Ctrl + Z )

    • 停止进程的运行,进程收到该信号后可以选择处理或忽略

    软件相关信号

    子进程退出:父进程收到 SIGCHLD 信号

    父进程退出:子进程可能收到信号 (什么信号?)

    定时器到期:alarm(),ualarm(),timer_create(),...

    主动发信号:kill(),raise(),...

    内核与信号

    信号的默认处理

    知识加油站:System V  vs  BSD

    System V

    • 也被称为 AT&T System V,是 Unix 操作系统众多版本中的一支

    BSD

    • 加州大学伯克利分校开创,Unix 衍生系统,代表由此派生出的各种套件集合

    自定义信号处理

    #include

    #include

    typedef void(*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);

    sighandler_t sysv_signal(int signum, sighandler_t handler);

    sighandler_t bsd_signal(int signum, sighandler_t handler);

    信号处理示例

    自定义信号发送

    #include

    #include

    int kill(pid_t pid, int sig); // pid > 0, pid == 0, pid < -1

    int raise(int sig); // 信号处理完毕才返回

    kill 函数当 pid > 0 时,将信号发给指定进程;当 pid == 0 时,将信号发送到当前的进程组中;当 pid < -1 时,将信号发送给指定的进程组中

    raise 函数将信号发送给当前进程

    信号发送示例

    信号处理与发送实验

    main.c

    1. // #define _GNU_SOURCE
    2. // #define _XOPEN_SOURCE 600
    3. // #define _POSIX_C_SOURCE 200800L
    4. #include <stdio.h>
    5. #include <stdlib.h>
    6. #include <sys/types.h>
    7. #include <unistd.h>
    8. #include <signal.h>
    9. void signal_handler(int sig)
    10. {
    11. printf("handler : sig = %d\n", sig);
    12. }
    13. int main(void)
    14. {
    15. signal(SIGINT, signal_handler);
    16. signal(40, signal_handler);
    17. // sysv_signal(SIGINT, signal_handler);
    18. // bsd_signal(SIGINT, signal_handler);
    19. while( 1 )
    20. {
    21. sleep(1);
    22. }
    23. return 0;
    24. }

    第 19 行和第 20 行,我们为 SIGINT 信号和 信号值为 40 的信号,注册信号处理函数,信号处理函数为 signal_handler

    第 20 行和第 21 行被注释掉的 sysv_signal 函数和 bsd_signal 函数的作用在这边的和 signal 函数的作用是一样的,都是在 SIGINT 信号或信号值为 40 的信号触发时,调用 signal_handler 函数

    程序运行结果如下图所示:

    我们键入 Ctrl + C时 内核会发送 SIGINT 信号给当前前台进程组的进程,SIGINT 信号的默认行为是终止进程的运行,由于在这里我们为 SIGINT 信号注册了信号处理函数,所以会调用到 signal_handler 函数打印 SIGINT 的信号值,最后我们键入 Ctrl + \,内核会发送 SIGQUIT 信号给当前前台进程组的进程,SIGQUIT 信号的默认行为也是终止进程的运行,由于我们没有改变 SIGQUIT 信号的处理行为,所以 a.out 进程就被终止运行了

    test.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main(int argc, char* argv[])
    7. {
    8. int pid = atoi(argv[1]);
    9. int sig = atoi(argv[2]);
    10. printf("send sig(%d) to process(%d)...\n", sig, pid);
    11. kill(pid, sig);
    12. raise(SIGINT);
    13. while( 1 )
    14. {
    15. printf("while...\n");
    16. sleep(1);
    17. }
    18. return 0;
    19. }

    第 15 行,使用 kill 函数向指定进程发送指定信号

    第 17 行,使用 raise 函数向自身发送 SIGINT 信号

    程序运行结果如下图所示

    test.out 执行后向 a.out 进程发送信号值为 40 的信号,然后给自己发送 SIGINT 信号终止了运行,a.out 进程收到信号值为 40 的信号后,由于注册了对应的信号函数,会调用 signal_handler 这个信号处理函数,打印出来的信号值为 40,是 test.out 发送过来的信号

    思考

    三种自定义信号处理函数有什么区别???

  • 相关阅读:
    实战模拟│JWT 登录认证
    linux的man命令
    5. Mongodb 面试题
    字节数组转成BLOB对象(不连接数据库)
    关于javaScript的学习总结
    【MySQL】mysql中有哪几种类型的备份技术?它们各自有什么优缺点?
    SQL练习 2022/7/2
    2023.11.19使用flask制作一个文件夹生成器
    GitHub 标星 120K 的 Java 面试知识点总结,真就物超所值了
    图书管理系统用户手册
  • 原文地址:https://blog.csdn.net/qq_52484093/article/details/134270297