LINUX.ORG.RU

Ядро не отдаёт приложению содержимое TCP-пакета, пока не придёт следующий

 , , ,


1

8

Есть приложение, которое общается с сервером по TCP. Протокол представляет собой просто запрос-ответ, плюс ещё клиент и сервер отправляют друг другу пинги.

Клиент в бесконечном цикле дёргает read() из сокета, который переведёт в неблокирующий режим.

Проблема в том, что через некоторое время случается так, что клиент не вычитывает ответ, пока не придёт очередной пинг от сервера. То есть, read() всё это время возвращает -1, errno=EAGAIN.

В tcpdump видно, что ответ пришёл вовремя (сразу после запроса). А также видно, что в тот момент, когда клиент наконец-то вычитал, пришёл следующий пакет (пинг).

Проблемы в приложении исключили, запускали под strace, постоянно пытается вычитать новые данные, но ядро их не отдаёт.

В какую сторону копать?


Проблемы в приложении исключили

Сюда копай, проблема в приложении: все остальные же приложения нормально работают с сетью на этом же железе и ядре?

Если поиграться с netcat-ом, текст по пересылать на соседний комп и обратно? Если собрать минимальный пример работы с сетью и попосылать пакетики на соседний комп?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

Какая проблема в приложении

Не знаю, смотреть надо

Я с таким же успехом могу спросить какая проблема в ядре, если все остальные приложения, кроме вашего, работают с сетью нормально. Запусти netcat в режиме сервера под strace-ом и посмотри так же в тот же момент он выплевывает данные когда они приходят или ждёт следующего пакета?

pihter ★★★★★
()
Последнее исправление: pihter (всего исправлений: 1)
Ответ на: комментарий от pihter

Есть, но, к сожалению, не могу поделиться. Говорю же, strace показывает, что клиент постоянно пытается вычитать новые данные из сокета. Проблемы не в приложении.

cudeta
() автор топика
Ответ на: комментарий от cudeta

А выдрать нужный кусок, подготовить простой сэмпл максимально эмулирующий ситуацию?

По крайней мере я иногда так проги отлаживаю. Когда накопил много всего, лучше абстрагироваться от мусора и проверить свою догадку на очищенном семпле.

hibou ★★★★★
()
Ответ на: комментарий от hibou

Не понимаю, чем может помочь кусок от довольно большой проги.

char buf[NBUF];

for (;;) {
    ssize_t r = read(fd, buf, sizeof(buf));
    if (r < 0) {
        if (errno != EAGAIN) {
            // обработать ошибку
        }
    } else if (r == 0) {
        // соединение оборвалось
    } else {
        // обработать новые данные
    }
}
cudeta
() автор топика
Ответ на: комментарий от hibou

Не получится, т.к. это конкретная железка в конкретной локальной сети. Устанавливать новую ОС туда нельзя.

См. моё предыдущее сообщение: в приложении проблемы быть не может, потому что оно вычитывает данные, только когда приходит следующий пакет.

cudeta
() автор топика
Ответ на: комментарий от cudeta

Это не получится попробовать, т.к. сервер не в нашем распоряжении

А как же ты говоришь что вы исключили проблемы с программой? Т. е. собрать из исходников и погонять измененную версию не выйдет? И как ты собрался фиксить, даже если определишь в чем проблема?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

только когда приходит следующий пакет

Скорее всего, что когда в сетевом буфере данных больше (или равно), чем читает приложение - данные попадают в приложение.

p.s. Если проблема, как вы пишете, в ядре - то каким образом вы в tcpdump-е всё корректно увидели?

DiMoN ★★★
()

Копать в сторону приложения.

Если проблема была бы в ядре, во что верит приблизительно никто по понятным соображениям, то написание сервера и клиента, демонстрирующих твой спецэффект — самая маленькая из твоих проблем. Пока этого не сделано, говорить толком не о чем.

t184256 ★★★★★
()
Ответ на: комментарий от DiMoN

Что значит «данных больше (или равно), чем читает приложение»? Приложение постоянно пытается вычитать новые данные, семантика read() — вернуть, если есть хотя бы один байт в буфере.

p.s. Если проблема, как вы пишете, в ядре - то каким образом вы в tcpdump-е всё корректно увидели?

Ну вот ядро почему-то не отдаёт приложению данные, пока не придёт следующий пакет.

cudeta
() автор топика
Ответ на: комментарий от cudeta

