首页 > 系统相关 >【转】问题:Linux 信号处理,当连续给一个进程同时发送多个信号时,部分信号丢失而未得到处理

【转】问题:Linux 信号处理,当连续给一个进程同时发送多个信号时,部分信号丢失而未得到处理

时间:2022-12-21 11:00:41浏览次数:77  
标签:信号处理 printf SIGINT alive 信号 Linux signal

转:https://blog.csdn.net/SLN2432713617/article/details/89138731

解释信号丢失问题:
阻塞,sigaction 函数有阻塞的功能,比如 SIGINT 信号来了,进入信号处理函数,默认情况下,在信号处理函数未完成之前,如果又来了一个 SIGINT 信号,其将被阻塞,只有信号处理函数处理完毕,才会对后来的 SIGINT 再进行处理,同时后续无论来多少个 SIGINT,仅处理一个 SIGINT,sigaction 会对后续 SIGINT 进行排队合并处理。
原文:https://blog.csdn.net/beginning1126/article/details/8680757

  1. 连续给一个进程发送多个相同信号时,部分信号丢失而未得到处理(使用 signal 函数处理信号),代码如下:
/* test2.c */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

// 信号处理函数使用的全局变量
int count_sig = 0;
void handleSig(int);

void main(){
    printf("main running...\n");
    signal(SIGINT, handleSig);

    // 循环等待信号的到来
    while(1){
        sleep(1);
        printf("alive.\n");
    }
    printf("done.\n");
}

// 信号处理函数
void handleSig(int sig){
    sleep(1);
    count_sig ++;
    printf("got signal. c:%d\n", count_sig);
}

运行程序,执行结果如下(使用键盘向进程连续 发送 3 个终端中断信号,却 只有 2 个信号被处理函数处理 了):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test2 test2.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test2
main running...
^C^C^Cgot signal. c:1     // 连续发送 3 个终端中断信号,却只有 2 个信号被处理
got signal. c:2
alive.
alive.
alive.
alive.
alive.
Killed         // 终止该进程:‘ kill -sigkill 39237 ’

再次运行该程序,结果如下(这次 连续发送更多的信号,并查看输出情况,发现***仅仅 2 个信号被处理***):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test2
main running...
^C^C^C^C^C^C^C^C^C^C^Cgot signal. c:1 // 连续发送更多的信号时,仅仅 2 个信号被处理
got signal. c:2
alive.
alive.
alive.
Killed      // kill 该进程

这是什么原因呢?
可能和 Linux 信号处理机制有关

  1. 实验:在一个信号处理(使用 signal 函数)过程期间,给进程发送另一个不同类型的信号:
/* test4.c */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

int count_sig = 0;
void handleSig(int);

void main(){
    printf("main running...\n");
    signal(SIGINT, handleSig);   // 处理 SIGINT 终端中断信号
    signal(SIGALRM, handleSig);     // 处理 SIGALRM 闹钟信号

    // 循环等待信号
    while(1){
        sleep(1);
        printf("alive.\n");
    }
    printf("done.\n");
}

// 信号处理函数
void handleSig(int sig){
    if(sig == SIGINT){
        // 处理 SIGINT 终端中断信号
        count_sig ++;
        printf("got signal: sigint. c:%d\n", count_sig);
        errno = 0;
        sleep(20);
        printf("got signal: sigint. done, sleep errno code:%d\n", errno);
    }else if(sig == SIGALRM){  
        // 处理 SIGALRM 闹钟信号
        printf("got signal: sigalrm. done\n");
    }
}

执行结果如下(在第一个 信号 SIGINT 处理期间,(发送 SIGALRM 信号,使得当前的信号处理函数被中断)进程转而执行 SIGALRM 信号处理函数,在处理完第二个信号(SIGALRM)后,进程又返回到第一个信号处理函数中断时的位置,但是并没有重入 sleep 函数,而是从 sleep 中立即返回(sleep 函数调用的错误为 4),紧接着继续执行下面的 printf 语句):

其中 sleep 函数的 errno code 错误码为 4 :Interrupted system call(被中断的系统调用),在睡眠期间,被第二个信号(SIGALRM)打断。
从运行结果可以看出,两个信号处理函数交织在一起执行:SIGINT 的处理函数还未运行结束时就收到 SIGALRM 信号,导致函数执行被中断。

  1. 使用 sigaction 函数解决信号处理函数的竞态问题),改写实验 2 中的 signal 函数部分,观察区别,代码如下:
/* test5.c */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

int count_sig = 0;
void handleSig(int);

