環境:WIN2000SP4 VC6.0SP6
2つのプロセス間で共有メモリを持っているのですが
何故か2つのプロセスから同時刻に参照した結果が違っています。
メモリのダンプは同じ値なのですがそのアドレスを参照している
構造体の中身が1バイトずれているようです。
上記現象について何か知っておられる方がいましたら
教えてください。
先頭からずれているのでしょうか。
途中からならば構造体メンバのアライメント
(プロジェクトの設定 C/C++ コード生成)の設定は
両プロセスとも同じでしょうか。
ずれると言うのは・・・
構造体中のメンバの宣言で、奇数バイトのものがあり、以降が
ずれると言うことであれば、構造体宣言でのパックの設定で、
回避できます。
#pragma pack()
をお調べ下さい。
以上。
pragma pack()で解決しました。
ありがとうございました。
で、また困ったことが起きたのですが
デバッグビルドとリリースビルトで処理結果が違うという現象が起きています。
BYTE a[128];
LPBYTE * b;
(bの内容取得)
(bの内容変換処理)
.
.
.
CopyMemory(a,*b,sizeof(a)); // bの保存
(bの内容取得2)
// 結合
for(i = 0 ; i < x ; i ++){
*(*b+ i) = a[i] | *(*b+ i);
}
こんな感じの処理をしているのですが
何故かリリースビルドではa,bの結合ができていなく
aのみになってしまっています。
結合処理を省くとbのみになるのでbの中身はちゃんとあると思うのですが。
何かログを書く処理などを埋め込むとうまく結合されたりと意味不明なことが起きています。
うまくいくのはいいのですが非常に怖いので原因を調べております。
アドレスの問題?なんでしょうか?
少し気になったので。
ここで提示されているbは共有メモリの内容を取得しているのですか?
bが指し示している領域の確保はどうなっているのですか?
bが指し示している領域のスコープは大丈夫ですか?
一般的に、デバッグビルドとリリースビルドの差は、メモリの初期化に起因し、
たまたま上手く動いているように見えただけ、のパターンが多いと思います。
bの指し示す領域は確保されているのか、とbで使用しているうちに、
開放などされていないのか、この辺りが怪しいと思います。
(別の関数内のローカル変数へのポインタを設定しているとか。。。)
すいません。話がいきなり変わりました。
宣言してるように書きましたが実際には、bはこの関数の引数です。
GlobalAllocで領域を確保しています。共有メモリの話とは関係ありません。
スコープについて再度見直してみますが問題はないと思います。
開放処理は処理が終わって他のプロセスの指示を受けてからしています。
aは作業用バッファですがZeroMemoryで初期化しています。
またリリースビルドでもログの出力処理を入れることによりうまくいく
当たりがすごく不思議です。
異なる質問は新しいスレッドでどうぞ
このような症状は、変数やポインタの初期化忘れや、
バッファのオーバーフローの可能性が高いです。
しかし、提供されている情報が不完全すぎて、具体的には特定できなそうです。
volatile の付け忘れとか。
新しいスレッドを立てなくてすいません。
何回も見直したのですがオーバーフローが起きる箇所は見当たりませんでした。
他にどのような情報が必要でしょうか?
aを参照するような処理(aの内容をファイルに出力など)を追加すると
うまく行くようです。
それは結合処理の先でも後でも関係ないようです。
voltileはあまり見かけない修飾子ですが複数プロセスから
変更可能にするようですね。
確保した領域を変更するプロセスは他にありませんので関係ないかと思います。
他のプロセスは参照のみ行います。
> voltileはあまり見かけない修飾子ですが複数プロセスから
> 変更可能にするようですね。
少し違う気がする。
voltileをつけていないと、特にループ処理内などで、
そのその変数への値を参照する際に、コンパイル時に最適化され、
最初にレジスタに値を代入し、その後ループ内では、レジスタの値を参照し、
元の変数そのものを参照しない可能性がある。
マルチスレッドなどで、他のスレッドから変数の値を変更しても、
変更前の値がレジスタに取り込まれており、変更後の値を参照しにいかないことがあり、
問題になることがある。
っていうのが、voltileに関する説明だと思う。
以下参照
> http://blogs.msmvp.jp/mikahosi/archive/2005/02/03/366.aspx
共有メモリ=他プロセスなどで変更されるメモリ=不明なタイミングで変更されうるメモリ
なわけで volatile を指定しないとうまく動かない可能性が高くなります。
int shared_memory; // volatile が必要なのにつけてない
...
if (shared_memory==0) {...} // ループとかしなけりゃ大丈夫なんだが
while (shared_memory==0) {...} // ループするととたんにダメ
比較等を直接行わない場合であっても、たとえば以下のような場合
ループ内部で shared_memory を変更するコードが無いから、ループ直前に
shared_memory をレジスタに持ってくるコードを生成する可能性あり
ソレはおそらく希望の動作とは異なる
for (i=0; i<NNN; ++i) {
x[i]+=shared_memory;
}
>aを参照するような処理(aの内容をファイルに出力など)を追加すると
その処理の前後で a が変更されうる、とコンパイラが判断したので最適化対象を外れた
すなわちその後の処理では新しく a を読み直すようなコードを生成した
ってことで volatile の付け忘れを疑いました。
非常に勉強になりました。
bにvolatileをつければ問題解決と思うのですが
ソースの都合上このbを変更することはできません。
最適化されないようログの出力処理を追加して対応しようと思います。
どうもありがとうございました。
そうか?本当の原因を追求せず終わらせていいのか?
volatile をつければ直るかもってのは俺の憶測(妄想レベル)にすぎないよ。
今のソースコードでたまたま動いているってことは、
ちょっと修正するとまた現象が再現する可能性があるってことだと思うけど。
そんなときにまたハマるのは山本さんだよ...
後からそのソース読んだ人が「なぜこんな無駄なログ吐いてんだろ」って消しちゃうとかあるかもよ。
その他人ってのは数ヵ月後の自分かもしれないよ。
最適化を今まで実行速度で行っていたのですが
試しにプログラムサイズにしてみればうまくいきました。
(最終的には設定を変えませんが)
原因が最適化によるものだということがわかったので
ローカル変数で処理後、確保した領域にコピーという処理に変更し
一応ログの出力も付加しました。
ログを書き込む理由もコメントで記述。
現状はこんな感じです。
volatile修飾子をつけたいものは引数ですので都合上volatileを
つけることはできません。
引数でもらったものに対してvolatileをつけたような
処理をすることは可能でしょうか?
void func(char* p) {
volatile char* q=p;
while (*q) {...}
}
は完全に正当なコードです。全く問題ありません。
引数として渡される p が、本質的に volatile char* でなきゃならんのであれば
今のうちに修正しておいたほうが無難かもしれないけど。
返信遅くなってすいません。
とりあえず時間がなかったのでvolatileでは対応しませんでした。
その後volatileを試してみたのですがうまくいきません。
試しにvolatileが活用できるサンプルソースを作ろうとしたのですが
うまくいきません。
どこかのサイトに載ってたサンプルコードを真似てもうまくいきません。
自分の理解不足かもしれません。
何かよいサンプルコードはないでしょうか?
詳しくは新規スレッドでどうぞ。
最適化をきっちり行わないと volatile の有用性は見えないでしょうね。
ツイート | ![]() |