1

我确定我遗漏了一些明显的东西,但我的问题如下。

作为一个编程练习,我正在尝试创建一个异步 posix 消息队列服务器来在处理程序中异步处理传入的消息。

现在根据我的文档sigaction(2)mq_notify(3)sigevent(7)我的思考过程应该有效。在我发布我的代码之前,我将概述我关于它应该如何工作的思考过程。

  • 使用sigactionandSA_SIGINFO我将为SIGIO.
  • 使用mq_notifyandstruct sigevent我会告诉我的消息队列SIGIO在消息到达时发送到当前进程。
  • 通过将指向我mqd_t的消息队列的指针放入struct sigevent.sigen_value.sigval_ptr我将能够在我的信号处理程序中通过struct siginfo.
  • 正如sigevent(7)告诉我 whenSIGEV_SIGNAL设置在sigev_notifythensi_code中,si_signo并且si_value设置在信号处理程序的siginfo. 具体si_value设置为sigev_value调用时传入的值mq_nofity
  • 在我的信号处理程序中,我应该能够mqd_t通过执行类似(mqd_t *)(info->si_value.sival_ptr)where infosiginfo_t*作为参数传递给我的信号处理程序的操作来获取我的消息队列。我有我mqd_t的结构mq_info_t,因为我稍后会用这个做更多的事情。

现在我的问题似乎源于不同的东西,但我想我会概述我的思维过程,以防它在某种程度上相关。根据sigaction(2)siginfo_t是一个包含各种信息的大型结构,包括si_value.

现在在我的 gdb 中,我收到错误消息,即我siginfo_t的信号处理程序中没有名为si_value. 这个问题在这里提到了同样的想法。

(gdb) p *info $1 = {si_signo = 29, si_errno = 0, si_code = -3, __pad0 = 0, _sifields = {_pad = {128611, 1000, -8896, 32767, 0 }, _kill = {si_pid = 128611 , si_uid = 1000}, _timer = {si_tid = 128611, si_overrun = 1000, si_sigval = { sival_int = -8896, sival_ptr = 0x7ffffffffdd40}}, _rt = {si_pid = 128611, si_uid = 1000, si_sigval = {sival_int = -886 sival_ptr = 0x7fffffffdd40}},_sigchld = {si_pid = 128611,si_uid = 1000,si_status = -8896,si_utime = 0,si_stime = 0},_sigfault = {si_addr = 0x3e80001f663,si_addr_lsb_add = -88896 = 0x0,_upper = 0x0},_pkey = 0}},_sigpoll = {si_band = 4294967424611,si_fd = -8896},_sigsys = {_call_addr = 0x3e80001f663,_syscall = -8896,_arch = 32767}}}

查看siginfo_t为我的构建定义的位置,我发现定义的结构/usr/include/bits/types/siginfo_t.h具有相同的结构。我认为这与我的构建的功能宏有关,但我在该领域知之甚少。我的构建有#define _POSIX_C_SOURCE 199309L.

现在我的代码的关键组件如下

#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <mqueue.h>
#include <string.h>

typedef struct mq_info {
    mqd_t fd;
} mq_info_t;

/** Handler for receiving async messages */
void sigHandler(int signal, siginfo_t *info, void *context)
{
    char buffer[MAX_SIZE + 1];
    int must_stop = 0;
    ssize_t bytes_read;
    mq_info_t *mq_i = (mq_info_t *)(info->si_value.sival_ptr);
    mqd_t mq = mq_i->fd;

    printf("In handler\n");

    do {
        bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);

        printf("MQ received: %s\n", buffer);
    } while (bytes_read > 0);

    printf("left handler\n");
}

int openMessageQueue(char *name, long max_msg_num, long max_msg_size)
{
    mq_info_t *mq_i = calloc(1, sizeof(mq_info_t));
    struct mq_attr attr;
    struct sigaction sa;
    struct sigevent ev;
    union sigval sv = { .sival_ptr = &mq_i };

    attr.mq_flags = O_NONBLOCK; // Async
    attr.mq_maxmsg = max_msg_num;
    attr.mq_msgsize = max_msg_size;
    attr.mq_curmsgs = 0; // Num of messages currently in queue

    if (-1 == (mq_i->fd = mq_open(name, O_CREAT | O_RDONLY, 0644, &attr)))
        goto error;

    printf("queue opened\n");

    /** Setup handler for SIGIO */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = sigHandler;
    printf("Sighandler: %p\n", sigHandler);
    sigfillset(&sa.sa_mask);
    sigdelset(&sa.sa_mask, SIGIO);
    if (sigaction(SIGIO, &sa, NULL))
        goto error;

    printf("sigaction done\n");

    /** Set up process to be informed about async queue event */
    ev.sigev_notify = SIGEV_SIGNAL; // Specify a signal should be sen
    ev.sigev_signo = SIGIO; // Signal of interest
    ev.sigev_value =
        sv; // Suplementary data passed to signal handling fuction
    ev.sigev_notify_function = NULL; // Used by SIGEV_THREAD
    ev.sigev_notify_attributes = NULL; // Used by SIGEV_THREAD

    /** Register this process to receive async notifications when a new message  */
    /**     arrives on the specified message queue  */
    if (mq_notify(mq_i->fd, &ev) < 0) {
        perror("notify failed");
        goto error;
    }

    printf("notify done\n");

    return 0;

error:
    mq_unlink(name);
    return -1;
}

在此先感谢,我希望我已提供所有相关信息。

Arch Linux w 5.3.8 内核

4

1 回答 1

2

看看最底部,bits/types/siginfo_t.h你会看到:

/* X/Open requires some more fields with fixed names.  */
#define si_pid          _sifields._kill.si_pid
#define si_uid          _sifields._kill.si_uid
#define si_timerid      _sifields._timer.si_tid
#define si_overrun      _sifields._timer.si_overrun
#define si_status       _sifields._sigchld.si_status
#define si_utime        _sifields._sigchld.si_utime
#define si_stime        _sifields._sigchld.si_stime
#define si_value        _sifields._rt.si_sigval
// ...

因此,在您的代码中,当您编写info->si_value.sival_ptr它时,它会被宏扩展为info->_sifields._rt.si_sigval.sival_ptr并且编译器“正确”很高兴。但是 GDB 不知道 的宏定义si_value,因此您必须在从调试器访问该字段时自己输入。

(是的,这很可怕,但我们无法在不破坏 ABI 的情况下清理它。)

(您为什么使用#define _POSIX_C_SOURCE 199309L?这不是问题的直接原因,但您可能会更乐于使用更现代的一致性模式,我建议#define _XOPEN_SOURCE 700使用应该在当前一代 Unix 中可移植的新代码。)

于 2019-11-24T16:52:24.853 回答