select () возвращает неверный аргумент - PullRequest
5 голосов
/ 06 декабря 2010

Я успешно читаю из канала из другого потока и печатаю вывод (в окне ncurses, как это происходит).

Мне нужно делать это по одному символу за раз по разным причинам, и я использую select () на FD для конца чтения канала вместе с несколькими другими FD (такими как stdin).

Моя идея состоит в том, чтобы пытаться читать из канала только тогда, когда он неизбежно готов к чтению, предпочтительнее обработки любого ввода. Это, кажется, работает - по крайней мере, для начала. select () устанавливает fd_set, и если FD_ISSET, я выполняю read () из 1 байта из FD. Но select () говорит да, один раз слишком много, и read () блокирует.

Итак, мой вопрос заключается в следующем: почему select () сообщает, что fd готов к чтению, если последующее чтение () блокирует?

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

Я могу опубликовать код по запросу, но это стандарт болота. Установите fd_set, скопируйте его, выберите копию, если FD установлен, вызовите функцию, которая читает байт из того же FD ... в противном случае верните копию fd_set

РЕДАКТИРОВАТЬ: по запросу, вот код:

Настройка моего fd_set:

fd_set fds;
FD_ZERO(&fds); 
FD_SET(interp_output[0], &fds);
FD_SET(STDIN_FILENO, &fds);
struct timeval timeout, tvcopy; timeout.tv_sec=1;
int maxfd=interp_output[0]+1; //always >stdin+1
fd_set read_fds;
FD_COPY(&fds, &read_fds);

В цикле:

if (select(maxfd, &read_fds, NULL, NULL, &timeout)==-1) {perror("couldn't select"); return;}
if (FD_ISSET(interp_output[0], &read_fds)) {
    handle_interp_out();
} else if (FD_ISSET(STDIN_FILENO, &read_fds)) {
//waddstr(cmdwin, "stdin!"); wrefresh(cmdwin);
    handle_input();
}

FDCOPY(&fds, &read_fds);

handle_interp_out (): * * тысяча двадцать-один

void handle_interp_out() {
    int ch;
    read(interp_output[0], &ch, 1);
    if (ch>0) {
            if (ch=='\n') { if (cmd_curline>=cmdheight) cmdscroll(); wmove(cmdwin, ++cmd_curline, 1); }
            else waddch(cmdwin, ch);
            wrefresh(cmdwin);
    }
}

РЕДАКТИРОВАНИЕ 2: Код записи - это просто fprintf для ФАЙЛА *, открытый с помощью fdopen (interp_output [1], "w") - это в другом потоке. Все, что я получаю, это мое «приглашение>» - оно печатает все это правильно, но делает еще одну итерацию, которую не должно делать. Я отключил буферизацию, которая вызывала у меня другие проблемы.

РЕДАКТИРОВАТЬ 3: Это стало проблемой с моим вызовом select (). Похоже, что сразу же возвращается -1, и errno устанавливается в «неверный аргумент». Read () не знает этого и просто продолжает. Что может быть не так с моим select ()? Я обновил код и изменил название, чтобы более точно отразить проблему ...

РЕДАКТИРОВАТЬ 4: Так что теперь я полностью сбит с толку. Значение тайм-аута .tv_sec = 1 не очень хорошо, так или иначе. Избавившись от этого, код работает просто отлично. Если у кого-то есть какие-то теории, я весь в ушах. Я бы просто оставил его в NULL, за исключением того, что этот поток должен периодически делать обновления.

Ответы [ 4 ]

8 голосов
/ 06 декабря 2010

Чтобы абсолютно гарантировать, что чтение никогда не заблокируется, вы должны установить O_NONBLOCK на fd.

Ваша ошибка выбора почти наверняка вызвана тем, что вы не устанавливаете всю структуру времени. Вы только устанавливаете секунды. Другое поле будет содержать данные для сбора мусора из стека.

Использовать инициализацию структуры. Это будет гарантировать, что другие поля установлены в 0.

Это будет выглядеть так:

struct timeval timeout = {1, 0};

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

3 голосов
/ 06 декабря 2010

В соответствии с man-страницей:

При ошибке возвращается -1, и значение errno устанавливается соответствующим образом;наборы и время ожидания становятся неопределенными, поэтому не следует полагаться на их содержимое после ошибки.

Вы не проверяете код возврата из select ().

Наиболее вероятное объяснение состоит в том, что select () прерывается (errno = EINTR) и, таким образом, возвращает ошибку, и бит FD все еще установлен в fd_set «read», давая вам поведение, которое вы видите.

Кстати, это очень плохая идеяименовать переменные после стандартных / системных / общих функций.«read_fds» будет НАМНОГО лучше, чем «read».

2 голосов
/ 06 декабря 2010

Это правильно.См. Справочную страницу select () в Linux, например: http://linux.die.net/man/2/select

"В Linux select () может сообщить дескриптор файла сокета как" готовый к чтению ", но тем не менее последующие блоки чтения"

Единственное решение - использовать НЕБЛОКИРУЮЩУЮ розетку.

0 голосов
/ 06 декабря 2010

Ответ "это не будет". Поведение, которое вы описываете, никогда не должно происходить. Что-то еще идет не так.

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

Если вы опубликуете свой код, возможно, мы сможем помочь вам лучше - опубликуйте код записи и прочитайте, пожалуйста.

Также рассмотрите возможность использования асинхронного ввода-вывода, даже если только для целей отладки. Если то, что вы подозреваете, действительно происходит, то read () вернет EWOULDBLOCK.

Также вы говорите, что копируете набор fd_set. Как? Вы можете опубликовать свой код для этого?

...