スタック領域について – 固定ページ 2 – プログラミング – Home

通知
すべてクリア

[解決済] スタック領域について

固定ページ 2 / 3

ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

試しにマップファイルを作ってみました。
結構細かく出力されています。
これならスタック領域も出力してくれてもいいような気がします。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

追加です。

実際のアドレスは分りませんが、マップファイルだけでも個々の関数のリンク時の
アドレスが見られます。
スタック領域は分らなくても、かなりの情報は得られると思います。


返信引用
とおりすがり
 とおりすがり
(@とおりすがり)
ゲスト
結合: 23年前
投稿: 180
 

あまり調べてないんですが、ビルド時の設定で決まるスタックサイズって
メインスレッドだけじゃないんですかね?
自分で立ち上げたサブスレッドは、指定可能じゃないですか?
指定しなければ、メインスレッドと同じになるでしょうけども。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> 試しにマップファイルを作ってみました。
-- snip --
> これならスタック領域も出力してくれてもいいような気がします。

静的に出力されるマップで、動的なスタックの使用量を得るのは無理でしょう。
# 仮に関数単位での消費スタックサイズがわかったとして、
# いくつの関数呼び出しがネストしてるかなんて実行時にしか特定できないわけで。

> 実際のアドレスは分りませんが、マップファイルだけでも個々の関数のリンク時の
> アドレスが見られます。

それを確認するのがマップの主目的(?)ですから、コードが配置されるアドレスは
確かにわかりますが、実行時の消費スタックサイズを知る助けにはなりません。

> スタック領域は分らなくても、かなりの情報は得られると思います。

ミミさんの目的がデバッグなどなら役に立つかも知れませんが、
使用メモリの確認(組み込みだとありがち)が目的の場合には直接関係してきません。

> 自分で立ち上げたサブスレッドは、指定可能じゃないですか?
> 指定しなければ、メインスレッドと同じになるでしょうけども。

そうですね。可能でした。

その上で、ミミさんが欲しいのは、そこで指定するスタックサイズ自体ではなく、
「今、その中からどれだけ使ってるか/どれだけ残っているか」かと。


返信引用
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

>> 自分で立ち上げたサブスレッドは、指定可能じゃないですか?
>> 指定しなければ、メインスレッドと同じになるでしょうけども。

> そうですね。可能でした。

それは

> シャノン 2006/01/09(月) 20:00:38

あたりのことを言っているのでは?

#CreateThread じゃなくて NtCreateThread 使うなら可能かもしれないけど。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

何か手がかりになりますか?

Windows CE .NET の高度なメモリ管理
http://www.microsoft.com/japan/msdn/library/default.asp?
url=/japan/msdn/library/ja/jpdnce/htm/advmemmgmt.asp

Windows CE Platform Builder Version 3.0 リリース ノート
http://www.microsoft.com/japan/msdn/library/default.asp?
url=/japan/msdn/library/ja/wcelib/htm/relnotes.asp


返信引用
ミミ
 ミミ
(@ミミ)
ゲスト
結合: 24年前
投稿: 63
Topic starter  

こんばんは、ミミです。

ahan様、Ban様、PATIO様、シャノン様、とおりすがり様、ITO様
先週の書き込みから、色々とご指導ありがとうございます。

・・皆様の言っておられる事がまだ理解できていません。(冷汗)
Windowsプログラムの基本はもちろん、コンピュータの仕組みや
OS側の知識まで必要なのだなと痛感しております。

>-------------------------------
>PATIO様 2006/01/10(火) 10:25:31
>変数の管理をきちんとしていれば、通常は規定値のサイズで十分動作すると思います。
>規定値のサイズで動かない場合は、むしろ設計とかプログラミング方法に問題があると
>考えるべきではないでしょうか。

ご指導ありがとうございます。
仕事の作業時間の都合上、結局このスレッドの一番上に示した
方法(あまりにも原始的ではありますが)を用いて調査してみました。

以下、要点だけまとめたコードで記述します。

(開発中の)プログラムが実行中の時に、
例えば OnButton1()等のタイミングで残りのスタックサイズがどのくらいか、
(=即ち、どのくらいのローカル変数(スタック確保分)を定義すればスタックオーバーフローに
なるか)
で調べた所、58KBの WinCE 制約上、43KB 程度の値は確保する事ができました。
一方で、スケルトンのみのプログラムでさえも、46KB 程度確保できました。
よって、開発中のプログラムでは 3KB (46-43)程度の消費量と考えています。

ただ、別の OnButton2()のタイミングで実行される関数に於いて、

void CHoge::OnButton2(){
// ☆
MY_TABLE myTbl; // ★1 MY_TABLEのサイズは分かりやすく10 KBとします。
myTbl = Func1();
}

MY_TABLE CHoge::Func1(){ // ★2
return Func2();
}

