cコード:
_// program break mechanism
// TLPI exercise 7-1
#include <stdio.h>
#include <stdlib.h>
void program_break_test() {
printf("%10p\n", sbrk(0));
char *bl = malloc(1024 * 1024);
printf("%x\n", sbrk(0));
free(bl);
printf("%x\n", sbrk(0));
}
int main(int argc, char **argv) {
program_break_test();
return 0;
}
_
次のコードをコンパイルする場合:
_ printf("%10p\n", sbrk(0));
_
警告のヒントが表示されます:
_format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’
_
質問1:なぜですか?
そして、私がmalloc(1024 * 1024)
を実行した後、プログラムブレークは変更されなかったようです。
出力は次のとおりです。
_9b12000
9b12000
9b12000
_
質問2:プロセスは、将来の使用のために起動時にヒープにメモリを割り当てますか?または、コンパイラが割り当てる時間を変更しますか?そうでなければ、なぜですか?
[update] Summary:brk()またはmmap()
TLPIを確認し、(TLPIの作成者の助けを借りて)マニュアルページを確認した後、次のようにmalloc()
がbrk()
またはmmap()
の使用を決定する方法を理解しました。
mallopt()
は、パラメータを設定してmalloc()
の動作を制御できます。一般に、_M_MMAP_THRESHOLD
_という名前のパラメータがあります。
brk()
が使用されます。mmap()
が使用されます。パラメータのデフォルト値は_128kb
_(私のシステムでは)ですが、テストプログラムでは1Mbを使用したため、mmap()
が選択され、要求されたメモリを32kbに変更するとbrk()
が使用されます。
本はTLPIの147ページと1035ページでそれを述べました、しかし私はその部分を注意深く読みませんでした。
パラメータの詳細情報はmallopt()
のmanページにあります。
プログラムを変更して、malloc
'dメモリがどこにあるかを確認すると、次のようになります。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void program_break_test() {
printf("%10p\n", sbrk(0));
char *bl = malloc(1024 * 1024);
printf("%10p\n", sbrk(0));
printf("malloc'd at: %10p\n", bl);
free(bl);
printf("%10p\n", sbrk(0));
}
int main(int argc, char **argv) {
program_break_test();
return 0;
}
sbrk
が変更されないことは、おそらくもう少し明確です。 malloc
から提供されたメモリは、まったく異なる場所にマッピングされています。
Linuxでstrace
を使用して、どのシステムコールが行われたかを確認し、malloc
がmmap
を使用して割り当てを実行していることを確認することもできます。
malloc
は、sbrk
を使用してメモリを割り当てることに限定されません。たとえば、mmap
を使用して、メモリの大きなMAP_ANONYMOUS
ブロックをマップできます。通常mmap
は、データセグメントから十分離れた仮想アドレスを割り当てます。
他の可能性もあります。特に、標準ライブラリのコア部分であるmmap
自体は、標準ライブラリ関数に限定されていません。オペレーティングシステム固有のインターフェイスを利用できます。
フォーマット ‘%p’はタイプ ‘void *’の引数が必要ですが、引数2のタイプは ‘int’です
質問1への回答:コンパイラーは引数が_void *
_である必要があると伝えていますが、代わりにint
を指定しています。エラーを読んで理解するのに5秒を費やした場合、それは明らかなはずです。わからないことはありますか?もしそうなら、「それはなぜですか」ではなく、あなたを混乱させるものについてより詳細な質問をしてください...
マニュアル に従って、printf("%x\n", sbrk(0));
にも同様の警告が発生するはずです。_%x
_はunsigned
パラメータに対応することが期待されています。また、マニュアルによると:
引数が対応する変換仕様の正しいタイプでない場合、動作は未定義です。
通常、どのシステムでも同じように機能するプログラムを作成するように努力する必要があります。そのためには、一連のルールを確立する必要があります。したがって、ルールを破って未定義の動作を呼び出していることを通知する警告が表示されます。未定義の動作にもかかわらず、コードは機能する可能性がありますシステムで期待どおりにこの時点で ...ただし、将来のある時点でコンピューターが更新をフェッチして、コードが微妙で壊滅的な方法で破損したり、他のコンピューターで機能しなくなったりする可能性があるため、これに依存しないでください... その月のその時刻。
質問2、3、4の回答:
プロセスは、後で使用するために起動時にヒープにメモリを割り当てますか?
標準Cの規則内に「ヒープ」が存在する必要はないので、これは「ノー」です...少なくとも、使用しているコンパイラ/標準ライブラリを教えてもらうまでは。
または、コンパイラが割り当てる時間を変更しますか?
たぶん。コンパイラは、割り当てを削除することさえできる最適化を実行できます。ただし、安全であると推定できる場合(例:観察可能な動作)変更されません)。
そうでなければ、なぜですか?
良い質問。
未定義の動作を自由に検討して、プログラマーの危険があっても実行できるようにするルールがあるのはなぜですか?最適化。
割り当てるメモリが heap にあるかどうかに関係があるのはなぜですか。なぜあなたは気にする必要がありますか?メモリが割り当てられている限り、そうですか?あなたがそれを使用できる限り、それはかなり高速です。最適化。
コンパイラはなぜ最適化を行うのですか?答えはあなたにお任せします;)
コードでmallocを使用すると、最初にbrk()が呼び出され、ヒープから0x21000バイトが割り当てられます。これが出力したアドレスなので、質問1:事前に割り当てられた領域から次のmallocs要件を満たすことができます。したがって、これらのmallocは実際にはbrkを呼び出しませんでした。これはmallocの最適化です。次にその境界を超えてサイズをmallocしたい場合は、新しいbrkが呼び出されます(mmapしきい値より大きくない場合)。