You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言管道信号处理实现的正确性验证问询

信号处理与命名管道实现问题排查

问题背景

外部应用Exchange会创建两个命名管道pe_exchange_<N>和pe_trader_<N>(<N>由命令行参数传入),我的程序Trader需要完成以下操作:

  • 从pe_exchange_<N>管道读取Exchange发送的内容(最大50字节)
  • 读取完成后,向作为父进程的Exchange发送SIGUSR1信号
  • 向pe_trader_<N>管道写入消息"Hello"

预期流程:

Exchange → Trader:Hi There!
Trader → Exchange:Hello

但当前代码既无法检测到SIGUSR1信号,也无法打印Exchange发送的消息,以下是我的实现代码:

void signal_handler(int SIG);

volatile sig_atomic_t g_sigint_flag = 0;
volatile sig_atomic_t g_sigusr1_flag = 0;
int fd1;
int fd2;

void exchange_signal_handler(int signo);

int main(int argc, char ** argv) {

    char exchange[100];
    char trader[100];

    sprintf(exchange, "/tmp/pe_exchange_%s", argv[1]);
    sprintf(trader, "/tmp/pe_trader_%s", argv[1]);

    fd1 = open(exchange,O_RDONLY);
    fd2 = open(trader,O_WRONLY);
    if (fd1== -1) {
        perror("open pipe failed");
        return 1;
    }

    if (fd2== -1) {
        perror("open pipe failed");
        return 1;
    }

    if (signal(SIGINT, signal_handler) == SIG_ERR) {
        printf("Unable to register signal handler for SIGINT\n");
        return 1;
    }

    if (signal(SIGUSR1, exchange_signal_handler) == SIG_ERR) {
        printf("Unable to register exchange signal handler for SIGUSR1\n");
        return 1;
    }

    pid_t exchange_pid = getppid();

    while (1) {
        if (g_sigusr1_flag) {
            g_sigusr1_flag = 0;
            printf("This is working partially");// Reset the flag
        }
        sleep(1);
    }

    close(fd2);
    close(fd1);
    return 0;
}

void signal_handler(int SIG) {
    if (SIG == SIGINT) {
        printf("Terminated\n");
        g_sigint_flag = 1;
    }
}

void exchange_signal_handler(int signo) {
    if (signo == SIGUSR1) {
        printf("Received SIGUSR1 signal from exchange\n");
        g_sigusr1_flag = 1;
    }
}

问题分析与修正

你的代码存在几个核心问题:

1. 完全缺失管道读取逻辑

代码里没有任何从pe_exchange_<N>管道读取数据的代码,根本不会处理Exchange发送的消息,自然也无法触发后续的信号发送和回复操作。

2. 信号逻辑完全搞反

根据需求,应该是Trader读取到管道数据后,主动向Exchange发送SIGUSR1,但你当前的代码是在等待Exchange向Trader发SIGUSR1,完全和需求相悖。

3. PID获取的潜在问题

如果Exchange不是Trader的直接父进程,getppid()会拿到错误的PID,导致信号发送失败。如果Exchange会通过管道传递自身PID,或者需要其他方式获取正确PID,需调整这部分逻辑。

修正后的代码示例

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

volatile sig_atomic_t g_sigint_flag = 0;
int fd_exchange;  // 读取Exchange消息的管道
int fd_trader;    // 向Exchange发送消息的管道
pid_t exchange_pid;

void signal_handler(int signo) {
    if (signo == SIGINT) {
        printf("Terminated\n");
        g_sigint_flag = 1;
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <pipe_suffix>\n", argv[0]);
        return 1;
    }

    char exchange_pipe[100];
    char trader_pipe[100];
    snprintf(exchange_pipe, sizeof(exchange_pipe), "/tmp/pe_exchange_%s", argv[1]);
    snprintf(trader_pipe, sizeof(trader_pipe), "/tmp/pe_trader_%s", argv[1]);

    // 打开管道:命名管道需两端都打开才会成功,这里会阻塞等待Exchange创建并打开管道
    fd_exchange = open(exchange_pipe, O_RDONLY);
    if (fd_exchange == -1) {
        perror("Failed to open exchange pipe");
        return 1;
    }

    fd_trader = open(trader_pipe, O_WRONLY);
    if (fd_trader == -1) {
        perror("Failed to open trader pipe");
        close(fd_exchange);
        return 1;
    }

    // 注册SIGINT信号处理(用于退出程序)
    if (signal(SIGINT, signal_handler) == SIG_ERR) {
        perror("Failed to register SIGINT handler");
        close(fd_trader);
        close(fd_exchange);
        return 1;
    }

    // 获取Exchange的PID:假设Exchange是当前进程的父进程,否则需调整获取方式
    exchange_pid = getppid();

    char buffer[51];  // 50字节内容+1字节终止符
    ssize_t read_len;

    while (!g_sigint_flag) {
        // 从Exchange管道读取数据
        read_len = read(fd_exchange, buffer, sizeof(buffer)-1);
        if (read_len == -1) {
            if (errno == EINTR) continue;  // 被信号中断,重试读取
            perror("Failed to read from exchange pipe");
            break;
        } else if (read_len == 0) {
            printf("Exchange closed the pipe\n");
            break;
        }

        // 确保字符串终止
        buffer[read_len] = '\0';
        printf("Received from Exchange: %s\n", buffer);

        // 向Exchange发送SIGUSR1信号
        if (kill(exchange_pid, SIGUSR1) == -1) {
            perror("Failed to send SIGUSR1 to Exchange");
        }

        // 向Trader管道写入回复消息
        const char *reply = "Hello";
        if (write(fd_trader, reply, strlen(reply)) == -1) {
            perror("Failed to write to trader pipe");
        }
    }

    // 清理资源
    close(fd_trader);
    close(fd_exchange);
    return 0;
}

关键修正点说明

  • 添加了管道读取逻辑,使用read()获取数据,处理信号中断的情况(EINTR)
  • 调整信号逻辑:读取数据后主动向Exchange发送SIGUSR1,而非等待信号
  • 增加参数检查,确保命令行参数正确传入
  • 使用snprintf替代sprintf,避免缓冲区溢出
  • 完善资源清理,确保管道描述符被正确关闭
  • 处理管道关闭的情况(read()返回0)

内容的提问来源于stack exchange,提问作者HyperCoderSuperion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:58:12