• Linux系统编程系列之线程的信号处理


     Linux系统编程系列(16篇管饱,吃货都投降了!)

            1、Linux系统编程系列之进程基础

            2、Linux系统编程系列之进程间通信(IPC)-信号

            3、Linux系统编程系列之进程间通信(IPC)-管道

            4、Linux系统编程系列之进程间通信-IPC对象

            5、Linux系统编程系列之进程间通信-消息队列

            6、Linux系统编程系列之进程间通信-共享内存

            7、Linux系统编程系列之进程间通信-信号量组

            8、Linux系统编程系列之守护进程

            9、Linux系统编程系列之线程

            10、Linux系统编程系列之线程属性 

            11、Linux系统编程系列之互斥锁和读写锁

            12、Linux系统编程系列之线程的信号处理

            13、Linux系统编程系列之POSIX信号量

            14、Linux系统编程系列之条件变量

            15、Linux系统编程系列之死锁

            16、 Linux系统编程系列之线程池

    一、为什么要有线程的信号处理

            由于多线程程序中线程的执行状态是并发的,因此当一个进程收到一个信号时,那么究竟由进程中的哪条线程响应这个信号就是不确定的,只能取决于哪条线程刚好在信号达到的瞬间被调度,这种不确定性在程序逻辑中一般是不能接受的。

    二、解决办法

            1、在多线程进程中选定某条线程去响应信号

            2、其余线程对该信号进行屏蔽

    三、相关函数API接口

            1、发送信号给指定线程

            

    1. // 在进程内部,只允许在线程之间进行发送
    2. int pthread_kill(pthread_t thread, int sig);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回错误码
    5. 参数thread:接收信号的线程号
    6. 参数sig:待发送的信号
    7. // 在进程之间进行的信号发送
    8. int kill(pid_t pid, int sig);
    9. // 接口说明
    10. 返回值:成功返回0,失败返回-1
    11. 参数pid:接受信号的进程号
    12. 参数sig:待发送的信号

             2、发送带参数的信号给指定线程

    1. // 发送带参数的信号给指定线程
    2. // 线程间
    3. int pthread_sigqueue(pthread_t thread,
    4. int sig,
    5. const union sigval value);
    6. // 接口说明
    7. 返回值:成功返回0,失败返回-1
    8. 参数thread:待接收信号的线程号
    9. 参数sig:待发送的信号
    10. 参数value:额外携带的参数
    11. // 进程间
    12. int sigqueue(pid_t pid, int sig, const union sigval value);
    13. // 接口说明
    14. 返回值:成功返回0,失败返回-1
    15. 参数pid:待接收信号的进程号
    16. 参数sig:待发送的信号
    17. 参数value:额外携带的参数

             3、屏蔽指定信号 

    1. // 屏蔽指定信号
    2. int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回-1
    5. // 参数解析:
    6. 1、how:操作命令字,比如阻塞、解除阻塞等
    7. SIG_BLOCK:阻塞set中的信号(原有正在阻塞的信号保持阻塞)
    8. SIG_SETMASK:阻塞set中的信号(原有正在阻塞的信号自动解除)
    9. SIG_UNBLOCK:解除set中的信号
    10. 2、set:当前要操作的信号集
    11. 3、oldset:若为非空,则将原有阻塞信号集保留到该oldset中
    12. 注意:该函数的操作参数不是单个信号,而是信号集。
    13. // 信号集操作函数组
    14. int sigemptypset(sigset_t *set); // 清空信号集set
    15. int sigfillset(sigset_t *set); // 将所有信号加入信号集set中
    16. int sigaddset(sigset_t *set, int signum); // 将信号signum添加到信号集set中
    17. int sigdelset(sigset_t *set, int signum); // 将信号signum从信号集set中剔除
    18. int sigsimember(const sigset_t *set, int signum); // 测试信号signum是否在信号集set中

    四、案例

            1、使用线程结合信号的方式完成数据的接收和发送,要求一条线程发送数据同时发送信号指定某条线程接收数据,另外有多余线程做伪任务。

    1. // 多线程信号处理的案例
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. char data[100];
    9. pthread_t tid1, tid2, tid3;
    10. // 信号响应函数
    11. void recv_handler(int sig)
    12. {
    13. printf("\nmy tid is %ld\n", pthread_self());
    14. printf("read data: %s\b", data);
    15. memset(data, 0, sizeof(data));
    16. }
    17. // 线程1的例程函数
    18. void *routine1(void *arg)
    19. {
    20. printf("I am recv_routine, my tid = %ld\n", tid1);
    21. // 设置线程分离
    22. pthread_detach(pthread_self());
    23. while(1)
    24. {
    25. printf("please input data:\n");
    26. fgets(data, sizeof(data), stdin);
    27. pthread_kill(tid2, 34); // 给线程2发送信号
    28. printf("send data success\n");
    29. }
    30. }
    31. // 线程2的例程函数,用来接收数据
    32. void *routine2(void *arg)
    33. {
    34. // 注册信号响应函数
    35. signal(34, recv_handler);
    36. printf("I am routine2, my tid = %ld\n", tid2);
    37. // 设置线程分离
    38. pthread_detach(pthread_self());
    39. while(1)
    40. {
    41. pause();
    42. }
    43. }
    44. // 线程3的例程函数
    45. void *routine3(void *arg)
    46. {
    47. printf("I am routine3, my tid = %ld\n", tid3);
    48. // 设置线程分离
    49. pthread_detach(pthread_self());
    50. while(1)
    51. {
    52. pause();
    53. }
    54. }
    55. int main(int argc, char *argv[])
    56. {
    57. // 创建线程1,用来发送和接收数据
    58. errno = pthread_create(&tid1, NULL, routine1, NULL);
    59. if(errno == 0)
    60. {
    61. printf("pthread create routine1 success, tid = %ld\n", tid1);
    62. }
    63. else
    64. {
    65. perror("pthread create routine1 fail\n");
    66. }
    67. // 创建线程2,用来做多余线程
    68. errno = pthread_create(&tid2, NULL, routine2, NULL);
    69. if(errno == 0)
    70. {
    71. printf("pthread create routine2 success, tid = %ld\n", tid2);
    72. }
    73. else
    74. {
    75. perror("pthread create routine2 fail\n");
    76. }
    77. // 创建线程3,用来做多余线程
    78. errno = pthread_create(&tid3, NULL, routine3, NULL);
    79. if(errno == 0)
    80. {
    81. printf("pthread create routine3 success, tid = %ld\n", tid3);
    82. }
    83. else
    84. {
    85. perror("pthread create routine3 fail\n");
    86. }
    87. // 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出
    88. // 或者加上while(1)等让主函数不退出
    89. pthread_exit(0);
    90. return 0;
    91. }

              2、使用线程结合信号的方式完成数据的接收和发送,要求一条线程完成数据的发送和接收,另外两个线程屏蔽信号,做伪任务。

    1. // 多线程信号处理的案例
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. char data[100];
    9. sigset_t sigs_set; // 信号集
    10. pid_t pid;
    11. pthread_t tid1, tid2, tid3;
    12. // 信号响应函数
    13. void recv_handler(int sig)
    14. {
    15. printf("\nmy tid is %ld\n", pthread_self());
    16. printf("read data: %s\b", data);
    17. memset(data, 0, sizeof(data));
    18. }
    19. // 线程1的例程函数
    20. void *routine1(void *arg)
    21. {
    22. printf("I am routine1, my tid = %ld\n", tid1);
    23. // 设置线程分离
    24. pthread_detach(pthread_self());
    25. while(1)
    26. {
    27. printf("please input data:\n");
    28. fgets(data, sizeof(data), stdin);
    29. printf("send data success\n");
    30. kill(pid, 34); // 给进程(所有线程)发送信号
    31. }
    32. }
    33. // 线程2的例程函数,用来接收数据
    34. void *routine2(void *arg)
    35. {
    36. printf("I am routine2, my tid = %ld\n", tid2);
    37. // 屏蔽(阻塞)信号集中的信号
    38. sigprocmask(SIG_BLOCK, &sigs_set, NULL);
    39. // 设置线程分离
    40. pthread_detach(pthread_self());
    41. while(1)
    42. {
    43. pause();
    44. }
    45. }
    46. // 线程3的例程函数
    47. void *routine3(void *arg)
    48. {
    49. printf("I am routine3, my tid = %ld\n", tid3);
    50. // 设置线程分离
    51. pthread_detach(pthread_self());
    52. // 屏蔽(阻塞)信号集中的信号
    53. sigprocmask(SIG_BLOCK, &sigs_set, NULL);
    54. while(1)
    55. {
    56. pause();
    57. }
    58. }
    59. int main(int argc, char *argv[])
    60. {
    61. // 注册信号响应函数
    62. signal(34, recv_handler);
    63. sigemptyset(&sigs_set); // 清空信号集
    64. sigaddset(&sigs_set, 34); // 把34信号加到信号集中
    65. pid = getpid(); // 获取进程号
    66. // 创建线程1,用来发送和接收数据
    67. errno = pthread_create(&tid1, NULL, routine1, NULL);
    68. if(errno == 0)
    69. {
    70. printf("pthread create routine1 success, tid = %ld\n", tid1);
    71. }
    72. else
    73. {
    74. perror("pthread create routine1 fail\n");
    75. }
    76. // 创建线程2,用来做多余线程
    77. errno = pthread_create(&tid2, NULL, routine2, NULL);
    78. if(errno == 0)
    79. {
    80. printf("pthread create routine2 success, tid = %ld\n", tid2);
    81. }
    82. else
    83. {
    84. perror("pthread create routine2 fail\n");
    85. }
    86. // 创建线程3,用来做多余线程
    87. errno = pthread_create(&tid3, NULL, routine3, NULL);
    88. if(errno == 0)
    89. {
    90. printf("pthread create routine3 success, tid = %ld\n", tid3);
    91. }
    92. else
    93. {
    94. perror("pthread create routine3 fail\n");
    95. }
    96. // 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出
    97. // 或者加上while(1)等让主函数不退出
    98. pthread_exit(0);
    99. return 0;
    100. }

    五、总结

            多线程进程中的信号处理可以采用选定某一条线程来接收信号,其余线程屏蔽该信号的做法,可以结合案例加深对多线程中信号的处理。

  • 相关阅读:
    推荐系统 学习笔记
    JS原型对象prototype
    mybatis源码探索之代理封装阶段
    msssql子查询列传行(stuff() 与for xml path())
    572. 另一棵树的子树
    Ubuntu Linux下安装 TensorFlow等开发环境
    【汇总】剑指 offer Ⅱ 1 ~ 40 题笔记
    【PHP代码审计】——开启你的代码审计生涯
    全量和已占用字符集
    STC15单片机-整合代码,完成软件设计
  • 原文地址:https://blog.csdn.net/AABond/article/details/133418970