精华内容
下载资源
问答
  • 写时复制机制
    千次阅读
    2014-07-19 11:24:08

          一个页面被多个进程共享,每当一个进程产生一次写保护 错误,内核将给进程分配一个新的物理页面,将共享页面的内容复制过来,新的页面将设置为可读写,而共享页面仍然是只读的,只是共享计数减小了。当其他共享进程都产生了一次写保护错误后,共享页面的共享计数减成了1,其实就是被一个进程独占了,但此时该共享页面仍然是只读的,如果独占它的进程对它进行写操作仍然会产生写保护出错。为什么不在共享计数减成了 1 之后就将共享页面置为可写呢?原因很简单,因为系统并不知道最后是哪个页表项指向这个共享页,如果要把它查找出来会有很大的系统开销,这是中断处理程序应当尽量避免的,所以采用了以逸待劳的办法。如果当初共享的页面不属于主内存块,在共享时就没有作共享计数的处理,就不存在共享计数的问题,直接复制就可以了。

          以上这段话是出自Linux0.11内存管理分析的一本书上的,在这个网址下载的:http://oldlinux.org/Linux.old/study/Ref-docs/Linux011-Mem-YuanYi.pdf。基本完全讲述了linux0.11内存管理的内容。之前写过一篇博客分析linux0.11内核些事复制机制:http://blog.csdn.net/yihaolovem/article/details/34204265。现在看来还有很多错误,故重新分析一下对内核写时复制机制的理解。

           写时复制机制,即是先通过系统调用fork使多个进程共享同一个进程的目录表项和页目录项,从而共享同一物理内存(可以这么说吧,准确来说是不对的)。假设有一个物理页面M,通过fork后由进程A、B、C(都是用户进程)共享。假设M一开始属于A。若C要写M,则会产生写时复制,重新在主内存申请一页内存newC替代M(newC页面内的内容完全复制自M)供C使用,此时,M由A、B进程共享使用。此后,进程A要对M进行写操作,又会发生写时复制,重新申请一页内存newA给A使用。这时,M仅由进程B独占了。但是,此时若B进程对M进行写操作,则同样会重新申请newB供B使用。那么,开始的M呢?呵呵,被释放了。因为A B C进程读写M的顺序不一定,所以最后一次写M的进程是不确定的,可能是B(所上如上分析过程),也可能是A,还可能是C。所以正如开头一段话所述,如果要查出最后是谁独占M的,从而改变其页表项的读写位,则要对页目录项表和页表项进行顺序遍历查找,是很费效率的。所以才用了以逸待劳的方法。

           以上就是写时复制机制的全过程。

           但是,对于内核空间来说,却是不同的---内核空间不使用写时复制机制!这也是Linus在其内核代码注释中提到的,一开始很不理解。但读了开头第一段话之后便慢慢地理解了。对于内核空间来说,还是以特殊的进程0和进程1来说。进程0是系统中第一个手工创建的程序,其特殊在其地址空间属于内核空间,也就是说,进程0是内核空间的物理页面。系统通过fork函数产生了进程1,此时进程0和1共享内核物理页面(假设为KM)。但是其特殊就特殊在在fork时,内核针对内核空间是特殊对待的,在fork函数过程中调用的函数copy_page_tables中有这样一段代码:

           if (this_page > LOW_MEM) {
         *from_page_table = this_page;
         this_page -= LOW_MEM;
         this_page >>= 12;
         mem_map[this_page]++;
    }

           也就是说,只有对于非内核地址空间的页面,才会将被fork共享的页面所对应的页表项设为只读,从而在最后一次写操作的时候,将源页面释放。而对于内核地址空间的地址共享,只将进程1的页目录的属性设置为只读,而源目录表项(进程0)依然是可读写的。这就导致进程1fork进程0而产生之后,只有进程1对共享的物理页面(内核地址空间)进行写操作的时候才会产生写时复制,为进程1在主内存中申请一页新的物理页面作为独属于进程1的物理页面。而进程0对其共享内存的写操作不会引起写时复制,即KM就好像独属于进程0一样。也正是因为如此,所以要保证在进程1写(拥有独属于自己的内存页面)之前保持KM页面的干净,主要是为了保证进程1的堆栈中不含进程0的多余信息,以免弄乱进程1的堆栈空间。

          总体来说,就是"内核空间不使用写时复制机制"的意思就是,对于内核空间的写操作,并不会去申请新的内存页面,即内核空间这种情况下是可读可写的。

           又写了一篇博客,感觉对这块内容完全理解了,呵呵。

    更多相关内容
  • 主要介绍了PHP中copy on write写时复制机制介绍,需要的朋友可以参考下
  • Linux-Copy On Write写时复制机制初探

    千次阅读 2020-05-08 10:55:33
    现在Linux的fork()使用写时拷贝页来实现新进程的创建,它是一种可推迟甚至避免数据拷贝的技术,刚开始内核并不会复制整个地址空间,而是让父子进程共享地址空间,只有在写时复制地址空间,使得父子进程都拥有...

    在这里插入图片描述

    生猛干货

    从系统安装到程序员必备的Linux技能,还原真实工作场景,手把手带你实战演练

    在这里插入图片描述


    COW概述

    来看下 https://en.wikipedia.org/wiki/Copy-on-write的说明

    Copy-on-write (COW), sometimes referred to as implicit sharing[1] or shadowing,[2] is a resource-management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources.[3] If a resource is duplicated but not modified, it is not necessary to create a new resource; the resource can be shared between the copy and the original. Modifications must still create a copy, hence the technique: the copy operation is deferred to the first write. By sharing resources in this way, it is possible to significantly reduce the resource consumption of unmodified copies, while adding a small overhead to resource-modifying operations.

    写入时复制(COW),有时也称为隐式共享,是一种计算机管理中用来有效地对可修改资源执行“复制”操作的资源管理技术。

    如果资源重复但未修改,则无需创建新资源,资源可以在副本和原始副本之间共享。

    修改仍然必须创建一个副本,因此使用COW,可以将复制操作推迟到第一次写入。

    通过以这种方式共享资源,可以显着减少未修改副本的资源消耗,当然了资源修改操作的时候也会增加少量开销。

    在这里插入图片描述

    简单来说 COW 写时复制是提高资源使用效率的一种手段, 在内存管理(进程的 fork),数据存储( 比如 Docker 的 AUFS 文件系统),软件开发(Java的Copy On Write容器)、高可用HA软件中广泛使用。


    *Unix

    在传统的Unix环境下,有两个基本的操作用于创建和修改进程:

    • 函数fork( )用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝

    • 函数族exec( )用来启动另外的进程以取代当前运行的进程(函数族exec( )是一组函数的统称, 它包括了execl()、execlp()、execv()、execle()、execve()、execvp()等)

    fork

    fork是类Unix操作系统上创建进程的主要方法,fork用于创建子进程。

    新的进程要通过老的进程复制自身得到,Linux下init进程是所有进程的父 。 Linux的进程都通过init进程或init的子进程fork(vfork)出来的

    
    #include <unistd.h>  
    #include <stdio.h>  
     
    int main ()   
    {   
        pid_t fpid; //fpid表示fork函数返回的值  
        int count=0;
    	
    	// 调用fork,创建出子进程  
        fpid=fork();
    
    	// 所以下面的代码有两个进程执行!
        if (fpid < 0)   
            printf("创建进程失败!/n");   
        else if (fpid == 0) {  
            printf("我是子进程,由父进程fork出来/n");   
            count++;  
        }  
        else {  
            printf("我是父进程/n");   
            count++;  
        }  
        printf("统计结果是: %d/n",count);  
        return 0;  
    }  
    
    

    输出结果

    
    我是子进程,由父进程fork出来
    
    统计结果是: 1
    
    我是父进程
    
    统计结果是: 1
    
    
    

    fork 函数会有两次返回 1) 将子进程的PID返回给父进程,2) 0返回给子进程。(如果小于0,则说明创建子进程失败)。

    当前进程调用fork(),会创建一个跟当前进程完全相同的子进程(除了pid),所以子进程同样是会执行fork()之后的代码。

    故: 父进程在执行if代码块的时候,fpid变量的值是子进程的pid,子进程在执行if代码块的时候,fpid变量的值是0


    函数族exec( )

    在Linux中要使用exec函数族。系统调用execve()对当前进程进行替换,替换者为一个指定的程序,其参数包括文件名(filename)、参数列表(argv)以及环境变量(envp)。

    exec函数族不止一个,但它们大致相同,在 Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp。

    一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。

    在这里插入图片描述


    为什么有了COW?

    早期的 Unix 在实现 fork 系统调用时,并没有使用该技术,创建新进程的开销很大。

    出于效率考虑,Copy On Write 技术引入到进程中,fork 之后的父进程和子进程完全共享数据段、代码段、堆和栈等的完全副本。

    Linux在使用fork()函数进程创建时,传统fork()的做法是系统把所有的资源复制给新创建的进程,这种方式不仅单一,而且效率低下。因为所拷贝的数据或别的资源可能是可以共享的。现在Linux的fork()使用写时拷贝页来实现新进程的创建,它是一种可推迟甚至避免数据拷贝的技术,刚开始时内核并不会复制整个地址空间,而是让父子进程共享地址空间,只有在写时才复制地址空间,使得父子进程都拥有独立的地址空间,即资源的复制是在只有需要写入时才会发生,因此而称之为Copy on Write(COW)。

    在此之前都是以读的方式去和父进程共享资源,这样,在页根本不会被写入的场景下,fork()立即执行exec(),无需对地址空间进行复制,fork()的实际开销就是复制父进程的一个页表和为子进程创建一个进程描述符,也就是说只有当进程空间中各段的内存内容发生变化时,父进程才将其内容复制一份传给子进程,大大提高了效率。

    通俗来说 fork创建出的子进程,与父进程共享内存空间。 如果子进程不对内存空间进行写入操作的话,内存空间中的数据并不会复制给子进程,这样创建子进程的速度就很快 ,因为不用复制,直接引用父进程的物理空间 ,并且如果在fork函数返回之后,子进程第一时间exec一个新的可执行映像,那么也不会浪费时间和内存空间了 。


    COW 原理

    fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。


    COW的优缺点

    优点

    • COW技术可减少分配和复制大量资源时带来的瞬间延时。
    • COW技术可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制。

    缺点

    • 如果在fork()之后,父子进程都还需要继续进行写操作,那么会产生大量的分页错误(页异常中断page-fault),这样就得不偿失。

    小结

    fork出的子进程共享父进程的物理空间,当父子进程有内存写入操作时,read-only内存页发生中断,将触发的异常的内存页复制一份(其余的页还是共享父进程的)。

    fork出的子进程功能实现和父进程是一样的。如果有需要, 会调用exec()把当前进程映像替换成新的进程文件,完成自定义的功能。


    参考:
    维基百科-Copy-on-write
    COW奶牛!Copy On Write机制了解一下


    搞定Linux核心技术

    在这里插入图片描述

    展开全文
  • Copy-on-Write机制简称COW,是一种并发设计策略。其基本思路是多线程同时共享同一个内容,当某个线程想要修改这个内容的时候,才会真正的把内容copy出去形成一个新的内容...参考:写时复制机制 redis快照持久化(bg...

    Copy-on-Write机制简称COW,是一种并发设计策略。其基本思路是多线程同时共享同一个内容,当某个线程想要修改这个内容的时候,才会真正的把内容copy出去形成一个新的内容然后修改,其它的线程继续读旧的内容,直到修改完成。这是一种延时懒惰策略。
    Copy-on-Write有那么几个应用场景:

    • linux系统中内存的管理和分配。参考:写时复制机制
    • redis快照持久化(bgm)时,会fork出一个子进程进行持久化操作,当主进程接收到写操作时,会copy出一份新的内存,对新的内存进行写操作。
    • jdk1.5引入了juc包下的CopyOnWriteArrayList和CopyOnWriteArraySet。

    我们这里来看下CopyOnWriteArrayList源码。在向ArrayList中添加元素时,是要加锁的,否则多线程写就会Copy出N个副本出来。

    public class CopyOnWriteArrayList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        //写时需要加锁
        final transient ReentrantLock lock = new ReentrantLock();
    
        //在修改之后需要保证其他读线程能立刻读到新数据
        private transient volatile Object[] array;
    
        final Object[] getArray() {
            return array;
        }
    
        final void setArray(Object[] a) {
            array = a;
        }
        //增加元素时需要加锁
        public boolean add(E e) {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                Object[] elements = getArray();
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len + 1);  //复制出一份新的数组,长度加一
                newElements[len] = e;  //把新元素加在末尾
                setArray(newElements);  //引用改为新建的副本数组
                return true;
            } finally {
                lock.unlock();
            }
        }
        //获取数组中的元素,一律从旧的数组中读
        public E get(int index) {
            return get(getArray(), index);
        }
    }

    存在问题:

    在进行写时复制时,会存在两份内存,将会占用两倍的内存空间,所以需要对内存进行精确的统计,要保证系统内存不会溢出。

    展开全文
  • 写入时复制(Copy-on-write)机制

    千次阅读 多人点赞 2019-03-13 11:18:00
    写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的...

    含义:
    写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。


    应用案例:
    Copy-On-Write在string类中的应用体现:
     

    #include<stdio.h>
    #include<string>
    using namespace std;

    main()
    {
           string str1 = "hello world";
           string str2 = str1;

           printf ("Sharing the memory:\n");
           printf ("str1 address: %x\n", str1.c_str() );
           printf ("str2 address: %x\n", str2.c_str() );

           printf ("After Copy-On-Write:\n");
           str1[1]='q';
           str2[1]='w';

           printf ("str1's address: %c\n", str1[0]);
           printf ("str2's address: %c\n", str1[1] );
           printf ("str1's address: %x\n", str1.c_str() );
           printf ("str2's address: %x\n", str2.c_str() );

           return 0;
    }
     
    代码中str2用str1构造,此时两个对象共享同一块内存addr1。当执行str1[1]='q';时,string类的cow机制就体现出来了,在该过程中会copy一个副本给str1,地址为addr2,让其修改str1[1]='q';,修改后内存值为str2为"hqllo world"。但是str2内存地址仍然不变,地址中存的东西也不变。

    [ss@xx mytest]$ g++ cowTest.cpp -g -o cowTest
    [ss@xx mytest]$ ./cowTest
    Sharing the memory:
    str1 address: 5e6d028
    str2 address: 5e6d028
    After Copy-On-Write:
    str1's address: h
    str2's address: q
    str1's address: 5e6d058
    str2's address: 5e6d028
     
    从结果中我们可以看到,在开始的两个语句后,str1和str2存放数据的地址是一样的,而在修改内容后,str1的地址发生了变化,而str2的地址还是原来的。这就是string类实现的Copy-On-Write技术。
     

    展开全文
  • 写时复制(Copy On Write)

    万次阅读 多人点赞 2019-07-03 14:41:12
    执行BGSAVE命令或者BGREWRITEAOF命令的过程中,Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子进程存在期间,服务器会提高负载因子的阈值...
  • phpCOW机制(写时复制)

    万次阅读 2018-03-27 15:17:55
    写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入才真正复制一份内存进行修改。 COW最早应用在*nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C++的STL等。 在PHP内核中,...
  • 50-写时复制COW机制

    千次阅读 2016-04-26 12:30:07
    50-写时复制COW机制写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入才真正复制一份内存进行修改。 COW最早应用在*nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C++的STL...
  • 写时复制机制 为了节约物理内存,在调用fork()生成新进程,新进程与原进程会共享同一内存区.只有当其中一进程进行操作,系统才会为其另外分配内存页面.这就是写时复制机制(copy on write)的意思. 当...
  • 进程共享(读共享写时复制

    千次阅读 2019-03-25 17:15:51
    父子进程之间在刚fork后。父子相同处: 全局变量、.data、.bbs、.text、栈、堆、环境变量、用户ID、宿主目录(进程用户家目录)、进程工作目录、信号处理方式等等,...似乎,子进程复制了父进程0-3G用户空间内容,以...
  • 彻底理解 fork 之写时复制 《一》

    千次阅读 2018-12-29 11:31:33
    彻底理解fork之写时复制&amp;lt;一&amp;gt; 一直以来都对操作系统都比较感兴趣,这篇文章呢就主要研究一下当我们调用fork系统掉用所用到的写时复制技术(copy-on-write)。 下图是fork系列函数的调用过程 &...
  • linux进程fork——写时复制

    千次阅读 2018-08-12 15:45:41
    fork到底复制了父进程的哪些资源? 我们来看一个例子 #include&amp;amp;amp;lt;stdio.h&amp;amp;amp;gt; #include&amp;amp;amp;lt;stdlib.h&amp;amp;amp;gt; #include&amp;amp;amp;lt;...
  • Linux内核学习---写时复制(COW)技术

    千次阅读 2020-06-02 11:20:25
    Linux内核学习—写时复制(COW)技术 写时复制技术(copy-on-write) 传统的Unix系统以一种比较统一的方式对待所有的进程:子进程复制父进程所有的资源。这种方法有一些很明显弊端: 将使用大量内存 复制操作耗费大量...
  • 写时拷贝技术:Copy-On-Write

    千次阅读 2016-05-25 10:46:38
    写时拷贝技术 1、概念 Scott Meyers在《More Effective C++》中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里,做出一副正在复习功课的 样子,...
  • MySQL 数据库——主从复制与读写分离

    万次阅读 多人点赞 2021-11-01 02:08:19
    文章目录前言一、MySQL主从复制1.支持的复制类型2.主从复制的工作过程是基于日志3.请求方式4.主从复制的原理5.MySQL集群和主从复制分别适合在什么场景下使用6.为什么使用主从复制、读写分离7.用途及条件8.mysql主从...
  • LInux fork的写时复制(copy on write)

    千次阅读 2020-09-04 10:52:10
    执行BGSAVE命令或者BGREWRITEAOF命令的过程中,Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子进程存在期间,服务器会提高负载因子的阈值...
  • 一把LOL的时间让你了解Redis的主从复制机制,Redis超详细主从复制解析,值得收藏!
  • 47-引用计数与写时复制

    千次阅读 2016-04-26 12:28:30
    47-引用计数与写时复制对于PHP这种需要同时处理多个请求的程序来说,申请和释放内存的时候应该慎之又慎,一不小心便会酿成大错。另一方面,除了要安全申请和释放内存外,还应该做到内存的最小化使用,因为它可能要...
  • linux写时复制机制就是,当fork出一个子进程的时候,子进程并不立刻复制数据段,而是当子进程要修改数据才分配相应内存给变量。按照这样的原理,我用C了一个程序,声明一个全局变量并初始化,然后在进程里fork...
  • 我们在之前的博文QVector的内存分配策略 与再谈QVector与std::vector——使用装饰者让std::vector支持连续赋值简单聊了聊QVector内存分配和赋值方面的一点东西,今天接着从QVector展开谈谈Qt的写时复制技术...
  • 系统通过内存管理系统的写时复制特性来防止以上情况的发生。任何时候当应用程序视图写入内存映射文件的时候,系统会首先截获此类尝试,接着为应用程序试图写入的内存映射文件页面分配一块新的内存,然后复制页面的额...
  • linux 写时复制 copyonwrite

    千次阅读 2015-05-12 22:21:35
    如果多个进程当父进程产生一个子进程,会把父进程的代码段、数据段等拷贝给子进程,这里边有一个写时拷贝原则,这个动作并不是马上执行的,要等到子进程去修改内存里面的变量时候,才会进行拷贝,拷贝的机制并不是...
  • 但在某些场景,我们可能想避免fail-fast机制抛出的异常,这时我们就要将ArrayList替换为使用fail-safe机制的CopyOnWriteArrayList. 采用安全失败机制的集合容器,在 Iterator 的实现上没有设计抛出 ConcurrentMo
  • 复制架构衍生史 在谈这个特性之前,我们先来看看MySQL的复制架构衍生史。 在2000年,MySQL 3.23.15版本引入了Replication。Replication作为一种准实时同步方式,得到广泛应用。这个时候的Replicaton的实现涉及到两个...
  • 一个主机 m1 用于处理所有请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责请求,m1、m2 互为备机。架构图如下: 1. 搭建 MySQL 数据库主从复制(双主双从)...
  • Redis主从复制原理

    万次阅读 2021-01-10 23:26:24
    一、为什么需要主从复制: 1、单台Redis节点的局限性: (1)单节点的Redis能够支撑QPS大概在5万左右,如果上千万的用户访问,Redis就承载不了,成为了高并发的瓶颈。 (2)内存上,单个Redis的内存不宜过大,...
  • Redis-主从复制和读写分离

    千次阅读 2018-09-05 09:51:56
     也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以为主,Slave以读为主。 二、它能干嘛?  1、读写分离;  2、容灾恢复。 三、怎么玩?  1、配从...
  • HDFS副本存放机制和流水线复制

    千次阅读 2019-09-05 18:44:34
    1.HDFS副本存放机制 在HDFS中,一个文件会被拆分为一个或多个数据块。默认情况下,每个数据块都会有3个副本。每个副本都会被存放在不同的机器上,而且每一个副本都有自己唯一的编号。 NameNode节点选择一个DataNode...
  • 介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成...
  • Redis主从复制及其心跳检测实现机制

    千次阅读 2019-07-25 15:50:00
    [Redis专题] Redis主从复制及其心跳检测实现机制1、Redis主从工作模式简述2、同步2.1老版本的Redis(2.8之前)新版本的Redis(2.8之后)3、命令传播4、新版本主从节点同步示例5、心跳检测5.1 检测主从服务器的网络...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 417,602
精华内容 167,040
关键字:

写时复制机制