記憶装置の種類~C言語でつくって学ぶ~【GitHub対応】

記憶装置 コンピュータのしくみ
OSくん
OSくん

お知らせ
この記事は2026年10月9日にリライトしました。
「つくって学ぶ」に合わせて、コマンドとC言語のプログラムで記憶装置を実際に確かめる内容に改めています。

記憶装置はデータを記憶する装置である。
ここでは、コンピュータの記憶装置を大きく3つに分けて見ていこう。

  • 主記憶装置
  • 補助記憶装置
  • キャッシュメモリ

主記憶装置

CPUがプログラムの命令やデータを読み書きするために利用する記憶装置である。
「主記憶」や「メインメモリ」と呼ぶこともある。
主記憶装置の役割は、実行すべき命令を一時的に保存することである。

入力された命令はいったん主記憶装置に保存される。CPUはそこから命令を順次取り出して処理を実行する。

主記憶装置の特徴

  • データを高速に出し入れできる。
  • 保存されたデータは、コンピュータの電源を落とすと消滅する。

補助記憶装置

主記憶装置を補助するための記憶装置である。
役割としては、データを長期的に保存することである。

補助記憶装置には、OSやアプリケーション、各種データなどが保存されている。 代表的な補助記憶装置には、ハードディスクやSSDがある。

SSDの特徴

  • 可動部品がないので、振動や衝撃に強い
  • 消費電力が少ない

他には、取り外し可能な補助記憶装置がある。

  • CD 保存容量約700MB
  • DVD 保存容量約4.7GB(片面1層の場合)
  • Blu-ray 保存容量約25GB(1層の場合)
  • USBメモリ
  • SDカード

キャッシュメモリ

CPUの内部に搭載されている超高速の記憶装置である。

CPUは、キャッシュメモリを使用して処理の待ち時間を短縮する。 頻繁に使うデータは主記憶装置ではなく、キャッシュメモリに保存する。

キャッシュメモリの効果をプログラムで確かめる方法は、次の記事で解説している。

3つの記憶装置の関係

3つの記憶装置は、次のような関係になっている。

記憶装置速度容量電源を切ると
キャッシュメモリ最も速い小さい消える
主記憶装置速い中くらい消える
補助記憶装置遅い大きい残る

速い記憶装置は容量が小さく、容量が大きい記憶装置は遅い。 そこで、3つを組み合わせて使うことで、速さと容量を両立している。

なお、主記憶やキャッシュに使われるメモリの種類(DRAMやSRAMなど)の違いは、次の記事で解説している。

手元のパソコンでどんな記憶装置があるのかを確かめてみよう

まずは、自分の環境にどんな記憶装置があるのか、コマンドで確認してみよう。
GitHubのCodespacesや、LinuxまたはWSLのターミナルで実行できる。 (GitHubについては、「GitHubとは?~ プログラミング学習を「つくって学ぶ」ための土台~」を参照してほしい。)
※GitHub CodespacesではCodespacesの環境を、LinuxやWSLではそれぞれの環境を確認できる。

コマンドわかること
free -h主記憶装置の容量と使用状況
df -h補助記憶装置の容量と使用状況
lsblk接続されている記憶装置の一覧
lscpu | grep -i cacheCPUのキャッシュメモリの大きさ
free -h
df -h
lsblk
lscpu | grep -i cache

表示される内容は環境によって異なる。実行して、次のことを見つけてみよう。

  • 主記憶装置(メモリ)は何GBか。
  • 補助記憶装置は何GBで、どれくらい使われているか。
  • キャッシュメモリ(L1、L2、L3)はそれぞれ何KB、何MBか。

主記憶よりも補助記憶のほうが容量が大きく、キャッシュはさらに小さいことが確認できるはずである。

キャッシュが表示されない場合

WSLなどの仮想環境では、lscpu にキャッシュの情報が表示されないことがある。その場合は、次の方法を試してほしい。

方法わかること
grep "cache size" /proc/cpuinfoキャッシュの大きさ(コアごとに1行表示される)
Windowsの「タスクマネージャー」→「パフォーマンス」→「CPU」L1、L2、L3キャッシュの大きさ

/proc/cpuinfo に表示されるのは、1種類分のキャッシュの大きさだけである。L1、L2、L3を分けて確認したいときは、タスクマネージャーを使うとよい。

UbuntuでCPUのキャッシュ情報を確認する方法

