Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Системное программирование и WinAPI > Стек. Получение размера стека процесса. |
Автор: MuForum 25.8.2009, 12:09 |
Доброе время суток. Мне необходимо программно получить размер максимального значения стека процесса и текущий размер стека процесса. Операционная система: Windows XP, 2003. |
Автор: Lazin 25.8.2009, 12:47 |
у процесса нет стека, стек может быть только у потока |
Автор: GoldFinch 25.8.2009, 13:52 |
значения в PE заголовке показывают каким был стек при загрузке программы, до того как начал выполняться какой-то ее код, и это относится только к первому потоку программы. В процессе работы программы стек первого потока может быть изменен, а стеки других потоков могут иметь произвольный размер. Правильные параметры стека надо смотреть в TEB(TIB), поля StackLimit и StackBase. Правильные потому что система берет информацию о стеке потока именно там. ЗЫ: GremlinProg, йа вернулсо |
Автор: GremlinProg 25.8.2009, 14:12 | ||||
и о чем ты так хотел нам поведать? особенно интересует, с чем ты несогласен, т.к. это ![]() если интерпретировать в вопросе "процесс - как первичный поток", то MuForum именно об этом и спрашивает, причем "относится только к первому потоку программы" - ересь, т.к. любой наследуемый поток смотрит именно в PE, если ему не сообщили параметры стека, причем по ссылке это указано, и если бы ты ее читал, писать такое бы не стал
правильные параметры не первичного потока GoldFinch, ты можешь опять пойти отдыхать, если будешь продолжать в том же духе, т.к. к теме твой пост не относится |
Автор: Lazin 25.8.2009, 15:34 |
[offtopic]Интересно, зачем знать размер стека главного потока программы? Судя по предыдущим постам, ТС пишет много-поточный сервер для какой-то игры. Какой должна быть логика работы приложения, что-бы потребовалось знание размера стека, я даже представить не могу. Прошу заранее простить мою ограниченность... ![]() |
Автор: GoldFinch 25.8.2009, 15:46 | ||
GremlinProg, действительно, при создании нового потока, если самому не указать параметры стека потока, то его размеры будут взяты из IMAGE_NT_HEADERS, в том числе и размеры первого потока при его создании берутся от туда же, но как этот факт относится к вопросу ТСа? При чем тут текущий размер стека и максимальный размер стека? Поля IMAGE_OPTIONAL_HEADER могут быть в любой момент изменены, и в них могут быть в любой момент времени записаны новые значения которые будут использоваться как значения по умолчанию при создании новых потоков. (Кроме того, заголовок программы в памяти может быть вообще удален, или его может вообще не быть в памяти, если программа загружалась не системным загрузчиком.) Но допустим IMAGE_OPTIONAL_HEADER никто не трогал. Однако сам стек в процессе выполнения потока может быть заменен на другой, путем замены esp, и значений в TEB, это будет правильный стек, с точки зрения ОС Можно просто поменять esp, однако возможны проблемы при вызове АПИ и эксепшенах, когда ОС будет проверять стек (что будет точно - не помню).
по каким страницам? где их искать? |
Автор: GremlinProg 25.8.2009, 16:19 |
GoldFinch, у меня складывается впечатление, что ты к нам ненадолго и очень спешишь скоротать этот и так не большой срок, тебя проще исключить из разговора, чем отвечать на каждый вопрос мимо темы при том же, при чем размер стека делится на commited и reserved, нагугли адресацию Win32 по обычным http://ru.wikipedia.org/wiki/Виртуальная_память, покрывающих всесь стек, где искать, я показал, прекращай флудить, ждем MuForum за ответом на вопрос Lazin, мне это тоже интересно, никогда не пользовался |
Автор: GoldFinch 25.8.2009, 16:29 | ||||
GremlinProg, 1) я написал что предложенный тобой метод
не позволяет получить текущие размеры стека. Потому что значения в заголовке никак не связаны ни с текущими размерами стека, ни с темы размерами которые были использованы при создании стека первого потока. Это понятно? 2) я спросил, где брать адрес базы стека, чтобы определить состояние его страниц. В твоем посте сказано что надо проверять страницы, но не сказано с какой страницы надо начинать проверку.
Где искать страницы принадлежащие стеку ты не сказал. Как найти базу (первую страницу) стека? |
Автор: GremlinProg 25.8.2009, 17:01 | ||
SizeOfStackReserve - общий размер стека, он нужен, чтобы определить адрес, начиная с которого он начнет расти вниз, регион стека, начиная с максимального адреса минус SizeOfStackCommit проверять не нужно, т.к. он точно выделен, для этого его и нужно посмотреть, можно и не смотреть, если не жалко времени на проверку этой страницы (обычно страницы, но тут может быть указано и больше) текущий размер стека можно получить вычитая esp из вершины стека (максимального его адреса), с которой он начался, это очевидно, поэтому я просто описал как найти вершину, за некоторыми дополненими, которые не вполне сходятся, и о которых я упомянул при расчетах а чтобы получить реальный объем памяти, используемый под стек, нужно пройти по страницам, т.е. под размерами стека можно понимать и то и другое, потому я и описывал оба варианта в расчетах все расписал, но ежели до сих пор непонятно, причем это даже в статье описано: GetSystemInfo SYSTEM_INFO::lpMinimumApplicationAddress, который обычно равен смещению первых 64байт (SYSTEM_INFO::dwAllocationGranularity) это и есть минимальный виртуальный адрес, но за поправками на кучу (см. расчеты), этот адрес нужно увеличить на dwAllocationGranularity |
Автор: GoldFinch 25.8.2009, 18:14 |
GremlinProg, то что ты предлагаешь, содержит столько допущений построенных на пустом месте, что я хз как такое вообще можно предлагать SizeOfStackReserve и SizeOfStackCommit можно использовать только в том случае, если их никто не менял. Определение вершины стека, которое ты предлагаешь, сработает только в том случае, если начиная с 0x10000 в памяти сначала расположена куча, а потом стек, и если известен размер кучи, и если известен размер стека. Я не знаю что именно гарантирует что куча и стек будут выделены именно в таком порядке. Есть большое подозрение что это поведение загрузчика характерно только для ХР и ранних ОС, а в висте и семерке все может быть по другому. Кроме того, это все относится к системному загрузчику. Но допустим, поля в PE заголовке не изменены, и нам известна организация памяти при запуске программы. Где гарантия что за время выполнения программы стек не перенесли в другое место? Или что его размер не уменьшили? Есть куда как более надежные способы определения размеров стека. Как я уже говорил, базу и максимальный размер текущего стека можно посмотреть в TEB. Это то, что ОС считает текущим стеком потока (его можно изменить.) Другой способ основан на том, что около вершины стека лежит самый первый SEH фрейм. Пройдя по цепочке SEH фреймов его можно найти, и соответственно найти вершину того стека, который использовался при запуске .exe --------------------------------- Чтобы это все не казалось чем-то нереальным, надо напомнить, что программа может запакована каким-нибудь UPX, это полностью убивает все предположения о расположении стека в памяти. Упаковщик может запускаться со стеком небольшого размера, затем править IMAGE_NT_HEADERS, запускать распакованный код в новом потоке, и убивать первый поток. |
Автор: GremlinProg 25.8.2009, 19:35 | ||||||||||
GoldFinch, какие допущения-то? и с каких щей это мой способ-то? не понимаю PE тоже я придумал? какой же кривой я придумал PE, чтобы хранить в нем параметры кучи и стека, какая мелочь, и зачем они там )) страничное выравние, минимальный диапазон страниц для распределения - это условие, читай MSDN и допущения - это простой расчет: если по верхним адресам точно стековый диапазон, а не хватает 64 байт, стало быть они внизу, а что может быть внизу? да куча, в которой этот стек и расположен, странно бы было, если бы курица появилось раньше яйца, так же странно было бы, если бы стек появился бы раньше кучи, которая его рождает, но это так - предположение, которое довольно просто проверить: проходим HeapWalk и ищем в ней стек, нет - значит нет, считаем, что так и росло
все может быть, лучше не гадать, а проверить проверяется элементарно: F11 под отладчиком на любом приложении -> CallStack -> первый исполняемый адрес -> Registers -> ESP если ESP меньше 0x00120000, значит порядок изменен
там же, где и гарантия на то, что порше, сброшенный со скалы ф пропасть поедет после этого своим ходом, несерьезно, конечно, но ты уже 3 поста потратил на это доказательство
вот с этого места ты начинаешь показывать чудеса техники:
а теперь назови хоть один критерий, который говорит, что обращение к TEB - это надежнее, чем обращение к PE, (формат PE, кстати в отношении стабильности обгоняет TEB, который может меняться от версии к версии операционки) с SEH - все аналогично, хотя и менее проблематично, но это вообще нонсенс: лезть в ран-тайм специфику за тем, что лежит в PE, причем получать приближенные значения с гаданием на молоке, а вдруг до SEH что-то есть и утверждать что это "куда как более надежные способы определения размеров стека", парадокс
да, это на самом деле самый эффективный способ упасть со скалы, ты прав, особенно когда траекторию движения с нее тебе будет указывать не сила тяготения (Ось), а упрямый арёл (UPX), который побывал на разных планетах и знает, какое ЖЕ должно быть на нашей, и не важно, что полетать на других планетах тебе уже не придется )) |
Автор: GoldFinch 25.8.2009, 19:55 | ||||
чтото пошел совсем какой-то оффтоп %)
Cтек не расположен в куче. Cтек выделяется через VirtualAlloc. Стек не может быть выделен в куче так как она не обеспечивает выравнивания. Стек создается отдельно от кучи. Куча создается при создании объекта процесса и его юзермодных структур, перед заполнением PEB. После этого создается поток при создании которого создается его стек.
Я уже говорил что размер стека в PE заголовке может не соответствовать реальному размеру стека. И документация на PE заголовок никак не позволяет определить вершину(или базу) стека. Хотя MS не рекомендует напрямую использовать TEB, де-факто она будет стабильна еще очень долго. Слишком много приложений ее используют (упаковщики, протекторы, антивирусы и т.п.). Добавлено через 2 минуты и 28 секунд GremlinProg, так ты согласен что предложенный тобой способ 1) не позволит запаковать или защитить программу; 2) не будет гарантированно работать в произвольный момент времени работы программы. |
Автор: GremlinProg 25.8.2009, 20:39 | ||||||||||
да, только кто ж его начал? напоминаю: свое первое предупреждение ты получил именно за это, видимо понравилось ну так я не спорю, может и вне кучи странное дело, чем же тогда пользуется куча ) хотя принципы ее действия я уже не раз описывал, можешь почитать
если в куче ни чего нет, чего бы ей и не обеспечивать выравнивания? нижний-то адрес для стека не важен, важен верхний ) кстати, если он в куче, то проще всего использовать HeapWalk для определения его размера хотя тут без разницы, где он расположен, я бы расположил в куче - удобно как на первичный, так и на последующие потоки, все в одном месте
а кто же мешает несоответствовать аналогичным параметрам в TEB'е?
меня терзают сомнения, GoldFinch, почему-то когда я обращаю внимание на ограничения, ты вдруг соглашаешься, я же мог соврать, смотри, подловлю )
вообще-то такое используют очень редко, когда это действительно надо, и нет возможности поступить иначе, как не залезти в недокументированные структуры т.е. приложений таких как раз слишком немного
я бы с удовольствием сказал "да, согласен", только тут есть небольшая проблемка: я не пытался даже это доказывать, а ты, в свою очередь, даже не пытался это опровергнуть, т.е. подозреваю, что это твое высказывание упало откуда-то свыше на тебя и ты решил его сразу записать, очевидно, чтобы не забыть вообще-то у нас такие обороты не приняты, спор был в контексте что использовать: документированное - PE или недокументированное - TEB, причем ты настаиваешь на том, что читать TEB гораздо надежнее, чем читать PE, и, учитывая, что ты перешел на висту и семерку, - кроссплатформеннее, о как, так что не перегибай |
Автор: MuForum 25.8.2009, 22:56 |
Извиняюсь за задержку. В общем так, мне нужно узнать максимальный размер стека всех потоков и текущий размер стеков всех потоков. Всё это должно быть под операционную систему семейства Windows, а именно XP, 2003, 2008, Vista. Стек во время работы программы изменяться не будет. Он будет задаваться при создание потоков. Всё это мне необходимо для того, чтобы сделать визуальную статистику использования стека. |
Автор: GoldFinch 26.8.2009, 00:00 |
MuForum, максимальный размер стека, не меняется если его не менять ![]() а чтобы определить размеры стеков каждого потока, надо очевидно их перечислить (Thread32First/Thread32Next) можно получить esp из контекста каждого потока: OpenThread/GetThreadContext но где брать границы их стеков я не знаю. Разве что выполнять свой код с контексте каждого потока чтобы получить TEB, но наверное список TEB можно получить както еще. |
Автор: GoldFinch 26.8.2009, 00:24 |
на самом деле все просто: NtQueryInformationThread с классом ThreadBasicInformation позволяет получить структуру THREAD_BASIC_INFORMATION в которой есть указатель на TEB, TebBaseAddress, откуда можно получить границы стека (база и максимальный размер). Тогда надо посчитать разницу между вершиной стека (база+макс_размер) и esp (который можно получить из контекста потока), это будет текущий размер всех данных в стеке. Однако для определения размера памяти которая выделена под стек, надо перебрать страницы стека, начиная с его базы, до первой страницы с флагом COMMITED. |
Автор: GremlinProg 28.8.2009, 09:23 | ||
тогда уж лучше всего использовать CRT и получить базу стека так, как это делает crtexe.c:
это вершина стека код более-менее переносим, и есть некоторая гарантия, что поддержка CRT будет периодически делать апгрейд для NtCurrentTeb |
Автор: DrHex 28.8.2009, 10:47 |
Максимальный размер стека узнать наверника не возможно(можно только предпологать). Так происходят случае когда стек не вылитает(переполнения стека) а происходит выход за границу стека, то и есть стек это виртуальная память, так как она не прерывна, то есть возможноть что после конца стека(конец памяти, которую выделал системный загрузчик) будет следующий буфер(то и есть валидная память). Так что есть вариант, очень простой, а именно ставим хук(лучше не виндовский) на создание потока, получаем максимальный размер стека. Ну а вот что на счет статистики, то сдесь вопрос сложнее все зависит от конкретики. 1) Если требуется только текущее состояние потока то читай GoldFinch 2) Если требуется полное состояние(то и есть история) я имею ввиду пик стека, средние значения и так далее, то это сложнея - CreateProcess(с параметром DEBUG) - Цикл дебагера - Дизассемблер длин() - Ставим бряки на calls(так как раскрутка стека почти всегда в начали функции) - И бряку на создания потоков(хоть и приходит сообщения(в цикле дебагинга), но там кажется нельзя получить стек потока ) Вообщем все довольно просто...... |
Автор: GremlinProg 28.8.2009, 11:14 | ||||
DrHex, перечитай тему, т.к. мы уже давно выяснили, что сделать это можно
смотря что понимать под "концом стека", верхний или нижний? вверху стоит сторож в виде одной страницы, а внизу - стек потому и распределется сразу, чтобы внизу были неиспользуемые приложением страницы, хотя, учитывая, что куча идет первой, внизу таки есть критическая зона, только при чем тут определение размеров стека? а в остальном, я так и не понял, что ты хотел сказать, т.к. язык которым ты пользуешься к программному не относится, а http://www.google.com/search?client=opera&rls=ru&q=%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0+%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%BE%D0%B3%D0%BE+%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0&sourceid=opera&ie=utf-8&oe=utf-8 у тебя сформулировать ни мысль ни план не получилось, и при чем тут дебагер, или ты предлагаешь писать отладчик чтобы собирать статистику по стеку? |
Автор: GoldFinch 28.8.2009, 12:43 |
Модератор: Сообщение скрыто. |
Автор: GremlinProg 28.8.2009, 13:26 |
скорее всего сторож стоит таки внизу (не дотягивает он вверху на страницу) |
Автор: Lycifer 28.8.2009, 17:37 | ||||||
GremlinProg что не понятного???
Можно взять готовый отладчик....
GremlinProg - и это отладчик????? - Без пользовательского интерфейса(точнее вообще без управлением отладчиком) - Без возможности трасировки(пошаговой и так далее). - Без загрузки символом - Без полного дизассембилирования - и еще пунктов 100-200. И так это часть отладчика. А какой другой вариант собирать полную статистику?????? Отладочное апи требуется для того что бы была возможность собирать инфу. (специально для GremlinProg такая информация как раскрутка стека, то и есть вычисление пика, среднее значения, какие потоки были запущенны и с каким размером стека) <...скрыто...>
|