メモリマッピング関数remap_pfn_rangelearning-分析例(1)



Memory Mapping Function Remap_pfn_range Learning Example Analysis



から来る : https://www.cnblogs.com/pengdonglin137/p/8149859.html

カタログを読む(内容)



トップに戻る(トップに移動)

簡単な例を見てみましょう。ドライバに32ページのバッファを適用します。PAGE_SIZEは4KBであるため、カーネルのバッファサイズは128KBです。 User_1とuser_2は、最初の64KBを独自のユーザースペースにマップします。ここで、user_1は文字列をバッファに書き込み、user_2はそれを読み取ります。 User_3とuser_4は、最後の64KBを独自のユーザースペースにマップします。ここで、user_3は文字列をバッファに書き込み、user_4は文字列を読み取ります。 User_5は、128KB全体を独自のユーザースペースにマップしてから、バッファーをクリアします。さらに、ドライバーでバッファーを申請する方法はたくさんあります。 kmallocまたはalloc_pagesを使用できます。もちろん、vmallocを使用することもできます。以下は、これら3つのインターフェースのドライバーを実装します。



関連するテストプログラムとドライバは、次のリンクからダウンロードできます。

https://github.com/pengdonglin137/remap_pfn_demo



まず、ドライバー

例として、kzallocアプリケーションバッファを取り上げましょう。 32ページを申請するためにkmallocと呼びます。 kzallocによって返される仮想アドレスは、対応する物理アドレスによって特徴付けられることがわかっているため、remap_pfn_rangeを呼び出すときに便利です。 initを駆動するときは、最初に128KBのバッファーを適用します。

1 static int __init remap_pfn_init(void) 2 { 3 int ret = 0 4 5 kbuff = kzalloc(BUF_SIZE, GFP_KERNEL) // The BUF_SIZE here is 128KB 6 if (!kbuff) { 7 ret = -ENOMEM 8 goto err 9 } 10 11 ret = misc_register(&remap_pfn_misc) // Register a misc device 12 if (unlikely(ret)) { 13 pr_err('failed to register misc device! ') 14 goto err 15 } 16 17 return 0 18 19 err: 20 return ret 21 }

行11は、その他のデバイスを次の情報で登録します。

1 static struct miscdevice remap_pfn_misc = { 2 .minor = MISC_DYNAMIC_MINOR, 3 .name = 'remap_pfn', 4 .fops = &remap_pfn_fops, 5 }

ドライバーをロードした後、remap_pfnという名前のノードが/ devの下に生成され、ユーザープログラムはこのノードを介してドライバーと通信できます。 remap_pfn_fopsの定義は次のとおりです。

1 static const struct file_operations remap_pfn_fops = { 2 .owner = THIS_MODULE, 3 .open = remap_pfn_open, 4 .mmap = remap_pfn_mmap, 5 }

3行目のopen関数は、ここでは実際の作業を行わず、プロセスのメモリレイアウト情報の出力など、いくつかのログを出力するだけです。

ユーザーのmmapリクエストを処理する4行目は心配する必要があります。

内容を印刷するためのopen関数を見てみましょう。

1 static int remap_pfn_open(struct inode *inode, struct file *file) 2 { 3 struct mm_struct *mm = current->mm 4 5 printk('client: %s (%d) ', current->comm, current->pid) 6 printk('code section: [0x%lx 0x%lx] ', mm->start_code, mm->end_code) 7 printk('data section: [0x%lx 0x%lx] ', mm->start_data, mm->end_data) 8 printk('brk section: s: 0x%lx, c: 0x%lx ', mm->start_brk, mm->brk) 9 printk('mmap section: s: 0x%lx ', mm->mmap_base) 10 printk('stack section: s: 0x%lx ', mm->start_stack) 11 printk('arg section: [0x%lx 0x%lx] ', mm->arg_start, mm->arg_end) 12 printk('env section: [0x%lx 0x%lx] ', mm->env_start, mm->env_end) 13 14 return 0 15 }

5行目はプロセスの名前とpidを出力します

6行目は、プロセスのコードセグメントの範囲を出力します

7行目は、初期化されたグローバル変数を格納するプロセスのデータセグメントの範囲を出力します。 bssセグメントは、初期化されていないグローバル変数を格納します。格納場所は、データセグメントの直後、ヒープ領域の前です。

8行目は、プロセスのヒープ領域の開始アドレスと現在のアドレスを出力します

9行目は、プロセスのmmap領域のベースアドレスを出力します。ここで、mmap領域は下に向かって大きくなります。特定のmmap領域のベースアドレスは、システムで許可されている現在のプロセスのユーザースタックのサイズに関連しています。ユーザースタックの最大サイズが大きいほど、mmap領域のベースアドレスは小さくなります。ユーザースタックの最大サイズを変更するには、ulimit -sxxxコマンドを使用する必要があります。単位はKBで、これはユーザースタックの最大サイズを意味します。ユーザースタックのサイズはGにすることができますが、カーネルスタックには2ページしかありません。

10行目は、プロセスのユーザースタックの開始アドレスを出力します。

11行目と12行目は関係ありません。

remap_pfn_mmapの実装は次のとおりです。

1 static int remap_pfn_mmap(struct file *file, struct vm_area_struct *vma) 2 { 3 unsigned long offset = vma->vm_pgoff <> PAGE_SHIFT) + vma->vm_pgoff 5 unsigned long virt_start = (unsigned long)kbuff + offset 6 unsigned long size = vma->vm_end - vma->vm_start 7 int ret = 0 8 9 printk('phy: 0x%lx, offset: 0x%lx, size: 0x%lx ', pfn_start vm_page_prot) 12 if (ret) 13 printk('%s: remap_pfn_range failed at [0x%lx 0x%lx] ', 14 __func__, vma->vm_start, vma->vm_end) 15 else 16 printk('%s: map 0x%lx to 0x%lx, size: 0x%lx ', __func__, virt_start, 17 vma->vm_start, size) 18 19 return ret 20 }

