如何知道写入时复制页面是否是实际的副本?

当我使用mmap创build写时复制映射(MAP_PRIVATE)时,只要我写入特定地址,该映射的某些页面就会被复制。 在我的程序中的某个时刻,我想知道哪些页面实际上已被复制。 有一个叫'mincore'的调用,但是只会报告页面是否在内存中,这与正在被复制的页面是不一样的。

有什么方法可以确定哪些页面已被复制?

Solutions Collecting From Web of "如何知道写入时复制页面是否是实际的副本?"

好,根据MarkR的建议,我给了它一个通过pagemap和kpageflags接口的机会。 下面是一个快速测试,检查一个页面是否在被称为“SWAPBACKED”的内存中。 当然还有一个问题,那就是kpageflags只能被root访问的问题。

int main(int argc, char* argv[]) { unsigned long long pagesize=getpagesize(); assert(pagesize>0); int pagecount=4; int filesize=pagesize*pagecount; int fd=open("test.dat", O_RDWR); if (fd<=0) { fd=open("test.dat", O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); printf("Created test.dat testfile\n"); } assert(fd); int err=ftruncate(fd,filesize); assert(!err); char* M=(char*)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_PRIVATE,fd,0); assert(M!=(char*)-1); assert(M); printf("Successfully create private mapping\n"); 

测试设置包含4页。 第0页和第2页很脏

  strcpy(M,"I feel so dirty\n"); strcpy(M+pagesize*2,"Christ on crutches\n"); 

第3页已阅读。

  char t=M[pagesize*3]; 

第1页将不被访问

页面映射文件将进程的虚拟内存映射到实际页面,稍后可以从全局kpageflags文件中检索页面。 读取文件/usr/src/linux/Documentation/vm/pagemap.txt

  int mapfd=open("/proc/self/pagemap",O_RDONLY); assert(mapfd>0); unsigned long long target=((unsigned long)(void*)M)/pagesize; err=lseek64(mapfd, target*8, SEEK_SET); assert(err==target*8); assert(sizeof(long long)==8); 

在这里,我们阅读每个虚拟页面的页面框架编号

  unsigned long long page2pfn[pagecount]; err=read(mapfd,page2pfn,sizeof(long long)*pagecount); if (err<0) perror("Reading pagemap"); if(err!=pagecount*8) printf("Could only read %d bytes\n",err); 

现在我们要读取每个虚拟框架的实际页面标志

  int pageflags=open("/proc/kpageflags",O_RDONLY); assert(pageflags>0); for(int i = 0 ; i < pagecount; i++) { unsigned long long v2a=page2pfn[i]; printf("Page: %d, flag %llx\n",i,page2pfn[i]); if(v2a&0x8000000000000000LL) // Is the virtual page present ? { unsigned long long pfn=v2a&0x3fffffffffffffLL; err=lseek64(pageflags,pfn*8,SEEK_SET); assert(err==pfn*8); unsigned long long pf; err=read(pageflags,&pf,8); assert(err==8); printf("pageflags are %llx with SWAPBACKED: %d\n",pf,(pf>>14)&1); } } } 

总而言之,我并不是特别满意这种方法,因为它需要访问我们通常无法访问的文件,而且它很复杂(如何简单地调用内核来检索页面标记?)。

我通常使用mprotect将我的跟踪的写时复制页面设置为只读,然后通过将给定页面标记为脏并启用写入来处理生成的SIGSEGV。

这是不理想的,但开销是相当易于管理的,它可以结合使用mincore等进行更复杂的优化,如管理您的工作集大小或近似指针信息的页面,你期望换出,这可以让运行时系统与内核协作,而不是与之竞争。

这并不容易,但可以确定这一点。 为了找出一个页面是否是另一个页面的副本(可能是另一个进程的),那么你需要执行以下操作(最近的内核):

  1. 阅读/ proc / pid / pagemap中的条目以获取流程中的相应页面(es)
  2. 询问/ proc / kpageflags

然后你可以确定两个页面在内存中实际上是相同的页面。

做到这一点相当棘手,你需要成为根,不管你做什么可能会有一些竞争条件,但这是可能的。

写入时复制是使用虚拟存储器硬件的存储器保护方案实现的。

当写入只读页面时,会发生页面错误。 页面错误处理程序检查页面是否带有写入时复制标志:如果是,则分配新页面,旧页面的内容并复制,并重试写入。

新页面既不是只读也不是写时复制,原始页面的链接已完全中断。

所以你所要做的就是测试页面的内存保护标志。

在Windows上,API是GetWorkingSet ,请参阅GetWorkingSet的说明。 我不知道相应的linux API是什么。

我给了一个有类似目标的人的答案,并引用了一个类似于你的问题。

我认为当这两个想法结合在一起时,bmargulies对这个问题的回答完全符合你所需要的。

我不记得这样的API被输出。 你为什么要做这样的事情(你解决问题的根源是什么?)

你可能想看看/ proc / [pid] / smaps(它提供了一个有点详细的页面使用/复制/存储统计)。

再一次,你为什么要这样做? 如果您确定这种方法是唯一的(通常使用和忘记虚拟内存),您可能需要考虑编写一个处理此类功能的内核模块。