给一个Linux向一个page写内容的c代码demo

以下是一个在 Linux 中使用 C 语言向内存页写入内容的示例代码。该程序通过 mmap 分配一个内存页,向其中写入数据,并演示基本操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main() {
// 获取系统内存页大小(通常为4096字节)
long page_size = sysconf(_SC_PAGESIZE);
if (page_size == -1) {
perror("sysconf() failed");
return EXIT_FAILURE;
}

// 使用 mmap 分配一个可读写内存页(匿名映射,不关联文件)
void *page = mmap(
NULL, // 由系统选择地址
page_size, // 分配一页大小
PROT_READ | PROT_WRITE, // 可读可写
MAP_ANONYMOUS | MAP_PRIVATE, // 匿名映射,私有区域
-1, // 文件描述符(此处无文件)
0 // 偏移量
);

if (page == MAP_FAILED) {
perror("mmap() failed");
return EXIT_FAILURE;
}

printf("成功分配内存页,地址: %p\n", page);

// 写入内容到内存页
const char *message = "Hello, Linux Page!";
size_t msg_len = strlen(message) + 1; // 包含终止符

// 确保不超过页大小
if (msg_len > page_size) {
fprintf(stderr, "消息过长,超出页大小\n");
munmap(page, page_size);
return EXIT_FAILURE;
}

memcpy(page, message, msg_len); // 将消息复制到内存页

// 验证写入的内容
printf("内存页内容: %s\n", (char *)page);

// 可选:填充整个页(例如用'A'填充)
// memset(page, 'A', page_size);

// 释放内存页
if (munmap(page, page_size) == -1) {
perror("munmap() failed");
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}

关键步骤说明:

  1. 获取页大小:使用 sysconf(_SC_PAGESIZE) 动态获取系统内存页大小(通常为 4096 字节)。
  2. 内存映射:通过 mmap 分配匿名内存页,设置 PROT_READ | PROT_WRITE 权限允许读写,MAP_ANONYMOUS 表示不关联文件。
  3. 写入数据:使用 memcpy 将字符串复制到内存页,确保不超过页大小。
  4. 释放内存:使用 munmap 释放映射的内存页。

编译与运行:

1
2
gcc page_write.c -o page_write
./page_write

输出示例:

1
2
成功分配内存页,地址: 0x7f8c5e2e6000
内存页内容: Hello, Linux Page!

注意事项:

  • 匿名映射:此示例使用 MAP_ANONYMOUS,内存页不关联到文件,仅存在于进程内存中。
  • 错误处理:检查 mmap 返回值是否为 MAP_FAILED,并处理可能的错误(如内存不足)。
  • 页大小适配:代码动态获取页大小,确保在不同系统上兼容。
  • 内存安全:写入数据时需确保不越界,避免内存损坏。

如需操作文件映射页,可修改 mmap 参数,传入文件描述符并移除 MAP_ANONYMOUS 标志。

给一个Linux内核态,向page写内容的c代码demo

以下是一个简单的 Linux 内核模块示例,演示在内核态向内存页写入内容的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>

#define MESSAGE "Hello from Kernel Space!"

static int __init page_writer_init(void)
{
struct page *page;
char *virt_addr;

// 分配一个物理页 (GFP_KERNEL 表示正常分配)
page = alloc_page(GFP_KERNEL);
if (!page) {
pr_err("Failed to allocate page\n");
return -ENOMEM;
}

// 将物理页映射到内核虚拟地址空间
virt_addr = kmap(page);
if (!virt_addr) {
pr_err("Failed to map page\n");
__free_page(page);
return -EFAULT;
}

// 写入数据到页面
strncpy(virt_addr, MESSAGE, strlen(MESSAGE) + 1);
pr_info("Written to page: %s\n", virt_addr);

// 解除映射
kunmap(page);

// 释放页面(实际生产环境可能需要保持页面有效)
__free_page(page);

return 0;
}

static void __exit page_writer_exit(void)
{
pr_info("Module unloaded\n");
}

module_init(page_writer_init);
module_exit(page_writer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Kernel page writing demo");

关键点说明:

  1. 内存分配

    • 使用 alloc_page(GFP_KERNEL) 分配单个物理页
    • GFP_KERNEL 标志允许在需要时触发直接内存回收
  2. 地址映射

    • kmap() 将物理页映射到内核虚拟地址空间
    • 对于高端内存(32位系统),可能需要特殊处理
    • 使用后必须用 kunmap() 解除映射
  3. 数据写入

    • 使用内核的 strncpy 进行安全拷贝
    • 写入长度不应超过 PAGE_SIZE (通常4096字节)
  4. 资源释放

    • 必须显式释放分配的页面
    • 实际驱动中可能需要保持页面长期有效

编译与测试:

  1. 创建 Makefile:

    1
    2
    3
    4
    5
    obj-m += page_writer.o
    all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  2. 编译和加载模块:

    1
    2
    3
    4
    make
    sudo insmod page_writer.ko
    dmesg | tail # 查看输出
    sudo rmmod page_writer

输出示例:

1
2
[ 1234.567890] Written to page: Hello from Kernel Space!
[ 1234.567891] Module unloaded

注意事项:

  1. 内存管理

    • 内核态没有自动内存回收机制
    • 必须严格配对 alloc_page/__free_pagekmap/kunmap
  2. 并发安全

    • 在多核环境下需考虑锁机制(如 spin_lock
    • 本示例未包含同步机制,实际使用时需根据场景添加
  3. 地址类型

    • 物理地址使用 page_to_phys(page) 转换
    • 虚拟地址映射后的有效性仅限于当前上下文
  4. 错误处理

    • 必须检查所有可能失败的操作
    • 使用 pr_err 输出错误信息到系统日志

该代码展示了内核态内存操作的基本模式,实际使用时需要根据具体需求添加同步机制、错误恢复等功能。对于高性能场景,可能需要使用 kmap_atomic 等原子映射方法。