3行目のvma_pgoffは、バッファ内のvmaで表される間隔のオフセットアドレスを示し、単位はページです。この値は、ユーザーがmmapを呼び出すときに渡される最後のパラメーターですが、ユーザースペースオフセットの単位はバイトです(もちろん、ページ整列する必要があります)。カーネルに入った後、カーネルはPAGE_SHIFT(12)によって値を右にシフトします。これはページに変換されます。このコンパイルアドレスは9行目に出力されるため、PAGE_SHIFTによって左に移動され、オフセットに割り当てられます。

4行目は、ユーザースペースにマップされるカーネルバッファー内のアドレスに対応する物理ページフレーム番号を計算します。仮想アドレスを物理アドレスに変換するには、virt_to_physが受け入れる仮想アドレスがローエンドメモリ範囲内にある必要があり、vmalocによって返される仮想アドレスがローエンドメモリ範囲内にないため、特別な関数が必要です。

5行目は、ユーザースペースのアドレスにマップされるカーネルバッファー内の仮想アドレスを計算します。

6行目は、vmaで表されるメモリ間隔のサイズを計算します

11行目はremap_pfn_rangeを呼び出して、物理ページフレーム番号pfn_startに対応する物理メモリをユーザースペースのvm-> vm_startにマップします。マッピングの長さは、仮想メモリ領域の長さです。カーネルバッファはkzallocによって割り当てられるため、物理アドレスの連続性が保証され、pfn_start(サイズ>> PAGE_SHIFT)の連続する物理ページフレームからの物理ページフレーム番号がユーザースペースに順次マップされます。 。

ドライバーをモジュールにコンパイルした後、カーネルにinsmodします。

第二に、ユーザーテストプログラム

ここでの5つのテスト手順は単純で、実際に同じメモリを共有していることを証明するためのものです。

user_1.c:

1 #define PAGE_SIZE (4*1024) 2 #define BUF_SIZE (16*PAGE_SIZE) 3 #define OFFSET (0) 4 5 int main(int argc, const char *argv[]) 6 7 int fd 8 char *addr = NULL 9 10 fd = open('/dev/remap_pfn', O_RDWR) 11 12 addr = mmap(NULL, BUF_SIZE, PROT_READ

