V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ChainLock
V2EX  ›  程序员

Linux 写时复制问题

  •  
  •   ChainLock · 76 天前 · 2024 次点击
    这是一个创建于 76 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int g=100;
    
    int main()
    {
    
    	static int x=666;
    
    	
    	pid_t pid = fork();
    
    
    	if (pid==-1){
    		printf("创建进程失败\r\n");
    	}
    
    	if (pid==0){
    		printf("pid=%d g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x);
    		g=111;
    		x=222;
    
    		printf("我子进程 pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x);
    	}else{
    
    		printf("pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x);
    		g=666;
    		x=777;
    		printf("我是父进程 pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x);
    		
    	}
    
    
    	return 0;
    }
    
    

    输出

    pid=4502,g=100,x=666,g=0x601044,x=0x601048
    我是父进程 pid=4502,g=666,x=777,g=0x601044,x=0x601048
    pid=0 g=100,x=666,g=0x601044,x=0x601048
    我子进程 pid=0,g=111,x=222,g=0x601044,x=0x601048
    

    我的问题是:

    子进程修改变量后,会重新开启一块新内存,再我重新修改变量值后,为什么在打印变量的地址还是相同的?

    21 条回复    2024-09-02 13:16:25 +08:00
    sagaxu
        1
    sagaxu  
       76 天前
    你打印的是虚拟内存的地址,不是物理内存,两个进程可以有完全一样的虚拟内存布局
    ChainLock
        2
    ChainLock  
    OP
       76 天前
    @sagaxu #1 父子虚拟内存地址一样,父子进程中各自修改数据,都不互相影响
    shijingshijing
        3
    shijingshijing  
       76 天前
    @ChainLock 打印出来的内存是虚拟内存地址,实际物理内存地址是 MMU 负责转换的。
    ChainLock
        4
    ChainLock  
    OP
       76 天前
    我纠结的点在于,父子进程第一次打印变量地址 相同,我可以理解,我在子进程里面都改变这个变量的值,再次打印还是相同的
    GeekGao
        5
    GeekGao  
       76 天前   ❤️ 2
    wkla
        6
    wkla  
       76 天前
    @ChainLock #4 进程内存空间互相独立,这点你得先整明白。然后物理内存(绝对地址)和虚拟内存映射的分页机制你得先整明白。

    写时复制可以在更底层做,没必要反应到进程虚拟内存上。你想象的就是写的时候,变量会开一个新地址放。事实上可能是这整个内存页在物理地址上不一样,也没必要变动进程的虚拟地址。
    dhb233
        7
    dhb233  
       76 天前
    对于内核来说,能让程序正常运行,还能让指针地址改变是更难的事情吧。。。
    写时拷贝是硬件提供的能力,fork 之后,设置页不可写,写的时候触发中断,复制一份就可以了
    heiher
        8
    heiher  
       76 天前 via Android
    COW 是为被写的虚拟地址创建了一个新的物理页,复制数据到上面,再映射到触发写进程的该虚拟页上。从始至终虚拟地址不变,物理地址改变。只打印虚拟地址当然看不出来啦。
    wxf666
        9
    wxf666  
       75 天前
    @dhb233 #7 请教一下,4GB 的程序被 fork 之后,系统会设置 100W 页不可写吗?(假设 4KB/页)
    ho121
        10
    ho121  
       75 天前 via Android
    写时复制是在比进程这个层级更底层中做的,对进程这一层是透明的。
    你想想系统怎么可能随时改变进程内的状态(比如某变量的地址),那不就乱套了。
    况且程序中拿到的地址是虚地址,不是物理内存的地址。写时复制是在物理内存级别做的。

    同样的可以参考一下文件系统的写时复制,写时复制是在文件系统层面做的,对文件本身是透明不可见的。不管何时写时复制,对于文件来说,文件还是那个文件,内容不会因为写时复制而改变,只是磁盘上的分布变了。
    yanqiyu
        11
    yanqiyu  
       75 天前 via Android
    @wxf666 是,除非是 mmap 的 shared 这种情况,之外父子进程都会看到 ro 的页面,等写入的时候中断介入复制
    r46mht
        12
    r46mht  
       75 天前
    @wxf666 概念上是这样的,实际的操作不需要 100w 页一个一个设置。x86 的页表是一个类似于字典树的结构,在父节点上标不可写相当于一次性设置了很多连续的页不可写
    ChainLock
        13
    ChainLock  
    OP
       75 天前
    @wkla #6 感谢佬
    ChainLock
        14
    ChainLock  
    OP
       75 天前
    @GeekGao #5 感谢佬
    ChainLock
        15
    ChainLock  
    OP
       75 天前
    @heiher #8 感谢佬,大彻大悟
    ChainLock
        16
    ChainLock  
    OP
       75 天前
    @ho121 #10 我一直把虚地址,物理内存地址搞混了,要想完全搞懂,得去看 Linux 源码了
    ChainLock
        17
    ChainLock  
    OP
       75 天前
    @r46mht #12 要想完全搞明白这些,只能去啃 linux 源码 ,我太菜了
    dhb233
        18
    dhb233  
       75 天前
    @wxf666 #9 具体的实现不是很清楚。。。至少 fork 的时候,新的进程的页表就是要全创建出来啊,有 100w 个页就要创建 100w 个页表
    ho121
        19
    ho121  
       75 天前
    @ChainLock 看源码不至于,看操作系统原理就行
    vituralfuture
        20
    vituralfuture  
       75 天前 via Android
    给楼主提供几个意见,这些是操作系统的知识,中国大学 mooc 上找个课程,买几本书,现代操作系统,操作系统导论等经典书籍看一看,然后找国外大学是公开课程跟着做个实验,就能理解了。linux 内核源码很难读,如果不是内核开发,就没有读的必要,里面很多奇技淫巧➕各种历史遗留。操作系统的理论很早就有了,到现在已经非常完善了,所谓各种内核只不过是将这些理论落地而已,实现不重要,背后的思想设计才是精华
    ChainLock
        21
    ChainLock  
    OP
       75 天前
    @vituralfuture #20 好的,佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1589 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:10 · PVG 01:10 · LAX 09:10 · JFK 12:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.