PostgreSQL与epoll_wait

时间:2016-02-03 18:25:33

标签: c linux postgresql

我想在我的linux项目中异步使用PostgreSQL(9.1)。为此,我必须使用epoll_wait(因为应用程序的其他部分)。目标是最终在边缘触发模式下使用epoll。但即使在非边缘触发模式下,我也无法使连接过程正常工作。我不知道为什么。但是,当用户名和密码正确时,它可以正常工作。但是当密码错误时它也必须工作。在那种情况下,我得到一些我不明白的错误。 : - /这是我使用的代码(连接已经使用PQconnectStart()初始化,参数列表可以正常使用PQconnectdb()):

void ConnectDB(PGconn * connection)
{
    int pq_fd = PQsocket(connection);
    int epoll_fd = epoll_create1(0);
    struct epoll_event event;
    struct epoll_event *eventList = (epoll_event *)calloc(64, sizeof(epoll_event));

    event.data.fd = pq_fd;
    event.events = EPOLLOUT | EPOLLERR;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pq_fd, &event);

    while (true) {
        PostgresPollingStatusType pt = PQconnectPoll(connection);
        switch (pt)
        {
        case PGRES_POLLING_OK:
            printf("*** connection established!\n");
            return;

        case PGRES_POLLING_FAILED:
            printf("*** connection failed: %s\n", PQerrorMessage(connection));
            return;

        case PGRES_POLLING_ACTIVE:
            printf("   --- poll result: PGRES_POLLING_ACTIVE\n");
            break;

        case PGRES_POLLING_READING:
            printf("   --- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN\n");
            event.events = EPOLLIN | EPOLLERR;
            if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, PQsocket(connection), &event) == -1) {
                printf("epoll_ctl() error: %u: %s\n", errno, strerror(errno));
                exit(1);
            }
            break;

        case PGRES_POLLING_WRITING:
            printf("   --- poll result: PGRES_POLLING_WRITING - Modifiing epoll to EPOLLOUT\n");
            event.events = EPOLLOUT | EPOLLERR;
            if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, PQsocket(connection), &event) == -1) {
                printf("epoll_ctl() error: %u: %s\n", errno, strerror(errno));
                exit(1);
            }
            break;
        }

        int n = epoll_wait(epoll_fd, eventList, 64, -1);
        if (n == -1) {
            printf("epoll_wait() error: %u: %s\n", errno, strerror(errno));
            exit(1);
        }
    }
}

这是我得到的输出:

--- poll result: PGRES_POLLING_WRITING - Modifiing epoll to EPOLLOUT
--- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN
--- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN
--- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN
--- poll result: PGRES_POLLING_WRITING - Modifiing epoll to EPOLLOUT
--- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN
--- poll result: PGRES_POLLING_READING - Modifiing epoll to EPOLLIN
--- poll result: PGRES_POLLING_WRITING - Modifiing epoll to EPOLLOUT
epoll_ctl() error: 2: No such file or directory

有人有想法吗?

1 个答案:

答案 0 :(得分:2)

postgresql客户端库首先尝试ssl-connection,如果失败,则在没有ssl的情况下重试。无论为什么连接失败的原因,这都是完成的,并且它完全没有通知呼叫者。因此,即使错误是错误的密码,客户​​端库也会关闭文件描述符并重新打开明文的套接字连接。

如果文件描述符已关闭,则会自动从epoll-set中删除 (这会导致您的"没有此类文件或目录"错误消息)。所以你必须手动重新添加它:

if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, PQsocket(connection), &event) == -1) {
    if (errno == ENOENT) {
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, PQsocket(connection), &event);
    } else {
        printf("epoll_ctl() error: %u: %s\n", errno, strerror(errno));
        exit(1);
    }
}

另一种选择是永久启用或禁用ssl,为此添加sslmode=requiresslmode=disable到您的连接字符串。但是,如果您打算使用PGreset()(或遇到套接字关闭并对调用者透明地重新打开的任何其他情况),那么您将遇到同样的问题。

不可否认,postgresql-client-library的这种行为不是很epoll() - 友好。在过去,使用select()poll()时,这不是问题,因为内核中没有状态,就像现在使用epoll()一样。