はじめに

CPUのキャッシュ(L1/L2/L3)のサイズや構成は、Ubuntuのコマンドで手軽に確認できる。
ここでは代表的な4つの方法をご紹介する。

注意:lscpu | grep -i cache は不向き

grep -i cache を使うと、キャッシュ情報以外の行も引っかかる。たとえば、脆弱性情報の行にも “cache flushes” という文字列が含まれる。

Vulnerability L1tf: Mitigation; PTE Inversion; VMX conditional cache flushes, SMT disabled

これは異常ではなく、L1TF脆弱性への対策が有効という表示である。

項目意味
L1tfL1データキャッシュ経由で情報が漏れる、Intel CPUの脆弱性(L1 Terminal Fault / Foreshadow)
Mitigation対策済み(未対策なら Vulnerable)
PTE Inversionページテーブルエントリを加工して攻撃を防ぐカーネルの対策
VMX conditional cache flushes仮想マシン実行時に、条件付きでL1Dキャッシュを消去する対策
SMT disabledSMT(ハイパースレッディング)が無効の状態

キャッシュ情報だけを見たいときは、次の方法を使おう。

方法1:lscpuでキャッシュ行だけを表示する

lscpu | grep -E "^L[0-9]"

L1d、L1i、L2、L3 の合計サイズが表示される。新しいバージョンでは次のようにインスタンス数も出る。

L1d cache: 192 KiB (4 instances)

この例なら、1コアあたり48 KiBである。

方法2:lscpu -Cで表形式にする

lscpu -C

lscpu --caches でも同じです。キャッシュの種類ごとに、合計サイズ、1コアあたりのサイズ、ウェイ数、共有単位などが表で見られる。util-linuxが比較的新しい環境で使える。

方法3:sysfsから詳細を見る

grep . /sys/devices/system/cpu/cpu0/cache/index*/{level,type,size,shared_cpu_list}

cpu0 が使うキャッシュのレベル、種類、サイズ、共有しているCPUがわかる。

方法4:dmidecodeでBIOS情報を見る

sudo dmidecode -t cache

BIOSが報告するキャッシュ情報(動作モードや連想度など)が表示される。root権限が必要となる。

使い分けの目安

目的おすすめ
とりあえず容量だけ知りたい方法1
種類・ウェイ数・共有単位まで見たい方法2
特定コアとの共有関係を調べたい方法3
BIOS側の報告値を確認したい方法4

補足:環境のバージョンも記録しておく

報告などに載せるときは、環境も併記するとよい。

# Ubuntuのバージョン
lsb_release -a

# カーネルのバージョン
uname -r

lsb_release が使えない環境では、cat /etc/os-release でも確認できる。

C言語でつくって確かめてみよう

次に、速度の違いをC言語のプログラムで確かめよう。 (memory_vs_file.c)
主記憶装置は「速い」、補助記憶装置は「遅い」と説明したが、実際にどれくらい違うのだろうか。

そこで、同じ量のデータ(128MB)について、次の3つにかかる時間を測る。

  1. メモリ上でコピーする
  2. ファイルに書き込む
  3. ファイルに書き込み、記憶装置に書き切るまで待つ
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define SIZE_MB 128                              /* 扱うデータ量(MB) */
#define SIZE ((size_t)SIZE_MB * 1024 * 1024)     /* バイト数に変換 */
#define FILENAME "testfile.bin"                  /* 書き込み先のファイル */

/* 現在時刻を秒(小数)で返す。I/Oの待ち時間も含めた実時間を測る */
static double now_sec(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec + ts.tv_nsec / 1e9;
}

static void report(const char *label, double sec) {
    printf("%-26s: %8.3f 秒  (%9.1f MB/秒)\n", label, sec, SIZE_MB / sec);
}

/* buf の内容をファイルに書き込み、かかった時間を返す。失敗したら -1 */
static double write_file(const unsigned char *buf, int use_fsync) {
    FILE *fp = fopen(FILENAME, "wb");
    if (fp == NULL) {
        perror("fopen");
        return -1;
    }

    double start = now_sec();
    if (fwrite(buf, 1, SIZE, fp) != SIZE) {
        perror("fwrite");
        fclose(fp);
        return -1;
    }
    fflush(fp);                    /* Cのバッファ → OSへ渡す */
    if (use_fsync) {
        fsync(fileno(fp));         /* OSのキャッシュ → 記憶装置へ書き切る */
    }
    double elapsed = now_sec() - start;

    fclose(fp);
    return elapsed;
}