MY_TABLE CHoge::Func2(){ // ★3
MY_TABLE myTbl; // ★4
memset(&myTbl, 0x00, sizeof(MY_TABLE) );
~ここで、myTblに対して値を設定する処理~
return myTbl;
}

という処理が行われています。
この場合に、☆の位置にて、残りのスタック容量が極端に少ないことが確認できました。
OnButton2()が呼ばれてすぐに、例えば
MY_TABLE myDmy;
等と追加するだけで、スタックオーバーフローを再現する事ができます。

関数が関数を呼んでいるので、
インスタンス化している、★1、★4では 10KB ずつスタックに確保されるのは分かります
が、
戻り値として定義しているだけの、★2、★3でもスタック消費されている気がしています。

# 単に私の勉強不足なのかもしれません。C言語の基礎知識でしょうか?
# 戻り値として定義してある関数を呼ぶだけでも、戻り値分のサイズがスタックに確保されるの
でしょうか??

現在、この回避方法として、★1ではローカル変数(スタック)ではなく、メンバ変数(ヒープ)
として確保し、
Func1()、Func2()も MY_TABLE 型のポインタでやりとりしようと考えています。

(↓下に続きます。)


返信引用
ミミ
 ミミ
(@ミミ)
ゲスト
結合: 24年前
投稿: 63
Topic starter  

>-------------------------------
>ITO様 2006/01/08(日) 15:33:27
>組み込み用C++言語という位置づけでいいんですよね。
>リンクのオプション等にもありそうな気がするのですが。

はい。組み込み用と言っていいのか分かりませんが、モバイル機を使用しています。
仰るとおり、リンクのオプションに「/stack:0x10000,0x1000」と指定されている箇所があり
ました。

また、マップファイルを作成して検証していただいたとのこと、誠にありがとうございます。
それを作成すれば、何か手がかりがつかめるのですね。
リンクオプションについてと、マップファイルが何なのかから、スタートしてみます。
一方で、MSDNへのリンク、ありがとうございます。

# 難しい内容ばかりで、凹みそうですが、空いた時間でじっくり読んでみます。

>-------------------------------
>Ban様 2006/01/14(土) 12:11:57
>その上で、ミミさんが欲しいのは、そこで指定するスタックサイズ自体ではなく、
>「今、その中からどれだけ使ってるか/どれだけ残っているか」かと。

はい。仰るとおりです。
今回の書き込みの最初に記載したとおり、結局はバグ探しの為にスタックサイズの取得方法を探
していました。
確かにスタックサイズを大きく指定すれば症状を「回避」することはできるのかもしれませんが
問題を「解消」したとは言えないですね。

当方で問題が発覚した時、がむしゃらに試し、メンバ変数化すれば症状は出ないことだけは確認
していました。
もちろん、当時は理由なんてこれっぽちも考えられませんでした。
メンバ変数化して逃げたかったのが本音なのですが、でもじっくり調査しなければ、
今回の件は(スタックオーバーフローするFunc1()、Func2()について)分からなかった訳
で・・・。

以上、走り書き&ご返信してくれた全ての方への返信が出来ないことをお許しください。
とりあえず、スタックオーバーフローが発生してしまう当方の件については、その対処法が見つ
かったので
それで試行してみます。
完全に確認がとれましたら、改めて解決チェックを付けさせて頂きます。

長文失礼しました。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> # 単に私の勉強不足なのかもしれません。C言語の基礎知識でしょうか?
> # 戻り値として定義してある関数を呼ぶだけでも、戻り値分のサイズがスタックに確保
されるの
> でしょうか??

確保される可能性があります。
実体を関数から戻そうとしていますが、この際の動作イメージを簡単に書くと、
一度「無名の一時変数」が作られて、それが戻り値を受ける変数にコピーされる感じで
す。
なので、この暗黙の一時変数がスタックに積まれる可能性があるわけです。

ただし「戻り値の最適化」というのが許可されてますので、ただの構造体などで
デストラクタ等の副作用もなければ、コンパイラの最適化によってはこの
一時変数がなくなる *可能性もあります*。
お使いの環境では消えないのでしょう。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

> # 単に私の勉強不足なのかもしれません。C言語の基礎知識でしょうか?
> # 戻り値として定義してある関数を呼ぶだけでも、戻り値分のサイズがスタックに確保
されるの
> でしょうか??

