Linux のメモリ管理について説明した記事があります

1. Linux メモリ管理の概要

Linuxのメモリ管理とは、システムメモリの割り当て、解放、マッピング、管理、交換、圧縮といった一連の操作を管理することを指します。Linux では、メモリが複数の領域に分割されており、各領域にはカーネル空間、ユーザー空間、キャッシュ、スワップ パーティションなどの異なる役割があります。Linux メモリ管理の目標は、システムの安定性と信頼性を確保しながら、利用可能なメモリを最大限に活用することです。

1.1 メモリ管理とは

メモリ管理は、コンピュータ システムのシステム メモリ リソースを管理するメカニズムであり、主にメモリ割り当て、メモリ解放、メモリ マッピング、仮想メモリ管理が含まれます。これはコンピュータ システムの非常に重要な部分であり、システムのリソース使用率とアプリケーション プログラムのパフォーマンスを効果的に向上させることができます。

オープンソース オペレーティング システムである Linux のメモリ管理メカニズムは柔軟性とカスタマイズ性が高く、さまざまなアプリケーション シナリオのニーズを満たすことができます。したがって、システム管理者と開発者にとって、Linux のメモリ管理メカニズムを理解することは非常に重要です。

オペレーティング システムは、仮想メモリ アドレス マッピング、メモリの割り当てと回復、プロセス メモリ管理などのメモリ管理メカニズムを通じてメモリの割り当てと管理を実行します。このうち仮想メモリとは、オペレーティングシステムがプロセスに割り当てる仮想アドレス空間を指し、各プロセスは物理メモリの制限を気にすることなく、一定サイズの仮想アドレス空間を独立して占有することができます。メモリの割り当てと回復とは、システムのリソース使用率と動作効率を確保するために、オペレーティング システムがプロセスの実行中に必要なメモリを割り当てたり解放したりすることを意味します。

たとえば、プロセスがメモリを割り当てる必要がある場合、オペレーティング システムに一定量のメモリ空間が適用されます。システムに十分な空きメモリがある場合、オペレーティング システムは、対応するメモリ空間をプロセスに割り当て、そのメモリ空間をプロセスの仮想アドレス空間にマップします。

また、システムに十分な空きメモリがない場合、オペレーティング システムはメモリ圧縮を実行するか、プロセスのデータの一部をハードディスクに保存して、他のプロセスが使用できる十分なメモリ領域を解放します。このようにして、メモリ管理メカニズムはプロセスの実行ニーズを確保し、システム リソースの使用を最大限に活用できます。

1.2 メモリ管理の重要性

メモリ管理はコンピュータ システムにおいて非常に重要な役割を果たします。まず、メモリ管理により、オペレーティング システムとアプリケーションが使用できるメモリの量が決まります。十分なメモリがないと、システムやアプリケーションが非常に遅くなったり、クラッシュしたりする可能性があります。次に、メモリ管理により、オペレーティング システムとアプリケーションが相互に干渉しないことが保証されます。メモリ管理を行わないと、異なるアプリケーションが同じメモリ領域を使用し、データの混乱やエラーが発生する可能性があります。さらに、メモリ管理によってシステム パフォーマンスも最適化でき、メモリの割り当てと解放を合理的に行うことで、メモリの断片化が減少し、メモリの使用効率が向上し、システム全体のパフォーマンスが向上します。

たとえば、オペレーティング システムにメモリ管理がない場合、アプリケーションがメモリを必要とするときに、物理メモリの特定の領域が直接使用される可能性があり、その結果、他のアプリケーションがこのメモリ領域を使用できなくなり、システムが停止する可能性があります。クラッシュ。さらに、複数のアプリケーションが大量のメモリを必要とするにもかかわらず、メモリが適切に割り当てられ、解放されない場合、メモリの断片化が発生し、システムのパフォーマンスが低下する可能性があります。したがって、メモリ管理はオペレーティング システムの非常に重要な部分です。

Linux メモリ管理の重要性は、システムの正常な動作とシステム リソースの効率的な使用を保証することです。メモリ管理を行わないと、次の問題が発生する可能性があります。

  • システムのクラッシュまたはフリーズ: メモリ管理は、メモリ不足やメモリ リークなどの問題によるシステムのクラッシュやフリーズを回避するのに役立ちます。

  • システム パフォーマンスの低下: メモリ管理によりメモリの使用が最適化され、システム パフォーマンスが向上します。メモリ管理がない場合、メモリの断片化の問題が発生し、システムが連続したメモリ空間を使用できなくなり、システムのパフォーマンスが低下する可能性があります。

  • セキュリティの問題: メモリ管理により、システムのセキュリティが向上し、一部の悪意のあるプログラムがメモリを変更してシステムのセキュリティを破壊するのを防ぐことができます。

  • リソースの無駄: メモリ管理が行われていない場合、メモリ リソースが無駄になる可能性があります。たとえば、一部のプログラムは大量のメモリを割り当てますが、それが時間内に解放されず、結果としてメモリ リソースが無駄になります。

したがって、メモリ管理はオペレーティング システムの中核機能の 1 つであり、システムの正常な動作とシステム リソースの効率的な使用を確保する上で重要な役割を果たします。

1.3 メモリ管理のコンポーネント

