Recvmsg() случайным образом возвращает EWOULDBLOCK

У меня есть набор программ клиент/сервер TCP, который без проблем работает уже более 10 лет. Задание «head» порождает тело дочерних процессов (я работаю на платформе AS400, которая не поддерживает fork()/exec()). Головное задание настраивается как TCP-сервер, и как только приходит соединение, оно немедленно отдает соединение одному из дочерних заданий через sendmsg(). Используется обычный шаблон socketpair() и т. д. Признаюсь, когда я собирал все это по кусочкам 12 лет назад, я не понимал всего, что происходит. Код, который я нашел для передачи открытого соединения, приведен ниже. Таким образом, главная работа ожидает accept() и вызывает sendfd(), когда accept() возвращается.

int sendfd(int s, int fd)
{
    char buf[1];
    struct iovec iov;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    int n;
    char cms[CMSG_SPACE(sizeof(int))];

    buf[0] = 0;
    iov.iov_base = buf;
    iov.iov_len = 1;

    memset(&msg, 0, sizeof msg);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_control = (caddr_t)cms;
    msg.msg_controllen = CMSG_LEN(sizeof(int));

    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    memmove(CMSG_DATA(cmsg), &fd, sizeof(int));

    if ((n = sendmsg(s, &msg, 0)) != iov.iov_len) {
        return -1;
    }
    return 0;
}

Таким образом, дочерние задания запускают процедуру для получения соединения. Это ниже. Пул дочерних процессов эффективно запускает эту процедуру, например "recvfd(0);" Наш отдел контроля качества запустит автоматизированный тест, и с прошлой недели все дочерние процессы будут умирать, по-видимому, случайным образом, вплоть до секунды, с EWOULDBLOCK. Меня смущает эта ошибка. recvmsg() всегда блокируется, ожидая сообщения. Относится ли ошибка к принимаемому сокету или к «0», для которого он используется? Ничто в моем коде не меняет качества файлового дескриптора 0. Я действительно озадачен тем, почему умирает все тело дочерних процессов. Что-то подсказывает мне, что файловый дескриптор 0 изменяется, но в коде ни одной из программ нет ничего, что могло бы выполнять подобные манипуляции.

int recvfd(int s)
{
    int ret, fd;
    char buf[1];
    struct iovec iov;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    char cms[CMSG_SPACE(sizeof(int))];

    iov.iov_base = buf;
    iov.iov_len = 1;

    memset(&msg, 0, sizeof msg);
    msg.msg_name = 0;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    msg.msg_control = (caddr_t)cms;
    msg.msg_controllen = sizeof cms;

    if ((ret = recvmsg(s, &msg, 0)) < 0) {
        return -1;
    }

    if (ret == 0) {
        return -1;
    }

    cmsg = CMSG_FIRSTHDR(&msg);
    memmove(&fd, CMSG_DATA(cmsg), sizeof(int));

    return fd;
}

Я могу опубликовать больше кода, если требуется больше деталей. Я должен найти эту ошибку прямо сейчас, так как это основа нашего бизнеса. Заранее спасибо!

Обновлено: Вот фрагмент дочернего кода. Обратите внимание, что 'worker_sd' явно установлен на 0. Проблема, которую мне нужно исправить, заключается в том, что выход из цикла означает выход из программы, но я не знаю, есть ли способ справиться с этой ошибкой.

Сообщение об ошибке в итоге будет таким: Дочерняя транзакция завершается с new_sock=-1 и error=Operation привела бы к приостановке процесса.

// worker_sd is explicitly set to 0 because the parent set the child's 0 file descriptor (the usual one
// reserved for standard input) to be the socket over which open descriptors come.
int worker_sd = 0;

jobSwitches.get();
logging_on = jobSwitches[QQJobSwitches::LOGGING_ON] == '1';
debug_on = jobSwitches[QQJobSwitches::DEBUG_ON] == '1';
wait_on_child_process = jobSwitches[QQJobSwitches::WAIT_ON_CHILD] == '1';

if (debug_on) {
    CERR << "START:  Waiting for a message..." << endl;
}
while ((new_sock = recvfd(worker_sd)) >= 0) {
    stringstream sstr;
    string ip_str, port_str;
    bool success;

Итак, это приложение работало более 10 лет, и теперь вы видите новое поведение? Код изменился? Какие-либо обновления ОС, библиотек, системных настроек и т. д., о которых вы знаете, могут изменить поведение ваших кодов?

Chimera 27.02.2019 19:18

MCVE, пожалуйста. Откуда взялся EWOULDBLOCK? Для начала я бы улучшил обработку ошибок в этом куске кода, чтобы было понятнее, откуда берется ошибка.

SergeyA 27.02.2019 19:25

@Chimera, сработало без проблем. Никакой реальный код не меняется в последнее время. Конечно, я рассматриваю возможности, связанные с системой, но поскольку это начало происходить, мы ничего не нашли (пока). В данный момент я добавляю больше кода регистрации. Я также провожу некоторые исследования с getockopt(), которые могут рассказать мне лучшую историю.

Kelly Beard 27.02.2019 19:27

@СергейА. Я уверен, что проблема связана с recvmsg() в подпрограмме recvfd(). Я добавляю больше кода регистрации на данный момент. Спасибо за ваш ответ.

Kelly Beard 27.02.2019 19:30

recvmsg() может сообщить EWOULDBLOCK только в том случае, если 1) s является неблокирующим сокетом и нет данных, ожидающих чтения при вызове recvmsg(), или 2) s является блокирующим сокетом с назначенным тайм-аутом приема и recvmsg() раз до получения каких-либо данных. Какую логику вы используете, чтобы определить, когда следует вызывать recvfd()? Вы вообще используете select() или (e)poll()? Вы уверены, что s на самом деле относится к правильному сокету, из которого вы ожидаете читать?

Remy Lebeau 27.02.2019 19:34

Это может быть полезно? ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzab6/rzab6.pdf

Chimera 27.02.2019 20:01

Я повторяю комментарий @RemyLebeau. Вы используете select() или poll(), чтобы определить, есть ли в сокете данные для чтения?

Chimera 27.02.2019 20:18

@Chimera По общему признанию, это не так. Это никогда не требовалось раньше, и вы можете прокручивать это весь день, и это не произойдет позже или может произойти сразу. что-то изменился - пока не уверен, что именно. Спасибо.

Kelly Beard 27.02.2019 21:12

@KellyBeard - вы должны использовать select по крайней мере, чтобы определить, действительно ли в сокете есть данные, готовые для чтения, чтобы избежать проблемы EWOULDBLOCK.

Chimera 27.02.2019 21:29

@Chimera Понятно, но теоретически мне это не нужно, потому что fd 0 является блокирующим сокетом. Я добавляю дополнительные операторы регистрации и вызов fcntl(), чтобы увидеть, изменился ли каким-либо образом статус блокировки. Однако я добавлю select().

Kelly Beard 27.02.2019 21:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
90
0

Другие вопросы по теме