Тема OS24асу. Совместное использование памяти. Простейшим способом взаимодействия процессов является совместный доступ к общей области памяти. Это выглядит так, как если бы два или более процесса вызвали функцию malloc () и получили указатели на один и тот же блок памяти. Когда один из процессов меняет содержимое памяти, другие процессы замечают это изменение. Быстрое локальное взаимодействие Совместное использование памяти - самый быстрый способ взаимодействия. Процесс обращается к общей памяти с той же скоростью, что и к своей собственной памяти, и никаких системных вызовов или обращений к ядру не требуется. Устраняется также ненужное копирование данных. Ядро не синхронизирует доступ процессов к общей памяти - об этом следует позаботиться программисту. Например, процесс не должен читать данные из совместно используемой памяти, пока в нее осуществляется запись, и два процесса не должны одновременно записывать данные в одну и ту же область памяти. Стандартная стратегия предотвращения подобной конкуренции заключается в использовании семафоров. Тем не менее в приводимом далее примере программы доступ к памяти осуществляет только один процесс: просто мы хотим сконцентрировать внимание читателей на механизме совместного использования памяти и не перегружать программу кодом синхронизации. Модель памяти При совместном использовании сегмента памяти один процесс должен сначала выделить память. Затем все остальные процессы, которые хотят получить доступ к ней, должны подключить сегмент. По окончании работы с сегментом каждый процесс отключает его. Последний процесс освобождает память. Для того чтобы понять принципы выделения и подключения сегментов памяти, необходимо разобраться в модели памяти Linux. В Linux виртуальная память (ВП) каждого процесса разбита на страницы. Все процессы хранят таблицу соответствий между своими адресами памяти и страницами ВП, содержащими реальные данные. Несмотря на то что за каждым процессом закреплены свои адреса, разным процессам разрешается ссылаться на одни и те же страницы. Это и есть совместное использование памяти. При выделении совместно используемого сегмента памяти создаются страницы ВП. Это действие должно выполняться только один раз, так как все остальные процессы будут обращаться к этому же сегменту. Если запрашивается выделение существующего сегмента, новые страницы не создаются; вместо этого возвращается идентификатор существующих страниц. Чтобы сделать сегмент общедоступным, процесс подключает его, при этом создаются адресные ссылки на страницы сегмента. По окончании работы с сегментом адресные ссылки удаляются. Когда все процессы завершили работу с сегментом, один (и только один) из них должен освободить страницы виртуальной памяти. Размер совместно используемого сегмента кратен размеру страницы ВП. В Linux последняя величина обычно равна 4 Кбайт, но никогда не помешает это проверить с помощью функции getpagesize (). Выделение сегментов памяти Процесс выделяет сегмент памяти с помощью функции shmget (). Первым аргументом функции является целочисленный ключ, идентифицирующий создаваемый сегмент. Если несвязанные процессы хотят получить доступ к одному и тому же сегменту, они должны указать одинаковый ключ. К сожалению, ничто не мешает посторонним процессам выбрать тот же самый ключ сегмента, а это приведет к системному конфликту. Указание специальной константы IPC_PRIVATE в качестве ключа позволяет гарантировать, что будет создан совершенно новый сегмент. Во втором аргументе функции задается размер сегмента в байтах. Это значение округляется, чтобы быть кратным размеру страницы ВП. Третий параметр содержит набор битовых флагов. Перечислим наиболее важные из них.  IPC_CREAT. Указывает на то, что создается новый сегмент, которому присваивается заданный ключ.  IPC_EXCL. Всегда используется совместно с флагом IPC_CREAT и заставляет функцию shmget () выдать ошибку в случае, когда сегмент с указанным ключом уже существует. Если флаг не указан и возникает описанная ситуация, функция shmget () возвращает идентификатор существующего сегмента, не создавая новый сегмент.  Флаги режима. В эту группу входят 9 флагов, задающих права доступа к сегменту для владельца, группы и остальных пользователей. Биты выполнения игнорируются. Проще всего задавать права доступа с помощью констант, определенных в файле (они описаны на man-странице функции stat ()). Например, флаги S_IRUSR и S^IWUSR предоставляют право чтения и записи владельцу сегмента, а флаги S_IROTH и S_IWOTH предоставляют аналогичные права остальным пользователям. В следующем фрагменте программы функция shmget () создает новый совместно используемый сегмент памяти (или возвращает идентификатор существующего, если значение shm_key уже зарегистрировано в системе), доступный для чтения/записи только его владельцу: int segment_id = shmget (shm_Hey, getpagesize(), IPC_CREAT | S_IRUSR | S_IWUSR); В случае успешного завершения функция возвращает идентификатор сегмента. Если сегмент уже существует, проверяются права доступа к нему. Подключение и отключение сегментов Чтобы сделать сегмент памяти общедоступным, процесс должен подключить его с помощью функции shmat(). В первом ее аргументе передается идентификатор сегмента, возвращенный функцией shmget (). Второй аргумент - это указатель, определяющий, где в адресном пространстве процесса необходимо создать привязку на совместно используемую область памяти. Если задать значение NULL, ОС Linux выберет первый доступный адрес. Третий аргумент может содержать следующие флаги.  SHM_RND. Указывает на то, что адрес, заданный во втором параметре, должен быть округлен, чтобы стать кратным размеру страницы. Если этот флаг не указан, необходимо самостоятельно позаботиться о выравнивании сегмента по границе страницы.  SHM_RDONLY. Указывает на то, что сегмент доступен только для чтения, но не для записи. В случае успешного завершения функция возвращает адрес подключенного сегмента. Дочерний процесс, созданный функцией fork (), унаследует этот адрес и в случае необходимости сможет отключить сегмент. По завершении работы с сегментом его необходимо отключить с помощью функции shmdt (). Ей следует передать адрес, возвращаемый функцией shmat (). Если текущий процесс был последним, кто ссылался на сегмент, сегмент удаляется из памяти. Функции exit () и ехес () автоматически отключают сегменты. Контроль и освобождение совместно используемой памяти Функция shmctl() возвращает информацию о совместно используемом сегменте и способна модифицировать его. Первым параметром является идентификатор сегмента. Чтобы получить информацию о сегменте, укажите в качестве второго параметра константу IPC_STAT, а в третьем параметре передайте указатель на структуру shmid_ds. Чтобы удалить сегмент, передайте во втором параметре константу IPC_RMID, а в третьем параметре - NULL. Сегмент удаляется, когда последний подключивший его процесс отключает сегмент. Каждый совместно используемый сегмент должен явно освобождаться с помощью функции shmctl (), чтобы случайно не был превышен системный лимит на общее число таких сегментов. Функции exit () и ехес () отключают сегменты, но не освобождают их. Описание других операций, выполняемых над совместно используемыми сегментами памяти, можно найти на man-странице функции shmctl (). Использование общего адресного пространства. Рассмотрим использование общего адресного пространства. В следующей программе представлен текст динамической библиотеки и программы, вызывающей процедуру из этой библиотеки. Здесь ничего сложного, просто мы хотели продемонстрировать, что основной процесс и динамическая библиотека используют одно и то же адресное пространство. Процесс передает адреса строк, которые находятся в блоке данных основного процесса. В свою очередь, процедура возвращает в основной процесс адрес строки, находящейся в блоке данных динамической библиотеки. Файл pr73_1.asm - динамически подключаемая библиотека. .386P ;Плоская модель. IFDEF MASM .MODEL FLAT, STDCALL ELSE .MODEL FLAT ENDIF PUBLIC DLLP1 ;Константы. ;Сообщения, приходящие при открытии ;динамической библиотеки. DLL_PROCESS_DETACH equ 0 DLL_PROCESS_ATTACH equ 1 DLL_THREAD_ATTACH equ 2 DLL_THREAD_DETACH equ 3 IFDEF MASM ;MASM ;Прототипы внешних процедур. EXTERN MessageBoxA@16: NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN MessageBoxA@: NEAR MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TEXT DB 'Строка в динамической библиотеке',0 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. DLLENTRY: MOV EAX,DWORD PTR [EBP+0CH] CMP EAX,0 JNE D1 ;Закрытие библиотеки. JMP _EXIT D1: CMP EAX, 1 JNE _EXIT ;Открытие библиотеки. _EXIT: MOV EAX,1 RET 12 ;------------------------------------- ;Адреса параметров ; [EBP+8] ; [ЕВР+0СН] DLLP1 PROC EXPORT PUSH EBP MOV EBP,ESP PUSH 0 PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+8] PUSH 0 CALL MessageBoxA@16 POP EBP LEA EAX,TEXT RET 8 DLLP1 ENDP _TEXT ENDS END DLLENTRY Файл pr73_2.asm - основной модуль, вызывающий процедуру из DLL. .386P ;Плоская модель. .MODEL FLAT, STDCALL ;Константы. ;Прототипы внешних процедур. IFDEF MASM ;MASM EXTERN GetProcAddress@8:NEAR EXTERN LoadLibraryA@4:NEAR EXTERN FreeLibrary@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN MessageBoxA@16:NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN GetProcAddress:NEAR EXTERN LoadLibraryA:NEAR EXTERN FreeLibrary:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR GetProcAddress@8 = GetProcAddress LoadLibraryA@4 = LoadLibraryA FreeLibrary@4 = FreeLibrary ExitProcess@4 = ExitProcess MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TXT DB 'Ошибка динамической библиотеки',0 MS DB 'Сообщение',0 LIBR DB 'pr73_1.DLL',0 HLIB DD ? MSI DB 'Сообщение из библиотеки',0 TEXT DB 'Строка содержится в основном модуле',0 IFDEF MASM NAMEPROC DB '_DLLP1@0',0 ELSE NAMEPROC DB 'DLLP1',0 ENDIF _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. START: ;Загрузить библиотеку. PUSH OFFSET LIBR CALL LoadLibraryA@4 CMP EAX,0 JE _ERR MOV HLIB,EAX ;Получить адрес процедуры. PUSH OFFSET NAMEPROC PUSH HLIB CALL GetProcAddress@8 CMP EAX,0 JNE YES_NAME ;Сообщение об ошибке. _ERR: PUSH 0 PUSH OFFSET MS PUSH OFFSET TXT PUSH 0 CALL MessageBoxA@16 JMP _EXIT YES_NAME: PUSH OFFSET MSI PUSH OFFSET TEXT CALL EAX PUSH 0 PUSH OFFSET MS PUSH EAX PUSH 0 CALL MessageBoxA@16 ;Закрыть библиотеку. PUSH HLIB CALL FreeLibrary@4 ;Библиотека автоматически закрывается также ;при выходе из программы. ;Выход. _EXIT: PUSH 0 CALL ExitProcess@4 _TEXT ENDS END START Тексты этих файлов можно взять здесь. Результат работы приложения изображен на рисунке 1: Рис.1. Результат работы приложения Трансляция динамической библиотеки: пакет MASM32: ML /с /coff /DMASM pr73_1.asm LINK /SUBSYSTEM:WINDOWS /DLL /ENTRY:DLLENTRY pr73_1.obj пакет TASM32: TASM32 /ml pr73_1.asm TLINK32 -aa -Tpd pr73_1.obj,,,,pr73_1.def Содержимое файла pr73_1.def: EXPORTS DLLP1 Трансляция программы: пакет MASM32: ML /с /coff /DMASM pr73_2.asm LINK /SUBSYSTEM:WINDOWS pr73_2.obj пакет TASM32: TASM32 /ml pr73_2.asm TLINK32 -aa pr73_2.obj На следующем шаге мы продолжим изучение этого вопроса. Совместное использование памяти разными процессами Приведем пример использования динамически подключаемой библиотеки различными процессами. Рассмотрим теперь вопрос о том, как используют динамическую библиотеку различные экземпляры приложения или разные процессы. Если вы немного знакомы с принципом функционирования операционной системы Windows, то, возможно, такая постановка вопроса у вас вызовет недоумение. "У каждого приложения свое адресное пространство, куда загружается DLL", - скажете вы. Конечно, это не совсем рационально, но зато безопасно. О памяти мы еще подробно будем говорить, здесь же заметим, что, вообще говоря, приложение может инициализировать так называемую разделяемую память. Мы вернемся к этому вопросу еще неоднократно, сейчас же рассмотрим этот вопрос чисто технически, применительно к DLL. Рассмотрим конкретную ситуацию. Запускаемое приложение загружает динамически подключаемую библиотеку и вызывает процедуру из DLL, которая меняет данные, расположенные опять же в динамически подключаемой библиотеке. Запустим теперь второй экземпляр приложения. Оно загружает еще один экземпляр DLL. Могут быть ситуации, когда желательно, чтобы второе запущенное приложение "знало", что по команде первого приложения данные уже изменились. Ясно, что в этом случае данные, которыми оперирует DLL, должны быть общими. Технически это делается очень просто. У редактора свзей LINK eсть опция /section: имя, атрибуты, которая позволяет объявить явно свойства данной секции. Мы будем говорить о секциях далее, здесь же достаточно сказать, секция - это просто сегмент в старом понимании. В редакторе связей TLINK32 то же действие можно осуществить с помощью DEF-файла. Представленные в примере программы весьма просты. По сути, они лишь иллюстрируют возможности использования разделяемой памяти. Перед выходом из процедуры DLL изменяется строка, хранящаяся в разделяемой секции памяти. При этом приложение не заканчивает своей работы. При запуске второго экземпляра приложения на экран выводится уже измененное первым приложением значение строки. Файл pr75_1.asm - динамически подключаемая библиотека. .386P ;Плоская модель. IFDEF MASM .MODEL FLAT, STDCALL ELSE .MODEL FLAT ENDIF PUBLIC DLLP1 IFDEF MASM ;MASM ;Прототипы внешних процедур. EXTERN MessageBoxA@16: NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN MessageBoxA@: NEAR MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TEXT DB 'В динамической библиотеке',0 MS DB 'Сообщение',0 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. DLLENTRY: MOV EAX,1 RET 12 ;------------------------------------- ;Адреса параметров ; [EBP+8] ; [ЕВР+0СН] DLLP1 PROC EXPORT PUSH EBP MOV EBP,ESP PUSH 0 PUSH OFFSET MS PUSH OFFSET TEXT PUSH 0 CALL MessageBoxA@16 ;Изменим строку, расположенную в разделяемой памяти. MOV TEXT,'И' MOV TEXT+1,'з' MOV TEXT+25,'и' POP EBP RET DLLP1 ENDP _TEXT ENDS END DLLENTRY Файл pr75_2.asm - основной модуль, вызывающий процедуру из DLL. .386P ;Плоская модель. .MODEL FLAT, STDCALL ;Константы. ;Прототипы внешних процедур. IFDEF MASM ;MASM EXTERN GetProcAddress@8:NEAR EXTERN LoadLibraryA@4:NEAR EXTERN FreeLibrary@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN MessageBoxA@16:NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN GetProcAddress:NEAR EXTERN LoadLibraryA:NEAR EXTERN FreeLibrary:NEAR EXTERN ExitProcess:NEAR EXTERN MessageBoxA:NEAR GetProcAddress@8 = GetProcAddress LoadLibraryA@4 = LoadLibraryA FreeLibrary@4 = FreeLibrary ExitProcess@4 = ExitProcess MessageBoxA@16 = MessageBoxA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' TXT DB 'Ошибка динамической библиотеки',0 MS DB 'Сообщение',0 LIBR DB 'pr75_1.DLL',0 HLIB DD ? IFDEF MASM NAMEPROC DB '_DLLP1@0',0 ELSE NAMEPROC DB 'DLLP1',0 ENDIF _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. START: ;Загрузить библиотеку. PUSH OFFSET LIBR CALL LoadLibraryA@4 CMP EAX,0 JE _ERR MOV HLIB,EAX ;Получить адрес процедуры. PUSH OFFSET NAMEPROC PUSH HLIB CALL GetProcAddress@8 CMP EAX,0 JNE YES_NAME ;Сообщение об ошибке. _ERR: PUSH 0 PUSH OFFSET MS PUSH OFFSET TXT PUSH 0 CALL MessageBoxA@16 JMP _EXIT YES_NAME: CALL EAX PUSH 0 PUSH OFFSET MS PUSH OFFSET MS PUSH 0 CALL MessageBoxA@16 ;Закрыть библиотеку. ;Библиотека автоматически закрывается также ;при выходе из программы. PUSH OFFSET NAMEPROC PUSH HLIB CALL FreeLibrary@4 ;Выход. _EXIT: PUSH 0 CALL ExitProcess@4 _TEXT ENDS END START Тексты этих файлов можно взять здесь. Результат работы приложения изображен на рисунке 1: Рис.1. Результат работы приложения Трансляция динамической библиотеки: пакет MASM32: ML /с /coff /DMASM pr75_1.asm LINK /SUBSYSTEM:WINDOWS /DLL /section:.data,SRW pr75_1.obj пакет TASM32: TASM32 /ml pr75_1.asm TLINK32 -aa -Tpd pr75_1.obj,,,,pr75_1.def Содержимое файла pr75_1.def: SECTIONS.DATA SHARED EXPORTS DLLP1 Трансляция программы: пакет MASM32: ML /с /coff /DMASM pr75_2.asm LINK /SUBSYSTEM:WINDOWS pr75_2.obj пакет TASM32: TASM32 /ml pr75_2.asm TLINK32 -aa pr75_2.obj Со следующего шага мы начнем рассматривать взаимодействие с ресурсами локальной сети. На этом шаге мы рассмотрим использование ресурсов из DLL. На этом шаге мы рассмотрим весьма интересный пример. Основной процесс использует ресурсы загруженной им динамической библиотеки. Мы уже отмечали, что файлы шрифтов, по сути, являются динамическими библиотеками. Не правда ли, удобно: ресурсы можно поместить отдельно от основной программы в динамическую библиотеку, загружая их по мере необходимости? Наша программа вначале загружает пиктограмму из ресурсов динамической библиотеки и устанавливает ее на окно. Если вы будете щелкать левой кнопкой мыши, направив курсор на окно, то будет вызываться процедура из динамической библиотеки, которая будет поочередно устанавливать то один, то другой значок на окно. Файл pr74_1a.rc - файл ресурсов динамически подключаемой библиотеки. //Идентификаторы. #define IDI_ICON1 3 #define IDI_ICON2 10 //Определили пиктограмму. IDI_ICON1 ICON "delphi.ico" IDI_ICON2 ICON "cbuilder.ico" Файл pr74_1.asm - динамически подключаемая библиотека. .386P PUBLIC SETIC ;Плоская модель. IFDEF MASM .MODEL FLAT, STDCALL ELSE .MODEL FLAT ENDIF ;Константы. WM_SETICON equ 80h IFDEF MASM ;MASM ;Прототипы внешних процедур. EXTERN LoadIconA@8:NEAR EXTERN PostMessageA@16:NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN LoadIconA:NEAR EXTERN PostMessageA:NEAR LoadIconA@8 = LoadIconA PostMessageA@16 = PostMessageA ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' PRIZ DB 0 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' ; [EBP+10H] - Резервный параметр. ; [ЕВР+0СН] - Причина вызова. ; [ЕВР+8] - Идентификатор DLL-модуля. DLLENTRY: MOV EAX,1 RET 12 ;------------------------------------- ;Адреса параметров ; [EBP+8] ; [ЕВР+0СН] SETIC PROC EXPORT PUSH EBP MOV EBP,ESP ;Выбрать, какую пиктограмму устанавливать. CMP PRIZ,0 JZ IC_1 MOV PRIZ,0 PUSH 3 JMP CONT IC_1: MOV PRIZ,1 PUSH 10 CONT: ;Загрузить пиктограмму из ресурсов библиотеки. PUSH DWORD PTR [EBP+0CH] ;Идентификатор динамической библиотеки. CALL LoadIconA@8 ;Установить значок окна. PUSH EAX PUSH 0 PUSH WM_SETICON PUSH DWORD PTR [EBP+08H] ;Дескриптор окна. CALL PostMessageA@16 POP EBP RET 8 SETIC ENDP _TEXT ENDS END DLLENTRY Файл pr74_1b.rc - файл ресурсов основной программы. //Определение констант. #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L #define DS_3DLOOK 0x0004L //Определение диалогового окна. DIAL1 DIALOG 0, 0, 340, 120 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK CAPTION "Диалоговое окно с пиктограммой из динамической библиотеки" FONT 8, "Arial" { } Файл pr74_2.asm - основной модуль, вызывающий процедуру из DLL. .386P ;Плоская модель. IFDEF MASM .MODEL FLAT, STDCALL ELSE .MODEL FLAT ENDIF ;Константы. ;Сообщение приходит при закрытии окна. WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SETICON equ 80h WM_LBUTTONDOWN equ 201h ;Прототипы внешних процедур. IFDEF MASM ;MASM EXTERN PostMessageA@16:NEAR EXTERN GetProcAddress@8:NEAR EXTERN LoadLibraryA@4:NEAR EXTERN FreeLibrary@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN LoadIconA@8:NEAR ;Директивы компоновщику для подключения библиотек. ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;TASM EXTERN PostMessageA:NEAR EXTERN GetProcAddress:NEAR EXTERN LoadLibraryA:NEAR EXTERN FreeLibrary:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN LoadIconA:NEAR PostMessageA@16 = PostMessageA LoadIconA@8 = LoadlconA EndDialog@8 = EndDialog GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA GetProcAddress@8 = GetProcAddress LoadLibraryA@4 = LoadLibraryA FreeLibrary@4 = FreeLibrary ExitProcess@4 = ExitProcess ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' LIBR DB 'pr74_1.DLL',0 HLIB DD ? HINST DD ? PA DB 'DIAL1',0 IFDEF MASM NAMEPROC DB '_SETIC@0',0 ELSE NAMEPROC DB 'SETIC',0 ENDIF _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 ;Создать диалог. MOV [HINST], EAX PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 ;Выход. _EXIT: PUSH 0 CALL ExitProcess@4 ;Процедура окна. ;Расположение параметров в стеке. ;[ВР+14Н] - LPARAM ;[ВР+10Н] - WAPARAM ;[ВР+0СН] - MES ;[ВР+8] - HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 ;Закрыть библиотеку. ;Библиотека автоматически закрывается также ;при выходе из программы. PUSH HLIB CALL FreeLibrary@4 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH] ,WM_INITDIALOG JNE L2 ;Загрузить библиотеку. PUSH OFFSET LIBR CALL LoadLibraryA@4 MOV HLIB,EAX ;Загрузить пиктограмму. PUSH 3 ;Идентификатор пиктограммы. PUSH [HLIB] ;Идентификатор процесса. CALL LoadIconA@8 ;Установить пиктограмму. PUSH EAX PUSH 0 ;Тип пиктограммы (маленькая). PUSH WM_SETICON PUSH DWORD PTR [EBP+08H] CALL PostMessageA@16 JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_LBUTTONDOWN JNE FINISH ;Получить адрес процедуры из динамической библиотеки. PUSH OFFSET NAMEPROC PUSH HLIB CALL GetProcAddress@8 ;Вызвать процедуру с двумя параметрами. PUSH [HLIB] PUSH DWORD PTR [EBP+08H] CALL EAX FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX,0 RET 16 WNDPROC ENDP _TEXT ENDS END START Тексты этох файлов, а также используемые пиктограммы, можно взять здесь. Результат работы приложения изображен на рисунке 1: Рис.1. Результат работы приложения Трансляция динамической библиотеки: пакет MASM32: ML /с /coff /DMASM pr74_1.asm rc pr74_1a.rc LINK /SUBSYSTEM:WINDOWS /DLL /ENTRY:DLLENTRY pr73_1.obj pr74_1a.res пакет TASM32: TASM32 /ml pr74_1.asm brcc32 pr74_1a.rc TLINK32 -aa -Tpd pr74_1.obj,,,,pr74_1.def,pr74_1a.res Содержимое файла pr74_1.def: EXPORTS SETIC Трансляция программы: пакет MASM32: ML /с /coff /DMASM pr74_2.asm rc pr74_1b.rc LINK /SUBSYSTEM:WINDOWS pr74_2.obj pr74_1b.res пакет TASM32: TASM32 /ml pr74_2.asm brcc32 pr74_1b.rc TLINK32 -aa pr74_2.obj pr74_1b.res Как мы уже не раз с вами убеждались, динамически подключаемая библиотека становится частью программы, обладая вместе с процессом всеми ее возможностями, Так, при помощи функций GetMessage и PeekMessage она может получать сообщения, предназначенные для процесса. Если вы хотите создать в DLL окно, то вам следует воспользоваться идентификатором вызвавшей динамически подключаемую библиотеку программы. Рассмотрим совместное использование памяти разными процессами. Стратегии размещения информации в памяти Те или иные стратегии размещения информации в памяти применяются для того, чтобы определить, в какое место основной памяти следует помещать поступающие программы и данные. В технической литературе чаще всего описываются три стратегии, приведенные на рис. 7.14. · Стратегия наиболее подходящего. Поступающее задание помещается в тот свободный участок основной памяти, в котором ему наиболее <тесно>, так что остается минимально возможное неиспользуемое пространство. Для многих людей выбор наиболее подходящего кажется интуитивно самой рациональной стратегией. · Стратегия первого подходящего. Поступающее задание помещается в первый встретившийся свободный участок основной памяти достаточного размера. Выбор первого подходящего по размеру свободного участка также кажется интуитивно рациональным, поскольку он позволяет быстро принять решение о размещении задания. · Стратегия наименее подходящего. На первый взгляд подобный подход кажется весьма странным. Однако более тщательное рассмотрение показывает, что выбор наименее подходящего по размеру также имеет сильные интуитивные аргументы в свою пользу. Этот принцип говорит о том, что при помещении программы в основную память нужно занимать свободный участок, имеющий наиболее далекий размер, т. е. максимальный возможный свободный участок. Интуитивный аргумент в пользу такого подхода достаточно прост: после помещения программы в большой свободный участок остающийся свободный участок зачастую также оказывается большим и, таким образом, в нем можно разместить относительно большую новую программу.