メモリ管理のコンポーネントには次の側面が含まれます。

  1. 仮想メモリ管理: 物理メモリとプロセスのアドレス空間がマッピングされて管理されるため、各プロセスは独立したアドレス空間を持つことができ、それによってプロセス間の分離と保護が実現されます。

  2. 物理メモリ管理: メモリの割り当て、リサイクル、マッピングなどの物理メモリを管理します。

  3. ページ置換アルゴリズム: 物理メモリが不足している場合、物理メモリを解放するために一部のページを置換する必要があります。ページ置換アルゴリズムは、どのページを置換するかを選択するアルゴリズムです。

  4. プロセスのアドレス空間管理: コード セグメント、データ セグメント、スタックなどを含むプロセスのアドレス空間を管理します。

  5. メモリ保護とアクセス制御:ページ属性やアクセス権などを設定することで、プロセスアドレス空間の保護とアクセス制御を実現します。

  6. メモリの統計と監視: システムのメモリ使用量を監視し、メモリのパフォーマンスのチューニングとトラブルシューティングのためにメモリの統計と分析を実行します。

これらのコンポーネントは相互に関連して、完全なメモリ管理システムを形成します。実際のオペレーティング システムでは、メモリ管理は通常、オペレーティング システムの最も複雑かつ中核的な部分の 1 つです。

2. 物理メモリ管理

物理メモリ管理は Linux メモリ管理の重要な部分であり、メモリの割り当てや解放など、システム内の物理メモリの使用状況を追跡および管理するために使用されます。物理メモリ管理の中心的なタスクは、メモリをより効率的に管理できるように、物理メモリを一連のページに分割することです。

2.1 物理メモリとは

物理メモリは、コンピュータ ハードウェアにプログラムとデータを保存するために使用される実際のメモリ チップを指し、メイン メモリ (メイン メモリ) とも呼ばれます。物理メモリは、データを保存するための固有のアドレスを持つ多くのストレージ ユニットで構成されます。物理メモリの容量は、コンピュータ システム ハードウェアの重要な指標の 1 つであり、コンピュータが処理できるデータ量と動作速度を直接決定します。

Linux では、通常、物理メモリはオペレーティング システムのメモリ管理モジュールによって管理されます。物理メモリは、コンピュータの起動時にカーネルに割り当てられて使用されます。オペレーティング システムは物理メモリをいくつかの固定サイズのページ (ページ) に分割し、各ページのサイズは通常 4 KB または 8 KB です。各ページには一意の物理アドレスがあり、プロセスまたはカーネル データの保存に使用できます。

物理メモリ管理の主なタスクは、各プロセスに物理メモリ領域を割り当てることです。プロセスがメモリを必要とする場合、オペレーティング システムは空きページのプールから 1 つ以上のページを割り当て、それらをプロセスの仮想アドレス空間にマップします。物理メモリ管理では、ページ スワップ (Page Swap) およびページ再利用 (Page Reclaim) 機能を実装する必要もあります。これにより、物理メモリが不十分な場合、一部のページがディスクに転送され、他のプロセスが使用できるように物理メモリ領域が解放されます。

2.2 物理メモリの管理方法

物理メモリ管理はオペレーティング システムの中核機能の 1 つであり、主にコンピュータ ハードウェアの物理メモリ リソースの管理を担当します。Linux システムでは、物理メモリ管理には主に 2 つの方法があります。連続メモリ管理と不連続メモリ管理です。

2.2.1 連続メモリ管理

継続的メモリ管理は、比較的単純な物理メモリ管理方法です。連続メモリ管理モードでは、オペレーティング システムは物理メモリ空間を連続アドレス空間とみなし、ポインタを介して任意の物理メモリ アドレスに直接アクセスできます。

Linux システムでは、Buddy System アルゴリズムを使用して継続的なメモリ管理が実装されています。バディ システムは、主にオペレーティング システムのメモリ割り当てと解放を管理するために使用される物理メモリ管理アルゴリズムです。システムで利用可能な物理メモリをサイズごとにブロックし、隣接するブロックをペアのパートナーに結合します。

メモリのブロックを割り当てる必要がある場合、バディ システムは適切なサイズのメモリ ブロックを見つけようとします。見つかったブロックが必要なブロックよりわずかに大きい場合、そのブロックは 2 つの同じサイズのバディ ブロックに分割されます。一方のブロックはリクエスターに割り当てられたメモリのブロックとして機能し、もう一方のブロックは引き続きシステムによる割り当て用に予約されます。メモリのブロックが解放されると、バディ システムは、その後のメモリ割り当てのために、そのブロックを隣接するブロックとマージしてより大きなブロックを作成しようとします。これにより、メモリの断片化の問題が軽減され、メモリの使用率が向上します。

2.2.2 不連続メモリ管理

非連続メモリ管理とは、メモリ空間を物理メモリ内の連続したアドレス順序で割り当てる必要がないことを意味し、連続メモリ管理よりも柔軟です。一般的な不連続メモリ管理方法には、ページングとセグメンテーションが含まれます。

ページングメモリ管理では、物理メモリを固定サイズのページに分割し、仮想アドレス空間も同じサイズのページに分割することで、仮想アドレスから物理アドレスへのマッピングを実現し、プロセスが影響を受けないようにします。メモリにアクセスするときは物理アドレスを考慮する必要があります。メモリの実際のアドレスです。このようにメモリの確保と解放はページ単位で行われます。

セグメント化されたメモリ管理では、仮想アドレス空間が異なるサイズの複数のセグメントに分割され、各セグメントはセグメント ベース アドレスと長さを持ちます。セグメント サイズは動的に変更できるため、より柔軟なメモリ管理が可能になります。このようにメモリの割り当てと解放はセグメント単位で行われます。

