9

このコード スニペットは、stdin から文字「u」を読み取るたびに 2Gb を割り当て、「a」を読み取ると、割り当てられたすべての文字を初期化します。

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#define bytes 2147483648
using namespace std;
int main()
{
    char input [1];
    vector<char *> activate;
    while(input[0] != 'q')
    {
        gets (input);
        if(input[0] == 'u')
        {
            char *m = (char*)malloc(bytes);
            if(m == NULL) cout << "cant allocate mem" << endl;
            else cout << "ok" << endl;
            activate.push_back(m);
        }
        else if(input[0] == 'a')
        {
            for(int x = 0; x < activate.size(); x++)
            {
                char *m;
                m = activate[x];
                for(unsigned x = 0; x < bytes; x++)
                {
                    m[x] = 'a';
                }
            }
        }
    }
    return 0;
}

3Gb の RAM を搭載した Linux 仮想マシンでこのコードを実行しています。htop ツールを使用してシステム リソースの使用状況を監視しているときに、malloc 操作がリソースに反映されていないことに気付きました。

たとえば、'u' を 1 回だけ入力した場合 (つまり、2GB のヒープ メモリを割り当てた場合)、htop でメモリ使用量が 2GB 増加することはありません。'a' を入力したとき (つまり、初期化) にのみ、メモリ使用量が増加しています。

その結果、存在するよりも多くのヒープ メモリを "malloc" することができます。たとえば、6GB (RAM とスワップ メモリよりも大きい) を malloc でき、malloc はそれを許可します (つまり、malloc は NULL を返しません)。しかし、割り当てられたメモリを初期化しようとすると、プロセスが強制終了されるまでメモリとスワップ メモリがいっぱいになるのがわかります。

-私の質問:

1.これはカーネルのバグですか?

2.この振る舞いが許可されている理由を誰か説明してもらえますか?

4

6 に答える 6

10

からman malloc(オンラインはこちら):

デフォルトでは、Linux は楽観的なメモリ割り当て戦略に従います。これは、malloc() が非 NULL を返す場合、メモリが実際に使用可能であるという保証がないことを意味します。

したがって、あまりにも多くのメモリを割り当てたい場合、それは「嘘」です。割り当てられたメモリを使用したい場合、十分なメモリを見つけようとし、十分なメモリが見つからない場合はクラッシュする可能性があります。

于 2013-11-03T07:37:45.913 に答える
5

いいえ、これはカーネルのバグではありません。レイト ページング (またはオーバーコミット) と呼ばれるものを発見しました。

カーネルで割り当てられたアドレスにバイトを書き込むまではmalloc (...)、アドレス範囲を「予約」するだけです。もちろん、これは実際にはメモリ アロケータとオペレーティング システムの実装に依存しますが、ほとんどの優れたメモリ アロケータでは、メモリが最初に使用されるまでカーネル オーバーヘッドの大部分は発生しません。

ホード アロケータは、すぐに頭に浮かぶ大きな犯罪者の 1 つです。広範なテストを通じて、遅延ページングをサポートするカーネルをほとんど利用しないことがわかりました。割り当て直後にメモリ範囲全体をゼロで埋めると、どのアロケータでも遅延ページングの影響をいつでも軽減できます。

VxWorks などのリアルタイム オペレーティング システムでは、ページングが遅れると深刻な遅延が発生するため、この動作は許可されません。技術的には、後の不確定な時間までレイテンシーを延期するだけです。

より詳細な議論については、IBM の AIX オペレーティング システムがページ割り当てオーバーコミットを処理する方法に興味があるかもしれません。

于 2013-11-03T07:48:41.113 に答える
3

これは、Basile がコミット メモリを超えて言及した結果です。しかし、説明は興味深いものです。

基本的に、Linux (POSIX?) で追加のメモリをマップしようとすると、カーネルはそれを予約するだけで、アプリケーションが予約されたページの 1 つにアクセスした場合にのみ、実際にそれを使用することになります。これにより、複数のアプリケーションが RAM/スワップの実際の合計量よりも多くを予約できます。