10行目と12行目では、デバイスノードが開かれ、64KBのメモリがカーネルスペースからユーザースペースにマップされます。最初のアドレスはaddrに格納されます。後で書き込まれて共有されるため、対応するフラグが設定されます。ここで指定されたオフセットは0で、マッピング前の64KBです。

14行目は、addrが指す仮想アドレス空間に文字列を出力します。

user_2.c:

1 #define PAGE_SIZE (4*1024) 2 #define BUF_SIZE (16*PAGE_SIZE) 3 #define OFFSET (0) 4 5 int main(int argc, const char *argv[]) 6 PROT_WRITE, MAP_SHARED

User_2はuser_1と同じですが、addrが指す仮想アドレス空間の内容が出力される点が異なります。

user_3.c:

1 #define PAGE_SIZE (4*1024) 2 #define BUF_SIZE (16*PAGE_SIZE) 3 #define OFFSET (16*PAGE_SIZE) 4 5 int main(int argc, const char *argv[]) 6 7 int fd 8 char *addr = NULL 9 10 fd = open('/dev/remap_pfn', O_RDWR) 11 12 addr = mmap(NULL, BUF_SIZE, PROT_READ

12行目のOFFSETは64KBに設定されています。これは、カーネルバッファーの最後の64KBがユーザースペースにマップされていることを意味します。

14行目、文字列をバッファに入力します

user_4.c:

1 #define PAGE_SIZE (4*1024) 2 #define BUF_SIZE (16*PAGE_SIZE) 3 #define OFFSET (16*PAGE_SIZE) 4 5 int main(int argc, const char *argv[]) 6 PROT_WRITE, MAP_SHARED

12行目のOFFSETは64KBに設定されています。これは、カーネルバッファーの最後の64KBがユーザースペースにマップされていることを意味します。

14行目、出力バッファの内容

user_5.c:

1 #define PAGE_SIZE (4*1024) 2 #define BUF_SIZE (32*PAGE_SIZE) 3 #define OFFSET (0) 4 5 int main(int argc, const char *argv[]) 6 7 int fd 8 char *addr = NULL 9 int *brk 10 11 fd = open('/dev/remap_pfn', O_RDWR) 12 13 addr = mmap(NULL, BUF_SIZE, PROT_READ

13行目、カーネルバッファの128KB全体をユーザースペースにマップします

14行目、バッファの内容をクリアします

第三に、テスト

1、カーネル空間の仮想メモリレイアウト

カーネル起動ログ カーネルスペースの仮想メモリレイアウト情報を表示するには、次の手順に従います。

1 [ 0.000000] Virtual kernel memory layout: 2 [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) 3 [ 0.000000] fixmap : 0xffc00000 - 0xfff00000 (3072 kB) 4 [ 0.000000] vmalloc : 0xf0800000 - 0xff800000 ( 240 MB) 5 [ 0.000000] lowmem : 0xc0000000 - 0xf0000000 ( 768 MB) 6 [ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) 7 [ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB) 8 [ 0.000000] .text : 0xc0008000 - 0xc0800000 (8160 kB) 9 [ 0.000000] .init : 0xc0b00000 - 0xc0c00000 (1024 kB) 10 [ 0.000000] .data : 0xc0c00000 - 0xc0c7696c ( 475 kB) 11 [ 0.000000] .bss : 0xc0c78000 - 0xc0cc9b8c ( 327 kB)

kzallocで割り当てられたメモリは、5行目で示される仮想メモリ内に収まります。

vmallocで割り当てられたメモリは、4行目で示される仮想メモリ内に収まります。

2、ユーザー仮想アドレス空間のレイアウト

以下は、Linuxでのユーザーの仮想メモリレイアウトに関する一般的な情報です。

ここでの注意点は次のとおりです。

mallocにメモリが割り当てられているときに、mallocに渡されるパラメータが128KB未満の場合、システムは、brkポインタの位置を上位アドレスに調整することにより、ヒープ領域にメモリを割り当てます。 mallocに渡されるパラメーターが128KBより大きい場合、システムはmmap領域に割り当てます。つまり、新しいvmaを割り当てます。これには、vmaマージ拡張などの操作が含まれる場合があります。

参照できます: Linuxプロセスがメモリを割り当てる2つの方法-brk()とmmap()

3、user_1およびuser_2

user1を実行します。

[root@xxxxx mnt]# ./user_1

次のカーネルログを確認できます。

1 [ 2494.835749] client: user_1 (870) 2 [ 2494.835918] code section: [0x8000 0x87f4] 3 [ 2494.836047] data section: [0x107f4 0x1092c] 4 [ 2494.836165] brk section: s: 0x11000, c: 0x11000 5 [ 2494.836307] mmap section: s: 0xb6f17000 6 [ 2494.836441] stack section: s: 0xbe909e20 7 [ 2494.836569] arg section: [0xbe909f23 0xbe909f2c] 8 [ 2494.836689] env section: [0xbe909f2c 0xbe909ff3] 9 [ 2494.836943] phy: 0x8eb60000, offset: 0x0, size: 0x10000 10 [ 2494.837176] remap_pfn_mmap: map 0xeeb60000 to 0xb6d75000, size: 0x10000

プロセス番号は870です。以下を使用して、プロセスのアドレス空間のマップ情報を表示できます。

1 [root@xxxxx mnt]# cat /proc/870/maps 2 00008000-00009000 r-xp 00000000 00:12 1179664 /mnt/user_1 3 00010000-00011000 rw-p 00000000 00:12 1179664 /mnt/user_1 4 b6d75000-b6d85000 rw-s 00000000 00:10 8765 /dev/remap_pfn 5 b6d85000-b6eb8000 r-xp 00000000 b3:01 143 /lib/libc-2.18.so 6 b6eb8000-b6ebf000 ---p 00133000 b3:01 143 /lib/libc-2.18.so 7 b6ebf000-b6ec1000 r--p 00132000 b3:01 143 /lib/libc-2.18.so 8 b6ec1000-b6ec2000 rw-p 00134000 b3:01 143 /lib/libc-2.18.so 9 b6ec2000-b6ec5000 rw-p 00000000 00:00 0 10 b6ec5000-b6ee6000 r-xp 00000000 b3:01 188 /lib/libgcc_s.so.1 11 b6ee6000-b6eed000 ---p 00021000 b3:01 188 /lib/libgcc_s.so.1 12 b6eed000-b6eee000 rw-p 00020000 b3:01 188 /lib/libgcc_s.so.1 13 b6eee000-b6f0e000 r-xp 00000000 b3:01 165 /lib/ld-2.18.so 14 b6f13000-b6f15000 rw-p 00000000 00:00 0 15 b6f15000-b6f16000 r--p 0001f000 b3:01 165 /lib/ld-2.18.so 16 b6f16000-b6f17000 rw-p 00020000 b3:01 165 /lib/ld-2.18.so 17 be8e9000-be90a000 rw-p 00000000 00:00 0 [stack] 18 bed1c000-bed1d000 r-xp 00000000 00:00 0 [sigpage] 19 bed1d000-bed1e000 r--p 00000000 00:00 0 [vvar] 20 bed1e000-bed1f000 r-xp 00000000 00:00 0 [vdso] 21 ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]

上記の各行は、vmaマッピング情報を表すことができます。4番目の行が関係します。

1 b6d75000-b6d85000 rw-s 00000000 00:10 8765 /dev/remap_pfn

意味:

「b6d75000」はvma-> vm_startの値、「b6d85000」はvma-> vm_endの値、b6d85000からb6d75000を引いた値は64KBで、これはvmaで表される仮想メモリ領域のサイズです。

「rw-s」はvma-> vm_flagsを意味し、「s」は共有を意味し、「p」はプライベートを意味します

「00000000」はオフセットを意味し、vma-> vm_pgoffの値です。

「00:10」は、デバイスノードのプライマリおよびセカンダリデバイス番号を示します

「8765」は、デバイスノードのiノード値を示します

'/ dev / remap_pfn'は、デバイスノードの名前を表します。

pmapを使用して、プロセスの仮想アドレス空間マッピング情報を表示することもできます。