不連続メモリ管理方法の実装は比較的複雑で、より多くのハードウェアおよびソフトウェアのサポートが必要であり、一定のパフォーマンスのオーバーヘッドが生じることに注意してください。したがって、実際のアプリケーションでは、その柔軟性とパフォーマンスのオーバーヘッドとの関係を比較検討する必要があります。

2.3 物理メモリ管理に関する機能と例

Linux の物理メモリ管理で使用される主な機能は次のとおりです。

  • memblock_init(): この関数は、物理メモリ ブロックを初期化するために使用されます。つまり、物理メモリを使用可能なメモリ ブロックに分割します。

  • memblock_reserve(): この関数は、メモリ アロケータによって割り当てられないように物理メモリのブロックを予約するために使用されます。

  • memblock_free(): この関数は物理メモリブロックを解放するために使用されます。

  • memblock_alloc(): この関数は、物理メモリ ブロックを割り当てるために使用されます。

  • memblock_find_in_range(): この関数は、指定された範囲内で空き物理メモリ ブロックを見つけるために使用されます。

以下は、物理メモリのブロックを割り当て、そのアドレスを出力する簡単なコード例です。

#include 
#include 
#include 
#include 
#include 

static int __init test_init(void)
{
    unsigned long size = 4096;
    unsigned long *ptr;

    ptr = memblock_alloc(size, PAGE_SIZE);
    if (!ptr) {
        pr_err("Failed to allocate memory\n");
        return -ENOMEM;
    }

    pr_info("Allocated %ld bytes of physical memory at address %p\n", size, ptr);

    return 0;
}

