Форум: "Система";
Текущий архив: 2002.10.21;
Скачать: [xml.tar.bz2];
ВнизРабота с большой областью оперативки из Ассемблера Найти похожие ветки
← →
Yury Yakhno (2002-08-07 23:46) [0]Здравствуйте, уважаемые мастера.
Знает ли кто-нибудь решение этой проблемы:
Есть неразрывная область оперативной памяти размером до нескольких десятков мегабайт. На неё указывает Pointer. В ассемблерном коде нужно получить каждый байт по очереди. Проблема в том, что если написать:
var
M:Pointer;
N:Cardinal;
.
.
.
GetMem(M, N);
asm
.
.
.
mov ESI,M
.
.
.
inc ESI
mov AL,[ESI]
.
.
.
end;
то это работает на маленьких областях оперативной памяти, но если области большие, то надо ещё переходить через границу сегмента данных.
Кто-нибудь сталкивался с этой проблемой? Не подскажет ли кто-нибудь, например, как узнать какой сегментный регистр используется для моих данных (ds, es, gs, fs), или какая модель адресации используется?
← →
Anatoly Podgoretsky (2002-08-07 23:49) [1]Какая версия Дельфи?
← →
Слесарь Матерящийся (2002-08-08 00:25) [2]Модель flat. [Местами stdcall. Ой. Шютка :)]
32 бита. плоский мир. про сегментные регистры просто необходимо забыть.
и что означает:
"работает на маленьких областях оперативной памяти, но если области большие, то надо ещё переходить через границу сегмента данных."
-- совершенно непонятно. Поясните, pls.
← →
Юрий Зотов (2002-08-08 08:56) [3]Не только сегментые регистры - даже ассемблер не нужен. Простое приведение Pointer к DWORD решает проблему адресной арифметики.
← →
Yury Yakhno (2002-08-08 13:53) [4]> Anatoly Podgoretsky © (07.08.02 23:49)
> Какая версия Дельфи?
Delphi 6
> Слесарь Матерящийся (08.08.02 00:25)
> Модель flat. [Местами stdcall. Ой. Шютка :)]
> 32 бита. плоский мир. про сегментные регистры просто необходимо забыть.
> и что означает:
> "работает на маленьких областях оперативной памяти, но если области
> большие, то надо ещё переходить через границу сегмента данных."
> -- совершенно непонятно. Поясните, pls.
Насколько я знаю, Pointer - 32 битное смещение относительно сегмента данных программы. Когда я заношу указатель в ESI, он уже имеет какое-то значение. Так вот если выделить себе мегов сто и зациклить ESI на inc, не достигнет ли он своего максимального значения, а потом обнулится?
Скорее всего, я просто чего-то не догоняю, но регулярно получаю Access Violtation при обращении к [ESI] через некоторое время после начала работы.
> Юрий Зотов © (08.08.02 08:56)
> Не только сегментые регистры - даже ассемблер не нужен.
> Простое приведение Pointer к DWORD решает проблему
> адресной арифметики.
Разумеется, но я с каждым байтом ещё определённые действия произвожу. Ассемблер мне для этого нужен. А про DWORD я выше писал.
← →
Digitman (2002-08-08 14:04) [5]DS = ES = CS = SS
размер "сегмента", о коем ты так беспокоишься, формально может достигать 4GB - это предостаточно для того , чтобы надолго забыть обо всем касаемом "сегментов"
← →
Слесарь Матерящийся (2002-08-08 14:46) [6]>>>Насколько я знаю, Pointer - 32 битное смещение относительно сегмента данных программы.
В принципе так. Но видимо, вы путаете две модели памяти -- "традиционную" сегментированную для DOS и flat (protected mode).
В режиме адресации flat вы (в принципе) можете обращаться к любому адресу в рамках 0..4 Гб. Для этого вы можете использовать индексные регистры или ebx.
Модель flat подразумевает перекрытие сегментов адреса и данных в одном адресном пространстве.
Так, например (например!), если по адресу 0x04567870 в сегменте кода записан байт 0x45, то он будет виден в сегменте данных по тому же (0x04567870) адресу.
Разумеется, сейчас мы не учитывали принципы работы виртуальной памяти и т.п. Речь шла только об адресации.
--------
>>>Так вот если выделить себе мегов сто и зациклить ESI на inc, не достигнет ли он своего максимального значения, а потом обнулится
В реальном режиме DOS-программ такое могло быть. Но в protected mode работает механизм защиты памяти.
Вы можете обращаться т_о_л_ь_к_о к заранее выделенной области памяти.
Если вы обратились "мимо", сработает механизм защиты и вы получите msg "Access violation".
Просто контролируйте, чтобы указатель не переходил границы выделенной области.
Так, к примеру, если выделенный фрагмент расположен по адресу
0x00770000 и имеет длину 0x00004000 байт, то диапазон памяти, к которому можно обращаться -- 0x00770000..0x00773FFF
//
← →
Digitman (2002-08-08 15:11) [7]Добавлю только к выступлению уважаемого Слесаря, что обращение по смещению в любом текущем сегменте за пределы 64к совершенно необязательно требует работу в защищенном режиме. На современных машинах это вполне успешно реализуемо и в реальном режиме DOS, выполняемой CPU не ниже i80386. При этом, установив единожды нужные сегментные регистры и имея достаточно памяти (при окрытой 20-й адресной линии) можно запросто обращаться к любому участку физ.памяти, не заботясь более о содержимом сегментных регистров
← →
Сергей Медяник (2002-08-09 23:13) [8]Я тут сдул пыль со справочника по ASM. Небольшое дополнение и корректива словам Слесаря (Digitman © - ты заблуждаешься). В ProtectedMode сегментные регистры загружаются не базовыми адресами, а селекторами дискрипторов. Дискрипторы - структурки в памяти, которые и описывают назначение, базовый адрес, размер страницы памяти и количество памяти в этих страницах, права доступ для блока памяти (код, стек, данные) => этим и реализуется ЗАЩИТА - процессор сам не залезет указателем данных в область кода или стека, например. Дискрипторы есть глобальные, используются ядром операционки, и локальные - прикладной программой. А Pointer, собственно указывает смещение.
Так что в принципе, обращение может быть действительно к любому байту в выделенной области, заковырька может быть в виртуальной памяти. Этот механизм реализуется программно ядром ОС. Win управляет страничным обменом сама => может быть разница между физической организацией 100 блоков по 1Mb и 1 блоком 100Mb.
TO Yury Yakhno: Попробуй выделять себе память не более доступной физической или, лучше небольшими (относительно) блоками. Определение объема памяти я подсмотрел на этом форуме, цитирую:
Function GetRAMSize:integer;
var MS : TMemoryStatus;
Begin
GlobalMemoryStatus(MS);
Result := MS.dwTotalPhys;
end;
dwTotalPhys - Полный объем ОЗУ (т.е. физической памяти)
dwAvailPhys - Свободный объем ОЗУ (как правило небольшая величина)
dwTotalVirtual - Полный объем виртуальной памяти
dwAvailVirtual - Свободный объем виртуальной памяти
dwMemoryLoad - Процент использования памяти (0-не используется, 100-используется вся)
dwTotalPageFile - Общий размер данных (в байтах), которые могут быть сохранены в файле подкачки (но это не является его размером на диске !!)
dwAvailPageFile - Доступный объем в файле подкачки
Решение не фонтан, конечно, прийдется управлять самому переходами между блоками, но я сейчас сам ломаю голову над подобной проблемой (нужно расположить в памяти огромное количество данных и иметь возможность их быстро перегруппировывать, но управлять блоками хочется самостоятельно, без New, GetMem и прочего стандартного - оно тормозное для моей задачи).
Попутно вопрос, а сколько всего физической памяти у тебя и какая OS? (W2k и W98 могут по разному реагировать на такое насилие :)
← →
Yury Yakhno (2002-08-10 14:23) [9]Спасибо ВСЕМ за объяснения. Дело в том, что мой справочник по ассемблеру несколько оторван от системы. В нём описаны механизмы работы сегментных регистров, но нет ни слова о том, что с ними делает Windows. Просмотрев свой ассемблерный код и не найдя там ничего подозрительного, я подумал на сегменты. Что ж, Вы меня почти убедили, что я ошибался. Осталось выяснить причину "Access Violtation". Если я не смогу сделать это сам, я выложу здесь проблемный кусок программы.
2 Сергей Медяник
> Этот механизм реализуется программно ядром ОС. Win управляет страничным обменом
> сама => может быть разница между физической организацией 100 блоков по 1Mb и 1
> блоком 100Mb.
Я эту разницу вижу только в строгой неразрывности 1 блока по 100 мб, о чём я говорил в начале.
> Попробуй выделять себе память не более доступной физической или, лучше небольшими
> (относительно) блоками. Определение объема памяти я подсмотрел на этом форуме, цитирую:
Собственно, я так и поступаю. Для определения свободной памяти я использую ту же функцию, о которой говоришь ты.
Кстати, там перед строкойGlobalMemoryStatus(MS)
нужно ещё написатьMS.dwLength:=SizeOf(MS)
.
> Решение не фонтан, конечно, прийдется управлять самому переходами между блоками, но
> я сейчас сам ломаю голову над подобной проблемой (нужно расположить в памяти огромное
> количество данных и иметь возможность их быстро перегруппировывать, но управлять
> блоками хочется самостоятельно, без New, GetMem и прочего стандартного - оно тормозное
> для моей задачи).
Мне кажется, что для моей задачи подойдёт один большой кусок оперативки. В том, что с ним можно работать без глюков, я уверен. Интересно узнать, как :-)
> Попутно вопрос, а сколько всего физической памяти у тебя и какая OS? (W2k и W98
> могут по разному реагировать на такое насилие :)
Физической памяти у меня 256 MB, так что а могу себе позволить подобные массивы :-)
Операционка - Win2k, но я запускал exeшник и под Win95 - та же ошибка.
← →
Anatoly Podgoretsky (2002-08-10 14:43) [10]Для тебя эта память будет неразнывная, зотя на самой деле не обязательно так, да и в разные моментц времени она может оказываться в разных местах оперативной памяти
← →
Сергей Медяник (2002-08-12 03:07) [11]ЕСТЬ РЕШЕНИЕ!!!
5 часов копания в Helpe и десятки эксперементов привели к следующему:
Чтобы обратиться к любому байту из выделенных GetMem нужно писать:
GetMem(PointerFromGetMem)
asm
//ОБЯЗЯТЕЛЬНО! Именно отсюда Access Violation...
push eax
push ebx
//Загружаю в EBX адрес, содержащийся в переменной.
mov ebx, DWORD PTR PointerFromGetMem
///// ВМЕСТО ebx, PointerFromGetMem !!!!!!!!!!
//EBX содержит нужный указатель, индексируй как хошь!!!
mov al, b
mov [ebx], al
//ОБЯЗЯТЕЛЬНО! Именно отсюда Access Violation...
pop ebx
pop eax
end;
Так же разобрал ЕЩЕ 3 способа выделения памяти через WinAPI. У каждого свои особенности (Через кучу, виртуальная, разделяемая процессами).
Есть программка для демонстрации. Кому надо, пишите на мэйл - скину (200kb ZIP).
Рекомендация по работе с большими блоками вообще: Не обрабатывайте сразу много в одной процедуре. Если выделили больше доступной физической - свапит страшно, аж WinAmp гавкает!
Короче, я родственник этого ассемблера, этого виндовз, этой виртуальной машины, ее создателей и БИЛА ГЕЙТСА в частности!!!
P.S. Сделано на: Duron 600, 196Mb, Win2k, Delphi6, проверял на блоках по 200 метров (больше просто тормознее, принципиально ничего не меняется).
← →
mumu (2002-08-12 05:22) [12]Странно. Такой простой вопрос и столько словопрений.
Кидаем на форму кнопку и вставляем обработчик:
procedure TForm1.Button1Click(Sender: TObject);
var
Size: Integer;
Buffer: PChar;
t1,t2,t: LongInt;
begin
// выделяем 500 000 000 байт
Size := 500000000;
GetMem(Buffer, Size);
// для приличия можно перехватывать EOutOfMemory
t1:=GetTickCount;
try
asm
mov ecx,size
mov esi,buffer
@l1:
mov al,[esi]
// делаем, что хотим с al
//-------------------------
inc al
// ------------------------
mov [esi],al
inc esi
loop @l1
end;
t2:=GetTickCount;
Showmessage("Затрачено времени на обработку"+#13+
IntToStr(size)+" байт"+#13+
IntToStr(t2-t1)+" миллисекунд.");
finally
FreeMem(Buffer);
end;
end;
Если файл подкачки определяется системой, то она автоматически увеличит его до нужных размеров (верно для w2k и w9x).
Если в объем виртуальной памяти фиксирован и меньше выделяемой памяти, то для:
w2k получим сообщение об увеличении файла подкачки
w9x сообщение об ошибке выделении памяти.
На обработку блока памяти ушло примерно 20-22 сек.
Сделано на: PIII 866, 256Mb, Win2k Pro+SP2, Delphi6.
Не вижу кода, но рискну предположить, что у тебя ошибка в ассемблерной вставке.
---------------
С уважением, mumu
← →
Digitman (2002-08-12 09:25) [13]>Сергей Медяник
В чем же я заблуждаюсь, позволь полюбопытствовать ?
p.s.
Про селекторы - заметь - я ни слова ни сказал, благо прекрасно понимаю разницу между сегментным адресом и селектором сегмента в сегментном регистре.
← →
Сергей Медяник (2002-08-12 10:26) [14]>Digitman ©
Деталь, насчет сегментных регистров. Туда пишустся СЕЛЕКТОРЫ дискрипторов (их может быть несколько тысяч в системе и меняются они системой с бешенной скоростью).
>mumu
Попробуй свой код в действии. (см. Код автора топика и мою коррективу).
← →
mumu (2002-08-12 10:38) [15]>Сергей Медяник
Именно с работающего проекта и снято.
А как же иначе я стал бы советовать???
---------------
С уважением, mumu
← →
Digitman (2002-08-12 11:18) [16]>Сергей Медяник
Вот спасибо тебе за "новость" ! Как раз вот этого я и не знал)
Может и не стоило бы дополнять, но я вообще-то о другом сказал, ты вникни в мое дополнение.
А сказал я , что для доступа к любому участку физ.памяти вовсе необязательна работа в PM i80x86 - сие возможно и в RM, когда сег.регистры интерпретируются CPU обычным для MSDOS и ее приложений способом : как 16-разрядный адрес параграфа, а не как селектор сегмента.
← →
mumu (2002-08-12 11:38) [17]2Digitman ©
Но ведь в реальном режиме DOS (REAL-ADDRESS MODE)
используется 16-битный IP. Как же можно вызезти
за пределы 64 Кб?
← →
Shaman_Naydak (2002-08-12 11:54) [18]> mumu и Сергей Медяник
Выдержка из помощи
An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers, but can freely modify the EAX, ECX, and EDX registers.
А EAX тебе пришлось скорее всего сохранять тоже из-за оптимизатора..
а с mumu как раз наборот.. повезло, что регистры не были нужны, потому и сбоев не было
← →
Digitman (2002-08-12 12:03) [19]А я и не говорил конкретно про соответствующие возможности для расширенной адресации процессором инструкций исполняемого кода.
Зато ничто не мешает, скажем, успешному выполнению такого вот блока :
mov es, ...
mov ds, cs
mov esi, offset src_offset
mov edi, offset dst_offset
mov ecx, dwords_count
cld
rep movsd
при этом в физическое АП осн.памяти (предел "досягаемости" cs:ip при адресации инструкций в RM/VM ) в один прием (без переключения сегментных адресов) можно скопировать сколь угодно
большой блок данных из АП расширенной памяти (и далее передать управление в нужную точку, если того требует использование данной возможности при организации, скажем, нестандартного overlay-механизма)
← →
mumu (2002-08-12 12:06) [20]2Shaman_Naydak ©:
Именно так. Спасибо за подсказку. Регистры в стек и прения закрыты.
(Вот она -- сила документации :)))
---------------
С уважением, mumu
← →
Yury Yakhno (2002-08-12 13:32) [21]Спасибо всем, принимавшим участие в решение проблемы. Я отловил все глюки, и теперь всё работает.
Сделаем выводы:
- сегменты памяти не при чём
- с файлом подкачки разбирается Windows
- регистры надо спасать перед вставкой (в начале разработки можно писать pushad и popad, чтобы не мучаться)
← →
Сергей Медяник (2002-08-12 16:35) [22]>Digitman © и остальным
Я признаю, что мог погорячиться с навязыванием своего представления о дискрипторах и прочем. Хотя я просто пересказал описания в книге, замечу, что там был ЧИСТЫЙ Asm, то есть в листингах описывались все механизмы управления памятью (в Win и Delphi за нас это делает ядро) => я наплел лишнего. Тем более, что экспериментально выяснил (Protected Mode): память действительно линейная для процессора (включая файл подкачки), более того начинается с 0, то есть необходимо заботиться не о DS, ES и т.п., а о верном базовом адресе при отсчете типа [EBX]. При этом Win сам распределяет блоки памяти (назначая им правила доступа), постранично свапит и следит за фрагментацией. Каждому блоку памяти, откушенному процессом соответствует Handle, по которому Winda и ориентируется в его расположении, размере, правах. Кстати, в WinAPI я вообше не обнаружил дискрипторов, только Handlы.
> mumu
Если работает, спорить не буду. Дело в том, что эти "DWORD PTR", которыми мой код отличен от Yury Yakhno играет ключевую роль, я так понял, для нетипизированных указателей
Mov ebx, VarOfPointer
записывает в ebx не значение указателя из VarOfPointer, а адрес переменной VarOfPointer в сегменте данных программы => попытка обратиться к 100Mb-му блоку там где всего 4 байта ПРОСТО ОБЯЗАНА ВЫКИНУТЬ ОШИБКУ.
> Shaman_Naydak ©
EAX сохранил с перепугу и недосыпу :-))) - это действительно необязательно.
>Digitman ©
В Real Mode дополнительные мегабайты использовались через маленькое окошко памяти, банки переключались через какой-то порт. Удобный способ давали дрова типа HIMEM.SYS - просто вызываешь сервис через SOFT-прерывания.
Попутно вопрос: ты уверен, что RealMode позволяет использовать 32-битные регистры. Я не ехидничаю, просто интересуюсь - не уверен, что это так.
>mumu
О простоте вопроса. Тут недавно обсуждали как от String отрезать последние 3 символа, так что здесь решается глобальная проблема! :-)))))
← →
limon (2002-08-12 18:48) [23]> Сергей Медяник (12.08.02 16:35)
и не только RM, но даже 16-разрядный сегмент кода имеет право на использование 32-х р регистров, т.к. все зависит от типа кодового сегмента.
для USE16 при использовании EAX... добавляется префикс команды (если не ошибаюсь 0х66), для USE32 - соответственно - наоборот.
← →
Yury Yakhno (2002-08-12 21:00) [24]> Сергей Медяник
> Если работает, спорить не буду. Дело в том, что эти "DWORD PTR",
> которыми мой код отличен от Yury Yakhno играет ключевую роль, я так
> понял, для нетипизированных указателей
> Mov ebx, VarOfPointer
> записывает в ebx не значение указателя из VarOfPointer, а адрес переменной
> VarOfPointer в сегменте данных программы => попытка обратиться к 100Mb-му
> блоку там где всего 4 байта ПРОСТО ОБЯЗАНА ВЫКИНУТЬ ОШИБКУ.
Нет, записывается именно значение VarOfPointer. Какой это указатель всё равно. Это легко можно проверить при пошаговой трансляции.
2 All
Могу сказать, что из всех Ваших примеров у меня была самая тупая ошибка - неправильно работал внутренний счётчик, поэтому прога пролетала весь буфер и лезла дальше. При этом я не забывал сохранять все регистры в стеке. Теперь всё работает, как я уже писал, так что есть работающая версия.
Всё равно, на мой взгляд, здесь многие узнали что-то новое о работе с ассемблером в Delphi :-)
← →
Сергей Медяник (2002-08-13 09:14) [25]>Yury Yakhno
> Нет, записывается именно значение VarOfPointer. Какой это
> указатель всё равно. Это легко можно проверить при пошаговой
> трансляции
Поверю на слово, но у меня рабочий код от нерабочего отличался только этим.
И с моей стороны всем спасибо!
← →
Shaman_Naydak (2002-08-13 13:23) [26]> ALL
Кстати, всем желающим знать кухню работы различных режимов работы процессора и адресаций советую прочесть книжку
Процессоры Pentium 4, Athlon и Duron.
Не помню чье издательство. Правда, книжка не для новичков ;)
← →
Dmk (2002-08-14 02:48) [27]Так, на всякий случай. Пример отображения 32 битного изображения зеркалом.
//...
{
Страницы: 1 вся ветка
Форум: "Система";
Текущий архив: 2002.10.21;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.008 c