• 【Linux】信号


    1. //myproc.cc
    2. #include
    3. #include
    4. using namespace std;
    5. int main()
    6. {
    7. while(1)
    8. {
    9. sleep(1);
    10. }
    11. return 0;
    12. }

    信号捕捉 signal

    1. #include<iostream>
    2. #include<unistd.h>
    3. #include<signal.h>
    4. using namespace std;
    5. void handler(int signo)
    6. {
    7. cout<<"捕捉到的信号:" <<signo<<endl;
    8. }
    9. int main()
    10. {
    11. //只有当SIGINT(ctrl+c)产生的时候才会调用handler函数
    12. signal(SIGINT,handler);
    13. while(true)
    14. {
    15. cout<<"还未捕捉到SIGINT"<<endl;
    16. sleep(1);
    17. }
    18. return 0;
    19. }

     

    我们可以使用2号信号(ctrl+c)、3号信号(ctrl+\)、9号信号(kill -9)来终止进程,其中9号信号是不能被捕捉,永远都是默认处理方式

     自己实现一个kill命令

    1. #include<iostream>
    2. #include<unistd.h>
    3. #include<stdlib.h>
    4. #include<cstring>
    5. #include<sys/types.h>
    6. #include<signal.h>
    7. #include<signal.h>
    8. using namespace std;
    9. void handler(int signo)
    10. {
    11. cout<<"捕捉到的信号:" <<signo<<endl;
    12. }
    13. static void Usage(const string &proc)
    14. {
    15. cerr << "Usage:\n\t" << proc << "signo pid" << endl;
    16. }
    17. // mykill 9 1234
    18. int main(int argc, char *argv[])
    19. {
    20. if(argc != 3)
    21. {
    22. Usage(argv[0]);
    23. exit(1);
    24. }
    25. if(kill( static_cast<pid_t>(atoi(argv[2])), atoi(argv[1])) == -1)
    26. {
    27. cerr << "kill:" << strerror(errno) << endl;
    28. exit(2);
    29. }
    30. return 0;
    31. }

    raise()

    给自己发送一个信号

    abort()

    终止进程,发送6号信号,可以被捕捉但是还是会终止

     alarm()

    将14号信号在seconds秒后发给该进程

    进程崩溃的本质是该进程收到了异常信号

    1. #include<iostream>
    2. #include<signal.h>
    3. #include<unistd.h>
    4. using namespace std;
    5. void handler(int sig)
    6. {
    7. cout << "进程:"<< getpid() << "收到一个信号 :" << sig << endl;
    8. exit(1);
    9. }
    10. int main()
    11. {
    12. for(int i = i ;i <= 32; i++)
    13. {
    14. signal(i,handler);
    15. }
    16. int a = 10;
    17. a/=0; //0错误
    18. return 0;
    19. }

    1. #include<iostream>
    2. #include<signal.h>
    3. #include<unistd.h>
    4. using namespace std;
    5. void handler(int sig)
    6. {
    7. cout << "进程:"<< getpid() << "收到一个信号 :" << sig << endl;
    8. exit(1);
    9. }
    10. int main()
    11. {
    12. for(int i = i ;i <= 32; i++)
    13. {
    14. signal(i,handler);
    15. }
    16. int* p = nullptr;
    17. *p = 23; //空指针错误
    18. return 0;
    19. }

     man 7 signal

     Action表是接收到信号后执行的动作,Term表示终止,Core表示除了终止外,还会生成一个core文件,这个文件在云服务器上是默认关闭的,需要设置才能打开 

    对于一些代码内部原因而产生的错误一般都会有Core文件,这个文件可以帮助我们定位错误,方便调试

    内核结构

     阻塞表示拦截信号(区别忽略,忽略是处理中的一种),当信号被处理后,未决中的1变为0

    信号集操作函数

    上图的中阻塞和未决标志是通过位图来实现的,但是用户层不允许直接使用位操作,所以提供了信号集操作,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态

    1. #include
    2. int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit清零
    3. int sigfillset(sigset_t *set);//初始化set所指向的信号集,使其中所有信号的对应bit变为1
    4. int sigaddset (sigset_t *set, int signo);//在特定的信号集中,加入特定的信号
    5. int sigdelset(sigset_t *set, int signo);//在特定的信号集中,去掉特定的信号
    6. int sigismember(const sigset_t *set, int signo); //查看一个信号是否在该信号集中

    sigprocmask

    调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)

    1. #include
    2. int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    3. 返回值:若成功则为0,若出错则为-1

    how的可选值

    sigpending

    int sigpending(sigset_t *set)

    获取当前进程的pending信号集

    1. #include<iostream>
    2. #include<signal.h>
    3. #include<unistd.h>
    4. #include<sys/types.h>
    5. #include<wait.h>
    6. using namespace std;
    7. void handler(int sig)
    8. {
    9. cout << "进程:"<< getpid() << " 收到一个信号 :" << sig << endl;
    10. exit(1);
    11. }
    12. //打印pending信号集
    13. void showPending(sigset_t* pendings)
    14. {
    15. for(int sig = 1; sig < 32; sig++)
    16. {
    17. if(sigismember(pendings,sig))
    18. {
    19. cout << "1";
    20. }
    21. else{
    22. cout << "0";
    23. }
    24. }
    25. cout << endl;
    26. }
    27. int main()
    28. {
    29. cout << "pid: " << getpid() << endl;
    30. sigset_t blockSig, o_blockSig;
    31. sigemptyset(&blockSig);
    32. sigemptyset(&o_blockSig);
    33. //sigfillset()
    34. for(int sig = 1; sig <= 31;sig++)
    35. {
    36. sigaddset(&blockSig,sig);
    37. signal(sig,handler);
    38. }
    39. sigprocmask(SIG_SETMASK, &blockSig, &o_blockSig);
    40. //1.不断的获取当前进程的pending信号集
    41. sigset_t pendings;
    42. int cnt = 0;
    43. while(true)
    44. {
    45. //1.1 清空信号集
    46. sigemptyset(&pendings);
    47. if(sigpending(&pendings) == 0)
    48. {
    49. showPending(&pendings);
    50. }
    51. sleep(1);
    52. cnt++;
    53. if(cnt == 20)
    54. {
    55. cout << "解除对所有信号的block"<<endl;
    56. sigset_t sigs;
    57. sigemptyset(&sigs);
    58. sigaddset(&sigs, 2);
    59. sigprocmask(SIG_UNBLOCK, &sigs,nullptr);
    60. }
    61. }
    62. return 0;
    63. }

    进程信号是从内核态转为用户态的时候处理的

    sigaction

      

    1. #include<iostream>
    2. #include<unistd.h>
    3. #include<signal.h>
    4. using namespace std;
    5. void handler(int signo)
    6. {
    7. cout << "进程:" << getpid() <<"获取到一个信号: "<< signo << endl;
    8. sigset_t pending;
    9. while(true)
    10. {
    11. sigpending(&pending);
    12. cout<<"pending :";
    13. for(int i = 1; i <= 31; i++)
    14. {
    15. if(sigismember(&pending,i))
    16. {
    17. cout << '1';
    18. }
    19. else
    20. {
    21. cout << '0';
    22. }
    23. }
    24. }
    25. }
    26. int main()
    27. {
    28. struct sigaction act, oact;
    29. act.sa_handler =handler;
    30. act.sa_flags = 0;
    31. sigemptyset(&act.sa_mask);
    32. sigaddset(&act.sa_mask,3);
    33. sigaction(2,&act,&oact);
    34. while(true)
    35. {
    36. cout <<" main running" << endl;
    37. sleep(1);
    38. }
    39. return 0;
    40. }

     volatile

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int flag = 0;
    6. void handler(int sig)
    7. {
    8. flag = 1;
    9. cout<< "flag: 0->1"<
    10. }
    11. int main()
    12. {
    13. signal(2,handler);
    14. while(flag != 1);
    15. printf("进程退出\n");
    16. return 0;
    17. }

     优化程度高的编译器会把flag放入寄存器,屏蔽了CPU对内存的可见性,而在handler中对flag的修改会放回到内存中,但是CPU没去内存读取,进程没法退出

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. //进制编译器优化,保持对内存的可见性
    6. volatile int flag = 0;
    7. void handler(int sig)
    8. {
    9. flag = 1;
    10. cout<< "flag: 0->1"<
    11. }
    12. int main()
    13. {
    14. signal(2,handler);
    15. while(flag != 1);
    16. printf("进程退出\n");
    17. return 0;
    18. }

    SIGCHLD

     子进程退出的时候会给父进程发送SIGCHLD信号

    1. #include<iostream>
    2. #include<unistd.h>
    3. #include<signal.h>
    4. using namespace std;
    5. void handler(int signo)
    6. {
    7. cout<<"进程:" << getpid() << "收到一个信号 :" << signo << endl;
    8. }
    9. int main()
    10. {
    11. pid_t id = fork();
    12. if(id == 0)
    13. {
    14. //子进程
    15. sleep(1);
    16. exit(1);
    17. }
    18. else
    19. {
    20. //父进程
    21. signal(SIGCHLD,handler);
    22. sleep(5);
    23. }
    24. return 0;
    25. }

     在父进程等待子进程的过程中,每次都是父进程主动询问,而有了这个信号,父进程只需等这个信号过了,然后回收僵尸进程即可

    1. #include <iostream>
    2. #include <cstdlib>
    3. #include <unistd.h>
    4. #include <sys/wait.h>
    5. #include <sys/types.h>
    6. #include <signal.h>
    7. #include <cassert>
    8. using namespace std;
    9. void FreeChld(int signo)
    10. {
    11. assert(signo == SIGCHLD);
    12. while (true)
    13. {
    14. // waitpid 什么时候调用失败呢?如果你已经没有子进程了
    15. // -1: 等待任意一个子进程
    16. pid_t id = waitpid(-1, nullptr, WNOHANG); // bug??
    17. if (id > 0)
    18. {
    19. cout << "父进程等待成功, chld pid: " << id << endl;
    20. }
    21. else if(id == 0)
    22. {
    23. //还有子进程,但是现在没有退出
    24. cout << "还有子进程,但是现在没有退出, 父进程要去忙自己的事情了" << endl;
    25. break;
    26. }
    27. else
    28. {
    29. cout << "父进程等待所有子进程结束" << endl;
    30. break;
    31. }
    32. }
    33. }
    34. int main()
    35. {
    36. // signal(SIGCHLD, FreeChld);
    37. // 子进程退出的时候,默认的信号处理就是忽略吗?
    38. // 调用signal/sigaction SIG_IGN, 意义在哪里呢?
    39. // SIG_IGN手动设置,让子进程退出,不要给父进程发送信号了,并且自动释放
    40. signal(SIGCHLD, SIG_IGN);
    41. for (int i = 0; i < 10; i++)
    42. {
    43. pid_t id = fork();
    44. if (id == 0)
    45. {
    46. //子进程
    47. int cnt = 10;
    48. // if (i < 7)
    49. // cnt = 5;
    50. // else
    51. // cnt = 20;
    52. while (cnt)
    53. {
    54. cout << "我是子进程, pid: " << getpid() << " 当前的cnt: " << cnt-- << endl;
    55. sleep(1);
    56. }
    57. cout << "子进程退出,进入僵尸状态" << endl;
    58. exit(0);
    59. }
    60. // sleep(1);
    61. }
    62. while (true)
    63. {
    64. cout << "我是父进程,我正在运行: " << getpid() << endl;
    65. sleep(1);
    66. }
    67. // //父进程,都是要自己主动等待
    68. // if(waitpid(id, nullptr, 0) > 0)
    69. // {
    70. // cout << "父进程等待子进程成功" << endl;
    71. // }
    72. return 0;
    73. }

    用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。

  • 相关阅读:
    大数据毕业设计选题推荐-污水处理大数据平台-Hadoop-Spark-Hive
    数据分析三剑客之一:Pandas详解
    上周热点回顾(2.20-2.26)
    论文阅读笔记 | 三维目标检测——SECOND算法
    用户中心框架搭建
    Maya v2024(3D动画制作软件)
    【分享】“有赞商城“ 在集简云平台集成应用的常见问题与解决方案
    React 基础学习
    网络中使用最多的图片格式有哪些
    Mac电脑版鼠标连点工具 RapidClick for Mac
  • 原文地址:https://blog.csdn.net/holle_world_ldx/article/details/127861572