static void __exit test_exit(void)
{
    pr_info("Exiting test module\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Test module");

上記のサンプル コードでは、最初に呼び出しmemblock_alloc()関数が物理メモリのブロックを割り当て、そのアドレスをポインタに格納しますptr割り当てが失敗した場合は、エラー メッセージが出力されて返されます-ENOMEM割り当てが成功した場合は、割り当てられたメモリ ブロックのサイズとアドレスを出力します。

3. 仮想メモリ管理

仮想メモリは、オペレーティング システムがプロセスに提供する抽象的なメモリ管理方法を指します。これは、プロセスが必要とするアドレス空間を物理メモリの特定の領域にマップするため、プロセスから見えるメモリ空間は連続的になります。実際の上記のメモリは、異なる物理メモリ ページに分散している場合があります。仮想メモリは、メモリ管理の向上、可用性の向上、セキュリティの向上、パフォーマンスの向上など、オペレーティング システムに多くのメリットをもたらします。

3.1 仮想メモリとは

仮想メモリは、物理メモリとハード ディスク上の領域の一部を組み合わせたコンピュータのメモリ管理テクノロジであり、オペレーティング システムがメモリをより柔軟に管理できるようにします。4KB仮想メモリは、プロセスに必要なメモリ スペースを複数の仮想ページに分割します。各仮想ページのサイズは通常 です。プロセスで使用できる仮想ページの数は膨大で、物理メモリのサイズよりもはるかに大きくなります。

仮想メモリ テクノロジを使用すると、オペレーティング システムはプロセスに必要な仮想ページの一部を物理メモリに転送できます。プロセスがこれらの仮想ページを必要としなくなった場合、オペレーティング システムはそれらをディスクにスワップして、物理メモリ スペースを解放します。その他の工程で使用します。

仮想メモリを使用する場合、各プロセスが使用するメモリ空間は物理アドレスではなく仮想アドレスで構成されます。オペレーティングシステムは、プロセスに必要な仮想アドレスを実際の物理アドレスにマッピングすることで、仮想アドレスから物理アドレスへの変換を実現します。このように、各プロセスは物理メモリ全体を独占しているように見えますが、実際には複数のプロセスが同じ物理メモリを共有することができます。

仮想メモリの導入により、オペレーティング システムのメモリ管理機能が大幅に向上しました。仮想メモリ テクノロジを通じて、オペレーティング システムはメモリ リソースをより適切に管理できるようになり、システムのパフォーマンスと安定性が向上します。

3.2 仮想メモリ管理の原理

仮想メモリ管理では、各プロセスは連続した独立した仮想アドレス空間を持ちますが、すべてのアドレスに物理メモリが割り当てられているわけではありません。物理メモリが割り当てられていない仮想アドレスにプロセスがアクセスする必要がある場合、オペレーティング システムはそのアドレスに物理メモリを割り当て、仮想アドレスを物理アドレスにマップして、プロセスがそのアドレスにアクセスできるようにします。

仮想メモリ管理の実装は、ハードウェア上のMMU( Memory Management Unit) サポートに依存しています。MMU は主に、仮想アドレスから物理アドレスへの変換を担当します。プロセスが仮想アドレスにアクセスすると、MMU はそのアドレスを対応する物理アドレスに変換し、プロセスが物理メモリにアクセスできるようにします。

コンピューターに 4GB の物理メモリがあり、プロセスが 10GB のメモリ空間を使用する必要があるとします。このプロセスは、従来の物理メモリ管理では適切に機能しません。したがって、オペレーティング システムは物理メモリ空間の一部を仮想メモリとして使用し、プロセスが仮想メモリを使用して利用可能なメモリ空間を拡張できるようにします。プロセスが仮想メモリ内の特定のページにアクセスする必要がある場合、そのページはディスクから物理メモリに転送され、仮想メモリ空​​間にマッピングされ、プロセスはそのページに直接アクセスできます。ページが不要になると、オペレーティング システムはそのページを物理メモリから解放し、ディスクに保存し直します。

このようにして、物理メモリがプロセスのメモリ要件を満たすのに不十分な場合でも、プロセスは仮想メモリを使用して利用可能なメモリ領域を拡張できます。この仮想メモリ テクノロジにより、コンピュータ システムは限られた物理メモリでより多くのプロセスと大規模なプログラムをサポートできるようになります。

3.3 仮想メモリ管理に関する機能と例

仮想メモリ管理には多くの機能が含まれており、その中でより一般的に使用される機能は次のとおりです。

1.  mmap: ファイルまたはその他のオブジェクトをプロセスのアドレス空間にマップするために使用され、その関数プロトタイプは次のとおりです。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

ここで、addrはマッピング領域の先頭アドレスを示し、lengthマッピング領域の長さを示し、protマッピング領域のアクセス権限を示し、flagsマッピングオプションを示し、fdマッピングされるファイル記述子を示し、offsetマッピングされるファイルオフセットを示します。使用例は次のとおりです。

#include 
#include 
#include 
#include 

#define FILENAME "file.txt"

int main() {
    int fd = open(FILENAME, O_RDONLY);
    if (fd < 0) {
        perror("open file failed");
        exit(1);
    }

    char *map = mmap(NULL, 1024, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        perror("mmap failed");
        exit(1);
    }

    printf("content of the file:\n%s\n", map);

    munmap(map, 1024);
    close(fd);

    return 0;
}

このサンプル プログラムは、ファイルを開き、ファイルをメモリにマップし、ファイルの内容を出力し、メモリを解放し、ファイルを閉じます。

2. munmap: マッピング関係を解除するために使用されます。その関数プロトタイプは次のとおりです。

int munmap(void *addr, size_t length);

ここで、addrはマッピング領域の最初のアドレスを表し、lengthはマッピング領域の長さを表します。使用例は上記のコードに示されています。

3. mlock: 仮想メモリ領域が置き換えられないようにロックするために使用されます。その関数プロトタイプは次のとおりです。

int mlock(const void *addr, size_t len);

ここで、addrはロックされる仮想メモリ領域の最初のアドレスを示し、 はlenロックされる仮想メモリ領域の長さを示します。

使用例は次のとおりです。

#include 
#include 
#include 

#define LEN (1 << 20)

int main() {
    void *p = malloc(LEN);
    if (p == NULL) {
        perror("malloc failed");
        exit(1);
    }

    int ret = mlock(p, LEN);
    if (ret != 0) {
        perror("mlock failed");
        exit(1);
    }

    printf("locked %d MB memory\n", LEN / (1 << 20));

    free(p);

    return 0;
}

サンプルプログラムでは、malloc関数を使用して 1MB のメモリを割り当て、次にmlock関数を使用してメモリ領域をロックし、最後にメモリを解放します。locked 1 MB memory プログラムを実行すると、正常にロックされたメモリが出力されることがわかります 1MB

4. mprotect: 仮想メモリ領域のアクセス権を変更するために使用します。ファイルまたは他のオブジェクトをプロセスのアドレス空間にマップして、これらのオブジェクトへのアクセスを実現できます。その関数プロトタイプは次のとおりです。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

各パラメータの意味は次のとおりです、addrマッピング領域の開始アドレス (通常は NULL に設定され、カーネルによって自動的に割り当てられます)、lengthマッピング領域の長さ (バイト単位)、protマッピング領域の保護方法 (つまり、領域は読み取りと書き込み、実行または共有などが可能です;flagsプライベートマッピングまたは共有マッピングなどのマッピング領域のタイプと属性、マッピング領域が更新できるかどうかなど; マッピングfd先のオブジェクトを指定しますプロセスのアドレス空間 (通常はファイル記述子)。offsetマッピングを開始するオフセットをオブジェクトから指定します (通常は 0 に設定されます)。

使用例は次のとおりです。

#include 
#include 
#include 
#include 

#define FILE_PATH "test.txt"
#define MAP_SIZE 1024

int main() {
    int fd = open(FILE_PATH, O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    char *addr = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }
    printf("%s", addr);
    if (munmap(addr, MAP_SIZE) < 0) {
        perror("munmap");
        close(fd);
        return -1;
    }
    close(fd);
    return 0;
}

4. メモリの割り当てと解放

メモリの割り当てと解放は、オペレーティング システムの非常に重要な機能であり、メモリ管理の中核部分です。Linux では、メモリの割り当てと解放は通常、C ライブラリ関数またはカーネル関数を使用して実装されます。以下では、メモリの割り当てと解放に関する関連知識を詳しく紹介します。

4.1 メモリの割り当てと割り当て解除の概要

メモリの割り当てと割り当て解除は、オペレーティング システムのメモリ管理の重要な部分です。メモリ割り当てとは、アプリケーションがその機能を実行できるように、オペレーティング システムがアプリケーションにメモリ領域を割り当てることです。メモリの割り当て解除とは、アプリケーションが割り当てられたメモリ領域を解放し、他のアプリケーションやオペレーティング システム自体が使用できるようにすることを指します。

オペレーティング システム内では、メモリの割り当てと割り当て解除はメモリ管理サブシステムの役割を果たします。メモリ管理サブシステムは、利用可能なメモリと割り当てられたメモリの状態を追跡し、最適なメモリの割り当てと割り当て解除の戦略を決定します。最新のオペレーティング システムでは、メモリの割り当てと割り当て解除の戦略は通常、仮想メモリの概念に基づいています。

メモリの割り当てと割り当て解除は、アプリケーションのパフォーマンスの重要な要素の 1 つです。一方で、メモリが不足するとスワップやガベージ コレクションが頻繁に発生する可能性があるため、メモリの割り当てが過剰になるとアプリケーションのパフォーマンスが低下する可能性があります。一方、解放されていないメモリはメモリ リークを引き起こす可能性があり、アプリケーションのクラッシュやその他の予期しない動作が発生する可能性があります。

アプリケーションを作成するときは、メモリを正しく割り当て、解放することが非常に重要です。メモリ リークはよくある問題であり、コードを注意深く記述し、メモリ分析ツールを使用することで回避できます。また、メモリを過剰に割り当てるとパフォーマンスの問題が発生する可能性があるため、メモリの使用方法には注意が必要です。

4.2 メモリの割り当てと解放の方法

Linux システムでは、メモリの割り当てと解放にはいくつかの方法があります。

  1. 静的メモリ割り当て: メモリ空間はプログラムのコンパイル時に割り当てられ、通常は小さなメモリ ブロックを割り当てるために使用されます。

  2. スタック メモリの割り当て: 関数が呼び出されると、システムはローカル変数や関数の戻りアドレスなどの情報を保存するために、関数にメモリを自動的に割り当てます。関数が実行されると、このメモリはシステムによって自動的に解放されます。

  3. ヒープ メモリの割り当て: プログラムは、malloc() などのメモリ割り当て関数を呼び出すことによって、指定されたサイズのメモリ空間を適用できます。プログラムがこのメモリを必要としなくなった場合は、メモリを呼び出してシステムに解放する必要があります。 free()などの解放関数。

  4. メモリ マップされたファイル: プログラムは、ファイルをメモリにマッピングすることでメモリを割り当てることができます。メモリ マップド ファイルのアプローチは、通常、大きなファイルを処理するために使用されます。

  5. 共有メモリ: 複数のプロセスが同じメモリ空間を共有できるため、プロセス間の通信とデータ共有が実現します。共有メモリは、システムが提供する API を呼び出して管理する必要があります。

これらの方法は実際の開発で広く使用されており、開発者はさまざまなシナリオや要件に応じて適切なメモリ割り当てと解放方法を選択する必要があります。

4.3 メモリの割り当てと解放に関する機能と例

Linux では、メモリの割り当てと解放は主に次の関数を通じて実装されます。

  1. malloc() と free()機能

  2. calloc() と realloc()機能

  3. mmap() と munmap()機能

上記の機能の使用例を以下に示しますので、学習用の例を確認することができます。

4.3.1 malloc() 関数と free() 関数

malloc() および は、 ライブラリによって提供されるfree()、最も一般的に使用されるメモリ割り当ておよび割り当て解除関数です。stdlib.hこのうち、malloc()関数は指定されたサイズのメモリ空間を割り当ててその先頭アドレスを返すために使用され、関数はfree()以前に割り当てられたメモリ空間を解放するために使用されます。

サンプルコード:

#include 
#include 

int main() {
    int* p = (int*)malloc(sizeof(int));  // 分配一个int类型的内存空间
    if (p == NULL) {  // 判断是否分配成功
        printf("Failed to allocate memory!\n");
        return -1;
    }
    *p = 123;  // 对分配的内存空间进行赋值
    printf("%d\n", *p);
    free(p);  // 释放之前分配的内存空间
    return 0;
}

4.3.2 calloc() 関数と realloc() 関数

calloc() これら realloc()2 つの関数は、メモリ領域を割り当てるためにも使用できます。このうち、calloc()関数は指定したサイズのメモリ空間を割り当て、その空間を初期化するために使用され0realloc()関数は以前に割り当てられたメモリ空間を再割り当てするために使用されます。

サンプルコード:

#include 
#include 

int main() {
    int* p1 = (int*)calloc(5, sizeof(int));  // 分配5个int类型的内存空间,并将它们初始化为0
    if (p1 == NULL) {  // 判断是否分配成功
        printf("Failed to allocate memory!\n");
        return -1;
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ", p1[i]);  // 输出分配的内存空间中的值
    }
    printf("\n");
    int* p2 = (int*)realloc(p1, 10 * sizeof(int));  // 重新分配10个int类型的内存空间
    if (p2 == NULL) {  // 判断是否分配成功
        printf("Failed to allocate memory!\n");
        return -1;
    }
    for (int i = 5; i < 10; i++) {
        p2[i] = i * 2;  // 对分配的新的内存空间进行赋值
    }
    for (int i = 0; i < 10; i++) {
        printf("%d ", p2[i]);  // 输出分配的内存空间中的值
    }
    printf("\n");
    free(p2);  // 释放之前分配的内存空间
    return 0;
}

4.2.3 mmap() および munmap() 関数

mmap() これら munmap()2 つの関数は、ファイルのセクションまたはプロセスのアドレス空間内の匿名メモリ空間をマップするために使用されます。このうち、mmap()関数は新しいマッピングを作成してマッピング領域の開始アドレスを返すために使用され、munmap()関数は以前に作成されたマッピングをキャンセルするために使用されます。

サンプルコード:

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[]) {
    int fd;
    char *file_memory;
    struct stat statbuf;

    /* 打开文件 */
    fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    /* 获取文件信息 */
    if (fstat(fd, &statbuf) == -1) {
        perror("fstat");
        exit(EXIT_FAILURE);
    }

    /* 将文件映射到内存中 */
    file_memory = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (file_memory == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* 打印文件内容 */
    printf("%s", file_memory);

    /* 撤销映射 */
    if (munmap(file_memory, statbuf.st_size) == -1) {
        perror("munmap");
        exit(EXIT_FAILURE);
    }

    /* 关闭文件 */
    if (close(fd) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
    }

    return 0;
}

5. プロセス切り替えとメモリ管理

プロセスの切り替えとメモリ管理は密接に関係しています。プロセスの実行がスケジュールされると、それに対応する仮想メモリを物理メモリにマップする必要があります。つまり、ページ テーブルの切り替えが実行されます。プロセスの切り替え中、現在のプロセスのページ テーブルが保存され、次のプロセスのページ テーブルがロードされます。これには、メモリ管理におけるページ テーブルのメカニズムが必要です。したがって、プロセスの正常な動作を保証するために、プロセスの切り替えとメモリ管理は密接に関係しています。

5.1 プロセス切り替えの概要

プロセス切り替えはオペレーティング システムの重要な概念の 1 つであり、実行中のプロセスから別のプロセスに切り替えて実行を開始することを指します。複数のプロセスが同時に実行されている場合、各プロセスが実行して必要なリソースを取得できるように、オペレーティング システムはこれらのプロセスを切り替える必要があります。

プロセスの切り替えには通常、プロセスの実行を後で再開できるように現在のプロセスの状態を保存することが含まれます。次に、OS は次に実行するプロセスを選択し、その状態を CPU にロードします。プロセス切り替えは、切り替え中に大量のデータを保存およびロードする必要があるため、リソースを大量に消費するプロセスです。ただし、これはマルチタスク オペレーティング システムが適切に機能することを保証するために必要なプロセスでもあります。

プロセスの切り替えには、コンテキストの切り替え、スケジューリング アルゴリズム、プロセスの状態など、多くの側面が関係します。メモリ管理の場合、プロセス切り替えにはメモリ マッピングと仮想メモリの管理も含まれ、各プロセスが必要なメモリ領域に確実にアクセスできるようになります。

5.2 プロセス切り替えとメモリ管理の関係

プロセス スイッチングとメモリ管理はオペレーティング システムの 2 つの重要な概念であり、それらの間には密接な関係があります。プロセス切り替えとは、マルチプログラミング環境において、オペレーティング システムが特定の戦略に従って CPU の制御をあるプロセスから別のプロセスに移すプロセスを指します。プロセス切り替えのプロセス中、オペレーティング システムは、プログラム カウンタ、レジスタ、スタック ポインタなどを含む現在のプロセスのコンテキスト情報を保存する必要があります。これにより、元のプロセスに切り替えたときにプロセスの実行状態を復元できます。プロセス。

メモリ管理とは、プロセスにメモリ リソースを提供するために、オペレーティング システムによるメモリの割り当て、回復、および管理のことです。オペレーティングシステムは、仮想メモリ機構を通じて物理メモリと仮想アドレス空間を対応させ、プロセスに仮想アドレス空間を提供することで、プロセス間のメモリの分離と保護を実現します。プロセスの切り替え中、オペレーティング システムは、プロセスに切り替えるときにプロセスのメモリ マッピング状態を正しく復元できるように、現在のプロセスのメモリ マッピング情報を保存する必要があります。

したがって、プロセス切り替えとメモリ管理は相互依存しています。オペレーティング システムがプロセスの切り替えを実行するときは、仮想アドレス空間のマッピングや物理メモリの使用状況など、現在のプロセスのメモリ状態を考慮して、元のプロセスに切り替えるときにプロセスのメモリ状態を正しく復元する必要があります。プロセス。同時に、メモリ管理では、オペレーティング システムの安定性とセキュリティを確保するために、メモリの割り当てやリサイクルの際に他のプロセスのメモリに影響を与えないようにするなど、プロセスの切り替えによる影響も考慮する必要があります。

5.3 プロセス切り替えとメモリ管理に関する機能と例

プロセスの切り替えとメモリ管理に関連する関数は数多くありますが、一般的に使用される関数と例をいくつか示します。

5.3.1 fork()関数

fork()機能: 新しいプロセスの作成に使用されます。新しいプロセスは親プロセスと同じメモリ イメージを持ちますが、親プロセスと子プロセス間のメモリは独立しています。例:

#include 
#include 
#include 

int main() {
    int pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid == 0) {
        // Child process
        printf("Child process\n");
        exit(0);
    } else {
        // Parent process
        printf("Parent process\n");
    }
    return 0;
}

5.3.2 exec() 関数

exec()この関数は、元のプロセスのメモリ イメージを上書きする新しいプログラムをロードして実行するために使用されます。例:

#include 
#include 
#include 

int main() {
    char* argv[] = {"ls", "-l", NULL};
    execvp("ls", argv);
    perror("exec error");
    exit(1);
}

5.3.3 mmap() 関数

mmap()この関数は、ファイルまたはデバイスへのアクセスを実現するために、ファイルまたはデバイスをプロセスのアドレス空間にマップするために使用されます。例:

#include 
#include 
#include 
#include 

int main() {
    int fd = open("file.txt", O_RDONLY);
    if (fd < 0) {
        perror("open error");
        exit(1);
    }

    char* ptr = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap error");
        exit(1);
    }

    printf("%s", ptr);

    if (munmap(ptr, 4096) < 0) {
        perror("munmap error");
        exit(1);
    }

    close(fd);
    return 0;
}

