いつもお世話になります。ミミです。
毎回冗長的な質問で申し訳ありません。
WinCE 3.0 環境でのスタック領域は 58KB、
Win2K等のPC環境でのスタック領域は 1MB 程度と認知していますが、
このスタック領域の残容量を取得する方法を探しています。
現在は、このスタック領域をわざとオーバーフローするように細工し、
その結果から導くしかないかと考えていますが、もう少しスマートに残量が
分かるような API 等はありませんでしょうか?
最終的な目標は WinCE 機でのスタック領域残量の取得ですが、
まずは、PCにて意図的にスタックオーバーフローさせ、
指定した値から残りの残量を把握する為のテストプログラムを作ってみました。
<<テストプログラム>>
ダイアログベースでテストプロをつくり、
App::InitInstance()で、Dlgクラスをインスタンス化し(スケルトンで既に生成されています)
さらにOnButton1()内に、大きなサイズのローカル変数(=スタック)を宣言しました。
CHogeDlg::OnButton1(){
char sTest[☆]; // 下記参照
memset(&sTest, 0x00, sizeof(sTest) );
MessageBox(End of Func., ", MB_OK);
}
と記述し、PCにてDebug環境で試してみました。
sTestのサイズ(☆)が、1048576(1024x1024B)と指定した時、
Debug環境だと「c00000fd Stack Overflow」と例外が発生します。
しかしRelease環境だと何もエラーが発生せず処理が続きます。
ですが、偶然動いているだけと思うので非常に危険と考えています。
この☆の値を徐々に減少させ、例外が発生する・しないの境目となった時の☆の値が
残りのスタック領域と認識できますが…非現実的な様な気がします。
テストアプリ作成環境
Win2000、VC6、MFCを使用したダイアログベースアプリ
# お金を出してよければ、動的解析ツールにスタック計測機能もありそうな気がします
が、
# ここでは自作する前提で。
実行パスがプロファイラ(VC6pro以上ならついてるはず)等で判明していれば、
そのときのスタックポインタの値をアセンブラで引っこ抜けば、
大まかな残量は取れるのではないかと思い、ざっと探してみたらこんなスレが。
http://forums.belution.com/ja/vc/000/133/80.shtml
# スタックサイズ(1Mなど)の方は、PE ヘッダに書かれるので、これを読めば取れるは
ず。
# CEは分かりませんが.....。
また、使用スタックサイズは概ね呼び出し関数毎の使用スタックサイズの和なので、
理論値でよければ、使ってみたことはありませんが、
http://www.vector.co.jp/soft/win95/prog/se119765.html?site=n
このあたりのツールで関数毎のスタックサイズを出して、
関数のコールスタックと照らし合わせる(ようなツールを書く)とか。
> ですが、偶然動いているだけと思うので非常に危険と考えています。
御意。
お疲れ様です。
> WinCE 3.0 環境でのスタック領域は 58KB、
> Win2K等のPC環境でのスタック領域は 1MB 程度と認知していますが、
WinCEは知見がありません。
少なくとも、WinNT,Win2k,WinXPは指定可能です。
この値はリンク時に決まります。
スレッド毎に設定することはできません。(WinNT系)
多分CEも同じでしょう。
スタックのアーキテクチャが大きく違うとは思えないのと、
MSDNを覗いて見た所、利用するAPIは対応している模様なので
そのまま答えてしまいます。
さて、この問題は以下の理解が必要ですが、どうでしょうか?
・論理アドレスの管理され方
→コミット、リザーブ、等
・スタックの動作
→主にガードページ
・スレッドコンテキスト
→論理レジスタ等
以下の話で、詰まる部分があるのならば、
上記単語を頼りに調べて貰えれば良いと思います。
■解析の話
以下に、Win2kのとあるプロセスの論理アドレスの状況を示します。
独自ツールで引っこ抜いた情報です。
【論理アドレス状況 -- 抜粋】
BaseAddr Size State Type Acc Attr
:
1: 0x00030000 0x000F9000 RESV PRI RW
2: 0x00129000 0x00001000 CMIT PRI RW G RW
3: 0x0012A000 0x00006000 CMIT PRI RW RW
:
【スレッドコンテキスト情報 -- 抜粋】
TID:0x00000AAC ESP:0x0012FE48
3行目のブロック内にスタックポインタがあるようです。
(つまりこの領域はスタック)
次にスタックポインタの直上のブロック(2行目)は1ブロックだけ
コミットされています。
これがガードページです。
更にその上に1行目に予約領域(未コミット)があります。
スタックはこの3行で1セットです。
(理由はガードページの仕組みにあります)
→ 3行のブロックの総和は1Mになってますね。
以上、スタックのボトム、トップ、ポインタ、それぞれの位置が判明したので
スタック領域の残量も計算できます。
これで後は、実際の手法の話だけです。
■技術的な手法の話
ここら辺はポインタだけで良いでしょう。
・論理アドレス状況の抜き方
VirtualQueryを利用します。(winbase.h)
・スレッドコンテキストの抜き方
自スレッドの情報で良いなら、適当なスタック変数を見るか
直接レジスタの値を見れば良い。
別スレッドから抜く場合、
『toolhelp32』ライブラリを利用します。(WinCE 2.0以降)
→CreateToolhelp32Snapshot() 等を使う。
以上です、暇があったらまた来ます。
追記というか修正です。。
> 3: 0x0012A000 0x00006000 CMIT PRI RW RW
> TID:0x00000AAC ESP:0x0012FE48
ガードページが、スタックポインタの直上に来ていません。
むしろ底ですね・・・
ただ、コレもガードページの仕組みです。
・成長過程のスタックの場合、スタックポインタの直上がガードページです。
・その後、スタックが巻き戻される(衰退する)と
スタックポインタだけ底に落ちていきます。(今回の例)
WinCE用のVisualC++にその手のツールはないんですか?
組み込み用C++言語という位置づけでいいんですよね。
リンクのオプション等にもありそうな気がするのですが。
CE で通用するかどうか分かりませんが、参考になりますか?
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200503/05030004.txt
> スタックはこの3行で1セットです。
> (理由はガードページの仕組みにあります)
> → 3行のブロックの総和は1Mになってますね。
なるほど。
スタック1MBと言っても、1MB確保されているのではなく予約されているだけで、
ガードページに触っちゃったら例外トラップして確保しているわけですか。面白い。
こんばんは、ミミです。
Ban様、Bosscat様、ITO様、シャノン様、ご返信ありがとうございます。
今回もまたお世話になります。
・・・率直な所、皆様が折角ご指導してくれたにも関わらず、殆ど理解できていません。
自分の技力の無さが思い知らされます。
理解しないうちは手も足も出ませんので、
まず、Bosscat様がご説明してくださいました解説を理解する所から始めてみます。
Ban様とシャノン様がリンクしてくださいましたURL先も調べてみます。
一方で、ITO様のご指摘のあったリンクオプションも調べてみます。
# 個人的には API さえ発行すれば、
# GlobalMemoryStatus() で現在のメモリステータスを取得する方法と同じような感じに
# スタックサイズも取得できるものかと思っていました・・。
> この値はリンク時に決まります。
> スレッド毎に設定することはできません。(WinNT系)
プロセスのメインスレッドはそうですが、CreateThread で作る場合は個別に指定できま
すね。
CreateThread で指定するスタックサイズは、
<MSDN>
スタックの初期のコミットサイズを、バイト単位で指定します
</MSDN>
なんですよね。リザーブサイズではなく。
んで、1MBってのは、リザーブの方。
追記:原文の方.
<MSDN Thread Stack Size>
To increase the amount of stack space which is to be
initially committed for a thread, specify the value
in the dwStackSize parameter of the CreateThread or CreateRemoteThread function.
</MSDN>
>However, if the initially committed size specified by dwStackSize
>is larger than the default reserve size, the reserve size is
>this new commit size rounded up to the nearest multiple of 1 MB.
とも書いてありますね。
あ、ホントですね。
そうだったのか…orz
基本的にスタックサイズに関しては実行ファイル作成時に決まる物であって
OSの環境による物ではないと思います。
要するにビルド時の設定で決まると考えてよいのではないでしょうか。
PCとCEの規定値の違いは単にリソースがリッチなのかプアなのかと言う条件の違いから
来るものであって、ビルド時の設定によっては変わってくるでしょう。
ただ、スタック領域をあまり大きくしてしまうとプアな環境では動作しないという
事になってしまうでしょうから、あまり頻繁に変えるような設定ではないと思います。
なのでスタックの残り領域を云々するよりもきちんと使用するローカル変数の管理を
行ってスタックを大きく消費するような変数はヒープ領域から取得するように
プログラマ側で使い分けるべきです。もしくは設計時にはじめからきちんと決めます。
変数の管理をきちんとしていれば、通常は規定値のサイズで十分動作すると思います。
規定値のサイズで動かない場合は、むしろ設計とかプログラミング方法に問題があると
考えるべきではないでしょうか。
> あ、ホントですね。
> そうだったのか…orz
#ほ、ほーら、俺の言ったとおりだ(嘘付け
#ごめんなさい間違えてました