You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

devmem2无法写入指定地址,修改进程变量失败问题咨询

为什么devmem2无法修改进程的内存页?

我尝试用devmem2修改某进程中变量的内容,编写了如下C程序:

static inline __attribute__ ((always_inline))
void clflush(volatile void *p)
{
    asm volatile ("clflush (%0)"::"r" (p):"memory");
}

uint64_t get_pfn(uint64_t entry) {
    return ((entry) & 0x3fffffffffffff);
}

uint64_t get_phys_addr(uint64_t v_addr) 
{
    uint64_t entry; 
    uint64_t offset = (v_addr/4096) * sizeof(entry);
    uint64_t pfn; 
    int fd = open("/proc/self/pagemap", O_RDONLY);
    assert(fd >= 0);
    int bytes_read = pread(fd, &entry, sizeof(entry), offset);
    close(fd);
    assert(bytes_read == 8);
    assert(entry & (1ULL << 63));
    pfn = get_pfn(entry);
    assert(pfn != 0);
    return (pfn*4096) | (v_addr & 4095);

}

int main(){
int a = 44;

uint64_t v_addr = (uint64_t) &a;
printf("%lx\n",v_addr);
uint64_t p_addr = get_phys_addr(v_addr);
printf("%lx\n",p_addr); 
clflush(&a);
sleep(20);

int b = a;
printf("value of b: %d\n",b);
printf("value of a: %d\n",a);
uint64_t v_addr2 = (uint64_t) &b;
printf("%lx\n",v_addr2);
uint64_t p_addr2 = get_phys_addr(v_addr2);
printf("%lx\n",p_addr2);    

}

程序运行时输出变量a的物理地址为17580fab8,我在另一个终端执行命令:

sudo devmem2 -x17580fab8 w 0x1235

devmem2输出显示写入成功:

/dev/mem opened.
Memory mapped at address 0x7f464d50c000.
Value at address 0x0 (0x7f464d50c000): 0x1235
Written 0x1235; readback 0x1235

但sleep结束后,变量a和b的值并未变为0x1235。devmem2宣称可读写任意内存地址,为何无法修改进程的内存页?


问题原因分析

1. 编译器优化导致寄存器缓存变量值

代码中变量a未声明为volatile,编译器会进行优化:将a的初始值44存储到CPU寄存器中,sleep后读取a时直接使用寄存器中的旧值,不会重新从内存中加载修改后的内容。这就导致即使devmem2修改了物理内存中的值,进程也无法感知到。

2. 物理地址可能已失效

在sleep的20秒内,内核可能因内存压力触发页交换、内存压缩等机制,将变量a所在的物理页换出到磁盘。当进程恢复运行时,内核会重新分配新的物理页加载该数据,此时之前获取的物理地址已经不再对应变量a的当前内存页,devmem2写入的内容自然无法被进程读取。


解决方案

1. 阻止编译器优化

将变量a声明为volatile,强制编译器每次读取都从内存中获取值,避免寄存器缓存优化:

volatile int a = 44;

2. 锁定物理内存页

使用mlock将变量a所在的内存页锁定在物理内存中,防止内核将其换出。需要添加头文件<sys/mman.h>和<stdlib.h>,并修改main函数:

#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>

// ... 其他函数保持不变 ...

int main(){
volatile int a = 44;

// 锁定内存页,需要CAP_IPC_LOCK权限,运行时需加sudo
if (mlock(&a, sizeof(a)) == -1) {
    perror("mlock failed");
    exit(EXIT_FAILURE);
}

uint64_t v_addr = (uint64_t) &a;
printf("%lx\n",v_addr);
uint64_t p_addr = get_phys_addr(v_addr);
printf("%lx\n",p_addr); 
clflush(&a);
sleep(20);

int b = a;
printf("value of b: %d\n",b);
printf("value of a: %d\n",a);
uint64_t v_addr2 = (uint64_t) &b;
printf("%lx\n",v_addr2);
uint64_t p_addr2 = get_phys_addr(v_addr2);
printf("%lx\n",p_addr2);    

// 解锁内存页
munlock(&a, sizeof(a));
}

运行程序时需要使用sudo,因为mlock需要特殊权限。


内容的提问来源于stack exchange,提问作者xdxdxd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 09:15:35