В приложении проблемы быть не может, потому что оно вычитывает данные, только когда приходит следующий пакет.

Ошибаешься: приложение может быть написано по-разному и данные вычитывать по-разному.

Ты вот пришел за помощью, тебе дали очень толковый совет: собери минимальный пример без всего лишнего из твоей программы и погоняй-отладь на разных системах. Это очень, очень толковый совет из практики. Я вот тоже так делал и так бы и на твоем месте сделал, а ты, вместо того чтоб послушать совет за которым пришел, стоишь на своем и утверждаешь нам что проблема в ядре (довольно сильное утверждение в наше время для линукса) и без доказательств. Ну допустим ты прав и проблема в ядре – мы-то тебе, в таком случае, чем поможем?

Но послушайся опыта и здравого смысла и попробуй допустить что проблема все же не в ядре, а в приложении – и это мы как раз можем помочь диагностировать и, возможно, исправить

pihter ★★★★★
()
Ответ на: комментарий от pihter

Ошибаешься: приложение может быть написано по-разному и данные вычитывать по-разному.

Как по-разному можно вычитывать данные? Ну вот как можно сделать так, чтобы вычитать, только когда пришёл следующий пакет, с помощью обычного read()?

Ты вот пришел за помощью, тебе дали очень толковый совет: собери минимальный пример без всего лишнего из твоей программы и погоняй-отладь на разных системах.

Сервер не в нашем распоряжении. Ни компьютер, ни серверный софт. Клиентский компьютер — в нашем. И клиентский софт — в нашем.

допустим ты прав и проблема в ядре – мы-то тебе, в таком случае, чем поможем?

Очевидно, найти конкретную проблему (возможно, с помощью eBPF), потом действовать по обстоятельствам, возможно баг засабмитить.

cudeta
() автор топика
Ответ на: комментарий от cudeta

Очевидно, найти конкретную проблему (возможно, с помощью eBPF)

Не видим для этого репродьюсера. Наши попытки это написать, очевидно, спецэффект ни за какое разумное время не воспроизведут.

t184256 ★★★★★
()
Ответ на: комментарий от cudeta

Ядро просто не отдаёт новые данные почему-то, пока не придёт следующий пакет.

netcat -l -p 22222 <devie ip>

и на другой машине в сети: echo "hello" | netcat <device ip> 22222

привет сразу появится или надо будет еще в догонку пакет прислать?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

Нет, не невозможно. Таки запусти свой код на разных системах и посмотри как он принимает данные. Данные не обязательно принимать от того самого секретного сервера, можно и из любого другого места.

firkax ★★★★★
()
Ответ на: комментарий от annulen

Ядро выдает данные в raw-сокет tcpdump’а, но отказывается давать их вашему приложению?

Да, получается, что так.

Проблема явно не в ядре.

Почему?

cudeta
() автор топика
Ответ на: комментарий от cudeta

Ты вот пришел за помощью, тебе дали очень толковый совет: собери минимальный пример без всего лишнего из твоей программы и погоняй-отладь на разных системах.

Сервер не в нашем распоряжении.

И? Напиши свой, который так же отвечает такими же пакетами, которые в tcpdump’е так же выглядят, а в юзерспейс не отдаются.

t184256 ★★★★★
()
Ответ на: комментарий от cudeta

Я имел в виду компьютер, который выступает в роли TCP-сервера. Компьютер-клиент — в нашем распоряжении, как и программа-клиент, мы её написали.

Постой-постой, ЯННП: есть сервер на недоступной для правок машине в сети и есть ваш самописный клиент, который доступен для правок, проблемы с вычиткой из сокета где? У вас на клиенте или на недоступном сервере?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

Бесконечный цикл без sleep с неблокирующим read?

А ты уверен, то дело в получении следующего пинга, а не в банальной работе планировщика? Ну в смысле приложение жрет процессор как не в себя, ну ок. Но раз в несколько миллисекунд ядро переключает контекст и процессор следующие несколько миллисекунд занимается другой задачей

router ★★★★★
()
Ответ на: комментарий от router

Бесконечный цикл без sleep с неблокирующим read?

Да.

А ты уверен, то дело в получении следующего пинга, а не в банальной работе планировщика? Ну в смысле приложение жрет процессор как не в себя, ну ок. Но раз в несколько миллисекунд ядро переключает контекст и процессор следующие несколько миллисекунд занимается другой задачей

