Linux下共享对象(.so)代码段未在进程间共享的原因探究
为什么修改共享对象代码段不会影响其他进程实例?
核心原因:写时复制(Copy-On-Write,COW)机制
共享库的.text段确实会被多个进程只读共享物理内存页,但当你通过mprotect()修改页权限并写入数据时,操作系统会触发写时复制机制:
- 当进程尝试写入原本只读的共享页时,内核会为该进程复制一份物理页的私有副本
- 后续该进程的读写操作都针对这份私有副本,其他进程仍使用原始的共享物理页
- 这就导致你的修改只对当前进程生效,完全不会影响其他进程
你的代码中的关键细节
mprotect()的作用:你将页权限修改为PROT_READ | PROT_WRITE | PROT_EXEC,打破了原本的只读属性,为写时复制的触发创造了条件- 写入操作触发COW:当执行
*ip = (int)time(NULL);时,内核检测到对只读共享页的写入,立刻复制私有页,后续该进程的getval()读取的是自己私有页里的数值
额外补充
- 共享库的.text段默认是只读的,设计目的就是让多个进程共享同一份物理内存以节省资源
- 如果要实现进程间共享可修改的数据,应该使用共享内存段(如
shmget/mmap带MAP_SHARED)或者其他进程间通信(IPC)机制
复现代码
libmy.c
int getval() { return 123; }
main.c
#include <stdio.h> #include <time.h> #include <unistd.h> #include <stdint.h> #include <stdlib.h> #include <sys/mman.h> int getval(); void unprotect(uint64_t addr) { uint64_t pagesize = sysconf(_SC_PAGE_SIZE); addr -= addr % pagesize; if (mprotect((void*)addr, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { printf("mprotect failed\n"); exit(1); } } int main() { for (int i = 0; i < 30; i++) { int* ip = (int*)((char*)&getval + i); if (*ip == 123) { printf("found value at offset %i\n", i); unprotect((uint64_t)ip); unprotect((uint64_t)ip + 3); *ip = (int)time(NULL); printf("value changed successfully\n"); break; } } while (1) { printf("getval() returns %i\n", getval()); sleep(1); } }
run.bash
#!/bin/bash set -e gcc -shared -fPIC -o libmy.so libmy.c gcc main.c -L. -lmy -Wl,-rpath . -o bin ./bin & sleep 2 ./bin
内容的提问来源于stack exchange,提问作者zomega
相关产品推荐
相关产品推荐