int main(void) {
    unsigned char *src = malloc(SIZE);
    unsigned char *dst = malloc(SIZE);
    if (src == NULL || dst == NULL) {
        perror("malloc");
        return 1;
    }

    /* 計測前に両方のメモリを使い始めておく(初回アクセスの遅れを除くため) */
    for (size_t i = 0; i < SIZE; i++) {
        src[i] = (unsigned char)(i * 31);
    }
    memset(dst, 0, SIZE);

    printf("データ量: %d MB\n\n", SIZE_MB);

    /* ① 主記憶 → 主記憶(コピー) */
    double start = now_sec();
    memcpy(dst, src, SIZE);
    double t_mem = now_sec() - start;
    report("① メモリ上でコピー", t_mem);

    /* ② ファイルに書き込む(fsyncなし) */
    double t_file = write_file(src, 0);
    if (t_file < 0) return 1;
    report("② ファイルへ書き込み", t_file);

    /* ③ ファイルに書き込み、記憶装置に書き切るまで待つ(fsyncあり) */
    double t_sync = write_file(src, 1);
    if (t_sync < 0) return 1;
    report("③ ファイルへ書き込み+fsync", t_sync);

    printf("\n倍率:③は①の約 %.1f 倍の時間\n", t_sync / t_mem);
    printf("(確認用: dst の末尾 = %d)\n", dst[SIZE - 1]);

    remove(FILENAME);
    free(src);
    free(dst);
    return 0;
}

次のコマンドでコンパイルして実行する。

gcc -O2 -o memory_vs_file memory_vs_file.c
./memory_vs_file

実行結果例

データ量: 128 MB

① メモリ上でコピー:    0.092 秒  (   1395.6 MB/秒)
② ファイルへ書き込み:    0.116 秒  (   1098.8 MB/秒)
③ ファイルへ書き込み+fsync:    2.619 秒  (     48.9 MB/秒)

倍率:③は①の約 28.6 倍の時間
(確認用: dst の末尾 = 225)

※結果は環境によって大きく変わる。自分の環境で実行して確かめてほしい。

プログラムのポイント

時間の測り方
「主記憶の実効アクセス時間」の記事では clock() で時間を測っていたが、clock() はCPUが実際に処理した時間しか測れない。 ファイルへの書き込みでは、記憶装置の完了を待つ時間が発生する。この待ち時間を含めるために、今回は実際に経過した時間を測る clock_gettime(CLOCK_MONOTONIC, ...) を使っている。

②と③の違い(fsync)
fwrite で書き込んだデータは、すぐに記憶装置へ書かれるとは限らない。 OSは、書き込むデータをいったん主記憶上に置き、あとでまとめて記憶装置へ書き出すことがある。そのため、②の時間はOSのキャッシュなどの影響を受けるため、補助記憶装置への物理的な書き込み時間を直接表しているとはいえない。

そこで、③では fsyncを呼んで、OSがファイルのデータを記憶装置へ反映するのを待つ。
こちらが、補助記憶装置の速さに近い値になる。

「補助記憶装置にデータを保存する」と言っても、その途中では主記憶が使われていることがわかる。

まとめ

  • 記憶装置は、主記憶装置・補助記憶装置・キャッシュメモリの3つに分類される。
  • 速い記憶装置は容量が小さく、容量が大きい記憶装置は遅い。3つを組み合わせて、速さと容量を両立している。
  • free、df、lscpu などのコマンドで、自分の環境の記憶装置を確認できる。
  • 主記憶上の処理と、ファイルへの書き込みでは、かかる時間が異なる。ファイルへの書き込みは、OSが主記憶を使うため、fsync で書き切るまで待たないと補助記憶装置の速さは測れない。

(参考)筆者のリポジトリ

この記事で扱ったコードは、以下の筆者のGitHubリポジトリの storage フォルダで公開している。

GitHub – HappyTalk10/pc-labo-hands-on
Contribute to HappyTalk10/pc-labo-hands-on development by creating an account on GitHub.

更新履歴
2023.06.30 公開
2026.10.09 「つくって学ぶ」に合わせてリライト(手元での確認方法とC言語のプログラムを追加、光ディスクの容量表記を修正、誤字を修正)

コメント

タイトルとURLをコピーしました