Объектно ориентированное программирование

Тема в разделе "WASM.WIN32", создана пользователем EvilsInterrupt, 16 ноя 2004.

  1. NeuronViking

    NeuronViking New Member

    Публикаций:
    0
    Регистрация:
    29 окт 2004
    Сообщения:
    476
    Адрес:
    где-то в Сиднее
    хы хы хы... похоже винища хлебнул лишку... пора баиньки



    в опщем эта.. хотел сказать еще одно умничиство... а скока на самом деле бит в октете? ;))) хы хы... ответ: 9



    почиму отгадывайте сами =)



    споки ноки!
     
  2. Foamplast

    Foamplast New Member

    Публикаций:
    0
    Регистрация:
    6 ноя 2003
    Сообщения:
    80
    Адрес:
    Russia
    Как называется группа из восьми вооружённых вакхабитов?
     
  3. Foamplast

    Foamplast New Member

    Публикаций:
    0
    Регистрация:
    6 ноя 2003
    Сообщения:
    80
    Адрес:
    Russia
    EvilsInterrupt

    Я тебе неоднократно разъяснял. Но попадаю всегда в конец топика, и ты, похоже, не читаешь. Если хочешь, я тебе всё объясню так, что ты поймёшь.
     
  4. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    NeuronViking

    В оригинале этот вопрос звучал так: сколько дорожек на восьми-дорожечной перфоленте? 8)
     
  5. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    Foamplast



    извини, но я имею свойство перечитывать что имею из форума и не только по своим постам.



    Дело в том, как правильно заметил старший товарищ Володя. Много авторов рассказывают иногда об одной вещи разными понятиями и иногда довольно криво. Плюс к этому не все люди умеют правильно излагать свои мысли, и я не исключение, Поэтому я пришел к выводу что если мысль потвердена тремя старшими товарищами с этого форума, то она имеет высокую вороятность быть верной!



    А вообще, объясняй! Ты же знаешь знаниям я всегда рад.
     
  6. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    EvilsInterrupt

    > "Много авторов рассказывают иногда об одной вещи разными понятиями и иногда довольно криво"



    Ну вот тогда тебе еще одна "кривая" попытка - может поможет (масло масляное).



    Начнем с виртуального адресного пространства. Это есть "концептуальное понятие", которое отражает тот факт что Win32 в принципе позволяет любому процессу (с некоторыми оговорками) оперировать адресами в диапазоне от 0 до 2^32-1. Это пространство является виртуальным (логическим), т.к. оно никак не связано ни с объемом свободной физической памяти, ни с адресами физической памяти, ни с адресами других процессов. У каждого процесса могут храниться свои данные по некоторому логическому адресу P, а система для каждого процесса транслирует один и тот же логический адрес P в разные области физической памяти. Также система позволяет нам в разумных пределах не беспокоится о доступном объеме физической памяти. Не хватит ей свободного места в ОЗУ - начнет перекачку страниц в swap-файл, исчерпается место на диске - создаст исключительную ситуацию (а может прибьет наш процесс или сама "забъется в истерике" - фиг ее знает - это не наше дело).



    Поэтому можно сказать, что с точки зрения системы вся работа с памятью построена по динамическому принципу. Из всех тонкостей механизмов работы системы с памятью для нас важно то, что выделение и освобождение физической памяти (commit\decommit) осуществляется страницами по 4К. Поэтому "попросив" у системы один дополнительный байтик к уже выделенной памяти мы сразу получим "подарок" в виде аж 4К байтиков. Базовым (первичным) механизмом работы с динамической памятью в Win32 является использование процедур VirtualAlloc\VirtualFree, которые позволяют резервировать\освобождать (reserve\release) логические адреса блоками кратными 64К и выделять\освобождать (commit\decommit) физическую память страницами по 4К. Ес-но такая "гранулярность" накладывает определенные ограничения на прямое использование этих функций в программе. Напрямую VirtualAlloc удобно использовать для записи сравнительно больших объемов данных - массивов, файлов и прочих "непрерывных" потоков данных (stream), и соответсвенно после использования освобождать всю область.



    А вот для всякой динамической "мелочевки", которую по многу раз приходится распределять и удалять существует специальный механизм под названием "куча" (heap). Как уже отмечалось, механизм кучи это просто надстройка над VirtualXXX. Менеджеры кучи использует VirtualXXX для запроса памяти у системы (оптом) и затем на вторичной основе раздают ее маленькими порциями (в розницу). Основная проблема при создании менеджера это борьба с разрастанием и фрагментацией кучи, возникающих при частом создании и удалении переменных - нужно следить за освободившемся местом и стараться "оптимально" подыскивать место для новых блочков. (Кстати если можно "наплевать" на удаление или запись\удаление идет по принципу стека first in\last out, то не составляет никакого труда смастерить свой менеджер).



    Win32 представляет системные фунуции для работы с кучами - это HeapXXX и GlobalXXX\LocalXXX. Между Global и Local вообще нет никакой разницы, обе они - пережитки Win3.1 для обратной совместимости. Между GlobalXXX и HeapXXX - почти никакой. Грубо говоря GlobalXXX - это вариант HeapXXX неограниченного размера (dwMaximumSize = 0) и "глобальным" хэндлом, хранящимся в системе. Все хитрые флаги GlobalAlloc достались ей по наследству и практического значения не имеют.

    Поскольку управление кучей дело хитрое и творческое, то ес-но каждый "уважающий себя" компилятор высокого уровня предлагает свой менеджер динамической памяти. Полностью избежать проблем с фрагментацией и разрастанием кучи все равно не удается. В частности в Win32.hlp подчеркивается, что при использовании HeapAlloc однажды выделенная страница памяти (commit) автоматически не decommit при HeapFree, и для попытки приведения кучи в относительный порядок нужно использовать HeapCompact, которая тоже ничего не гарантирует.

    Еще вопросик, о котором часто не задумываются - это оверхеды. Для управления кучей менеджер использует память самой же кучи. В частности перед каждым выделенным блоком идет заголовок (как минимум 4 байта для указания размера), а минимальный выделяемый размер может составлять 12-16 байт (для управления свободными блоками). Поэтому запрашивать по одному или даже по 4 байта в любом случае - расточительство.



    Уфф, "рука бойцов рубить устала" ...

    Продолжение следует ?
     
  7. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    EvilsInterrupt

    Я считаю, что для того чтобы ты мог лучше разобраться со многими вопросами, связанными с виртуальной памятью тебе нужно прочитать следующие книги:

    * Джеффри Рихтер, "Создание эффективных Win-32 приложений";

    * Д.Соломон, М.Руссинович, "Внутреннее устройство Microsoft Windows 2000. Структура и алгоритмы работы компонентов Windows 2000 и NTFS 5";

    * Ну а для того, чтобы понять: как это все поддерживается на уровне железа, тебе нужно прочитать какую ни будь книжку, описывающую работу современных процессоров в защищенном режиме. Мне, например, в этом помогла книга М.Гука, "Процессоры Pentium II, Pentium Pro и просто Pentium".
     
  8. volodya

    volodya wasm.ru

    Публикаций:
    0
    Регистрация:
    22 апр 2003
    Сообщения:
    1.169
    leo



    Очень неплохо.

    Для новичка - самое то. Продолжай, пожалуйста, а я помещу это в FAQ. На тему распределения памяти! Очень уж внятно и по полочкам ты разложил это дело.
     
  9. volodya

    volodya wasm.ru

    Публикаций:
    0
    Регистрация:
    22 апр 2003
    Сообщения:
    1.169
    Да, и еще.

    Изложи:

    1) Что такое malloc/free (упомянуть о различных реализациях)

    2) new/delete (тоже самое)

    3) Применительно к ООП - зачем для объекта делается new.

    Примерно так...

    Хотя, разумеется, решать тебе :)
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    EvilsInterrupt

    Ну как, не устал еще от "лапши" ? Если нет, то вот тебе еще порция:



    Что скрывается за оператором new и как осуществляется контроль адресов ?



    Оператор new это "фикция" компилятора за которой скрывается функция malloc или аналогичная более "глубокая" функция менеджера кучи СИ. Поскольку у меня с сями отношения не столь "близкие", поясню на дельфийском примере. В дельфях аналогом оператора new является процедура New(var типированный_указатель), которая вызывает функцию GetMem (= аналог malloc), которая в свою очередь через структуру MemoryManager ссылается на аналогичную функцию SysGetMem дельфийского менеджера кучи. Такой подход позволяет при желании использовать другой менеджер, заменив ссылки в указанной структуре. GetMem или malloc запрашивает у менеджера блок памяти заданного размера и возвращают нетипированный (void) указатель на выделенный блок. Грубо говоря, new оличается тем, что мы задаем не размер, а тип переменной, а компилятор сам подставляет sizeof в аналог malloc и заодно проверяет соответствие запрашиваемого и присваиваемого типа. Аналогично оператор delete является надстройкой над функцией free или ее аналогом из менеджера кучи.



    О контроле адресов. Менеджеру кучи обычно достаточно определить три функции типа malloc, free и realloc (в дельфях GetMem, FreeMem и ReallocMem). Соответственно проверку валидности адреса он может проверить только при освобождении или релокации динамической переменной, а контролировать чтение и запись по адресу ему ес-но не дано. Запись\чтение данных контролируется системой, но опять же не на 100%. Система проверяет только наличие права доступа к соответствующей странице памяти. Если страница commited и ее protection флаги дают право на чтение или запись, то с точки зрения системы все OK. Теперь представим ситуацию, когда мы запросили у менеджера кучи блочок памяти небольшого размера и решили "пошалить" и записать по выделенному адресу больше байт, чем запрашивали. Тут возможно 3 варианта. Первый: при записи мы вылезли за пределы commited страницы и нарвались на PAGE_GUARD или PAGE_NOACCESS, тогда ес-но получим от системы исключительную ситуацию. Второй вариант: за пределы валидной страницы не вылезли, но менеджер выдал нам блок из конца кучи. В этом случае может вообще ничего страшного не произойти или в худшем случае при запросе нового блока и записи в него мы затрем свои шалости. Третий вариант самый неприятный: менеджер выкроил нам свободный блочок откуда-то из середины кучи и при шаловливой записи мы затерли последующие данные вместе с их служебным оверхедом. Тут уж последствия самые печальные, т.к. совершенно не понятно когда эта инвалидность всплывет наружу и в каком виде. Разумеется аналогичные ситуации могут возникнуть и при шалостях с изменением значения адреса выделенного блока, но добавится еще четвертый вариант - при попытке delete\free или realloc менеджер кучи обнаружит инвалидность адреса и выдаст ошибку. Такие ошибки могут возникать и в случае повторного вызова free или realloc для уже удаленного блока памяти.



    Еще вопросы, или первоисточники почитаем да в сорцах пороемся ?



    PS: volodya, ты конечно не думаешь что я за 10 минут ответ настрочил, это так мысли совпали.



    А для объектов делается именно new по той причине, что new это "продвинутая" реализация malloc (similar, but superior). Т.е. компилятор зная тип переменной распределяет нужный размер памяти, заполняет его нулями и вызывает конструктор для инициализации экземпляра класса. Тут можно еще пояснить, что для сложных операторов, применимых к разными типам данных, компилятор обычно имеет не одну, а несколько специализированных функций из разряда compiler magic и выбирает из них ту, которая соответствует типу данных для компилируемого оператора.
     
  11. volodya

    volodya wasm.ru

    Публикаций:
    0
    Регистрация:
    22 апр 2003
    Сообщения:
    1.169
    PS: volodya, ты конечно не думаешь что я за 10 минут ответ настрочил, это так мысли совпали.



    Есть пословица - у умных людей и мысли сходятся :) Birds of feather fly together :)
     
  12. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    EvilsInterrupt

    Я хочу немного дополнить инфу, которую дал в своем посте (leo) (надеюсь, он не будет против). Он пишет:



    В частности в Win32.hlp подчеркивается, что при использовании HeapAlloc однажды выделенная страница памяти (commit) автоматически не decommit при HeapFree, и для попытки приведения кучи в относительный порядок нужно использовать HeapCompact, которая тоже ничего не гарантирует.



    Если система видит, что в куче освободилась целая страница, то момент, когда она вернет ее себе зависит от того, какая используется ось. Например, Windows 98 ориентированна на более эффективное управление памятью, поэтому она постарается сделать это как можно быстрее. В то же время, Windows 2000 ориентированна на повышение производительности и по этому может не сразу сделать это, а через некоторое время...
     
  13. volodya

    volodya wasm.ru

    Публикаций:
    0
    Регистрация:
    22 апр 2003
    Сообщения:
    1.169
    на более эффективную работу с памятью

    на повышение производительности



    Ты это... Людей не запутывай, да?
     
  14. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Oleg_SK

    Насчет того, что разные оси могут по разному работать со страницами ты наверное прав. Но речь идет об управлении кучей. Здесь проблема в том, как определить что страница осводилась, если она была заполнена множеством мелких кусочков, которые могли освобождаться в произвольном порядке. Насчет дельфийского менеджера могу сказать, что он следит за освобождением соседних кусков и укрупняет свободное место, объединяя свободные куски. Но вот следит ли он за тем, что освободилась целая страница и делает ее decommit - точно сказать не могу. Если освободилось место в конце кучи, то да. А вот если где-то в середине - не знаю. Надо порыться в сорцах.
     
  15. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    volodya





    :dntknw: И в мыслях не было... Просто хотел дополнить общую картину, чтобы человек понимал, что разные оси по разному подходят к этому вопросу... Может быть ты имеешь в виду, что я не четко изложил свою мысль? Вот цитата из книги Рихтера (Глава 18, "Динамически распределяемая память", рассказывающая об использовании кучь):

    Microsoft не документирует правила, по которым диспетчер передает или отбирает физическую память. Эти правила различны в Windows 98 и Windows 2000. Могу сказать Вам лишь следующее; Windows. 98 больше озабочена эффективностью использования памяти и поэтому старается как можно быстрее отобрать у куч физическую память. Однако Windows 2000 нацелена главным образом на максимальное быстродействие, в связи с чем возвращает физическую память в страничный файл, только если страницы не используются в течение определенного времени. Microsoft постоянно проводит стрессовое тестирование своих операционных систем и прогоняет разные сценарии, чтобы определить, какие правила в большинстве случаев работают лучше. Их приходится менять по мере появления как нового программного обеспечения, так и оборудования. Если эти правила важны Вашим программам, использовать динамически распределяемую память не стоит — работайте с функциями виртуальной памяти (т.e. VirtualAlloc и VirtualFree), и тогда Вы сможете сами контролировать эти правила.



    leo

    Понятно, значит, я не правильно тебя понял. Я же объяснил, как поступит ось, когда ей будет уже ясно, что в куче освободилась целая страница...



    З.Ы: Я немного исправил свой предыдущий пост.
     
  16. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Oleg_SK

    Полностью согласен с тобой и с Рихтером.

    Использование кучи дело "тонкое", особенно в "многозадачных" программах, оперирующих большими объемами разнородных данных. Имеются ввиду программы которые могут "висеть" в памяти длительное время и выполнять разнородные прихоти юзера (ну к примеру, всяческие САDы, ГИСы и примочки к ним). Я сам сталкивался с "неприятным" разрастанием кучи в дельфах, после чего пришлось познакомиться поближе с работой менеджера кучи и сделать выводы. А вывод такой - не стоит все "валить в одну кучу" и для временных данных, которые должны быть уничтожены по завершении задачи лучше использовать VirtualAlloc или отдельные приватные кучи HeapCreate. Тогда эти данные легко и без последствий уничтожаются. А обычная куча - для "обычных" данных (динамических строк и экземпляров классов). Я уже говорил, что если не требуется уделять много внимания удалению пременных, то не составляет никакого труда создать свой простенький менеджер на основе VirtualAlloc. Я например, "слепил" себе когда-то класс TVMemStream на основе VirtualAlloc - теперь радуюсь и голова не болит (особенно приятно, что не надо удалять каждую переменную - грохнул все скопом и все).



    Можно учесть и "мелкие" рекомендации. К примеру, лучше придерживаться негласного правила - уничтожать динамические переменные в порядке, обратном их созданию (якобы это облегчает работу менеджера, не знаю насколько). Еще стоит избегать использования глобальных динамических переменных, если они создаются или перелоцируются не в начале программы. Думаю ясно, если мы запустим задачку, "загадим" кучу и перед тем как ее очистить создадим глобальную динамическую переменную (ну например запишем какое-то сообщение в динамическую строку) - то после чистки эта переменная так и останется висеть в конце кучи.
     
  17. captain cobalt

    captain cobalt New Member

    Публикаций:
    0
    Регистрация:
    21 дек 2003
    Сообщения:
    222
    Адрес:
    /ru/perm
    leo> К примеру, лучше придерживаться негласного правила - уничтожать

    leo> динамические переменные в порядке, обратном их созданию

    leo> (якобы это облегчает работу менеджера, не знаю насколько)



    Вот как раз создавать переменные в таком порядке следует на стеке.



    P.S. Не по теме, но

    _http://www-106.ibm.com/developerworks/linux/library/l-memory/?ca=dgr- lnxw07InsideMe

    Inside memory management
     
  18. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    captain cobalt > "Не по теме, но ..."

    А еще можно заглянуть в исходники используемого компилятора. Например, в Borland С++ Builder heap.c есть и краткое пояснение работы менеджера кучи со ссылкой на Кнута и есн-но все исходные коды, включая _internal_malloc и _internal_free. Можно также заглянуть в "карликовые" файлики new.cpp и delete.cpp.



    Однако к чему эти ссылки, если мало желающих в них копаться ?



    PS: Как и следовало ожидать в Delphi и С++ от Borland использован один и тот же алгоритм работы менеджера кучи.
     
  19. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Гы, да и кодогенератор у них практически один, не удивительно, что Borland забило на разработку компилеров %)
     
  20. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    Дамс...во так бы и в инстуте учили!