void main(){
    printf("main running...\n");
    struct sigaction act;
    act.sa_handler = handleSig;
    sigemptyset(&act.sa_mask);
    // 将信号 SIGALRM 加入到进程屏蔽字中,
    // 在 SIGINT 信号处理期间,接收到的 SIGALRM 信号会排队直到 SIGINT 信号处理完毕,
    // !信号屏蔽 不是遗弃信号,被屏蔽的信号会被排队,并在随后得到处理!
    sigaddset(&act.sa_mask, SIGALRM);
    act.sa_flags = SA_RESTART;
    
    sigaction(SIGINT, &act, 0);
    sigaction(SIGALRM, &act, 0);

    while(1){
        sleep(1);
        printf("alive.\n");
    }
    printf("done.\n");
}

void handleSig(int sig){
    if(sig == SIGINT){
        count_sig ++;
        printf("got signal: sigint. c:%d\n", count_sig);
        errno = 0;
        int res = sleep(20);
        printf("got signal: sigint. done, sleep errno code:%d\n", errno);
    }else if(sig == SIGALRM){  
        printf("got signal: sigalrm. done\n");
    }
}

执行结果如下(信号被排队,依次处理):

ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test5 test5.c
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test5main running...
alive.
alive.
^Cgot signal: sigint. c:1
got signal: sigint. done, sleep errno code:0     // sleep 调用未被中断
got signal: sigalrm. done        // SIGINT 信号处理完毕后,SIGALRM 信号得到处理
alive.
alive.
got signal: sigalrm. done        // 单独发送 SIGALRM 信号
alive.
alive.
^Cgot signal: sigint. c:2        // 再次尝试
got signal: sigint. done, sleep errno code:0
got signal: sigalrm. done
alive.
alive.
Killed         // kill 该进程

 

实际运用中,需要对不同到signal设定不同的到信号处理函数,SIG_IGN忽略/SIG_DFL默认,这俩宏也可以作为信号处理函数。同时SIGSTOP/SIGKILL这俩信号无法捕获和忽略。注意,经过实验发现,signal函数也会堵塞当前正在处理的signal,但是没有办法阻塞其它signal,比如正在处理SIG_INT,再来一个SIG_INT则会堵塞,但是来SIG_QUIT则会被其中断,如果SIG_QUIT有处理,则需要等待SIG_QUIT处理完了,SIG_INT才会接着刚才处理。

 

标签:信号处理,printf,SIGINT,alive,信号,Linux,signal
From: https://www.cnblogs.com/ainingxiaoguai/p/16995764.html

相关文章

  • linux教程
    安装谷歌浏览器https://cloud.tencent.com/developer/article/2165028//安装依赖yuminstallredhat-lsb*yum-yinstalllibXss*yuminstalllibappindicator*......
  • Linux 防火墙之TCP Wrappers
    1、TCPWrappers 原理Telnet、SSH、FTP、POP和SMTP等很多网络服务都会用到TCPWrapper,它被设计为一个介于外来服务请求和系统服务回应的中间处理软件。  基本处理过......
  • python 运程连接 linux
    python实现远程连接,操作linux1.安装依赖pip3installparamiko2.实现原理#-*-coding:utf-8-*-importparamikodefconnect(cmd,try_times=3):while......
  • Linux xattr shell command All In One
    LinuxxattrshellcommandAllInOnemacOS$manxattr>xattr.mdmanxattrXATTR(1)GeneralCommandsManualXAT......
  • Linux Shell开发功能点
    背景需要操作一批次服务器安装Docker功能特色一键执行bash<(curl-s-Lhttp://server.com/installDocker.sh)MemberNode参数传递hostname修改if[!-n"$1"......
  • Linux软件安装包中devel与非devel包之间的区别
    带devel(develop)的包,俗称开发包。功能上与普通包相同,但体积更大使用rpm-qi看看这两类包的区别:#rpm-qiglibc-devel-2.12-1.149.el6.x86_64Name:glibc-devel......
  • #yyds干货盘点#Linux使用最频繁的命令:ls 命令
    ls命令介绍ls命令是英文list的简写形式。我们用它来列出文件。它的用法就是在任何目录下直接执行ls,或者在ls后接目录路径(包括相对路径和绝对路径)。当然我们还可以使用......
  • Linux下如何安装MySQL?
    目标主机:centos8MySQL安装所有平台的MySQL下载地址为:https://dev.mysql.com/downloads/repo/yum/。挑选你需要的MySQLCommunityServer版本及对应的平台。注意:安......
  • QT QComboBox信号与槽
    1.QComboBox简介QComboBox就是下拉框,外观如下图所示:2.QComboBox信号在界面设计窗口上随便找一个下拉框控件,鼠标右键->转到槽,可以看到QComboBox支持的信......
  • linux动态库(.so)搜索路径(目录)设置方法
     https://blog.csdn.net/my_angle2016/article/details/121770237 { 在Linux中,动态库的搜索路径除了默认的搜索路径外,还可通过三种方法来指定:方法一:在配置文件/et......