The Russian Electronic Developer Magazine | |
Русский электронный журнал разработчика | |
Copyright (c) 1998, David C. Zimmerli. All rights reserved.
Перевел с английского Денис Батранков, bdv.da.ru
(5 ноября 1998г)
Главным инструментом использованным для исследования в этой статье был Kernel Debugger (KDB), любезно предоставленный компанией IBM. А также 4 том книги "OS/2 Debugging Handbook". В статье "Приключения при отладке ядра" (EDM/2, ноябрь 1996г., http://www.edm2.com/0410/kdb.html) даны указания по установке и использованию KDB. Чтобы получить набольшую пользу от сегодняшней статьи, вы должны хорошо разбираться в архитектуре Intel 80x86 и особенностях управления памятью.
Для подробного рассмотрения ядра конечно необходимо основываться на определенной версии OS/2, поскольку код ядра меняется и обновляется вместе с другими системными компонентами в различных версиях и обновлениях. Я выбрал OS/2 2.1 для Windows, версия 6.514, как общий знаменатель систем, которые скорее всего еще используются. Так как большинство пользователей будут использовать более новые версии, то размещение памяти, размер файлов и многое другое будут отличаться от рассмотренных здесь. Но основные структуры данных и компоненты кода не изменятся.
Системные DLL DOSCALL1.DLL, KBDCALLS.DLL, QUECALLS.DLL, SESMGR.DLL, OS2CHAR.DLL и так далее не являются частью Presentation Manager, но также, строго говоря, не входят в состав ядра. Большая часть их кода просто перенаправленные точки входа API функций ядра, начинающихся с приставки DOS-. Код SESMGR заслуживает особенного рассмотрения, но я отложу его на будущее с целью сохранить приемлемой длину текущей статьи.
Файл OS2KRNL стандартный сегментированный выполняемый файл формата "LX" и, таким образом, может быть исследован любой утилитой просмотра EXE файлов. Самый легкий и более информативный способ использовать команду "lg" отладчика KDB, которая читает символьный файл поставляемый IBM. Она выводит следующие сегменты:
##lg os2krnl os2krnl: 0400:00000000 DOSGROUP 1100:00000000 DOSCODE 0120:00000000 DBGCODE 0128:00000000 DBGDATA 0030:00000000 TASKAREA 0138:00000000 DOSGDTDATA 0140:00000000 DOSINITDATA 0148:00000000 DOSINITR3CODE %00110000 DOSMVDMINSTDATA %00120000 DOSSWAPINSTDATA %ffeff000 DGROUP 0150:00000000 DOSHIGH2CODE 0158:00000000 DOSHIGH3CODE 0160:00000000 DOSHIGH4CODE %fff3f000 DOSHIGH32CODE ##
Среди этих сегментов есть 16 разрядные кодовые: DOSINITR3CODE, DOSCODE, DOSHIGH2CODE, DOSHIGH3CODE, DOSHIGH4CODE и DBGCODE. 16 разрядные сегменты данных: DOSGROUP, DOSINITDATA и DBGDATA. (DBGCODE и DBGDATA нужны только для поддержки отладчика KDB и отсутствуют в поставляемом ядре.) 32 разрядный кодовый сегмент DOSHIGH32CODE. 32 разрядный сегмент данных DGROUP.
Сегменты "DOS" относятся, конечно, не к DOS, а к Control Program (программа управления) - преемнику DOS в защищенном режиме. Сегменты "HIGH" будут загружены вверх, то есть, в физическую память выше 1 Мб.
Четыре сегмента требуют специальных комментариев. TASKAREA сегмент данных который представляет собой текущий PTDA (Per-Task Data Area), как описано в Debugging Handbook. DOSGDTDATA содержит системную GDT (Global Descriptor Table), которая содержит шлюзы вызовов к API функциям ядра и точки входа к сегментам содержащим 16 разрядный код ядра. Символьный файл содержит имена многих этих сегментов, дающих путь к их функциям. (Наберите команду "ls dosgdtdata" в отладчике KDB.) Сегменты DOSMVDMINSTDATA и DOSSWAPINSTDATA, как подразумевают их имена, осуществляют поддержку Multiple VDMs (Virtual DOS Machines), обсуждаемых в главе VII.
Каждый школьник знает, что при включении или перезапуске любого IBM совместимого компьютера соответствующий загрузочный сектор операционной системы загружается с диска в память по адресу 07C0:0. Выполнение начинается с этого адреса в реальном режиме процессора. (Этот адрес выбран так, чтобы могли работать ранние версии IBM PC с минимальным объемом оперативной памяти в 32Кб)
Загрузочный сектор операционной системы начинается с 3-х байтовой команды JMP. За ней следует структура, содержащая параметры диска, общая для DOS и OS/2. Ее формат описан во многих книгах, поэтому не будем на ней останавливаться. (См. например Ray Duncan, Advanced MS-DOS Programming, 2d. ed. 1988, Microsoft Press, p. 180 или русское издание Р.Данкан, "Профессиональная работа в MS-DOS", 1993, Москва, "Мир", стр. 143.)
Итак, загрузочный сектор загружается и запускается, он загружает и запускает маленький файл (1Кб) OS2BOOT в 0800:0, который загружает и запускает OS2LDR в 2000:0. Конечно загрузка производится используя функции BIOS, поскольку пока еще нет никакой файловой системы.
Содержимое файла OS2LDR одна из наиболее непонятных и слабо документированных частей ядра. Для него нет символьного файла, его нельзя трассировать в отладчике KDB, поскольку KDB находится в отладочной версии OS2KRNL, который еще не загружен. Однако, поскольку OS2LDR выводит множество данных на терминале отладчика, то анализируя выводимую информацию и дизассемлированный код OS2LDR, можно получить хорошее представление о работе этого модуля.
Мы начинаем с проверки оборудования и основных настроек системы - запрашиваем имеющуюся память и установленные устройства, тестируем скорость процессора и запоминаем допустимые видеорежимы. По пути мы получаем загадочные сообщения на терминале отладки:
IODel 000aЭто задержка ввода/вывода - время ожидания между командами IN и OUT - она установлена в 10, основываясь на скорости процессора.
Int12 st 00000000 end 0009f7ff Int1588 st 00100000 end 03ffffffЗдесь BIOS собщает о 640 Кб обычной памяти (приблизительно 9f7ff hex) и 64Мб (03ffffff hex) дополнительной.
CPUUsable = 00000001 CPUWeAre = 00000001У нас 486 процессор. (0 это 386, 1 это 486 и т.д.)
SLFrm len a342Длина сегмента OS2LDR, вместе со стеком составляет 0a342 hex.
cgviВызываем процедуру получения видеорежимов.
cldrНаконец мы переходим к действиям, благодаря которым назван загрузчик: загружаем OS2KRNL или, если этот файл не найден, то OS2KRNLI, устанавливаем ядро.
Загрузчик сначала выдает информацию о сегментах OS2KRNL:
ob flags oi-flags paddr/sel glp laddr/fladdr msz/vsz Object name 01 rw--sfTLaA 00005063 004000/0400 0001 ffe00000/ffe00000 009000/00c5b3 DOSGROUP 02 r-x-sfTLa- 00001065 011000/1100 000a ffe0d000/ffe0d000 00c000/00bfb0 DOSCODE 03 r-x-sf-LaA 00005025 01d000/0120 0016 ffe19000/ffe19000 00b000/00aeea DBGCODE 04 rw--sf-LaA 00005023 028000/0128 0021 ffe24000/ffe24000 009000/0085c0 DBGDATA 05 rw--sN-LaA 0000d0a3 031000/0130 002a ffe2d000/ffe2d000 010000/010000 стек 06 rw--sN-LaA 0000d023 041000/0138 003a ffe3d000/ffe3d000 002000/001e50 DOSGDTDATA 07 rw--sf-LaA 00005023 043000/0140 003c ffe3f000/ffe3f000 002000/004b4e DOSINITDATA 08 r-x-sf-LaA 00005025 048000/0148 003e ffe44000/ffe44000 002000/001fe8 DOSINITR3CODE 09 rw-BPf-h-- 00002213 100000/0000 0040 ffefc000/00110000 001000/0001ac DOSMVDMINSTDATA 0a rw-BPf-h-- 00002013 101000/0000 0041 ffefd000/00120000 002000/001948 DOSSWAPINSTDATA 0b rw-Bsf-h-A 00006033 103000/0000 0043 ffeff000/ffeff000 012000/015326 DGROUP 0c r-x-sf-ha- 00001035 119000/0150 0055 fff15000/fff15000 010000/00fdcc DOSHIGH2CODE 0d r-x-sf-ha- 00001035 129000/0158 0065 fff25000/fff25000 00a000/009a08 DOSHIGH3CODE 0e r-x-sf-ha- 00001035 133000/0160 006f fff2f000/fff2f000 010000/00f304 DOSHIGH4CODE 0f r-xBsf-h-- 00002035 143000/0000 007f fff3f000/fff3f000 081000/080628 DOSHIGH32CODEВ крайнем правом столбце мои комментарии, связывающие список выведенных объектов с сегментами рассмотренными в предыдущем разделе.
Кстати термин "объект" здесь означает 32 разрядный выполняемый модуль эквивалентно термину "сегмент" в 16 разрядном модуле. Объект сложнее чем сегмент, поскольку он содержит страницы, которые могут быть загружены и выгружены в файл подкачки, независимо друг от друга. Вот почему чувствовалась необходимость нового трмина. Но во многих случаях объект это просто сегмент не ограниченный длиной в 64Кб. Я буду использовать эти термины по-необходимости.
Для оценки таблица содержит атрибуты, физические и линейные адреса, селекторы, размеры каждого сегмента OS2KRNL. В действительности загрузка OS2KRNL проходит в два этапа: сначала загружаются в обычную память "верхние" сегменты, а затем перемещаются на положенное место используя функцию BIOS Int 15h/87h "Перенос блока дополнительной памяти". (Помните, что мы пока находимся в реальном режиме!) При втором проходе, загружаются сегменты находящиеся в обычной памяти.
В заключение OS2LDR выдает нам карту памяти:
pa=00000000 sz=00001000 va=00000000 sel=0000 fl=2000 of=00000003 ow=0000 Real mode IVT pa=00001000 sz=00002300 va=ffef9000 sel=0100 fl=2014 of=00001004 ow=ff6d OS2LDR 32-bit int dispatch pa=00004000 sz=0000c5b3 va=ffe00000 sel=0400 fl=2144 of=00005063 ow=ffaa DOSGROUP pa=00011000 sz=0000bfb0 va=ffe0d000 sel=1100 fl=2244 of=00001065 ow=ffaa DOSCODE pa=0001d000 sz=0000aeea va=ffe19000 sel=0120 fl=2344 of=00005025 ow=ffaa DBGCODE pa=00028000 sz=000085c0 va=ffe24000 sel=0128 fl=2444 of=00005023 ow=ffaa DBGDATA pa=00031000 sz=00010000 va=ffe2d000 sel=0130 fl=2544 of=0000d0a3 ow=ffaa стек pa=00041000 sz=00001e50 va=ffe3d000 sel=0138 fl=2644 of=0000d023 ow=ffaa DOSGDTDATA pa=00043000 sz=00004b4e va=ffe3f000 sel=0140 fl=2744 of=00005023 ow=ffaa DOSINITDATA pa=00048000 sz=00001fe8 va=ffe44000 sel=0148 fl=2844 of=00005025 ow=ffaa DOSINITR3CODE pa=0004a000 sz=00000ac8 va=00000000 sel=4a00 fl=2001 of=00000000 ow=0000 OS2DUMP pa=0004b000 sz=00049000 va=00000000 sel=0000 fl=2002 of=00000000 ow=0000 не используется pa=00094000 sz=0000a762 va=ffeee000 sel=0000 fl=2054 of=00001003 ow=ffab OS2LDR (перемещен) pa=0009f000 sz=00000800 va=00000000 sel=0000 fl=2002 of=00000000 ow=0000 не используется pa=0009f800 sz=00000800 va=ffeed800 sel=0000 fl=2004 of=00000000 ow=ff37 romdata pa=000a0000 sz=00060000 va=00000000 sel=0000 fl=0001 of=00000000 ow=0000 video/BIOS area pa=00100000 sz=000001ac va=ffefc000 sel=0000 fl=0944 of=00002213 ow=ffaa DOSMVDMINSTDATA pa=00101000 sz=00001948 va=ffefd000 sel=0000 fl=0a44 of=00002013 ow=ffaa DOSSWAPINSTDATA pa=00103000 sz=00015326 va=ffeff000 sel=0000 fl=0b44 of=00006033 ow=ffaa DGROUP pa=00119000 sz=0000fdcc va=fff15000 sel=0150 fl=0c44 of=00001035 ow=ffaa DOSHIGH2CODE pa=00129000 sz=00009a08 va=fff25000 sel=0158 fl=0d44 of=00001035 ow=ffaa DOSHIGH3CODE pa=00133000 sz=0000f304 va=fff2f000 sel=0160 fl=0e44 of=00001035 ow=ffaa DOSHIGH4CODE pa=00143000 sz=00080628 va=fff3f000 sel=0000 fl=0f44 of=00002035 ow=ffaa DOSHIGH32CODE pa=001c4000 sz=00e3c000 va=00000000 sel=0000 fl=0002 of=00000000 ow=0000 не используется pa=01000000 sz=00000000 va=00000000 sel=0000 fl=0001 of=00000000 ow=0000 не используется pa=01000000 sz=03000000 va=00000000 sel=0000 fl=0002 of=00000000 ow=0000 не используется pa=04000000 sz=00000000 va=00000000 sel=0000 fl=4000 of=00000000 ow=0000 предел физической памятиЗдесь я снова добавил комментарии в самой правой колонке. Во второй колонке с конца находится "System Object Id" описание которого находится в разделе 4.6 Debugging Handbook, том IV.
Мы настраиваем микросхему 8259 PIC так, чтобы IRQ с 0 по 7 соответствовали прерывания 50h-57h, а IRQ с 8 по 0Fh соответствовали прерывания 70h-77h:
rPICМы переходим к процедуре syiInitializeOS2 в сегменте DOSCODE файла OS2KRNL:
j syiТеперь мы в настоящем ядре и можем просмотреть остальную часть кода инициализации ядра, используя отладчик KDB. Специальные возможности KDB позволяют при нажатии и удержании клавиш "R", "P" или пробел на терминале отладки прервать запущенную программу либо перед переключением в защищенный режим, либо после переключения, но до инициализации загрузчика и менеджера страниц, либо после того и другого, соответственно. (Проверьте установку коэффициента повторения на терминале отладчика в максимальное значение, иначе нажатие клавиши может быть пропущено при передаче по COM порту.)
В основном часть инициализации DOSCODE содержит логику разбора файла CONFIG.SYS. Также в нем содержится компонент называемый "начальная файловая система системы" (sistem init file system - коротко sifs), использемый для чтения CONFIG.SYS, поскольку перед установкой полноценной файловой системы нужны различные BASEDEV и другие файлы. Каждая важная часть ядра (планировщик задач, менеджер страниц, загрузчик и др.) имеет процедуру инициализации, которая вызывается из этой точки, и мы "вернемся" к процедуре syiProcess в сегменте DOSINITR3CODE.
syiProcess теперь загружает и инициализирует обычные (не основные) устанавливаемые драйверы устройств, загружает системные DLL и запускает shell. Это первое место где мы имеем доступ к ADD драйверам, используемым полностью инициализированной системой для доступа к жесткому диску. Поскольку одна из первых процедур вызываемой syiProcess находится в модуле inicp.asm (инициализация кодовой страницы), то здесь мы можем получить печально известное сообщение об ошибке "Не найден COUNTRY.SYS", даже когда нет проблем с COUNTRY.SYS, если основные драйвера устройств не установлены правильно.
В первом случае мы рассмотрим вызов DOS32READ с файлом, находящимся в FAT разделе SCSI диска. Это точка входа в DOSCALL1.DLL, которая вскоре через шлюз вызовов передается в 32 разрядную часть ядра и вызывает FS32IREAD. Для доступа к FAT, FS32IREAD затем вызывает 16 разрядную процедуру h_DOS_Read в DOSHIGH4CODE, которая, убедившись в том что требуемые данные не находятся в ранее считанном буфере, формирует "список запросов" и передает его в драйвер устройства OS2DASD.DMD. Список запросов создается в процедурах _BufReadExecute и _ReqListExecute из DOSHIGH32CODE и содержит один запрос с расширенным кодом команды 1Eh для чтения.
Запрос указывает начальный блок, число блоков для чтения и адрес буфера для считанных данных. OS2DASD.DMD затем вызывает соответствующий ADD драйвер устройства - для примера, AHA152X.ADD - для доступа к физическому носителю.
Во втором случае все также, только файл для чтения находится в разделе HPFS. В этом случае FS32IREAD обходит 16 разрядную процедуру для FAT в DOSHIGH4CODE, и вызывает вместо нее FS_READ из HPFS.IFS. Файловая система HPFS затем проверяет данные в буфере и взаимодействует с модулем OS2DASD.DMD.
В третьем случае расмотрим чтение фала с гибкого диска. В этом случае ядро работает также как в первом случае, но OS2DASD.DMD передаст запрос в IBM1FLPY.ADD (или IBM2FLPY.ADD на Micro Channel машинах), а не к AHA152X.ADD.
Наконец для чтения файла с SCSI CD-Rom, FS32IREAD вызывает FS_READ из драйвера файловой системы CD-Rom CDFS.IFS. CDFS также проверяет буфер и посылает список запросов к OS2CDROM.DMD, который вызовет соответствующий BASEDEV - например LMS206.ADD для Philips CD-Rom.
_tkSchedNext обращается к планировщику (_SCHGetNextRunner), чтобы решить какая из готовых к выполнению нитей следующей получит управление. Некоторые аспекты планировщика, с его множеством состояний и переходов, очередями приоритетов и так далее изложены в Debugging Handbook. Сейчас мы просто заметим, что _SCHGetNextRunner возвращает в регистре EAX указатель на TCB новой или следующей нити. Этот указатель затем становится единственным аргументом процедуры _PGSwitchContext.
Код _PGSwitchContext занимает 559 байт в DOSHIGH32CODE и полность закрыт от изучения. Мы не можем пройти по шагам его код в отладчике KDB, поскольку таблица страниц и системные структуры находятся в переходном состоянии, которое отладчик не может сделать понятным. Но изучая дизассемблированный код мы можем понять его работу и получить важные знания о происходящем в OS/2 процессе.
Дальше мы действуем в зависимости от того переключаемся ли мы в другой процесс, или просто в другую нить того же самого процесса. Если это переключение процессов, мы должны перезаписать часть таблицы страниц соответствующих памяти пользователя (как правило около 256Мб), чтобы показать новые физические адреса. Переключение процессов также требует изменить значение LDTR, поскольку заполнение LDT может быть другим для другого процеса.
В любом случае при переключении процесса или нити мы должны изменить сегмент TASKAREA (селектор 30), поскольку этот селектор адресует текущий TCB и TSD, также как PTDA. Мы должны также обновить различные системные глобальные переменные: _pPTDACur, _TKSSBase, _TKTCBBias, _pTCBCur, _pTSDCur и указатели стека кольца 0 и кольца 2.
Другие детали механизма переключения контекстов изложены на странице 339 Debugging Handbook, том I.
Конечно существует возможность, что приложение не будет делать никаких системных вызовов долгие периоды времени. Возможно программа решает дифференциальное уравнение, делает сложные поиски текстовых строк, или иначе занимается своим собственным делом без обращения к сервисам ядра и без ввода/вывода. Будет ли процедура KMExitKmodeEvents тогда пропущена и будут ли все остальные нити терять время ожидая когда такая программа завершится?
Ответ, как Вы ожидаете, нет, спасибо программируему таймеру 8254, встроенному в материнскую плату. При загрузке счетчик 0 микросхемы 8254 настраивается на работу в режиме 2 (режим генерации), чтобы возникало IRQ 0 приблизительно 18.2 раз в секунду. Подобно другим IRQ, это прерывание перехватывается процедурой intIRQRouter в DOSHIGH32CODE, и на посылку IRQ 0 intIRQRouter вызывает KMExitKmodeEvents, как описано выше. Это заставляет приложение подвергнуться такому же рассмотрению планировщика, как если бы оно обратилось к ядру непосредственно.
Однако после вызова DosAllocMem не выделяется физическая память или место в файле подкачки. Этот механизм называется "ленивая фиксация": когда кто-нибудь попытается считать или записать в эту область виртуальной памяти будет сгенерирована ошибка страницы (trap 0eh) и процедура обработки ошибки из DOSHIGH32CODE уже затем разместит физическую память и установит бит присутствия в соответствующей записи таблицы страниц.
Простейший эксперимент показывает состояние "до" и "после" таблицы страниц в результате выполнения DosAllocMem. Здесь программа перед выполнением вызова запроса о выделении 00020000h байт или 128Кб:
eax=0006eb03 ebx=000a0000 ecx=0006eb88 edx=0006ebb0 esi=00000000 edi=00019010 eip=000120f7 esp=0006eb7c ebp=0006ebd4 iopl=2 rf -- -- nv up ei pl nz na pe nc cs=005b ss=0053 ds=0053 es=0053 fs=150b gs=0000 cr2=00093ffe cr3=001f6000 005b:000120f7 e8385a011a call DOS32ALLOCMEM (1a027b34) ##d ss:esp l 20 0053:0006eb7c 88 eb 06 00 00 00 02 00-13 00 00 00 03 00 00 00 .k.............. 0053:0006eb8c 74 34 01 00 d0 eb 06 00-08 00 00 00 00 00 00 00 t4..Pk.......... ##А здесь записи таблиц страниц для %120000 и %130000:
##dp %120000 linaddr frame pteframe state res Dc Au CD WT Us rW Pn state %00120000* 02ec1 frame=02ec1 2 0 D A U W P resident ##dp %130000 linaddr frame pteframe state res Dc Au CD WT Us rW Pn state %00130000* 02ec1 frame=02ec1 2 0 D A U W P resident ##Наберем "p" и изучим стек и таблицу страниц снова:
##p eax=00000000 ebx=000a0000 ecx=0006eb88 edx=0006ebb0 esi=00000000 edi=00019010 eip=000120fc esp=0006eb7c ebp=0006ebd4 iopl=2 -- -- -- nv up ei pl nz na pe nc cs=005b ss=0053 ds=0053 es=0053 fs=150b gs=0000 cr2=00093ffe cr3=001f6000 005b:000120fc 83c40c add esp,+0c ##d %6eb88 l 20 %0006eb88 00 00 12 00 74 34 01 00-d0 eb 06 00 08 00 00 00 ....t4..Pk...... %0006eb98 00 00 00 00 00 00 00 00-00 00 00 00 e0 5d 01 00 ............`].. ##dp %120000 linaddr frame pteframe state res Dc Au CD WT Us rW Pn state %00120000* 02ec1 frame=02ec1 2 0 D A U W P resident %00120000 vp id=01608 0 0 c u U W n pageable %00121000 vp id=01609 0 0 c u U W n pageable %00122000 vp id=0160a 0 0 c u U W n pageable %00123000 vp id=0160b 0 0 c u U W n pageable %00124000 vp id=0160c 0 0 c u U W n pageable %00125000 vp id=0160d 0 0 c u U W n pageable %00126000 vp id=01635 0 0 c u U W n pageable %00127000 vp id=01636 0 0 c u U W n pageable %00128000 vp id=01637 0 0 c u U W n pageable %00129000 vp id=01638 0 0 c u U W n pageable %0012a000 vp id=01639 0 0 c u U W n pageable %0012b000 vp id=0163a 0 0 c u U W n pageable %0012c000 vp id=0163b 0 0 c u U W n pageable %0012d000 vp id=0163c 0 0 c u U W n pageable %0012e000 vp id=0163d 0 0 c u U W n pageable %0012f000 vp id=0163e 0 0 c u U W n pageable ##dp %130000 linaddr frame pteframe state res Dc Au CD WT Us rW Pn state %00130000* 02ec1 frame=02ec1 2 0 D A U W P resident %00130000 vp id=0163f 0 0 c u U W n pageable %00131000 vp id=01640 0 0 c u U W n pageable %00132000 vp id=01641 0 0 c u U W n pageable %00133000 vp id=01642 0 0 c u U W n pageable %00134000 vp id=01643 0 0 c u U W n pageable %00135000 vp id=01644 0 0 c u U W n pageable %00136000 vp id=01645 0 0 c u U W n pageable %00137000 vp id=01646 0 0 c u U W n pageable %00138000 vp id=01647 0 0 c u U W n pageable %00139000 vp id=01648 0 0 c u U W n pageable %0013a000 vp id=01649 0 0 c u U W n pageable %0013b000 vp id=0164a 0 0 c u U W n pageable %0013c000 vp id=0164b 0 0 c u U W n pageable %0013d000 vp id=0164c 0 0 c u U W n pageable %0013e000 vp id=0164d 0 0 c u U W n pageable %0013f000 vp id=0164e 0 0 c u U W n pageable ##Ядро разместило записи о 128Кб памяти в таблице страниц начинающейся с линейного адреса %120000.
Главной рабчей процедурой ядра, которая делает это является _VMAllocMem, которая вызывает процедуры _VMReserve, _PGAlloc и _SELAlloc.
Мы можем также захотеть увидеть что случается, когда программа действительно пытается обратиться к памяти. По команде "vsp e" KDB будет перехватывать все ошибки страниц, перед их обработкой и это можно использовать совместно с "zs" (изменить команду по-умолчанию), чтобы собрать статистику по механизму обработки ошибок страниц и его влияния на работу системы. В целях трассировки, легче всего задать точку остановки в начале процедуры _PGPageFault, которая обрабатывает это исключение.
Существует по-существу три части эмуляции DOS в OS/2: менеджер MVDM, настоящий эмулятор DOS и эмулятор x86. Четвертая часть, виртуальные драйвера устройств необходимые для запуска многих программ DOS, существует отдельно от ядра, но использует вызовы Virtual DevHelp API выполняемые в менеджере MVDM.
Давайте посмотрим работу по трассировке в KDB простой программы "Hello, world", написанной на ассемблере. Мы открываем окно DOS, вследствие чего ядро дает нам VDM с копией "части виртуального ядра DOS" - файл C:\OS2\MDOS\DOSKRNL - загруженной в обычную память чтобы обеспечить сервисы int 21h. Мы теперь запускаем программу HELLO.EXE. Это полный дизассемблированный код:
--u ac2:0 l 7 0ac2:00000000 b8c30a mov ax,0ac3 0ac2:00000003 8ed8 mov ds,ax 0ac2:00000005 b409 mov ah,09 ; выводим строку из ds:dx 0ac2:00000007 ba0000 mov dx,0000 0ac2:0000000a cd21 int 21 0ac2:0000000c b44c mov ah,4c 0ac2:0000000e cd21 int 21 --d ac3:0 l 10 0ac3:00000000 48 65 6c 6c 6f 2c 20 77-6f 72 6c 64 0d 0a 24 00 Hello, world..$.Заметьте, что KDB использует приглашение в виде двух тире "-" вместо обычного "##" чтобы показать, что мы работаем в режиме V86.
После набора "t" несколько раз мы достигаем системного вызова DOS:
--t eax=000009c3 ebx=00000000 ecx=000000ff edx=00000000 esi=00000000 edi=00000100 eip=0000000a esp=00000100 ebp=0000091c iopl=3 -- vm -- nv up ei pl zr na pe nc cs=0ac2 ss=0ac4 ds=0ac3 es=0ab2 fs=0000 gs=0000 cr2=01390000 cr3=001f6000 0ac2:0000000a cd21 int 21 --Однако мы не можем выполнить "t" в этот вызов, поскольку эта инструкция вызывает General Protection Exception (Trap 0D) даже если IOPL равен 3, по-видимому потому, что вход IDT для int 21h неправильный или содержит нулевой указатель:
--di 21 0021 TrapG Sel:Off=0000:00000000 DPL=3 PМы задаем точку останова у trap0d в 32 разрядном ядре и продолжаем:
--br e trap0d --g Debug register hit eax=000009c3 ebx=00000000 ecx=000000ff edx=00000000 esi=00000000 edi=00000100 eip=fff491bc esp=00006708 ebp=0000091c iopl=3 rf -- -- nv up ei pl zr na pe nc cs=0170 ss=0030 ds=0000 es=0000 fs=0000 gs=0000 cr2=01390000 cr3=001f6000 os2krnl:DOSHIGH32CODE:trap0d: 0170:fff491bc 6a0d push +0d ;br0 ##Этот код вызовет em86opINTnn, чтобы эмулировать программное прерывание и мы скоро выполним "iretd" и вернемся в режим V86. Вызов затем будет передан DOSKRNL в обычной памяти.
Мы оставим на следующий день окончание сказки, где DOSKRNL должен еще обрабатывать обращения к BIOS, которые будут снова вызывать General Protection Exception и будут перенаправлены к виртуальным драйверам (VDD), таким как VBIOS.SYS и VVGA.SYS. Они будут взаимодействовать с SESMGR и физическими драйверами (PDD), чтобы закончить вывод приветствия на экран.
Некоторые дополнительные знания о работе DOS эмуляции в OS/2 могут быть найдены в The Design of OS/2, 1992 Addison-Wesley, by H. M. Deitel and M. S. Kogan, pp. 290-300.
Эта функция отключает драйверы IFS подменив все их точки входа адресом процедуры ShutdownBlock в DOSHIGH2CODE. Все нити таким образом при попытке обратиться к FSD будут блокированы. Некоторые процедуры, однако, остаются нетронутыми для использования в коде закрытия системы: FS_COMMIT, FS_DOPAGEIO, FS_FSCTL, FS_FLUSHBUF и FS_SHUTDOWN. Также для драйверов файловых систем использующих программу подкачки некоторые ключевые точки входа сохраняются на месте: FS_SDCHGFILEPTR, FS_SDFSINFO, FS_SDREAD и FS_SDWRITE. Это дает возможность процедурам управления страниц продолжать выполнять заключительные операции, пока все остальные нити заблокированы.
Мы затем посылаем всем установленным драйверам устройств команду "shutdown" (код команды 1Ch) через пакет запроса. Каждый драйвер вызывается дважды с параметрами 0 и 1 для начала и конца закрытия сисемы, соответственно. Также для каждого драйвера IFS процедура FS_SHUTDOWN вызывается дважды с флагами начала и конца закрытия системы. Между этими вызовами процедуры shutdown$FlushAllSFTs и h_FSD_FlushBuf стабилизируют кешируемые части файловых систем.
Аббревиатуры: TCB - task control block TSD - task IVT - interrupt vector table IFS - installable file system FSD - file system driver SFT - PTDA - Per-Task Data Area DDK - device driver kit GDT - global descriptor table PLDT - pointer local descriptor table
Интересные ссылки:
Юридическое представительство в арбитражном суде в Новосибирске.
Комментариев к странице: 0 | Добавить комментарий
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов