週刊? Linux Kernel Patch Watch 20180427

前書き

2018 Linux Storage, Filesystem, and Memory-Management Summit関連の記事でlwn.netに面白そうな記事が増えてる今日この頃ですヽ(=´▽`=)ノ

今週のpatch

メモリを確保してデータを書き込んだあとはR/Oにするpmalloc()

前にlwn.netでも紹介されてたかも。24日の時点で[RFC PATCH v23 0/6] mm: security: write protection for dynamic dataのpatchが流れてました。メモリは専用のpoolを作って管理して、alloc時はそのpoolからメモリを取得します。このpoolはスラブアロケータみたいに特定のサイズ毎にチャンクを作るのではなくて、pmalloc()でサイズを指定して確保するようになっています。 一番メインであろうpmalloc()とかの実装があるpatchはこれっぽいです。R/Oに設定するのはpmalloc()で取得した領域を個別にと言うか、pmalloc_protect_pool()でpool全体に対して行うようです。

virtioにpersistent memoryのデバイス追加

[RFC v2] qemu: Add virtio pmem deviceです。RFCなんですぐに入ることはないと思いますが。kvmのゲストからはpersistent memoryっぽく使えるという感じです。

profcfsでseq_fileを使ったファイル処理の改善

simplify procfs code for seq_file instances V2が一連のpatchです。これの3番目のpatchが改善のメイン箇所っぽいです。

その他カーネルねた

vmallocとGFP_NOFS

vmalloc with GFP_NOFSで議論されています。vmalloc()にGFP_NOFSフラグを使っても意味ないけど、実際使ってるところあるし・・・という話のようです。単純に直せるものでもなさそうですね。 ちなみに、vmalloc()はサイズしか引数無いけど、アンダースコアが複数付くタイプのvmallocはGFPフラグを受け取るのでそれらの場合ですね。vmallocの処理で実はGFP_KERNELをハードコードしているところがあるので、渡したフラグが使われない箇所があるということのようです。

あまりメンテされていないファイルシステム

Moving unmaintained filesystems to stagingでどこかに移動しませんか?という提案が出てました。気持ちはなんとなくわかりますね。 まあ安定しているからコードが変更されていないのか?とか実際にどれくらいに人が使ってるのか?とかが出てきますね。

2038年対応

カーネルの方でも2038年対応が着々と進められているようです。最近のステータスがy2038 kernel status updateで投稿されてました。

あとがき

(´-`).。oO(ubuntuとかfedoraの新バージョンがそろそろ出ますね

まんがでわかるLinux シス管系女子3

まんがでわかるLinux シス管系女子3

週刊? Linux Kernel Patch Watch 20180420

前書き

今週も細かいネタを拾っていきます。重要そうなpatchはLWN.netをチェックしましょう。lwnはSubscriberになると最新の記事がすぐに読めて快適ですΣ(゚∀゚ノ)ノキャー

今週のpatch

cpuタイマーのバグフィックス

LKML: Laura Abbott: [PATCH] posix-cpu-timers: Ensure set_process_cpu_timer is always evaluatedというpatchです。fedoraのbugzillaでBug 1568337 – kernel: prlimit64 with RLIMIT_CPU ignored]と報告されたものの修正ですね。バグの内容はsetrlimit(2)でRLIMIT_CPUを使ってcpu時間の上限をセットしてもプロセスが終了しないということでした。

vm_fault_t

patchはmm: change return type to vm_fault_tだけではないんですが、戻り値にintを使っていたところが新しく追加されたvm_fault_t型になってます。これ関連のpatchはいくつか流れていました。VM_FAULT_ 系のエラーを返すところに使われる感じですね。

Variable Length Arraysは使わない

VLAはこんな感じの配列のサイズに定数以外を使うやつですね。GCCのドキュメントはUsing the GNU Compiler Collection (GCC): Variable Lengthです。

void foo(char str)
{
    char s[strlen(str) + 1];
}

特定のpatchというかいくつかこの系統のpatchが流れてました。Re: VLA removal (was Re: [RFC 2/2] lustre: use VLA_SAFE)がきっかけみたいです。

その他カーネルねた

CONFIG_SLAB_FREELIST_RANDOMがvm環境であまりランダムにならないという話し

こちらでCONFIG_SLAB_FREELIST_RANDOMを有効にしててもあまりランダムになってないんだけどという話が出てました。VM環境でブートの初期段階だとエントロピーはこの機能に関わらず問題になりやすいとか、qemu-system-x86_64はデフォルトではRDRAND機能を使わないから、RDRANDを使う場合は-cpu hostオプションが必要だよとか、virtio-rngはこの場合には使えないよーなんて話がでてました。

Fedoraのkernel Test Day

Test Day:2018-04-13 Kernel 4.16 Test Day - Fedora Project Wikiというカーネルのテストをするイベントがありました。 テストは基本的にQA:Testcase kernel regression - Fedora Project Wikiの手順で行います。実行自体は特に難しいことはありません。これ以外にも人によっては任意のテストを実行したりもしてます。結果のページに結果があります。カーネルに限らず特定の機能のテストイベントはよく開かれているのでFedoraを使ってる人は参加しておくと、良いことがあるかもしれません。

あとがき

4.17系のrc1が出ましたね。

まんがでわかるLinux シス管系女子3 (日経BPパソコンベストムック)

まんがでわかるLinux シス管系女子3 (日経BPパソコンベストムック)

週刊? Linux Kernel Patch Watch 20180413

まえがき

週刊Railsウォッチというrailsを中心とした記事がとても良い感じで、これのカーネル版あったら嬉しなということで自分でやってみました(*´Д`) どれくらいのボリュームでとか、毎週ちゃんと書くのかは全く不明でございます。

我々にはWelcome to LWN.net [LWN.net]Linux Hardware Reviews, Open-Source Benchmarks & Linux Performance - Phoronixがあるし、基本的にこれらを読めば事足りる気はしますが。まあ、小ネタを拾っていこう的な感じです。 読むpatchについてはマージされるかどうかは別として、気になったものとかを取り上げようと思います。

今週のpatch

sl*bでコンストラクタ使う場合にスラブオブジェクトの取得時に__GFP_ZEROフラグ使わない

[PATCH v2 2/2] slab: __GFP_ZERO is incompatible with a constructor — Linux Kernelというpatchのお話です。4/12時点ではマージされてませんが。 sl*bではページを確保してスラブオブジェクトを新規に作る時にコンストラクタを渡して、作成時に処理を行うことができます。スラブの作成でkmem_cache_create()系の関数を使うとコンストラクタを渡せます。

struct kmem_cache *kmem_cache_create(const char *name, size_t size,
            size_t align, slab_flags_t flags,
            void (*ctor)(void *));
struct kmem_cache *kmem_cache_create_usercopy(const char *name,
            size_t size, size_t align, slab_flags_t flags,
            size_t useroffset, size_t usersize,
            void (*ctor)(void *));

slubだとsetup_object()で1オブジェクト毎に指定されたコンストラクタを実行します。

static void setup_object(struct kmem_cache *s, struct page *page,
                void *object)
{
    setup_object_debug(s, page, object);
    kasan_init_slab_obj(s, object);
    if (unlikely(s->ctor)) {
        kasan_unpoison_object_data(s, object);
        s->ctor(object);
        kasan_poison_object_data(s, object);
    }
}

そして、kmem_cache_alloc()でスラブオブジェクトを取得する時にgfpフラグを渡して、メモリの確保方法を指定できます。

void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __assume_slab_alignment __malloc;

ここで、flagsにGFP_ZEROを渡した場合ですが、オブジェクトを確保してそのオブジェクトをmemset()で0クリアするのがslubでの処理です。そうすると、コンストラクタで何かしらの処理をオブジェクトにしていてもそれが0で上書きされてしまうので、その場合の対応が今回のpatchでした。 簡単なテストコードを書いて試してみましょう。コードはこんな感じです。kmem_cache_alloc()にGFP_ZEROを渡さない場合と渡す場合の2関数を作って呼びます。

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/string.h>

MODULE_DESCRIPTION("slab constructor test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

#define SCT_SLAB_NAME "sct"

static struct kmem_cache *sct_cache;

struct sct_test_struct {
    int n;
    char s[16];
};

static void sct_constructor(void *data)
{
    struct sct_test_struct *p = (struct sct_test_struct *) data;
    memset(p->s, 0x0, sizeof(p->s));

    p->n = 0xffff;
    strcpy(p->s, "hello");
}

static void sct_test(void)
{
    struct sct_test_struct *p = kmem_cache_alloc(sct_cache, GFP_KERNEL);

    pr_info("%s: %d, %s\n", __func__, p->n, p->s);

    kmem_cache_free(sct_cache, p);
}

static void sct_test_with_gfp_zero(void)
{
    struct sct_test_struct *p = kmem_cache_alloc(sct_cache, __GFP_ZERO);

    pr_info("%s: %d, %s\n", __func__, p->n, p->s);

    kmem_cache_free(sct_cache, p);
}

static int slab_ctor_test_init(void)
{
    slab_flags_t flags = SLAB_PANIC;

    pr_info("%s start\n", __func__);

    sct_cache = kmem_cache_create(SCT_SLAB_NAME,
                      sizeof(struct sct_test_struct),
                      __alignof__(struct sct_test_struct),
                      flags,
                      &sct_constructor);
    if (!sct_cache) {
        pr_info("failed to create slab cache\n");
        return -ENOMEM;
    }

    sct_test();
    sct_test_with_gfp_zero();

    return 0;
}

static void slab_ctor_test_cleanup(void)
{
    if (sct_cache)
        kmem_cache_destroy(sct_cache);

    pr_info("%s bye\n", __func__);
}

module_init(slab_ctor_test_init);
module_exit(slab_ctor_test_cleanup);

実行結果はこうなります。__GFP_ZEROを付けた方はコンストラクタ設定した値が0で上書きされてますね/(^o^)\

masami@kerntest:~/slab_ctor_test$ dmesg
[  225.077109] slab_ctor_test: slab_ctor_test_init start
[  225.077156] slab_ctor_test: sct_test: 65535, hello
[  225.077157] slab_ctor_test: sct_test_with_gfp_zero: 0, 

(´-`).。oO(言われてみればその通りで、コンストラクタと__GFP_ZEROが共存するような使い方はあまり意味がないんだけどシステム的にはノーチェックだったんですね。ちなみに、これはf2fsの方で見つかったバグが発端になってるようです

アドレスを定義するときはunsigned longを使おう

[PATCH] x86/mm: vmemmap and vmalloc base addressess are usngined longs — Linux Kernelのpatchです。

こんな感じでULを付けましょうということです。

-#define __VMEMMAP_BASE_L4   0xffffea0000000000
-#define __VMEMMAP_BASE_L5  0xffd4000000000000
+#define __VMEMMAP_BASE_L4  0xffffea0000000000UL
+#define __VMEMMAP_BASE_L5  0xffd4000000000000UL

(´-`).。oO(忘れずにULをつけましょう

sizeof(void)とgccのワーニング

www.spinics.netのpatchです。gccのオプションで-Wpointer-arithを付けると c sizeof(void) を使ってる時に警告がでます。この警告-Wallではでません。sparseがこの警告を大量に出してくるので警告のon/off切り替えをできるようにしたようです。

#include <stdio.h>

int main(int argc, char **argv)
{
        sizeof(void);
        return 0;
}
masami@saga:~$ gcc sizeof_void.c -Wpointer-arith
sizeof_void.c: In function ‘main’:
sizeof_void.c:5:9: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
  sizeof(void);
         ^~~~

(´-`).。oO(sizeof(void)は1です

あとがき

来週も書けるかな?

gccのインラインアセンブラ内からcのラベルにgotoでジャンプ

最近のgccインラインアセンブラ内からcのgotoラベルに飛べる仕組みがあるのでそれのメモです。

ドキュメントはUsing the GNU Compiler Collection (GCC): Extended AsmUsing the GNU Compiler Collection (GCC): Extended Asmです。たしかLKMLに送られてたpatchにもこれを使ったコードがあった気がします。

それはさておき、gotoを使う場合の書式は以下のようになっていて、asm gotoという形式です。

asm [volatile] goto ( AssemblerTemplate 
                      : 
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

ドキュメントには-ansiオプションを付けてコンパイルする場合はasmではなくてasmを使うようにと書かれています。また、出力のオペランドは指定できないようです。そのため、計算の途中経過を保持したいときとかはClobbersのところでメモリに書き出すようにするみたいです。

以下が簡単なサンプルです。argcの数が3かどうか調べて3だったらsameラベルにジャンプしてます。

#include <stdio.h>

int main(int argc, char **argv)
{
    __asm__ goto("cmpl $0x3, %[argc]\n\t"
             "je %l[same]\n\t"
             :
             : [argc] "r"(argc)
             :
             :same);

    printf("argc is %d\n", argc);

    return 0;
same:
    printf("argc == 3\n");
    return 0;
}

機械語になるとこんな感じです。

0000000000400527 <main>:                                                                                                                                                                                           
  400527:       55                      push   %rbp                                                                                                                                                                
  400528:       48 89 e5                mov    %rsp,%rbp                                                                                                                                                           
  40052b:       48 83 ec 10             sub    $0x10,%rsp                                                                                                                                                          
  40052f:       89 7d fc                mov    %edi,-0x4(%rbp)                                                                                                                                                     
  400532:       48 89 75 f0             mov    %rsi,-0x10(%rbp)                                                                                                                                                    
  400536:       8b 45 fc                mov    -0x4(%rbp),%eax                                                                                                                                                     
  400539:       83 f8 03                cmp    $0x3,%eax                                                                                                                                                           
  40053c:       74 1b                   je     400559 <main+0x32>                                                                                                                                                  
  40053e:       8b 45 fc                mov    -0x4(%rbp),%eax                                                                                                                                                     
  400541:       89 c6                   mov    %eax,%esi                                                                                                                                                           
  400543:       bf 00 06 40 00          mov    $0x400600,%edi                                                                                                                                                      
  400548:       b8 00 00 00 00          mov    $0x0,%eax                                                                                                                                                           
  40054d:       e8 ee fe ff ff          callq  400440 <printf@plt>                                                                                                                                                 
  400552:       b8 00 00 00 00          mov    $0x0,%eax                                                                                                                                                           
  400557:       eb 0f                   jmp    400568 <main+0x41>                                                                                                                                                  
  400559:       bf 0c 06 40 00          mov    $0x40060c,%edi                                                                                                                                                      
  40055e:       e8 cd fe ff ff          callq  400430 <puts@plt>                                                                                                                                                   
  400563:       b8 00 00 00 00          mov    $0x0,%eax
  400568:       c9                      leaveq
  400569:       c3                      retq
  40056a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

低レベルプログラミング

低レベルプログラミング

vdsoの領域を読んでファイルに保存

ふとvdsoで提供されている関数の一覧が見たくなったので。

dump vdso

masami@saga:~$ gcc read_vdso.c -o read_vdso                                                                                                                                                                        
masami@saga:~$ ./read_vdso                                                                                                                                                                                         
[*]read 2 pages                                     
[*]vdso start address is 0x7ffd532fb000             
[*]write data                                       
[*]Done.                                            
masami@saga:~$ file vdso_dump.so                                                                                                                                                                                   
vdso_dump.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=dbcfbfba9068a599aa0b0964ac2ab58244ce7cb4, stripped                                                         
masami@saga:~$ readelf -s vdso_dump.so                                                                                                                                                                             

Symbol table '.dynsym' contains 10 entries:         
   Num:    Value          Size Type    Bind   Vis      Ndx Name                                          
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND                                               
     1: 0000000000000a20   773 FUNC    WEAK   DEFAULT   12 clock_gettime@@LINUX_2.6                      
     2: 0000000000000d30   449 FUNC    GLOBAL DEFAULT   12 __vdso_gettimeofday@@LINUX_2.6                
     3: 0000000000000d30   449 FUNC    WEAK   DEFAULT   12 gettimeofday@@LINUX_2.6                       
     4: 0000000000000f00    21 FUNC    GLOBAL DEFAULT   12 __vdso_time@@LINUX_2.6                        
     5: 0000000000000f00    21 FUNC    WEAK   DEFAULT   12 time@@LINUX_2.6                               
     6: 0000000000000a20   773 FUNC    GLOBAL DEFAULT   12 __vdso_clock_gettime@@LINUX_2.6               
     7: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS LINUX_2.6                                     
     8: 0000000000000f20    42 FUNC    GLOBAL DEFAULT   12 __vdso_getcpu@@LINUX_2.6                      
     9: 0000000000000f20    42 FUNC    WEAK   DEFAULT   12 getcpu@@LINUX_2.6   

vdsoの開始アドレスはgetauxval(3)にAT_SYSINFO_EHDRを渡すと取得できる。

xv6のメモリ管理周りのコードリーディング

xv6のページング周りをちょっと見てたので記録をφ(..)メモメモ

参照したドキュメントはbook-rev10.pdfです。

book-rev10.pdfのP21、 Figure 1-2にxv6のメモリレイアウトがあります。

f:id:masami256:20180305233136p:plain

仮想アドレスの0から0x80000000がユーザー空間で、0x80000000〜0xFFFFFFFFがカーネル空間ですね。カーネル空間のうち先頭の0x100000バイトはBIOSの領域となってます。

カーネルアドレス空間vm.cにあるkmap構造体で管理してます。

static struct kmap {
  void *virt;
  uint phys_start;
  uint phys_end;
  int perm;
} kmap[] = {
 { (void*)KERNBASE, 0,             EXTMEM,    PTE_W}, // I/O space
 { (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0},     // kern text+rodata
 { (void*)data,     V2P(data),     PHYSTOP,   PTE_W}, // kern data+memory
 { (void*)DEVSPACE, DEVSPACE,      0,         PTE_W}, // more devices
};

KERNBASEやKERNLINKなどの定数はmemorylayout.hにて定義されています。

#define EXTMEM  0x100000            // Start of extended memory
#define PHYSTOP 0xE000000           // Top physical memory
#define DEVSPACE 0xFE000000         // Other devices are at high addresses

// Key addresses for address space layout (see kmap in vm.c for layout)
#define KERNBASE 0x80000000         // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM)  // Address where kernel is linked

V2Pはこんなマクロです。

#define V2P(a) (((uint) (a)) - KERNBASE)

ここでkmapのコメントにあるようなメモリレイアウトにしてるわけですね。

//   0..KERNBASE: user memory (text+data+stack+heap), mapped to
//                phys memory allocated by the kernel
//   KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)
//   KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)
//                for the kernel's instructions and r/o data
//   data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,
//                                  rw data + free physical memory
//   0xfe000000..0: mapped direct (devices such as ioapic)

このkmap構造体を使ってマッピングを行っているのがsetupkvm()です。setupkvm()の戻り値の型はpde_t *で、ようはページグローバルディレクトリです。これのアドレスがcr3レジスタに設定されます。

setupkvm()を呼ぶのは4箇所有ります。

exec()ではexecシステムコールで既存のプロセスのアドレス空間を新しいプロセスのアドレス空間に変える時に呼んでいます。userinit()は一番最初のユーザープロセスを作る時に呼んでいます。 kvmalloc()ではカーネルのスケジューラー用に作っているようです。最後のcopyuvm()はfork()の実行時に親プロセスのページテーブルをコピーする時に呼んでいます。

実際にマッピングを行っているのはmappages()です。mappages()は引数としてページグローバルディレクトリの先頭アドレス、マッピングしたい仮想アドレス、そのアドレスのサイズ、仮想アドレスにマッピングする物理アドレス、ページに設定するパーミッションを受け取ります。1番目のページグローバルディレクトリは呼び出し側がkalloc()でメモリを確保しています。kalloc()はkalloc.cに実装があって、空きページを1ページ確保して返す関数です。

mappages()を呼び出している箇所は4箇所です。

setupkvm()は先程も出てきたとおりで、kmap構造体のデータを実際にマッピングしてます。inituvm()はuserinit()の処理中に呼ばれます。_binary_initcode_startなどはカーネルをビルドした後にできるkernel.symというファイルで確認できます。allocuvm()はユーザー空間の大きさを大きくする時に呼んでいます。copyuvm()はfork()の実行時に呼ばれます。

ページフォルトですがxv6ではデマンドページングはやっていないのでエラーとして処理してます。処理しているのはtrap.cにあるtrap()です。カーネルアドレス空間ページフォルトが発生した場合はpanic、ユーザー空間の場合はプロセスを終了させるようです。

default:
    if(myproc() == 0 || (tf->cs&3) == 0){
      // In kernel, it must be our mistake.
      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
              tf->trapno, cpuid(), tf->eip, rcr2());
      panic("trap");
    }
    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %d err %d on cpu %d "
            "eip 0x%x addr 0x%x--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno,
            tf->err, cpuid(), tf->eip, rcr2());
    myproc()->killed = 1;

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

ページングでメモリを割り当てる処理の動きを確認できるものを作ってみた

Linuxのしくみの5章でページングの説明がありますね。なんとなく、この辺の挙動をエミュレートする感じのものでも作ってみようかなと思ったりしたので。

リポジトリはこちらです。

github.com

ページングのコードを実際に書くなら自作OSを書くのが良いと思いますが、ページの割り当てくらいならユーザーランドのプログラムでもそれっぽい感じにはなるかなということで^^;

動かし方はこんな感じで、4MiBのメモリがあって、4プロセスが5回ずつページの割り当てを行った感じが下のコマンドです。

masami@saga:~/codes/how_paging_works (master=)$ ./pg -p 4 -l 5 -m 4 | jq .

出力はjson形式になるようにしたので、pipeでjqコマンドに渡せば綺麗に見れます。

{
  "physical memory": {
    "size": "4MiB",
    "range": {
      "start": "0x7fd86ae2e000",
      "end": "0x7fd86b22e000"
    },
    "page flames": 1024
  },
  "page data": [
    {
      "pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae2e000"
    },
    {
      "pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae2f000"
    },
    {
      "pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae30000"
    },
    {
      "pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae31000"
    },
    {
      "pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae32000"
    },
    {
      "pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae33000"
    },
    {
      "pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae34000"
    },
    {
      "pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae35000"
    },
    {
      "pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae36000"
    },
    {
      "pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae37000"
    },
    {
      "pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae38000"
    },
    {
      "pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae39000"
    },
    {
      "pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae3a000"
    },
    {
      "pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae3b000"
    },
    {
      "pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae3c000"
    },
    {
      "pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae3d000"
    },
    {
      "pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae3e000"
    },
    {
      "pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae3f000"
    },
    {
      "pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae40000"
    },
    {
      "pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae41000"
    }
  ]
}

プロセスのアドレス空間(仮想アドレス)は0x100000000000からスタートしてます。物理アドレスの方はposix_memalign(3)で取得したアドレスで、ASLRが有効な環境というのもあってどんな値かは基本的に予測できません。その代わりページサイズでアライメントを調整してあります。

このプログラムがやってるのは、こんな感じです。

  1. 最初に指定されたサイズのメモリを確保
  2. ページフレームを管理する構造体の初期化
  3. -pオプションで指定された数のスレッドを準備(プロセスの代わりにスレッドを使ってます)
  4. スレッドは-lオプションで指定された回数のループでページの割り当てを行う
  5. 全部のスレッドが終了したらjson形式のデータを出力

ページグローバルディレクトリのpgdはスレッドの開始時にそのスレッド用に作ってます。pmd、pud、pteはdo_page_fault()の中で作ってます。なんでdo_page_fault()って名前かって言うと、ページフォルトが起きた時に呼ばれる関数がこれなので(ここから更に関数の呼び出しはありますが)、それっぽい名前にしました。 他にもpgdやpte用のメモリ確保はpgd_alloc()や、pte_alloc()でLinuxを同じような名前にしてます。

これ自体は単にメモリを確保していって最後に表示するだけですが、適当に弄るともうちょっと遊べるんじゃないかと思いますm( )m

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識