1 [root@xxxxx mnt]# pmap -x 870 2 870: {no such process} ./user_1 3 Address Kbytes PSS Dirty Swap Mode Mapping 4 00008000 4 4 0 0 r-xp /mnt/user_1 5 00010000 4 4 4 0 rw-p /mnt/user_1 6 b6d75000 64 0 0 0 rw-s /dev/remap_pfn 7 b6d85000 1228 424 0 0 r-xp /lib/libc-2.18.so 8 b6eb8000 28 0 0 0 ---p /lib/libc-2.18.so 9 b6ebf000 8 8 8 0 r--p /lib/libc-2.18.so 10 b6ec1000 4 4 4 0 rw-p /lib/libc-2.18.so 11 b6ec2000 12 8 8 0 rw-p [ anon ] 12 b6ec5000 132 64 0 0 r-xp /lib/libgcc_s.so.1 13 b6ee6000 28 0 0 0 ---p /lib/libgcc_s.so.1 14 b6eed000 4 4 4 0 rw-p /lib/libgcc_s.so.1 15 b6eee000 128 122 0 0 r-xp /lib/ld-2.18.so 16 b6f13000 8 8 8 0 rw-p [ anon ] 17 b6f15000 4 4 4 0 r--p /lib/ld-2.18.so 18 b6f16000 4 4 4 0 rw-p /lib/ld-2.18.so 19 be8e9000 132 4 4 0 rw-p [stack] 20 bed1c000 4 0 0 0 r-xp [sigpage] 21 bed1d000 4 0 0 0 r--p [vvar] 22 bed1e000 4 0 0 0 r-xp [vdso] 23 ffff0000 4 0 0 0 r-xp [vectors] 24 -------- ------ ------ ------ ------ 25 total 1808 662 48 0

次に、user_2を実行します。

1 [root@xxxxx mnt]# ./user_2 2 I am ./user_1

user_1によって書かれた情報を見ることができます。以下は、カーネルログと仮想アドレス空間のマッピング情報です。

1 [ 2545.832903] client: user_2 (873) 2 [ 2545.833087] code section: [0x8000 0x87e0] 3 [ 2545.833178] data section: [0x107e0 0x10918] 4 [ 2545.833262] brk section: s: 0x11000, c: 0x11000 5 [ 2545.833346] mmap section: s: 0xb6fb5000 6 [ 2545.833423] stack section: s: 0xbea0ee20 7 [ 2545.833499] arg section: [0xbea0ef23 0xbea0ef2c] 8 [ 2545.833590] env section: [0xbea0ef2c 0xbea0eff3] 9 [ 2545.833761] phy: 0x8eb60000, offset: 0x0, size: 0x10000 10 [ 2545.833900] remap_pfn_mmap: map 0xeeb60000 to 0xb6e13000, size: 0x10000 11 12 [root@xxxxx mnt]# cat /proc/873/maps 13 00008000-00009000 r-xp 00000000 00:12 1179665 /mnt/user_2 14 00010000-00011000 rw-p 00000000 00:12 1179665 /mnt/user_2 15 b6e13000-b6e23000 rw-s 00000000 00:10 8765 /dev/remap_pfn 16 b6e23000-b6f56000 r-xp 00000000 b3:01 143 /lib/libc-2.18.so 17 b6f56000-b6f5d000 ---p 00133000 b3:01 143 /lib/libc-2.18.so 18 b6f5d000-b6f5f000 r--p 00132000 b3:01 143 /lib/libc-2.18.so 19 b6f5f000-b6f60000 rw-p 00134000 b3:01 143 /lib/libc-2.18.so 20 b6f60000-b6f63000 rw-p 00000000 00:00 0 21 b6f63000-b6f84000 r-xp 00000000 b3:01 188 /lib/libgcc_s.so.1 22 b6f84000-b6f8b000 ---p 00021000 b3:01 188 /lib/libgcc_s.so.1 23 b6f8b000-b6f8c000 rw-p 00020000 b3:01 188 /lib/libgcc_s.so.1 24 b6f8c000-b6fac000 r-xp 00000000 b3:01 165 /lib/ld-2.18.so 25 b6fb0000-b6fb3000 rw-p 00000000 00:00 0 26 b6fb3000-b6fb4000 r--p 0001f000 b3:01 165 /lib/ld-2.18.so 27 b6fb4000-b6fb5000 rw-p 00020000 b3:01 165 /lib/ld-2.18.so 28 be9ee000-bea0f000 rw-p 00000000 00:00 0 [stack] 29 beedf000-beee0000 r-xp 00000000 00:00 0 [sigpage] 30 beee0000-beee1000 r--p 00000000 00:00 0 [vvar] 31 beee1000-beee2000 r-xp 00000000 00:00 0 [vdso] 32 ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]

