• lv5 嵌入式开发-10 信号机制(下)


    目录

    1 信号集、信号的阻塞

    2 信号集操作函数

    2.1 自定义信号集

    2.2 清空信号集

    2.3 全部置1

    2.4 将一个信号添加到集合中

    2.5 将一个信号从集合中移除

    2.6 判断一个信号是否在集合中

    2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)

    2.8  使进程挂起(暂停执行)直到收到一个信号

    2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来


    掌握:信号集和信号屏蔽

    1 信号集、信号的阻塞

    应用:

    有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延

    时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。

    信号的阻塞概念

    信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

    信号的状态:

    信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)

    信号未决(Pending):从产生到递达之间的状态(挂起)

    下图表示了,每一个任务控制块都有一个挂起信号集(可读)和信号屏蔽字(可写可读)。信号屏蔽置1,代表忽略该信号的到来

    2 信号集操作函数

    2.1 自定义信号集

    sigset_t set; 

    自定义信号集。  是一个  64bit  128bit的数组。

    2.2 清空信号集

    sigemptyset(sigset_t *set);	

    代表接收所有信号

    2.3 全部置1

    sigfillset(sigset_t *set);	

    接收全部信号

    2.4 将一个信号添加到集合中

    sigaddset(sigset_t *set, int signum);	

    接收某一个位的信号

    2.5 将一个信号从集合中移除

    sigdelset(sigset_t *set, int signum);	

    屏蔽某一个位的信号

    2.6 判断一个信号是否在集合中

    sigismember(const sigset_t *set,int signum); 

    2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)

    1. #include
    2. int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );

    参数说明:

    • how:表示如何修改信号屏蔽字的标志,可取以下值:
      • SIG_BLOCK:将set中指定的信号添加到当前信号屏蔽字中。
      • SIG_UNBLOCK:将set中指定的信号从当前信号屏蔽字中移除。
      • SIG_SETMASK:用set中指定的信号集替换当前信号屏蔽字。
    • set:指向要修改的新的信号屏蔽字的指针。如果为NULL,则不会修改信号屏蔽字。
    • oset:用于保存旧的信号屏蔽字的指针。如果为NULL,则不会保存旧的信号屏蔽字。

    函数返回值:

    • 成功:返回0,并将旧的信号屏蔽字保存到oset中(如果oset不为NULL)。
    • 失败:返回-1,并设置errno来指示错误的原因。

    该函数用于在多线程编程、信号处理等场景中控制信号的屏蔽,以保证信号处理的正确性和可靠性。

    示例:

    1. #include
    2. #include
    3. #include
    4. #include
    5. void handle(int sig){
    6. printf("I get sig=%d\n",sig);
    7. }
    8. int main(){
    9. struct sigaction act;
    10. act.sa_handler = handle; //注册回调函数
    11. sigemptyset(&act.sa_mask); //清空信号集,代表接收所有信号
    12. act.sa_flags = 0; //通过将其设置为0,表示不使用任何额外的标志位,即默认行为。
    13. sigaction(SIGINT,&act,NULL); //信号捕获
    14. sigset_t set; //定义信号集
    15. sigemptyset(&set); //清空信号集
    16. sigaddset(&set,SIGINT); //SIGINT,屏蔽SIGINT信号
    17. sigprocmask(SIG_BLOCK,&set,NULL); //ctrl+c的信号阻塞,接收不到
    18. sleep(5);
    19. sigprocmask(SIG_UNBLOCK,&set,NULL); //取消信号阻塞
    20. while(1){
    21. sleep(1);
    22. }
    23. }

    执行效果 :

    2.8  使进程挂起(暂停执行)直到收到一个信号

    int pause(void);

    进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR

    函数行为:

    -1 如果信号的默认处理动作是终止进程,则进程终止,pause函数没有机会返回。

    -2 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回

    -3 如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause返回-1。

    -4 pause收到的信号如果被屏蔽,那么pause就不能被唤醒 

    示例:pause阻塞效果

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main(int argc, char * argv[])
    6. {
    7. pause();
    8. printf("after pause\n");
    9. }
    10. //运行后结果程序阻塞中,未打印after pause,按ctrl+c或ctrl+\结束
    11. linux@linux:~/Desktop$ ./pause

     示例:pause阻塞效果2

    1. #include
    2. #include
    3. #include
    4. #include
    5. void handle(int sig)
    6. {
    7. printf("Handle:I get sig=%d\n",sig);
    8. }
    9. int main(int argc, char * argv[])
    10. {
    11. struct sigaction act;
    12. act.sa_handler = handle;
    13. act.sa_flags = 0;
    14. sigemptyset(&act.sa_mask);
    15. sigaction(SIGINT,&act,NULL);
    16. pause();
    17. printf("after pause\n");
    18. }
    19. //运行结果,未恢复至原处理函数的执行效果
    20. linux@linux:~/Desktop$ ./pause
    21. ^CHandle:I get sig=2
    22. after pause
    23. linux@linux:~/Desktop$

     示例:pause阻塞效果3 ,实现一个等待信号的任务

    1. #include
    2. #include
    3. #include
    4. #include
    5. void handle(int sig)
    6. {
    7. printf("Handle:I get sig=%d\n",sig);
    8. }
    9. void mytask()
    10. {
    11. printf("mytask:start\n");
    12. sleep(3);
    13. printf("mytask:end\n");
    14. }
    15. int main(int argc, char * argv[])
    16. {
    17. struct sigaction act;
    18. act.sa_handler = handle;
    19. act.sa_flags = 0;
    20. sigemptyset(&act.sa_mask);
    21. sigaction(SIGINT,&act,NULL);
    22. pause();
    23. while(1)
    24. {
    25. mytask();
    26. pause(); //每次等待信号到来再执行mytask
    27. }
    28. printf("After pause\n");
    29. }
    30. //运行结果
    31. linux@linux:~/Desktop$ ./pause
    32. ^CHandle:I get sig=2
    33. mytask:start
    34. mytask:end
    35. ^CHandle:I get sig=2
    36. mytask:start
    37. mytask:end
    38. ^CHandle:I get sig=2
    39. mytask:start
    40. mytask:end

    使用kill 发送SIGHUP信号关闭 

      示例:pause阻塞效果4 ,实现一个等待多个信号的任务(SIGINT,SIGHUP)

    1. #include
    2. #include
    3. #include
    4. #include
    5. void handle(int sig)
    6. {
    7. printf("Handle:I get sig=%d\n",sig);
    8. }
    9. void mytask()
    10. {
    11. printf("mytask:start\n");
    12. sleep(3);
    13. printf("mytask:end\n");
    14. }
    15. int main(int argc, char * argv[])
    16. {
    17. struct sigaction act;
    18. act.sa_handler = handle;
    19. act.sa_flags = 0;
    20. sigemptyset(&act.sa_mask);
    21. sigaction(SIGINT,&act,NULL);
    22. sigaction(SIGHUP,&act,NULL);
    23. pause();
    24. while(1)
    25. {
    26. mytask();
    27. pause();
    28. }
    29. printf("After pause\n");
    30. }

    按ctrl+c,或者使用kill -1发送关闭信号都可以捕获信号处理任务。

     示例:使用 sigaddset,屏蔽多个信号

    1. #include
    2. #include
    3. #include
    4. void handle(int sig)
    5. {
    6. printf("Handle:I get sig=%d\n",sig);
    7. }
    8. void mytask()
    9. {
    10. printf("mytask:start\n");
    11. sleep(3);
    12. printf("mytask:end\n");
    13. }
    14. int main(int argc, char * argv[])
    15. {
    16. struct sigaction act;
    17. act.sa_handler = handle;
    18. act.sa_flags = 0;
    19. sigemptyset(&act.sa_mask);
    20. sigaction(SIGINT,&act,NULL);
    21. sigaction(SIGHUP,&act,NULL);
    22. sigset_t set;
    23. sigaddset(&set,SIGHUP);
    24. sigaddset(&set,SIGINT);
    25. pause();
    26. while(1)
    27. {
    28. sigprocmask(SIG_BLOCK,&set,NULL);
    29. mytask();
    30. sigprocmask(SIG_UNBLOCK,&set,NULL);
    31. pause();
    32. }
    33. printf("After pause\n");
    34. }

    执行效果,在任务过程中设置了信号屏蔽,此时按ctrl+c或者kill -1去发送信号,不会执行handle打印,直到任务结束后再开启接收信号,防止任务被打断

    2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来

    设定对信号集内的信号屏蔽并恢复。

    int sigsuspend(const sigset_t *sigmask);

    功能:函数用于临时更改进程的信号屏蔽字,并等待一个特定的信号到来,然后恢复原有的信号屏蔽字。和 pause 函数不同的是,它可以临时更改进程的信号屏蔽字,以便等待某个特定信号。

    参数:

    sigmask:希望屏蔽的信号

    示例:解决任务中的信号丢失问题,sigsuspend相当于sigprocmask+pause

    1. #include
    2. #include
    3. #include
    4. #include
    5. void handle(int sig)
    6. {
    7. printf("Handle:I get sig=%d\n",sig);
    8. }
    9. void mytask()
    10. {
    11. printf("mytask:start\n");
    12. sleep(3);
    13. printf("mytask:end\n");
    14. }
    15. int main(int argc, char * argv[])
    16. {
    17. struct sigaction act;
    18. act.sa_handler = handle;
    19. act.sa_flags = 0;
    20. sigemptyset(&act.sa_mask);
    21. sigaction(SIGINT,&act,NULL);
    22. sigaction(SIGHUP,&act,NULL);
    23. sigset_t set;
    24. sigaddset(&set,SIGHUP);
    25. sigaddset(&set,SIGINT);
    26. sigset_t set2;
    27. sigemptyset(&set2); //接收所有信号
    28. pause();
    29. while(1)
    30. {
    31. sigprocmask(SIG_BLOCK,&set,NULL);
    32. mytask();
    33. //sigprocmask(SIG_UNBLOCK,&set,NULL);
    34. //pause();
    35. sigsuspend(&set2);
    36. }
    37. printf("After pause\n");
    38. }

    运行结果

    1. linux@linux:~/Desktop$ ./pause
    2. ^CHandle:I get sig=2 //1次CTRL+C
    3. mytask:start
    4. mytask:end
    5. ^CHandle:I get sig=2 //1次CTRL+C
    6. mytask:start
    7. mytask:end
    8. ^CHandle:I get sig=2 //1次CTRL+C
    9. mytask:start
    10. ^Cmytask:end //任务中途的1次CTRL+C
    11. Handle:I get sig=2 //也被获取到并执行了
    12. mytask:start
    13. mytask:end

    3 练习

    实现信号回收子进程程序

    提示:SIGCHLD 是一个用于处理子进程状态变化的信号。当一个子进程终止或停止时,它的父进程会收到 SIGCHLD 信号。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. void handle(int sig)
    7. {
    8. wait(NULL);
    9. printf("Get sig =%d\n",sig);
    10. }
    11. int main(int argc, char* argv[])
    12. {
    13. pid_t pid;
    14. struct sigaction act;
    15. act.sa_handler = handle;
    16. act.sa_flags = 0;
    17. sigemptyset(&act.sa_mask);
    18. pid = fork();
    19. if(pid > 0)
    20. {
    21. printf("father process\n");
    22. sigaction(SIGCHLD,&act,NULL);
    23. }
    24. else if(pid == 0)
    25. {
    26. printf("childe process\n");
    27. sleep(5);
    28. exit(0);
    29. }
    30. else
    31. {
    32. perror("fork");
    33. return 0;
    34. }
    35. while(1)
    36. {
    37. sleep(1);
    38. }
    39. return 0;
    40. }

    运行结果:5s后子进程退出,没有产生僵尸进程。

  • 相关阅读:
    anylogic 学习(3)—— 智能体相关操作
    Python数据分析----Numpy介绍与安装
    DLT645-2007智能电表通讯规约 协议读取数据实战
    Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
    Docker入门之安装Tomcat
    oracle数据库禁用触发器
    性能工具之 Kafka 快速 BenchMark 测试示例
    【Python脚本进阶】2.2、构建一个SSH僵尸网络(上):SSH交互脚本
    Linux内核分析(十七)--内存管理之用户栈与内核栈及内核地址空间分布
    RocketMQ实战之Consumer
  • 原文地址:https://blog.csdn.net/m0_60718520/article/details/133427495