5.3.4 malloc()関数

malloc()この関数はメモリを動的に割り当てるために使用され、割り当てられたメモリへのポインタを返します。例:

#include 
#include 

int main() {
    int* ptr = (int*) malloc(sizeof(int));
    if (ptr == NULL) {
        perror("malloc error");
        exit(1);
    }

    *ptr = 123;
    printf("%d\n", *ptr);

    free(ptr);
    return 0;
}

5.3.5 sbrk()関数

sbrk()この関数は、プロセスのヒープ領域を拡大または縮小するために使用され、ヒープの新しい先頭へのポインタを返します。例:

#include 
#include 
#include 

int main() {
    int* ptr1 = (int*) sbrk(sizeof(int));
    if (ptr1 == (void*) -1) {
        perror("sbrk error");
        exit(1);
    }

    *ptr1 = 123;
    printf("%d\n", *ptr1);

    int* ptr2 = (int*) sbrk(sizeof(int));
    if (ptr2 == (void*) -1) {
        perror("sbrk error");
        exit(1

6. Linuxのメモリ管理のチューニング

Linux メモリ管理のチューニングとは、システム パラメータと構成を調整して、システム メモリの使用効率を最適化し、より優れたパフォーマンスと信頼性を実現することを指します。

6.1 Linuxメモリ管理のパフォーマンスチューニング

Linux システムでは、メモリ管理のパフォーマンス チューニングには主に次の側面が含まれます。

  • メモリ使用量のチューニング: メモリ使用量を最適化し、メモリ使用効率を向上させます。メモリ使用量の分析を通じて、カーネル パラメータを目標を絞った方法で調整でき、システム動作パラメータを調整してメモリの無駄を回避し、メモリ使用率を向上させることができます。

  • メモリ スワップ チューニング: システムの安定性とパフォーマンスを確保するために、メモリ スワップ スペースとメモリ スワップ戦略を合理的に構成します。システムの実際の状況に応じて調整して、メモリ不足によるシステムクラッシュなどの問題を回避できます。

  • メモリ マッピングの最適化: メモリ マッピング操作のパフォーマンスのボトルネックを最適化し、メモリ マッピング操作の効率を向上させます。メモリマップドキャッシュのサイズを増やしたり、メモリマップのアクセス方法を最適化するなどして最適化できます。

  • メモリ割り当てのチューニング: メモリ割り当て操作を最適化して、メモリ割り当ての効率とパフォーマンスを向上させます。メモリ割り当てキャッシュのサイズを増やし、メモリ割り当てアルゴリズムを最適化することで最適化できます。

メモリ管理のパフォーマンスをチューニングする場合は、システムの実際の状況を十分に考慮し、さまざまなチューニング方法を総合的に使用して、最適なパフォーマンスと安定性を実現する必要があります。

6.2 メモリリークの検出とデバッグ

メモリ リークとは、プログラムがメモリを動的に割り当てた後、時間内にメモリを解放せず、メモリが再度使用できなくなり、最終的にシステムのメモリ不足などの問題が発生することを意味します。システムへのメモリ リークの影響を回避するには、検出とデバッグが必要です。

Linux には、メモリ リークの問題を検出およびデバッグするための次のようなツールが用意されています。

  • Valgrind: メモリデバッグやメモリリーク検出などのツールで、未解放メモリや繰り返し解放されるメモリなどの問題を検出できます。

  • AddressSanitizer: メモリ リーク、範囲外のメモリ アクセス、解放されたメモリの使用などを含むメモリ エラーを検出するツール。

  • GDB: メモリ リークのデバッグに使用できる強力なデバッガ。

  • LeakTracer: 動的に割り当てられたメモリが解放されているかどうかを監視できる軽量のメモリ リーク検出ツール。

これらのツールを使用すると、開発者がメモリ リークを適時に発見し、適切なタイミングで調整や修復を行うことができます。

6.3 メモリのデフラグと最適化

メモリの断片化とは、メモリ内に散在する未使用の小さなメモリの断片を指します。その合計はメモリのニーズを満たすのに十分である可能性がありますが、散在する性質により効果的に利用できません。メモリの断片化により、メモリ割り当ての失敗やパフォーマンスの低下が発生する可能性があります。したがって、メモリの利用効率とパフォーマンスを向上させるには、メモリのデフラグと最適化が必要です。

一般的なメモリのデフラグと最適化の方法には次のようなものがあります。

  • Buddy System: 未使用のメモリの小さなチャンクをより大きなチャンクにマージすることで断片化を軽減します。

  • メモリ プール: プログラムの初期化時に一定量のメモリが事前に割り当てられ、キャッシュ メカニズムによってメモリの断片化が回避されます。

  • メモリ圧縮: メモリ内のデータを圧縮して未使用のメモリ ブロックのサイズを削減し、メモリの断片化を軽減します。

  • メモリ マネージャー: メモリ マネージャーを使用すると、メモリの断片化を回避しながら、メモリ ブロックを動的に割り当てたり解放したりできます。

  • メモリの調整: 特定のルールに従ってメモリの割り当てと解放を調整することで、メモリの断片化を減らすことができます。

要約すると、さまざまなアプリケーション シナリオとメモリ使用量に応じて、適切なメモリ管理方法と最適化方法を選択することで、メモリの使用効率とパフォーマンスを向上させることができます。

7. Linuxメモリ管理の応用例

以下に、システムのさまざまな部分における一般的な Linux メモリ管理の例をいくつか示します。アプリケーション、ドライバー、システムの 3 つのレベルから、浅いものから深いものまで例を示します。

7.1 Linux メモリ管理の応用シナリオ

Linux のメモリ管理は広く使用されており、主な応用分野を次に示します。

  1. サーバー アプリケーション: 一般的なサーバー オペレーティング システムとして、Linux のメモリ管理ソリューションは高負荷時の安定性と信頼性を保証し、優れたパフォーマンスとスケーラビリティを提供します。

  2. 組み込みシステム: Linux は組み込み分野で広く使用されており、そのメモリ管理メカニズムにより、開発者は限られたメモリ領域でアプリケーションを効率的に実行できます。

  3. 科学コンピューティングとデータ処理: Linux は、ハイパフォーマンス コンピューティングとデータ処理をサポートします。これらのアプリケーションでは、メモリ管理ソリューションは、これらの計算の良好なパフォーマンスと精度を保証するために非常に重要です。

  4. オペレーティング システムの開発とカーネル プログラミング: Linux カーネルの開発では、システムの安定性と信頼性を確保し、パフォーマンスとスケーラビリティを向上させるために、メモリ管理メカニズムを深く理解する必要があります。

  5. 仮想化: Linux のメモリ管理メカニズムも、仮想化テクノロジにおいて重要な役割を果たします。仮想化テクノロジは物理メモリを複数の仮想メモリ空​​間に分割でき、Linux メモリ管理メカニズムはこれらの仮想メモリ空​​間を効果的に管理できます。

要約すると、Linux メモリ管理はさまざまなアプリケーション分野で重要な役割を果たしており、そのパフォーマンスと安定性はアプリケーションを正常に動作させるために非常に重要です。

7.2 ドライバ開発における Linux メモリ管理の適用

Linux のメモリ管理は、ドライバー開発において重要な用途を持っています。ドライバーは、さまざまな操作を実行するためにカーネル内のメモリを割り当てて管理する必要があります。以下に、ドライバー開発におけるメモリ管理の応用例をいくつか示します。

  1. キャラクタ デバイス ドライバ: 多くのキャラクタ デバイス ドライバは、デバイスから読み取られる、またはデバイスに書き込まれるデータを保存するためにメモリ バッファを割り当てる必要があります。この場合、ドライバーは通常、kmalloc() 関数または vmalloc() 関数を使用してメモリを割り当てます。

  2. ネットワーク デバイス ドライバー: ネットワーク デバイス ドライバーは、多くの場合、パケットを保存するためにメモリ バッファーを割り当てる必要があります。この場合、ドライバーは通常、alloc_pages() 関数を使用してページを割り当てます。

  3. ブロック デバイス ドライバー: ディスク上のデータ ブロックを管理するには、ブロック デバイス ドライバーが必要です。この場合、ドライバーは通常、カーネル バッファーとディスク データ ブロックがメモリ内で対応できるように、メモリ マッピング テクノロジを使用してメモリを管理します。

  4. ビデオ デバイス ドライバー: ビデオ デバイス ドライバーは、多くの場合、画像データを保存するためにメモリを割り当てる必要があります。この場合、ドライバーは通常、vmalloc() 関数を使用してメモリを割り当てます。

つまり、Linux ドライバー開発ではメモリ管理が重要なタスクであり、ドライバーはメモリの割り当て、解放、管理を行うことができなければなりません。したがって、Linux カーネルは、ドライバーがメモリを効率的に管理できるように、さまざまなメモリ管理ツールと機能を提供します。

7.3 システム最適化における Linux メモリ管理の応用

Linux のメモリ管理は、システムの最適化において重要な役割を果たします。メモリ管理を最適化すると、システムのパフォーマンスと安定性が向上し、システムの信頼性が高まります。

一般的なメモリ管理最適化手法には次のようなものがあります。

  • メモリ圧縮: 使用頻度の低いメモリ ページを圧縮してメモリ使用量を削減します。たとえば、zswap を使用すると、メモリ ページをハードディスクに圧縮できるため、システムの応答速度が向上します。

  • メモリの再利用: 再利用のために使用されなくなったメモリ ページを解放します。たとえば、Linux カーネルには、未使用のページを再利用してメモリを再割り当てすることにより、利用可能なメモリの使用を最大化するメモリ再利用メカニズムがあります。

  • 透過的ヒュージ ページ: THP は、メモリ ページをより大きなページにマージするための技術です。ページ テーブル エントリの数が減り、メモリの断片化が軽減されるため、システム パフォーマンスが向上します。

  • スワップ パーティションの設定: スワップ パーティションを設定して、未使用のメモリ ページをハード ディスクに移動し、必要に応じてメモリを再割り当てできます。ただし、スワップ パーティションを使用しすぎるとシステムの応答が遅くなる可能性があるため、スワップ パーティションの使用には注意が必要です。

つまり、Linux メモリ管理を適用すると、システムのパフォーマンスと安定性が向上し、システムの信頼性が高まります。

8. まとめ

この記事では、まずメモリ管理の概念と機能、および Linux メモリ管理の重要性と基本構造を紹介します。次に、物理メモリ管理と仮想メモリ管理について、その原理、手法、関連機能、例を含めて詳しく説明します。次に、メモリの割り当てと解放の概念、方法、関連機能、例、およびプロセス切り替えとメモリ管理の関係を紹介します。

最後に、メモリ管理のパフォーマンス チューニング、メモリ リークの検出とデバッグ、メモリのデフラグと最適化、ドライバ開発とシステム最適化における Linux メモリ管理の応用について説明します。つまり、この記事は Linux メモリ管理のあらゆる側面について詳しく説明しており、包括的かつ詳細なリファレンスです。

転載元: https://www.eet-china.com/mp/a213284.html

 

おすすめ

転載: blog.csdn.net/LinkSLA/article/details/131060731