上記のログ情報を表示できます。 https://github.com/pengdonglin137/remap_pfn_demo/blob/master/log/user2

上記の空間マッピング情報によれば、以下の概略図が得られます。

4、user_3およびuser_4

関連するログ情報を表示できます。

https://github.com/pengdonglin137/remap_pfn_demo/blob/master/log/user3

https://github.com/pengdonglin137/remap_pfn_demo/blob/master/log/user4

以下は、user3を実行するためのログとマッピング情報です。

1 [ 4938.000918] client: user_3 (884) 2 [ 4938.001117] code section: [0x8000 0x87f4] 3 [ 4938.001205] data section: [0x107f4 0x1092c] 4 [ 4938.001281] brk section: s: 0x11000, c: 0x11000 5 [ 4938.001410] mmap section: s: 0xb6ff1000 6 [ 4938.001485] stack section: s: 0xbea10e20 7 [ 4938.001549] arg section: [0xbea10f23 0xbea10f2c] 8 [ 4938.001606] env section: [0xbea10f2c 0xbea10ff3] 9 [ 4938.001793] phy: 0x8eb70000, offset: 0x10000, size: 0x10000 10 [ 4938.001996] remap_pfn_mmap: map 0xeeb70000 to 0xb6e4f000, size: 0x10000 11 12 [root@xxxxx mnt]# 13 [root@xxxxx mnt]# cat /proc/884/maps 14 00008000-00009000 r-xp 00000000 00:12 1179666 /mnt/user_3 15 00010000-00011000 rw-p 00000000 00:12 1179666 /mnt/user_3 16 b6e4f000-b6e5f000 rw-s 00010000 00:10 8765 /dev/remap_pfn 17 b6e5f000-b6f92000 r-xp 00000000 b3:01 143 /lib/libc-2.18.so 18 b6f92000-b6f99000 ---p 00133000 b3:01 143 /lib/libc-2.18.so 19 b6f99000-b6f9b000 r--p 00132000 b3:01 143 /lib/libc-2.18.so 20 b6f9b000-b6f9c000 rw-p 00134000 b3:01 143 /lib/libc-2.18.so 21 b6f9c000-b6f9f000 rw-p 00000000 00:00 0 22 b6f9f000-b6fc0000 r-xp 00000000 b3:01 188 /lib/libgcc_s.so.1 23 b6fc0000-b6fc7000 ---p 00021000 b3:01 188 /lib/libgcc_s.so.1 24 b6fc7000-b6fc8000 rw-p 00020000 b3:01 188 /lib/libgcc_s.so.1 25 b6fc8000-b6fe8000 r-xp 00000000 b3:01 165 /lib/ld-2.18.so 26 b6fed000-b6fef000 rw-p 00000000 00:00 0 27 b6fef000-b6ff0000 r--p 0001f000 b3:01 165 /lib/ld-2.18.so 28 b6ff0000-b6ff1000 rw-p 00020000 b3:01 165 /lib/ld-2.18.so 29 be9f0000-bea11000 rw-p 00000000 00:00 0 [stack] 30 bebe9000-bebea000 r-xp 00000000 00:00 0 [sigpage] 31 bebea000-bebeb000 r--p 00000000 00:00 0 [vvar] 32 bebeb000-bebec000 r-xp 00000000 00:00 0 [vdso] 33 ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]

16行目に注意する必要があります。「00010000」はオフセットを意味し、サイズは64KBで、これはvma-> vm_pgoffの値です。

以下は、user_3とuser_4の共有メモリの概略図です。

5、user_5

User_5は、128KBカーネルバッファーを独自のユーザースペースにマッピングし、その内容をクリアする役割を果たします。

ログ情報を表示できます。 https://github.com/pengdonglin137/remap_pfn_demo/blob/master/log/user5

以下はマッピング図です。

つづく...