Состояние страниц памяти.

Тема в разделе "WASM.BEGINNERS", создана пользователем mix_mix, 3 апр 2007.

  1. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    Как известно, память можно резервировать (флаг MEM_RESERVE) и непосредственно передавать для использования (MEM_COMMIT). Таким образом, если вначале зарезервировать, скажем, 64Мб памяти, а затем в этом зарезервированном регионе по чуть-чуть выделять память (скажем по 128Кб), то может получится очень неплохая реализация динамических массивов.
    Имеется ввиду, что вначале выделил 128Кб, записал что-то, если место закончилось, то на конце этого выделенного региона выделяется такой же блок памяти и т.д.
    Так вот вся проблема в том, что когда это все отработает, то оставшиеся, скажем, 60Мб зарезервированной памяти мне нафиг не нужны (у меня еще 100 таких массивов будет - памяти не хватит), и мне их надо как-то освободить. Так VirtualFree с MEM_RELEASE такой трюк делать не умеет - она не умеет освобождать память в середине зарезервированного региона, а если указывать на начало, то она и выделенным страницам ставит состояние свободных, а мне это как-то не особо улыбается :dntknw:
    Собственно что в данной ситуации можно сделать?
    P.S. Можно конечно создавать еще один буфер, уже зная размер данных, и копировать эти данные туда. Но это как-то не по-дзенски.
    P.P.S. Heap*** и иже с ними не предлагать :)
     
  2. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    кажется как-то мутновато.
    Как так не умеет? где такое прочитал?
    VirtualXX - функции работают с витруальной памятью, значит они работают со _страницами_, значит, к примеру, для освобождения памяти нужно передавать адресс кратный странице, обычно это 4Кб (FFF) т.е. ксориш указатель на конец памяти которую нада освободить с FFF, добавляешь 1000h, получается указываешь на начало следующей страници, освобождаешь её и всё последующие

    А почему хип не предлагать?
     
  3. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    имхо rain прав, VirtualAlloc выделяет память страницами дискретно и страницы друг от друга не зависят.. освободить можно любые из них по выбору.
     
  4. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    Ну вроде в msdn ясно написано
    Код (Text):
    1. MEM_RELEASE
    2. Releases the specified region of pages. After this operation, the pages are in the free state.
    3. If you specify this value, dwSize must be 0 (zero),
    4. and lpAddress must point to the base address returned
    5. by the VirtualAlloc function when the region is reserved.
    6. The function fails if either of these conditions is not met.
     
  5. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    ниче не понял, но VirtualFree не должна освобождать страницы посередине региона с флагом MEM_RELEASE, только с MEM_DECOMMIT (или как-то вроде)
     
  6. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Nouzui
    ну я и имел в виду decommit )
    mem_release конечно не прокатит
     
  7. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    а mix_mix'у зачем-то нужен именно MEM_RELEASE
    mix_mix, а может стоит задуматься, действительно ли это так необходимо?
     
  8. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    Ну MEM_RELEASE нужен для того, чтобы освободить страницы (они зарезервированные).
    Ну а насчет другой реализации, это будет несколько сложнее в реализации, да и помедленней, просто мне в голову пришла такая идея, да и вроде бы все работает за исключением одной детали.
    Ну в принципе, если создавать дополнительный буфер (сразу commit) уже определенного размера, скопировать туда данные, а затем полностью очистить вспомогательный буфер (тот который растущий), то все будет пинцетно. Просто это как-то не очень... Вот я и спрашивал, может как-то можно все сделать без гемора и виндовыми средствами...
     
  9. scf37

    scf37 New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2007
    Сообщения:
    44
    А что если зарезервировать сразу метров 512 памяти (виртуальной конечно, а не физической) и не отдавать?
    Ты можешь commit/decommit любую страницу в этой области, так что напиши собственный менеджер памяти, который и будет размещать твои массивы так, как тебе это надо.
     
  10. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    ой, что-то я намутил в прошлом посте, только что проверил, можно и не по страницам освобождать память а хоть и по байтам, просто windows не выгрузит страницу пока не будет вызвана VirtualFree + MEM_DECOMMIT для каждого байта в странице.
    Сами страници _не_ заризервированны, при MEM_RESERVE резервируется пространсво в АП твоего процесса, т.е. реально страници выделяются только после MEM_COMMIT, и соответственно освобождаются при MEM_DECOMMIT.
    Убедится в этом можно так:
    запускаешь эту программку, и открываешь Process Explorer, выбираешь процесс --> Properties --> Perfomance --> VirtualMemory. Запоминаешь значение Private Bytes. Клацаеш ОК, сначала выделяется память, потом начинает освобождаться по 4К, о чём свидетельствует ProcessExplorer :-Ъ
    Код (Text):
    1. .386
    2. .model flat, stdcall
    3. option casemap: none
    4.  
    5. include windows.inc
    6.  
    7. include macros.asm
    8.  
    9. include kernel32.inc
    10. includelib kernel32.lib
    11.  
    12. include user32.inc
    13. includelib user32.lib
    14.  
    15. .data
    16.  
    17.     buf db 256 dup (0)
    18.    
    19. .code
    20. start:
    21.    
    22.     invoke MessageBox, 0, chr$("open process explorer -> VirtualMemory -> private bytes "), 0,MB_OK
    23.     invoke VirtualAlloc, 0, 30000h, MEM_RESERVE+MEM_COMMIT, PAGE_EXECUTE_READWRITE
    24.     mov esi, eax   
    25.     mov edi, esi
    26.     add esi, 30000h
    27.  
    28.     invoke wsprintf, offset buf, chr$("reserved from = %x"), eax
    29.     invoke MessageBox, 0, offset buf,0,MB_OK
    30.  
    31.    
    32.     .repeat
    33.         sub esi, 1000h  
    34.         invoke VirtualFree, esi, 1000h, MEM_DECOMMIT
    35.         .if eax
    36.             invoke wsprintf, offset buf, chr$("released = %x"), esi
    37.             invoke MessageBox, 0, offset buf,0,MB_OK
    38.            
    39.         .endif
    40.        
    41.      .until esi<edi
    42.      
    43.     ret
    44. end start
     
  11. orbb

    orbb New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2007
    Сообщения:
    16
    всу ето звучит дорого ну на дела ето фантастико потому что на win98 60% из функции не работаут понятно и дорого.все изкрват из реклами а на деле ничто.я тоже имеу проблем с буфера памяти когда ползуус GlabalAlloc,Global Lock, потом GlobalUnlock i GlobalFree.
    моя идея ползоват динамическом разером буфера идет тяжелая для вин98 оф и толко проблеми радоват мои програмисткие ночи.
    Потом вопрос можно идет о RichEdit.DLL которои нито Rich нито Edit и основнъих функциях у нему не рабоут ,например перехвата адреса текстовои буфер которои нужен для дирекнои работои с него , адрес из функции не возвращается и т.д. вин система для меня изначало на первои взгляд хорошая и на второи - ужас ,смертелнии .:dntknw:((((((((((((
     
  12. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    rain
    Запусти вот этот код под отладчиком:
    Код (Text):
    1. @@:
    2. invoke  VirtualAlloc, 0, 1024*1024*700, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE
    3. invoke  VirtualFree, eax, 1024*1024*700, MEM_DECOMMIT
    4. jmp @B
    На третьей итерации цикла VirtualAlloc вернет ERROR_NOT_ENOUGH_MEMORY, несморя на то, что все предыдущие выделенные блоки по 700Мб ты возвращал системе и Process Explorer покажет это же.
    Вот здесь как раз все дело в том, что эти страницы еще и зарезервированны, поэтому аллокатор неможет их выделить. Я как раз об этом говорю.
    Наверное, здесь как раз нужно использование менеджера памяти.
     
  13. scf37

    scf37 New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2007
    Сообщения:
    44
    mix_mix
    На 32-битной машине размер виртуальной памяти всего 4 Гб, из которых старшая половина(на отдельных осях четверть) зарезервирована системой. Т.е. юзеру остаётся 0x00000000 - 0x7FFFFFFF
    Минус образ процесса, образы загруженных dll-ек.....
     
  14. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    не на отдельных осях, а при определенных параметрах загрузки и флагах PE образа =)
     
  15. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    Ну я знаю что процессу по умолчанию 2Гб виртуальной памяти дается.
    Я показал rain'у, что резервируя память и не отдавая ее, она (память) рано или поздно закончится.
     
  16. scf37

    scf37 New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2007
    Сообщения:
    44
    Great
    буду знать)
     
  17. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    mix_mix
    Даже если бы возможность уменьшить размер виртуального блока памяти существовала, она была бы бесполезной ;). Предназначение VirtualAlloc + MEM_RESERVE обеспечение массиву непрерывного адресного простанства, поэтому если ты нарезервируешь много виртуальных блоков а затем исхитришься поуменьшать их размер, то получишь дико фрагментированное адресное пространство, причём когда ты попытаешься исправить ситуацию "пристроив" второй массив в хвост к первому, то не факт, что тебе это удастся, поскольку промежуток может оказаться занят в результате "безобидных действий" какой нибудь win api :) В результате будешь иметь полный heap, только собственного изготовления :)

    Поэтому резервируй (MEM_RESERVE) сразу большой блок памяти, а затем выделяй в нём реальную память (MEM_COMMIT) для своих массивов (см. про "оторванный блок"). Тогда можешь тасовать свои массивы в зарезервированном пространстве как угодно и быть уверенным в подконтрольности ситуации. Только перемещая массивы не забывай выделять реальную память там куда копируешь массив и освобождать её (MEM_DECOMMIT) там, откуда скопировал :)
    Ну и есно не стоит забывать, что из 2Гб юзермодного пространства часть используется win api и им нужно оставить хоть немного места (метров этак 250-500 :).

    Интел начиная 386 камней позволяет перемещать страницы памяти (размером 4к, 2М или 4М) в виртуальном адресном пространстве не копируя их, а только сопоставляя им соответствующий адрес, но к сожалению в юзермоде винды мне удалось найти только достаточно заморочную AWE реализацию этого механизма, которая требует win 2000 или выше и хитрых прав доступа.
    Если я правильно понял Рихтера и msdn, то выделив блок реальных страниц (AllocateUserPhysicalPages) можно затем подставить его (MapUserPhysicalPages) в любую (кратную размеру страницы) позицию виртуального адресного пространства предварительно зарезервированного через VirtualAlloc + MEM_RESERVE, а затем при желании можно переремапить этот блок на новое место без копирования памяти, а также объединить несколько последовательных блоков в один (MapUserPhysicalPagesScatter) или временно отсоединить реальный массив от адресного пространства чтобы сопоставить адресное окно другому физическому масиву.
     
  18. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    mix_mix
    или юзай файлмаппинги
     
  19. mix_mix

    mix_mix Михаил

    Публикаций:
    0
    Регистрация:
    8 окт 2005
    Сообщения:
    277
    Адрес:
    Токио
    Всем спасибо. Кажется разобрался.
     
  20. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Nouzui
    Ага тоже хорошее решение, правда если бы виртуальная часть FileMapping была реализована в полном объёме (64 битное виртуальное адресное пространство, с плавающим окном в 1-1.5 Гб), то цены бы ей не было :))
    Но увы размер виртуального файла упирается в 2Гб.
    Зато можно наоткрывать кучу виртуальных файлов и по очереди подставлять их в окно :)
    Код (Text):
    1.    ; Резервируем 2Гб адресного пространства (это совсем не нагрузит систему :))))
    2.    invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0
    3.    mov [h_map_file_1], eax
    4.    ; Резервируем ещё 2Гб адресного пространства (это опять не нагрузит систему :))))
    5.    invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0
    6.    mov [h_map_file_2], eax
    7.    ; Резервируем ещё 2Гб адресного пространства (и это тоже не нагрузит систему :))))
    8.    invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0
    9.    mov [h_map_file_3], eax
    10.  
    11.    ; Выделяем 1Гб окно в адресном пространстве нашего процесса и сопоставляем ему начало первого мап-файла
    12.    invoke MapViewOfFile, [h_map_file_1], FILE_MAP_ALL_ACCESS, 0, 0, 1024*1024*1024
    13.    mov [h_mem_win], eax
    14.  
    15.    ; Наконец-то выделяем реальную память аж 12кБ :))
    16.    invoke VirtualAlloc, [h_mem_win], 12*1024, MEM_COMMIT, PAGE_READWRITE
    17.    invoke wsprintf, [h_mem_win], zSTR(<'Пробная запись в первую выделенную память', 13, 10>)
    18.    ; Выделяем "оторванный блок" памяти в середине окна в мап-файл
    19.    mov edi, 512*1024*1024
    20.    add edi, [h_mem_win]
    21.    invoke VirtualAlloc, edi, 12*1024, MEM_COMMIT, PAGE_READWRITE
    22.    invoke wsprintf, edi, zSTR(<'Пробная запись в первый оторванный блок', 13, 10>)
    23.    ; Закрываем окно в первый мап-файл
    24.    invoke UnmapViewOfFile, [h_mem_win]
    25.  
    26.    ; Открываем адресное окно для второго Гб второго мап-файла
    27.    invoke MapViewOfFile, [h_map_file_2], FILE_MAP_ALL_ACCESS, 0, 1024*1024*1024, 1024*1024*1024
    28.    mov [h_mem_win],eax
    29.    ; И в нём тоже выделяем реальную память
    30.    invoke VirtualAlloc, [h_mem_win], 12*1024, MEM_COMMIT, PAGE_READWRITE
    31.    invoke wsprintf, [h_mem_win], zSTR(<'Пробная запись во вторую выделенную память', 13, 10>)
    32.  
    33.    ; Закрываем окно во второй мап-файл
    34.    invoke UnmapViewOfFile, [h_mem_win]
    35.  
    36.    ; Считываем данные для проверки
    37.    ; Опять открываем адресное окно для первого мап-файла
    38.    invoke MapViewOfFile, [h_map_file_1], FILE_MAP_ALL_ACCESS, 0, 0, 1024*1024*1024
    39.    mov [h_mem_win], eax
    40.    ; считываем из него первый блок данных
    41.    mov esi, offset zsResult
    42.    invoke wsprintf, esi, [h_mem_win]
    43.    add esi, eax
    44.    ; считываем из него "оторванный" блок данных
    45.    mov edi, 512*1024*1024
    46.    add edi, [h_mem_win]
    47.    invoke wsprintf, esi, edi
    48.    add esi, eax
    49.    ; Закрываем окно в первый мап-файл
    50.    invoke UnmapViewOfFile, [h_mem_win]
    51.    ; Открываем адресное окно для второго мап-файла
    52.    invoke MapViewOfFile, [h_map_file_2], FILE_MAP_ALL_ACCESS, 0, 1024*1024*1024, 1024*1024*1024
    53.    mov [h_mem_win], eax
    54.    ; считываем из него второй блок данных
    55.    invoke wsprintf, esi, [h_mem_win])
    56.    
    57.    invoke MessageBox, NULL, addr zsResult, zSTR(<'Я изучаю Win32', 21h>), MB_OK
    Правда сработало это только в ХР, а в 98й всё это безобразие ведёт себя совершенно неадекватно :)