Меня блокируют в select() при работе с каналом. Первый select()
разблокирует, когда мы получим данные на стандартный ввод (я просто набираю одну букву и нажимаю Enter).
Затем я записываю данные в конец записи канала, но select()
не распознает никаких данных на считываемом диске канала. Поэтому он блокируется на неопределенный срок.
int fd_pipe[2];
char buf[20];
fd_set set;
pipe(fd_pipe);
FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0 , &set); // stdin fd
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point
if (FD_ISSET(0, &set))
{
read(0, buf, 20); // Can confirm we do get here
write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // <-- We get stuck here
if (FD_ISSET(fd_pipe[0], &set))
{
char d;
read(0, &d, 1);
assert(d == buf[0]);
}
Если я сделаю это без select()
, все будет работать нормально:
int fd_pipe[2];
pipe(fd_pipe);
char c = 'x';
write( fd_pipe[1], &c, 1);
c = 'a';
read( fd_pipe[0], &c, 1);
assert(c == 'x');
На более широкой картинке у меня есть многопоточная программа, которая использует select()
и pipe()
для эмуляции отмены операции read()
. Я пишу в канал один символ с намерением заставить select()
вернуться, чтобы я мог завершить операцию вместо того, чтобы пытаться отправить сигнал об отмене блокировки read()
на основном FD. Но select()
не возвращается.
См. Необходимо ли сбрасывать fd_set между системными вызовами select()?
Функция select
изменяет наборы, которые вы ей передаете. При возврате select
наборы будут содержать только активные дескрипторы.
В вашем случае будет установлен только STDIN_FILENO
, поэтому второй вызов select
не будет содержать fd_pipe[0]
.
На самом деле решение состоит не в том, чтобы повторно добавлять трубу в набор и вызывать select
второй раз, а в том, чтобы вызывать select
только один раз:
FD_ZERO(&set);
FD_SET(fd_pipe[0], &set); // Pipe read fd
FD_SET(0 , &set); // stdin fd
select(fd_pipe[0]+1, &set, NULL, NULL, NULL); // I type 1 char + ENTER to get past this point
if (FD_ISSET(0, &set))
{
read(0, buf, 20); // Can confirm we do get here
write(fd_pipe[1], buf, 1); // Lets put just one character in the pipe
}
else if (FD_ISSET(fd_pipe[0], &set))
{
char d;
read(fd_pipe[0], &d, 1);
assert(d == buf[0]);
}
Вам также необходимо проверить, что на самом деле возвращает select
. Он может вернуть -1
, что означает ошибку.
Если вам нужно вызвать select
несколько раз. Затем вам нужно будет обнулить/переустановить fd_set
.
pipe
ФД не будет готов после моего первого звонка select
, потому что я написал после первого select
. Означает ли это, что я должен FD_ZERO
и FD_SET
перед каждым вызовом select()
?
@Stewart Использовать цикл?
И да, обязательно проверяя возвращаемое значение select
, но не включил его в MCVE.
На моей странице руководства это описывается следующим образом: «После возвращения метода select() из readfds будут удалены все файловые дескрипторы, кроме тех, которые готовы к чтению».
Фантастика. Только что нашел еще одну справочную страницу, в которой говорится: «Обратите внимание: по возвращении каждый из наборов файловых дескрипторов изменяется на месте, чтобы указать, какие файловые дескрипторы в данный момент «готовы». Таким образом, при использовании select() в цикле наборы должны инициализироваться повторно перед каждым вызовом».
Я думаю, вам нужно будет сбрасывать это
fd_set
перед каждымselect()
звонком.