これは、リアルタイム OS や、誰が、いつ、どのような理由で、どのリソースを必要とするかを正確に把握している OS を使用していない限り、ほとんどの Linux 環境で望ましい動作です。

そうしないと、誰かがやって来て、すべての RAM を (実際には何もせずに) malloc し、アプリを OOM する可能性があります。

この遅延割り当てのもう 1 つの例は、mmap() です。ここでは、マッピングしているファイルが内部に収まる仮想マップがありますが、この作業専用の実メモリは少量しかありません。これにより、巨大なファイル (使用可能な RAM よりも大きい) を mmap() して、通常のファイル ハンドルのように使用することができます)。

-n

于 2013-11-03T07:40:36.033 に答える
3

初期化/メモリの操作はうまくいくはずです:

memset(m, 0, bytes);

また、callocメモリを割り当てるだけでなく、ゼロで埋めることもできます。

char* m = (char*) calloc(1, bytes);
于 2013-11-03T07:46:27.460 に答える
2

1.これはカーネルのバグですか?

いいえ。

2.この振る舞いが許可されている理由を誰か説明してもらえますか?

いくつかの理由があります:

  • 最終的なメモリ要件を知る必要性を軽減する- アプリケーションが実際に必要とするメモリ量の上限と見なすメモリ量をアプリケーションに提供できると便利な場合がよくあります。たとえば、何らかのレポートを準備している場合、レポートの最終的なサイズを計算するためだけの初期パスか、連続して大きな領域の realloc() (コピーが必要になるリスクがあります) のいずれかが、コードを大幅に複雑にし、パフォーマンスを低下させる可能性があります。 、一方、各エントリの最大長をエントリ数で掛けると、非常に迅速かつ簡単になります。アプリケーションのニーズに関する限り、仮想メモリが比較的豊富であることがわかっている場合は、仮想アドレス空間の割り当てを大きくすることは非常に安価です。

  • スパース データ- 仮想アドレス空間に余裕がある場合、スパース配列を使用して直接インデックスを使用するか、容量() とサイズ() の比率が大きいハッシュ テーブルを割り当てることができると、非常に高性能なシステムにつながる可能性があります。どちらも、データ要素のサイズがメモリ ページング サイズの倍数である場合、またはそれよりもはるかに大きいか小さい整数分数で失敗する場合に (オーバーヘッド/無駄が少なく、メモリ キャッシュを効率的に使用するという意味で) 最適に機能します。

  • リソース共有- 建物内の 1000 人の消費者に「毎秒 1 ギガビット」の接続を提供している ISP を考えてみてください。ISP は、すべての消費者が同時にそれを使用すると約 1 メガビットになることを知っていますが、実際の経験に依存しています。人々は 1 ギガビットを要求し、特定の時間にそのかなりの部分を望んでいますが、必然的に、同時使用の最大値がいくらか低くなり、平均がはるかに低くなります。同じ洞察をメモリに適用することで、オペレーティング システムは、他の方法よりも多くのアプリケーションをサポートできるようになり、期待を満足させる平均的な成功を収めることができます。より多くのユーザーが同時に要求を行うと、共有インターネット接続の速度が低下するのと同じように、ディスク上のスワップ メモリからのページングが開始され、パフォーマンスが低下する可能性があります。ただし、インターネット接続とは異なり、スワップ メモリには制限があり、そして、すべてのアプリが実際にその制限を超えてメモリを同時に使用しようとすると、メモリの枯渇を報告するシグナル/割り込み/トラップを取得し始めるアプリもあります。要約すると、このメモリのオーバーコミット動作を有効にして、単純にチェックしますmalloc()/ newNULL 以外のポインタが返されたとしても、物理メモリが実際に使用可能であることを保証するには不十分であり、プログラムは後でメモリを使用しようとして信号を受信する可能性があります。

于 2013-11-13T09:27:57.110 に答える