LINUX.ORG.RU

Особенности работы с poll

 , , ,


0

3

Добрый день!

Столкнулся с такой вот проблемой при работе с poll на некоторых устройствах, какой бы таймаут не задавал - первый poll может не дать никакого результата - возвращает 0, а во второй попытке уже есть нужное событие POLLIN. Это случается не всегда, но довольно часто. Это точно не таймаут, тк я пробовал задавать таймаут 30-60 сек., и ситуация так же воспроизводилась, как и на таймауте в 500мс.

Кто ни будь сталкивался с таким поведением?

ps Да, порт конечно же открыт как O_NONBLOCK

...
struct pollfd pfd = { .fd = fd, .events = POLLIN };
...
int retries = 0;
while (retries < 3) {
    res = poll (&pfd, 1, ms_to);

    if (res <= 0 || pfd.revents != POLLIN) {
        retries++;
        continue;
    }
    ssize_t chunksize = read (fd, buf + bytesread, size);
...

Обновление: Теперь работа с tcp запросами на сервер, посылку организовал +/- так же, система debian 10 (если это важно); Стоит таймаут 1000мс ожидания ответа от сервера, примерно на 15 запросов 3-4 остаются без ответа, первое что сделал - увеличил таймаут до 2000мс, да, стало меньше запросов без ответа, но все равно случаются… поставил 5 секунд - ответ от сервера стал приходить стабильно но блин - 5 секунд камон! И тут я вспомнил эту тему.

Как выглядело получение пакета изначально:

ssize_t
recv_to (int fd, void *buf, size_t size, int flags, int ms_to) {
	struct pollfd pfd = { .fd = fd, .events = POLLIN };
	size_t bytesread = 0;
	int res;

	while ((res = poll (&pfd, 1, ms_to)) > 0) {
		if (pfd.revents != POLLIN) break;
		ssize_t chunksize = recv (fd, buf + bytesread, size, flags);
		if (chunksize < 0) return chunksize;
		if (chunksize == 0) break;

		bytesread += chunksize;
		size -= chunksize;

		if (size == 0) break;
		pfd.revents = 0;
	}

	if (res < 0) return res;
	return bytesread;
}

Поделим таймаут на некоторое значение (на 3 например) и будем 3 раза пытаться вызывать poll с укороченным таймаутом. Таймаут ожидания ответа от сервера вернул в 1000мс, вот что получилось:

ssize_t
recv_to (int fd, void *buf, size_t size, int flags, int ms_to) {
	struct pollfd pfd = { .fd = fd, .events = POLLIN };
	size_t bytesread = 0;
	int res;

	const int trypoll = 3;
	ms_to = ms_to / trypoll;
	for (int i = 0; i < trypoll; i++) {
		while ((res = poll (&pfd, 1, ms_to)) > 0) {
			if (pfd.revents != POLLIN) break;
			ssize_t chunksize = recv (fd, buf + bytesread, size, flags);
			if (chunksize < 0) return chunksize;
			if (chunksize == 0) break;

			bytesread += chunksize;
			size -= chunksize;

			if (size == 0) break;
			pfd.revents = 0;
		}

		if (size == 0) break;
		dbgmsg_func ("try tcp poll #%d\n", i + 1);
	}

	if (res < 0) return res;
	return bytesread;
}

На trypoll==2 еще были недополученные пакеты, на trypoll==3 уже нет… WTF? Мне кажется, что я что-то делаю не так, как-то не правильно работаю с poll, но у меня нет никаких идей, почему 3 poll c таймаутом 333мс работают а один с таймаутом 1000мс - нет.

Что думаете?



Последнее исправление: id_thx1138 (всего исправлений: 3)

А что выдаётся в revents после первого запуска? Может быть, там есть что-нибудь типа POLLPRI?

tim239 ★★
()

Очень странно, так быть не должно. Такое ощущение, что приходит какой-то сигнал, но тогда res должно быть "-1" и errno == EINTR.

byko3y ★★★★
()

Я бы ещё поменял код pfd.revents != POLLIN на 0 == (pfd.revents & POLLIN).

Также если это какой-то специфический драйвер то у него может быть как-то специфически реализован poll().

tim239 ★★
()
Последнее исправление: tim239 (всего исправлений: 1)
14 августа 2020 г.
Ответ на: комментарий от tim239

Согласен с замечанием, такое не исключено, поправлю…

Сейчас я столкнулся со странностями poll уже по tcp на обычном debian)))

Никакого POLLPRI нет, но есть 2 типа ошибок:

  1. таймаут, результат poll == 0, poll 1000мс выходит по таймауту в 10% случаев, при этом 3 poll по 333мс - все ок
  2. poll вернул POLLIN, но recv вернул 0 байт… опять же в случае 3х(poll по 333мс) такую ситуацию нивелируют
id_thx1138
() автор топика
Ответ на: комментарий от id_thx1138

но recv вернул 0 байт

Если read/recv возвращают 0 байт при чтении из сокета, значит это соединение было закрыто.

Вообще могу порекомендовать запустить бинарник под strace (strace -e accept,close,poll,recv например) и посмотреть, что же происходит.

Ну и не игнорировать возвращаемое poll-ом значение.

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

ну вот c strace как-то так

Family: ipv4  Protocol: tcp  Host: 92.53.73.60  Port: 7777
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 8
connect(8, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("92.53.73.60")}, 16) = 0
poll([{fd=8, events=POLLIN}], 1, 1000)  = 1 ([{fd=8, revents=POLLIN}])
[ FAIL ] recv_to[128]: recv result = 0; poll result: 1

странно что recv нет в выводе strace…

id_thx1138
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.