Реальные проблемы:
1-я (но незначительная) проблема
struct pollfd pollArray[2] = {{0, POLLIN, 0}, {childOutPipe[0], POLLIN, 0}};
Вы делаете необоснованные предположения о порядке и содержании struct pollfd. Все, что говорится в стандарте, состоит в том, что он содержит (как минимум) три члена; ничего не сказано о порядке их появления.
Заголовок должен определять структуру pollfd, которая должна включать как минимум следующие члены:
int fd The following descriptor being polled.
short events The input event flags (see below).
short revents The output event flags (see below).
Поскольку вы используете C99, используйте обозначение безопасной инициализации:
struct pollfd pollArray[2] =
{
{ .fd = 0, .events = POLLIN, .revents = 0 },
{ .fd = childOutPipe[0], .events = POLLIN, .revents = 0 },
};
Вы можете заменить 0 для стандартного ввода на FILENO_STDIN
из <fcntl.h>
.
2-я (главная) задача
nfds_t nfds = sizeof(pollArray);
Размер массива опроса, вероятно, составляет 16 (байтов) - на большинстве, но не на всех машинах (32-битных и 64-битных). Вам нужно измерение массива poll (которое равно 2). Вот почему весь ад распадается; система смотрит на мусор и запутывается.
Обращаясь к комментарию :
Чтобы найти размерность массива, определенного в локальном файле или функции (но не параметр массива, переданный в функцию, или массив, определенный в другом файле), используйте вариант макроса:
#define DIM(x) (sizeof(x)/sizeof(*(x)))
Это имя восходит к использованию Бейсика в смутном, далеком прошлом; другие имена, которые я видел, были NELEMS
или ARRAY_SIZE
или DIMENSION
(возвращаясь к Фортрану IV), и я уверен, что есть много других.
Что происходит, так как вы не устанавливаете nfds
в 2, системный вызов читает данные после фактического массива struct pollfd
и пытается создать заголовок или хвост материала, который не является struct pollfd
. В частности, он, вероятно, записывает в то, что вы сказали, что это поле revents
строки в массиве struct pollfd
, но фактическим пространством является журнал FILE *
, поэтому он полностью облажался. Аналогично для других локальных переменных. Другими словами, у вас есть переполнение буфера стека - он же переполнение стека, имя, которое должно быть слегка знакомым. Но это происходит потому, что вы это запрограммировали.
Fix:
nfds_t nfds = DIM(pollArray);
3-я (средняя оценка) проблема
poll(pollArray, nfds, 1);
if (errcode < 0) {
Результат poll()
не сохраняется, и переменной errcode
никогда не присваивается значение, но вы сразу же проверяете, какое это значение. Исправленный код, вероятно, будет выглядеть так:
errcode = poll(pollArray, nfds, 1);
if (errcode < 0)
{
fprintf(stderr, "POLL returned with error %d!\n", errcode);
eofFlag = 1;
}
Обратите внимание на символ новой строки, добавленный к сообщению об ошибке - он вам нужен. Или:
if (poll(pollArray, nfds, 1) < 0)
{
int errnum = errno;
fprintf(stderr, "POLL returned with error (%d: %s)\n",
errnum, strerror(errnum));
eofFlag = 1;
}
Во втором случае вы добавили бы '#include <errno.h>
' в список заголовков. Сохранение значения errno
сохраняет его от изменения при вызове функций, но вы можете надежно проверить errno
только в случае сбоя функции (системного вызова). Даже успешные вызовы функций могут оставить errno
ненулевым. (Например, в некоторых системах, если stderr
не идет к терминалу, значение errno
после вызова ввода / вывода равно ENOTTY
, даже если вызов в целом завершился успешно.)
Предыдущие размышления
Некоторые предварительные мысли о том, в чем может быть проблема; Я думаю, что здесь есть еще некоторая полезная информация.
Я подозреваю, что ваша проблема в том, что poll()
«повреждает» набор опрошенных дескрипторов, и вы должны перестраивать его в каждом цикле. (После проверки страницы руководства в Open Group , похоже, что poll()
не имеет проблем, от которых select()
страдает.) Это, безусловно, проблема с системным вызовом select()
.
Ваш дочерний код не закрывает все файловые дескрипторы, когда это необходимо - вы закомментировали один 'close ()', а другого вообще не хватает. Когда дочерний процесс завершил подключение каналов к стандартному вводу и выводу, вы не хотите, чтобы дескрипторы файлов не были по-прежнему открыты; процессы не могут правильно определить EOF.
Подобные комментарии могут относиться к родителю.
Также обратите внимание, что процессу отправки может понадобиться отправить несколько пакетов данных дочернему элементу, прежде чем что-либо появится в стандартном выводе дочернего элемента. В качестве крайнего случая рассмотрим 'sort
'; который читает все свои данные перед генерацией любого вывода. Я беспокоюсь о коде переключения направления, поэтому, хотя я не полностью усвоил то, что он делает. Само по себе переключение направления безвредно - оно просто записывает новое направление, когда начинает писать в противоположном направлении. с прошлого раза.
Более серьезно, не используйте односимвольные операции чтения и записи; читать буферы разумного размера. Разумный размер может быть почти любой степенью двойки от 256 до 8192; Вы можете выбрать другие размеры по своему усмотрению (размер буфера канала может быть хорошим выбором). Обработка нескольких символов одновременно значительно улучшит производительность.
Я решил аналогичные проблемы, используя два процесса, выполняющих мониторинг: один для стандартного ввода, а другой для стандартного вывода - или эквиваленты. Это означает, что мне вообще не нужно использовать poll()
(или select()
). Процесс, обрабатывающий стандартный ввод, читает и блокирует ожидание дополнительной информации; когда что-то прибывает, это регистрирует это и записывает это в стандартный ввод childs. Аналогично для процесса обработки стандартного вывода.
Я могу выкопать код, который работает с каналами, если вам это нужно (см. Мой профиль). Я посмотрел на него год или два назад (хммм; последние изменения фактически были в 2005 году, хотя я перекомпилировал его в 2007 году), и он все еще был в рабочем состоянии (он был написан около 1989 года). У меня также есть код, который работает на сокетах вместо каналов. Им потребуется некоторая адаптация, чтобы удовлетворить ваши требования; они были довольно специализированными (и, в частности, версия канала знает о протоколе клиент-серверной базы данных и пытается обрабатывать полные пакеты информации).