【问题标题】:Invalid argument when read() from timerfd从 timerfd 读取()时参数无效
【发布时间】:2018-04-21 14:20:26
【问题描述】:

我有一个程序,它使用timerfd_create() 创建一个计时器,并指定了一个时间间隔,以便定期通知进程。然后这个定时器注册到epoll。在处理程序中执行read()时,错误是Invalid argumenterrno是22。当我在我的Raspberry Pi(Raspbian,Linux 4.9.80)上运行此程序时出现此错误,但是当我在笔记本电脑上运行它时一切都很好(Arch,Linux 4.15.15)。

相关代码贴在下面。非常感谢任何帮助。

void epset_reg(int epfd, int fd, u32 events)
{
    struct epoll_event ev;

    memset(&ev, 0, sizeof(ev));
    ev.data.fd = fd;
    ev.events = events;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
        handle_err("epoll_ctl");
}

int init_timer(u32 interval)
{
    int tfd;
    struct itimerspec tspec;

    /* specify the timer */
    tspec.it_value.tv_sec = 1;
    tspec.it_value.tv_nsec = 0;
    tspec.it_interval.tv_sec = interval;
    tspec.it_interval.tv_nsec = 0;

    /* create timerfd */
    if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
        handle_err("timerfd_create");

    /* arm (start) the periodic timer */
    if (timerfd_settime(tfd, TFD_TIMER_ABSTIME, &tspec, NULL) < 0)
        handle_err("timerfd_settime");

    return tfd;
}

void handler(int tfd)
{
    u64 exp;

    /* THE ERROR ! */
    if (read(tfd, &exp, sizeof(exp)) < 0)
        handle_err("read");

    /* irrelevant parts */
}

int main()
{
    int epfd, tfd, sock, nfds, i;
    struct epoll_event events[MAX_EVENTS];

    /* create new epoll instance */
    if ((epfd = epoll_create1(0)) < 0)
        handle_err("epoll_create1");

    /* obtain timerfd */
    tfd = init_timer(TIMER_INTERVAL);
    /* obtain socket to listen */
    sock = init_socket(CC_PORT);

    /* register sock and tfd to epoll set */
    epset_reg(epfd, tfd, EPOLLIN);
    epset_reg(epfd, sock, EPOLLIN | EPOLLET);

    for (;;) {
        if ((nfds = epoll_wait(epfd, events, MAX_EVENTS, -1)) < 0)
            handle_err("epoll_wait");

        for (i = 0; i < nfds; ++i) {
            if ((events[i].events & EPOLLERR) ||
                (events[i].events & EPOLLHUP) ||
                (!(events[i].events & EPOLLIN))) {
                fprintf(stderr, "epoll\n");
                close(events[i].data.fd);
                continue;
            }

            if (events[i].data.fd == tfd)
                handler(tfd);
            else if (events[i].data.fd == sock)
                accept_conn(sock, epfd);
            else
                handle_message(events[i].data.fd);
        }
    }
}

完整的程序托管在https://github.com/iamlazynic/centralized_wlan/tree/master/cccc.cmain.c

除了关于这个问题的建议之外,如果有关于如何在这种情况下调试的建议,那就太好了。谢谢!

【问题讨论】:

  • 您可能会将 64 字节读入一个 8 字节变量(sizeof(exp) == 8 不是 64)。通常最好做这样的事情read(tfd, &amp;exp, sizeof(exp)) 来避免这种错误。
  • @jszakmeister 对不起。我在问题中修复了该行,但实际上我的原始程序对此是正确的。当我将其粘贴到此处时,我出于某种愚蠢的原因更改了该行并犯了这个错误。不过谢谢指出。read()的错误不是因为这个。
  • 不用担心,但是上面的代码仍然是错误的。 :-) 你在sizeof(exp) 之后缺少一个括号。
  • 另外,你确定你的u64实际上是8字节吗? timerfd_create() 的手册页说,如果缓冲区太小,您可以获得EINVAL(这就是您所看到的)。我可以在您的原始代码中看到您将 u64 键入unsigned long int。但是,如果您的 Pi 是 32 位处理器,则 sizeof(u64) 是 4,而不是 8。您必须在 32 位平台上将其 typedef 为 unsigned long long int。考虑使用 stdint.h 来避免这些问题并改用 uint64_t ——它已经完成了弄清楚如何正确 typedef 的艰苦工作。 :-)

标签: c linux debugging


【解决方案1】:

在解决这个问题后,我们发现 u64 的 typedef 错误,代码试图读取几个字节,这将导致 EINVAL 错误代码。 typedef 是:

typedef unsigned long int u64;

但在 32 位系统上,这可能是 4 字节大小。取而代之的是,我们使用了 stdint.h 和 uint64_t 类型(它会为您使用的平台更仔细地键入定义内容)并且现在具有正确的大小。

【讨论】:

    【解决方案2】:

    下面是read函数的概要。

    ssize_t read(int fildes, void *buf, size_t nbyte);

    第二个是buf指针,第三个是要读取的大小。您将大小设置为 64 字节。但是指针是在堆栈中分配的 exp(8bytes) 的地址。我认为它会破坏堆栈。

    【讨论】:

      猜你喜欢
      • 2012-09-11
      • 1970-01-01
      • 1970-01-01
      • 2011-01-09
      • 2017-06-24
      • 1970-01-01
      • 2018-10-03
      • 2012-11-21
      • 1970-01-01
      相关资源
      最近更新 更多