C言語と言うよりもC++言語と言った方が良いと思います。
C言語では配列をそのまま返すような事はできませんから。
C言語だとこういうケースではアドレスで返すはずです。
中身的にはBanさんか説明されている通りだと思います。
あと、今されているように値返しする場合は、一時オブジェクトが
作成されるのでスピードも遅くなります。
CEの場合、環境的にPCよりもプアなので速度的には不利だと思います。
値返しの方がクラス内のデータ保護には良いのですけれど、
オブジェクトの生成は結構コスト高なのでループ内で連続して呼ばれる
ような場合は結構足を引っぱりますし。
あと、CEで10KBもあるような変数はスタックに積むべきではないと思います。
関数は基本的に何処からでも呼ばれる可能性がありますから
呼ばれたときにスタックに十分な空きがある保障はありません。
個人差はあると思いますが、私の場合だと1KB以上はヒープを使います。
ローカル変数に確保するのは大きくても数百バイトくらいまでです。
CEだと数百バイトでもどうしようかと思ったりします。


返信引用
ミミ
 ミミ
(@ミミ)
ゲスト
結合: 24年前
投稿: 63
Topic starter  

ミミです。

皆様のアドバイスにより、おかげさまで解決することができました。

>【ミミ 2006/01/17(火) 00:51:34】
>戻り値として定義しているだけの、★2、★3でもスタック消費されている

BAN様 と PATIO様 のご説明にありました「無名の一時変数」について調査を行いました。
MY_TABLE を戻り値とする関数を経由する回数を変えて試行してみたところ、
意図する結果となりました。やはり一時変数により、スタックを消費していたという
結果になります。
 ・OnButton2() -> Func1() -> Func2() の順で呼ぶ → 残りのスタックは n バイト程度
 ・OnButton2() -> Func1()       の順で呼ぶ → 残りのスタックは n + MY_TABLE
バイト程度

>★1ではローカル変数(スタック)ではなく、メンバ変数(ヒープ)として確保し、
>Func1()、Func2()も MY_TABLE 型のポインタでやりとりしようと考えています。

この方法で開発中のプログラムにも修正した所、スタック消費量が
3KB 程度の消費量まで回復しました。(【ミミ 2006/01/17(火) 00:51:34】の結果と同じ)

構造体が戻り値として定義できるのは、C++ ならではなのですね。
正直見落としていましたので、とても勉強になりました。

>【PATIO様 2006/01/17(火) 13:47:41】
>個人差はあると思いますが、私の場合だと1KB以上はヒープを使います。
>ローカル変数に確保するのは大きくても数百バイトくらいまでです。
>CEだと数百バイトでもどうしようかと思ったりします。

CE の世界での開発者の考え方も得られたので、とても嬉しく思います。
最近は『メンバ変数をたくさん使用すると、それぞれの関数の独立性が薄くなる』
という考え方を元に、極力メンバ変数は使用せず、引数を利用していました。

しかし、例えば func1()に TCHAR szPath1[MAX_PATH] と定義しただけでも
520 KB のサイズがスタックに確保されてしまいますので、『極力メンバ変数を
使用しない』という自分の今までの考え方を改善し、臨機応変に対応する方が
いい気がしてきています。

何にせよ、今回のバグ探しにより得られた事は大変多く、技力向上にも
なったと思います。

皆様、ありがとうございました。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> 構造体が戻り値として定義できるのは、C++ ならではなのですね。
> 正直見落としていましたので、とても勉強になりました。

いえ、構造体は C でもできるはずですが。
そして、配列は C++ でもできないはずですが。

ミミさんの例では
> MY_TABLE 型のポインタ
と書いてあり、勝手に私は構造体だと認識していましたが、
PATIOさんのご回答は、
> C言語では配列をそのまま返すような事はできませんから。
配列と書かれていますので、何か誤解があるのかもしれません。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

もしかすると、K&R 時代の C (ANSI規格化以前)だと構造体は戻り値にできないかも。
# 構造体の = によるコピーができかった時代


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

検索したら、C FAQにありました。
http://www.kouno.jp/home/c_faq/c2.html
<引用>
構造体を、変数に代入することも、関数に引数として渡すことも、
関 数の戻り値としても使うこともできると聞いた。
けれどK&R初版には できないと書いてある。
</引用>
きょうびのコンパイラ(たいていC89準拠?)であれば、まず大丈夫かと。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

> PATIOさんのご回答は、
>> C言語では配列をそのまま返すような事はできませんから。
> 配列と書かれていますので、何か誤解があるのかもしれません。
すいません。誤解してました。
ソースを見直すと確かに構造体になってますね。
只、私の認識では構造体も値返し出来ないという認識だったので
誤解していなくても同じレスがついていたと思います。

最新のC言語では構造体も値返しできるのですか。
私のC言語の知識も錆付いてきていますね。
私の中ではC言語で値返しできるのは組込み型のみという認識のままです。
となると、.NET2003あたりのコンパイラなら大丈夫なのかな。
一度、調べてみよう。
VC6で出来たかどうかも確認しときたいですね。
ちなみに私の周りにいる人間は多分、C言語で構造体の値返しが出来るというのは
知らないと思います。


返信引用
固定ページ 2 / 3

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました