【问题标题】:select() does not timeout after the interval specified in timeout structureselect() 在超时结构中指定的时间间隔后不超时
【发布时间】:2021-03-04 08:12:32
【问题描述】:

我正在尝试对https://man7.org/linux/man-pages/man2/eventfd.2.html 中提供的示例进行稍微修改的版本。

include <sys/eventfd.h>
#include <unistd.h>
#include <inttypes.h>           /* Definition of PRIu64 & PRIx64 */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>             /* Definition of uint64_t */

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
    int efd;
    uint64_t u;
    ssize_t s;
    int j;
    int retval;
    fd_set rfds;
    struct timeval tv;
    int count = 0;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    efd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE);
    if (efd == -1)
        handle_error("eventfd");

    switch (fork()) {
        case 0:
            for (j = 1; j < argc; j++) {
                printf("Child writing %s to efd\n", argv[j]);
                u = strtoull(argv[j], NULL, 0);
                /* strtoull() allows various bases */
                s = write(efd, &u, sizeof(uint64_t));
                if (s != sizeof(uint64_t))
                    handle_error("write");
            }
            printf("Child completed write loop\n");
            sleep(60);

            for (j = 1; j < argc; j++) {
                printf("Child writing %s to efd\n", argv[j]);
                u = strtoull(argv[j], NULL, 0);
                /* strtoull() allows various bases */
                s = write(efd, &u, sizeof(uint64_t));
                if (s != sizeof(uint64_t))
                        handle_error("write");
            }
            sleep(10);
            for (j = 1; j < 10; j++) {
                printf("Child writing %s to efd\n", argv[j]);
                u = strtoull(argv[j], NULL, 0);
                /* strtoull() allows various bases */
                s = write(efd, &u, sizeof(uint64_t));
                if (s != sizeof(uint64_t))
                        handle_error("write");
            }

            exit(EXIT_SUCCESS);

        default:
            FD_ZERO(&rfds);
            FD_SET(efd, &rfds);
            tv.tv_sec = 5;
            printf("Parent about to read\n");
            while(1) {
                count++;
                printf("entering\n");
                retval = select(efd + 1, &rfds, NULL, NULL, &tv);
                printf("THE RETVAL IS %d\n", retval);
                if (retval == -1) {
                    printf("fetched nothing\n");
                }
                else if (retval > 0) {
                    s = read(efd, &u, sizeof(uint64_t));
                    if (s != sizeof(uint64_t))
                        handle_error("read");
                printf("Parent read %"PRIu64" (%#"PRIx64") from efd\n", u, u);
                }
                printf("count...%d\n", count);
        }
        case -1:
            handle_error("fork");
    }
    return 0;
}

输出:

./a.out 10 3 4 5
event fd is 3
Parent about to read
entering
Child writing 10 to efd
Child writing 3 to efd
Child writing 4 to efd
Child writing 5 to efd
hey THE RETVAL IS 1
Child completed write loop
Parent read 1 (0x1) from efd
count...1
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...2
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...3
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...4
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...5
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...6
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...7
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...8
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...9
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...10
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...11
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...12
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...13
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...14
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...15
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...16
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...17
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...18
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...19
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...20
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...21
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...22
entering
Child writing 10 to efd
Child writing 3 to efd
Child writing 4 to efd
Child writing 5 to efd
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...23
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...24
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...25
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...26
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...27
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...28
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...29
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...30
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...31
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...32
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...33
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...34
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...35
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...36
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...37
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...38
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...39
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...40
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...41
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...42
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...43
entering
hey THE RETVAL IS 1
Parent read 1 (0x1) from efd
count...44
entering
.... upto 66

我的目标是基本上使用父进程中的select() 监视事件 fd,然后进行read() 调用。从输出来看,似乎父进程的 select 调用仅在子进程执行某些写入操作时才对 fd 起作用。如果子进程不做任何事情,即如果它处于睡眠状态,那么父进程就会阻止 select 调用,即使 5 秒过去了,它也永远不会超时。我在这里还缺少什么吗?

【问题讨论】:

  • 您忘记初始化tv 结构的tv_usec 成员。现在它将有一个 indeterminate 值,这可能导致未定义的行为。
  • 两者都必须申报吗?此外,tv_usec 必须初始化为与秒相同的值(以微秒为单位)。对吗?
  • 是的,您必须初始化整个结构。它可以被显式初始化为零(就像你对tv_sec 成员所做的那样),或者通过在定义上初始化tv 结构(如struct timeval tv = { 0, 0 };)。
  • 初始化tv_usec 工作。谢谢你。您可以将其添加为答案吗?

标签: c linux


【解决方案1】:

因为tv结构的tv_usec成员没有被初始化,所以它会有一个indeterminate值。

这个不确定的值几乎可以被视为随机或垃圾,并可能导致未定义的行为。

未定义的行为可能会像您的情况一样出现。

即使您不使用tv_usec 字段,它也需要初始化,最好是0,因为它没有被使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 2018-11-23
    • 2011-07-27
    • 2015-09-14
    • 2012-01-13
    相关资源
    最近更新 更多