【问题标题】:Signal handler won't see global variable信号处理程序不会看到全局变量
【发布时间】:2013-03-08 04:58:32
【问题描述】:

问题是:这个程序应该从标准输入接收输入并计算插入的字节数; SIGUSR1 信号将停止主程序并在文件标准错误上打印当我发送 SIGUSR1 时复制了多少字节。

这就是我的老师希望我这样做的方式:在一个终端运行

cat /dev/zero | ./cpinout | cat >/dev/null

同时从第二个终端发送信号

kill -USR1 xxxx

其中 xxxx 是 cpinout 的 pid。

我更新了之前的代码:

/* cpinout.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define BUF_SIZE 1024   

volatile sig_atomic_t countbyte = 0;
volatile sig_atomic_t sigcount = 0;

/* my_handler: gestore di signal */
static void sighandler(int signum) {
    if(sigcount != 0)
        fprintf(stderr, "Interrupted after %d byte.\n", sigcount);
    sigcount = countbyte;    
}

int main(void) {
  
    int c;
    char buffer[BUF_SIZE];
    struct sigaction action;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = sighandler;
    if(sigaction(SIGUSR1, &action, NULL) == -1) {
        fprintf(stderr, "sigusr: sigaction\n");
        exit(1);
    }
    while( c=getc(stdin) != EOF ) {
        countbyte++;
        fputc(c, stdout);
    }
    return(0);
}

【问题讨论】:

  • 即使使用 sigaction 也无法工作 >.

标签: c signals scope signal-handling


【解决方案1】:

根据 C89 和 POSIX 7 标准,信号只能写入 volatile sig_atomic_t 变量:

如果信号处理程序引用除 errno [Option End] 以外的任何对象 [CX] [Option Start] 且具有静态存储持续时间,而不是通过将值分配给声明为 volatile sig_atomic_t 的对象,则行为未定义

实现通常提供更多,但我怀疑使用非易失性全局变量或 printf 是您提供的。

【讨论】:

    【解决方案2】:


    编辑

    在您提到的 cmets 中,您将命令运行为:

    cat /dev/zero | ./namefile | cat &gt;/dev/null

    行为实际上很好。 /dev/zero 是无穷无尽的零流,正在发送到程序。所以它会很快地把它们计算出来。当你打断它时,它会停止,你会得到一个很大的数字。


    问题可能与在更新全局变量时可能调用信号处理程序有关(如果这需要多条指令)。但是,GNU 文档声明可以安全地假设 int 在 POSIX 系统上始终是原子的。

    我能想到的唯一另一种可能性是你在循环中调用fputc,在处理程序中使用printf(但是如果没有被调用,在处理程序中调用printf应该是安全的由程序)。尝试从循环中删除fputc,看看它是否可以解决问题。

    编辑:

    这似乎可以解释问题。这与可以从信号处理程序中安全调用的函数类型有关:

    如果函数使用静态数据结构,它们也可以是不可重入的 用于他们的内部簿记。最明显的例子 函数是 stdio 库的成员(printf()、scanf()、 等等),它会更新缓冲 I/O 的内部数据结构。 因此,当在信号处理程序中使用 printf() 时,我们可以 有时会看到奇怪的输出——甚至是程序崩溃或数据 损坏——如果处理程序在中间中断主程序 执行对 printf() 或另一个 stdio 函数的调用。 (Linux 编程接口

    您的程序正在中断一个 stdio 函数,这似乎非常适合。


    这是另一种方法:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <signal.h>
    
    int countbyte = 0;  // for main program
    int sigcount = 0;   // for signal handler
    
    /* my_handler: signal handler */
    static void sighandler(int signum)
    {
       sigcount = countbyte;
    }
    
    int main(void)
    { 
       int c;
       struct sigaction sigact;
    
       sigemptyset(&sigact.sa_mask);
       sigact.sa_flags = 0;
       sigact.sa_handler = sighandler;
       sigaction(SIGUSR1, &sigact, NULL);
       while ((c = getc(stdin)) != EOF) {
          countbyte++;
          fputc(c, stdout);
       }
       if (sigcount != 0) {
          printf("Interrupted after %d bytes\n", sigcount);
       }
    
       return 0;
    }
    

    【讨论】:

    • 您能提示一下为什么您认为信号和信号之间的差异在这里是相关的吗?
    • @AProgrammer - 文档指出“signal() 的行为因 UNIX 版本而异,并且在历史上因 Linux 的不同版本而异。避免使用它:改用 sigaction(2) ... signal() 的唯一可移植用途是将信号的处置设置为 SIG_DFL 或 SIG_IGN。使用 signal() 建立信号处理程序时的语义因系统而异(POSIX.1 明确允许这种变化); 请勿将其用于此目的。
    • 是的,unix 变体之间的行为存在差异,我知道与报告的问题无关。
    • @AndreaMazzocchi - 你的操作系统是什么?
    • Linux Xubuntu 12.10 3.5.0-23-generic
    猜你喜欢
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多