7. Для двустороннего взаимодействия процессов требуется наличие двух очередей сообщений (см. например, листинг А.15). Если бы мы изменили листинг 4.4 для использования очередей сообщений Posix вместо каналов, мы бы увидели, что родительский процесс считывает то, что сам же и отправил.
8. Взаимное исключение и условная переменная помещаются в отображаемый файл, совместно используемый всеми процессами, открывающими очередь. Очередь может быть открыта и другими процессами, поэтому при закрытии дескриптора очереди взаимное исключение и условная переменная не уничтожаются.
9. Массиву нельзя присвоить значение другого массива с помощью знака равенства в языке С, а вот структуре можно.
10. Функция main проводит большую часть времени заблокированной в вызове select, ожидая возможности чтения из конца канала. Каждый раз при получении сигнала возврат из обработчика прерывает вызов select, что приводит к возврату ошибки EINTR. Чтобы избежать этой проблемы, функция-обертка Select проверяет возврат данного кода ошибки и снова вызывает select, как показано в листинге Г.5. В книге [24, с. 124] вы можете найти более подробный рассказ о прерывании системных вызовов.
//lib/wrapunix.c
313 int
314 Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
315 struct timeval *timeout)
316 {
317 int n;
318 again:
319 if ( (n = select(nfds, readfds, writefds, exceptfds, timeout)) 0) {
320 if (errno == EINTR)
321 goto again;
322 else
323 err_sys("select error");
324 } else if (n == 0 timeout == NULL)
325 err_quit("select returned 0 with no timeout");
326 return(n); /* возвращаем 0 по тайм-ауту */
327 }
Глава 6
1. Оставшиеся программы должны принимать идентификатор очереди в числовом виде (вместо полного имени). Это изменение можно осуществить путем добавления нового аргумента командной строки или с помощью предположения, что полное имя, состоящее из одних цифр, является не именем файла, а идентификатором очереди. Поскольку большинство имен файлов, передаваемых ftok, являются абсолютными, они заведомо содержат по крайней мере один нецифровой символ (слэш), и это предположение является вполне корректным.
2. Передача сообщений с типом 0 запрещена, а клиент никогда не может иметь идентификатор 1, поскольку этот идентификатор обычно принадлежит процессу init.
3. При использовании единственной очереди на рис. 6.2 злоумышленник мог повлиять на все прочие процессы-клиенты. Если у каждого клиента есть своя очередь (рис. 6.3), злоумышленник портит только свою.
Глава 7
2. Вероятно, процесс завершит работу, прежде чем потребитель успеет сделать все, что нужно, поскольку вызов exit завершает все выполняющиеся потоки.
3. В Solaris 2.6 удаление вызова функций типа destroy приводит к утечке памяти, из чего становится ясно, что функции init осуществляют динамическое выделение памяти. В Digital Unix 4.0B такого не наблюдается, что указывает на разницу в реализации. Тем не менее вызывать функции destroy все равно нужно. С точки зрения реализации в Digital Unix 4.0B используется переменная типа attr_t как объект, содержащий атрибуты, а в Solaris 2.6 эта переменная представляет собой указатель на динамически создаваемый объект.
Глава 9
1. В зависимости от системы может потребоваться увеличивать счетчик до значений, больших 20, чтобы наблюдать ошибку.
2. Для отключения буферизации стандартного потока мы добавляем строку
setvbuf(stdout, NULL, _IONBF, 0);
к функции main перед циклом for. Это не должно ни на что влиять, поскольку вызов printf только один и строка завершается символом перевода. Обычно стандартный поток вывода буферизуется построчно, поэтому в любом случае один вызов printf превращается в один системный вызов write.
3. Заменим printf на
snprintf(line, sizeof(line), "%s: pid = 3.1d, seq# = %d\n", argv[0], (long) pid, seqno);
for (ptr = line; (c = *ptr++) != 0) putchar(c);
и объявим с как целое, a ptr — как char*. Если мы вызвали setvbuf для отключения буферизации стандартного потока вывода, библиотека будет делать системный вызов для вывода каждого символа. На это требуется больше времени, что дает ядру больше возможностей на переключение контекста между процессами. Такая программа должна давать больше ошибок.
4. Поскольку несколько процессов могут заблокировать на чтение одну и ту же область файла, в нашем примере это эквивалентно полному отсутствию блокировок.