Учебная лаборатория НГУ-Parallels - Написать нам Печать
LogoГлавнаяЛетняя практикаПроектыСеминарыСпецкурсыПубликацииДокументация
W Fat Brother / Мои Книги / Os Errata

Список замеченных содержательных ошибок в первом издании книги «Введение в операционные системы»


Раздел 4.4.1 Управление памятью в Mac OS? и Win 16?

Все инвективы в адрес «ручечного» управления памятью относятся к программированию на plain C и Pascal, т.е. на языках, в которых есть понятие указателя, но при этом указатель нельзя ни во что «завернуть» (как это можно сделать в C++). Действительно, в C++ можно спрятать «ручку» внутри класса smart pointer, который выглядит как указатель, поддерживает все те же самые операции, но делает GlobalUnlock в подходящий момент. Поэтому уже при программировании на C++ проблема далеко не так остра, как при программировании на C.

В языках более высокого уровня – Smalltalk, Java, даже в Basic – понятия указателя вообще нет, и разработчик среды исполнения языка волен реализовать объектные ссылки так, как сочтет нужным. В том числе путем инкапсуляции «ручки» внутри этой ссылки.
Реальные интерпретаторы Smalltalk и современные виртуальные машины Java/C# реализованы именно таким образом: каждая объектная ссылка представляет собой «ручку» – индекс в глобальном массиве указателей на объекты. Это позволяет перемещать объекты по памяти, не теряя ссылок на них, и реализовать такие стратегии сборки мусора, как копирующие и генерационные. В этом смысле, слова, что от «ручечного» управления памятью все отказались, просто не соответствуют действительности.

Тем не менее, критика эксплуатационных характеристик Win16 в этом разделе полностью соответствует действительности. Во второй половине 80х, когда начинась разработка приложений для MacOS, язык C++ был вообще только что придуман, и до середины 90х нормальных компиляторов этого языка вообще не было. Поэтому старые приложения для MacOS и большинство коммерческих приложений для Win16 разрабатывались на plain C и очень сильно страдали от ошибок работы с «ручками».

Исправлено во втором издании книги

Раздел 5.4 Разделяемые библиотеки

В книге утверждается, что в OS/2 и Win32 разделяемые библиотеки загружаются в определенный глобальный диапазон адресов и при загрузке перемещаются для настройки на свободные адреса. Это не совсем верно.

Точнее, это безусловно верно для OS/2: в этой системе приватные сегменты EXE и DLL формата LE/LX загружаются с начала 512-мегабайтного адресного пространства, а разделяемые – с конца. При этом все DLL перемещаются. При этом перемещенный код, разумеется, считается модифицированным и при подкачке сбрасывается в swap-файл. В Win32, DLL формата PE имеют таблицу перемещений (т.наз. fixup). В Windows 95 эта таблица даже принудительно используется – все DLL загружаются как разделяемые и всегда загружаются в третий гигабайт адресного пространства.

Я долгое время пребывал в убеждении, что аналогичная схема загрузки используется и в линии Windows NT, с той лишь разницей, что система не отображает DLL в адреса тех процессов, которые об этом явно не просили, а начиная с версии 4.5 (Windows 2000) еще и учитывает при загрузке полное путевое имя DLL, позволяя, таким образом, загрузить несколько одноименных библиотек. Это убеждение и отражено в книге.

Действительность оказалась гораздо богаче (спасибо Андрею Таранцову, который мне на это указал). Кроме fixup, PE DLL имеют также «рекомендованный» адрес загрузки. Windows NT пытается загрузить DLL по этому адресу. Если это получается, код DLL оказывается разделяемым и при подкачке берется из файла DLL. Поскольку он не модифицирован, то не требуется сбрасывать его в swap-файл. Самое интересное происходит, если код не удается загрузить по рекомендованному адресу. Тогда Windows использует fixup-таблицу и перемещает код по свободному адресу, который ищет в адресном пространстве текущего процесса. Как и в OS/2, перемещенный код считается модифицированным и подкачивается из swap-файла.

Разумеется, в адресных пространствах других процессов этот диапазон адресов может оказаться занят, поэтому Windows не разделяет сегмен кода такой DLL. Никогда.

Таким образом, основная схема загрузки DLL в Windows NT похожа на схему, применяемую в архаичных юниксах с форматами a.out и COFF (кстати, формат PE и является нестандартным расширением формата COFF). Только адресное пространство для разделяемых библиотек никто не распределяет. Вообще никто. Вместо этого, загрузчик использует фиксап-таблицу для разрешения конфликтов. Разумеется, на машинах, на которых установлена сложная смесь приложений, возникает много конфликтов и производительность сильно страдает. Это еще одна причина, по которой не рекомендуется устанавливать на Windows-серверах несколько серверных приложений.

Исправлено во втором издании книги

Раздел 11.4.1 Устойчивость к сбоям питания

Архиваторы PkZIP и InfoZip действительно сохраняют каталог архива в конце файла. И, действительно, недописанный или недокачанный архив не имеет каталога и оказывается испорченным. Однако утверждение, что он испорчен безнадежно, не соответствует действительности. Информация каталога, в действительности, дублируется в теле архива – перед каждым файлом размещается метаинформация о нем с указанием имени, даты создания, длины упакованного образа и т.д. На основе этой информации архиватор может воссоздать каталог – у InfoZip это делается запуском zip с ключом -F или -FF. Разумеется, если речь идет о недописанном архиве, последий файл в архиве будет поврежден. Эта операция осуществляется путем полного сканирования архива с проверкой контрольных сумм, поэтому работает довольно долго, но на выходе получается полноценный архив со всеми полагающимися каталогами.

Эта ошибка была допущена в определенном смысле сознательно – мне хотелось найти убедительный пример того, как нештатное завершение программы приводит к потере данных. Более удачным примером, возможно, было бы поведение MS Office – но и там, в версии Office 2003, была добавлена возможность «починки» поврежденных файлов. По видимому, удачный и в то же время общеизвестный пример найти практически невозможно, потому что все более или менее распространенные форматы файлов в той или иной степени допускают их починку после аварийного завершения программы. Форматы же файлов, которые этого не допускают (и приложения, которые этого не умеют) просто не получают распространения. Естественный отбор в действии.

Исправлено во втором издании книги

Раздел 2.2 Форматы команд машинного языка
В книге имеет место быть опечатка (впрочем это не содержательная ошибка):
на стр.52 в примерах команд ассемблера встречается такой текст:
BNEQ x
Передает управление по указанному адресу, если в слове состояния установлен флаг равенства нулю.

Должно быть либо BEQ, либо «...сброшен флаг...», т.к. BNEQ = Branch if Not EQual – а в случае неравенства операндов результат вычитания ненулевой и флаг равенства результата нулю должен быть сброшен.

Исправлено во втором издании книги

Если вы знаете (или считаете, что знаете) о каких-то других ошибках, пишите комментарии к этой странице.