Нет. Во-первых, ядер свободных там много. Во-вторых, задержки исчисляются минутами (интервал пинга).

cudeta
() автор топика
Ответ на: комментарий от cudeta

задержки исчисляются минутами

Агрументный аргумент. На всякий случай посмотри через proc, что происходит с приложением https://habr.com/ru/post/209446/ . Других идей у меня нет. Ну может ещё попробовать сильно другую версию ядра

router ★★★★★
()
Ответ на: комментарий от cudeta

Как по-разному можно вычитывать данные?

Ну, например, в блокирующем и неблокирующем режиме. Это не по-разному что-ли?

Ну вот как можно сделать так, чтобы вычитать, только когда пришёл следующий пакет, с помощью обычного read()?

Да как угодно: ночь программирования темна и полна ужасов. Например, твой сокет находится в блокирующем режиме (хоть ты и думаешь что нет), ты вичитываешь данные фиксированного размера, приходит пакет, а ты только в этот момент выгребаешь из буфера предыдущий – вот и твоя ситуация. Че угодно может быть, может ты не с тем сокетом работаешь с которым думаешь, может потоки портят данные, может у тебя одно приложение вызывает другое и данные о дескрипторах наследуются – откуда я знаю что там у тебя?

Сервер не в нашем распоряжении. Ни компьютер, ни серверный софт. Клиентский компьютер — в нашем. И клиентский софт — в нашем.

А багу ты собираешься исправлять где? Я так и не понял. Я так понял что ты грешишь на серверный сокет (который ждет данных и сидит постоянно дергает рид) или где?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

Очевидно, найти конкретную проблему (возможно, с помощью eBPF), потом действовать по обстоятельствам, возможно баг засабмитить.

а как это тебе поможет, если сервер не в твоем распоряжении? Разве что ответственность с себя скинуть, но для этого есть другие методы попроще :)

pihter ★★★★★
()
Ответ на: комментарий от pihter

А багу ты собираешься исправлять где? Я так и не понял.

Где-то на клиентской машине.

Я так понял что ты грешишь на серверный сокет (который ждет данных и сидит постоянно дергает рид) или где?

Ядро почему-то не отдаёт данные из read(), пока не придёт следующий пакет (интервал в минуты). В этом проблема.

cudeta
() автор топика
Ответ на: комментарий от cudeta

У нас нет доступа до другой машины. И это воспроизводится только через несколько итераций запрос-ответ.

До сервер нет доступа. Это я уже понял. Я тока не понял на какой машине предполагаемая проблема: на недоступном сервере или на доступном клиенте? В любом случает неткат-тест надо делать на машине на которой предполагаемая проблема, а раз ты собрался ее исправить значит доступ должен быть или я совсем – томат и не понимаю что происходит )

pihter ★★★★★
()
Ответ на: комментарий от pihter

Я тока не понял на какой машине предполагаемая проблема: на недоступном сервере или на доступном клиенте?

Так я же ответил, что на клиенте: Ядро не отдаёт приложению содержимое TCP-пакета, пока не придёт следующий (комментарий)

И вообще по ОП-посту вроде понятно, что проблема с клиентом.

cudeta
() автор топика
Ответ на: комментарий от cudeta

На клиенте.

тогда чего ты заладил про недоступность сервера? Пес с ним! Клиент же нам доступен – давай отлаживать его. Возвращаемся к первому вопросу, если клиент запустить на BSD он себя так же поведет? Понимаешь к чему этот вопрос?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

По поводу tcpdump-а стоит сделать два уточнения.

Во-первых, он показывает пакеты на интерфейсе, ещё до того, как их обработает файрволл. А файрволл может пакет вообще отклонить. Штуки, настраивающиеся через tc, скорее всего тоже уже после, а там можно и задержки ставить.

Во-вторых, если даже отбросить фокусы с пакетным фильтром, может быть такая ситуация: сервер прислал ответ в двух (или больше) пакетах, первый пакет потерялся, второй нет, и в tcpdump-е ты видишь второй пакет и его позицию с принятыми байтами. Пока первый пакет тоже не придёт - приложение ничего не увидит, а то в зависимости от настроек может и много времени занять, но «несколько минут» - всё-таки не особо реалистично. В любом случае, стоит внимательнее посмотреть, какие именно пакеты пришли, что они покрыли все seq номера без дырок.

firkax ★★★★★
()