Copy-on-write คืออะไร?


135

อยากทราบว่า copy-on-write คืออะไรและใช้ทำอะไร? คำว่า 'copy-on-write array' ถูกกล่าวถึงหลายครั้งในบทแนะนำ Sun JDK แต่ฉันไม่เข้าใจว่ามันหมายถึงอะไร

คำตอบ:


157

ฉันกำลังจะเขียนคำอธิบายของตัวเอง แต่บทความ Wikipedia นี้สรุปได้ค่อนข้างมาก

นี่คือแนวคิดพื้นฐาน:

Copy-on-write (บางครั้งเรียกว่า "COW") เป็นกลยุทธ์การเพิ่มประสิทธิภาพที่ใช้ในการเขียนโปรแกรมคอมพิวเตอร์ แนวคิดพื้นฐานคือหากผู้โทรหลายคนขอทรัพยากรซึ่งตอนแรกแยกไม่ออกคุณสามารถให้พวกเขาชี้ไปยังทรัพยากรเดียวกันได้ ฟังก์ชันนี้สามารถคงไว้ได้จนกว่าผู้โทรจะพยายามแก้ไข "สำเนา" ของทรัพยากรซึ่ง ณ จุดนั้นจะมีการสร้างสำเนาส่วนตัวที่แท้จริงเพื่อป้องกันไม่ให้ทุกคนเห็นการเปลี่ยนแปลง ทั้งหมดนี้เกิดขึ้นกับผู้โทรอย่างโปร่งใส ข้อได้เปรียบหลักคือหากผู้โทรไม่เคยทำการแก้ไขใด ๆ ก็ไม่จำเป็นต้องสร้างสำเนาส่วนตัว

นอกจากนี้นี่คือแอปพลิเคชั่นการใช้งานทั่วไปของ COW:

นอกจากนี้ยังใช้แนวคิด COW ในการบำรุงรักษาสแน็ปช็อตทันทีบนเซิร์ฟเวอร์ฐานข้อมูลเช่น Microsoft SQL Server 2005 สแน็ปช็อตแบบทันทีจะรักษามุมมองแบบคงที่ของฐานข้อมูลโดยการจัดเก็บสำเนาข้อมูลที่ปรับเปลี่ยนล่วงหน้าเมื่อมีการอัปเดตข้อมูลที่อยู่ด้านล่าง สแน็ปช็อตทันทีใช้สำหรับการทดสอบการใช้งานหรือรายงานที่ขึ้นอยู่กับช่วงเวลาและไม่ควรใช้เพื่อแทนที่การสำรองข้อมูล


อะไรก็ตามที่ใช้อาร์เรย์ปกติสำหรับ ... อย่างไรก็ตามในบางสถานการณ์กลยุทธ์ประเภทนี้จะให้ผลลัพธ์ที่ดีที่สุด
Andrew Flanagan

3
@hhafez: Linux ใช้เมื่อใช้clone()เพื่อใช้fork()- หน่วยความจำของกระบวนการพาเรนต์จะถูกควบคุมสำหรับเด็ก
Kerrek SB

@hhafez filesystems บางคนใช้วัวเช่นBTRFS
Geremia

นี่คือวิธีการทำงานของ SandboxIE หรือไม่? เมื่อโปรแกรมแซนด์บ็อกซ์ต้องการเขียนทับบางสิ่งที่แซนด์บ็อกซ์จะขัดขวางการทำงานของระบบไฟล์และคัดลอกไฟล์ไปยังโฟลเดอร์แซนด์บ็อกซ์และปล่อยให้โปรแกรมเขียนไปยังไฟล์แซนด์บ็อกซ์แทนต้นฉบับ ที่เรียกว่า Copy on write?
Ronnie Matthews

ในที่สุดการรวมจะเกิดขึ้นได้อย่างไร? หากมีสำเนา N สำเนาใดจะถูกเก็บไว้ในที่สุดเพื่อบันทึกลงในดิสก์ดังกล่าว
SimpleGuy

59

"คัดลอกเมื่อเขียน" หมายถึงสิ่งที่ดูเหมือนมากหรือน้อย: ทุกคนมีสำเนาข้อมูลเดียวกันที่ใช้ร่วมกันเพียงสำเนาเดียวจนกว่าจะมีการเขียนข้อมูลจากนั้นจึงทำสำเนา โดยปกติการคัดลอกเมื่อเขียนจะใช้เพื่อแก้ไขปัญหาประเภทต่างๆที่เกิดขึ้นพร้อมกัน ตัวอย่างเช่นในZFSบล็อกข้อมูลบนดิสก์จะถูกจัดสรรแบบ copy-on-write ตราบเท่าที่ไม่มีการเปลี่ยนแปลงคุณยังคงบล็อกเดิมไว้ การเปลี่ยนแปลงเปลี่ยนเฉพาะบล็อกที่ได้รับผลกระทบ ซึ่งหมายความว่าจะมีการจัดสรรจำนวนบล็อกใหม่ขั้นต่ำ

การเปลี่ยนแปลงเหล่านี้มักจะถูกนำไปใช้เพื่อทำธุรกรรมกล่าวคือมีคุณสมบัติของกรด สิ่งนี้ช่วยขจัดปัญหาการเกิดพร้อมกันบางอย่างเพราะคุณรับประกันได้ว่าการอัปเดตทั้งหมดเป็นแบบปรมาณู


1
หากคุณทำการเปลี่ยนแปลงอีกฝ่ายจะได้รับการแจ้งเตือนเกี่ยวกับสำเนาใหม่ของคุณอย่างไร พวกเขาจะไม่เห็นข้อมูลที่ผิด
powder366

12
@ powder366 - ไม่พวกเขาจะไม่เห็นข้อมูลผิดเพราะเมื่อคุณทำการเปลี่ยนแปลงนั่นคือเวลาที่ทำสำเนาขึ้นมา Aตัวอย่างเช่นคุณมีบล็อกของข้อมูลที่เรียกว่า กระบวนการ1, 2, 3, 4แต่ละต้องการให้สำเนาของมันและเริ่มอ่านมันใน "Copy ในการเขียน" ระบบอะไรที่จะถูกคัดลอก ๆ Aทุกอย่างจะยังคงอ่าน ตอนนี้กระบวนการ3ต้องการที่จะทำให้เกิดการเปลี่ยนแปลงไปของสำเนาของAกระบวนการ3ในขณะนี้จะจริงทำสำเนาและสร้างบล็อกใหม่ของข้อมูลที่เรียกว่าA Bกระบวนการ1, 2, 4ยังคงอ่านบล็อกAกระบวนการอยู่ในขณะนี้การอ่าน3 B
Puddler

1
@Puddler จะเกิดอะไรขึ้นหากมีการเปลี่ยนแปลงใน 'A' กระบวนการทั้งหมดจะอ่านข้อมูลปรับปรุงหรือเก่า?
พัฒนา

3
@ ผู้พัฒนา: กระบวนการใดที่ทำการเปลี่ยนแปลงAควรสร้างสำเนาใหม่ หากคุณกำลังถามว่าจะเกิดอะไรขึ้นหากกระบวนการใหม่ทั้งหมดเกิดขึ้นและมีการเปลี่ยนแปลงAคำอธิบายของฉันไม่ได้ให้รายละเอียดเพียงพอ นั่นจะเป็นการใช้งานที่เฉพาะเจาะจงและต้องการความรู้เกี่ยวกับวิธีที่คุณต้องการให้การใช้งานที่เหลือทำงานเช่นการล็อกไฟล์ \ ข้อมูลเป็นต้น
Puddler

10

ฉันจะไม่ตอบซ้ำคำตอบเดียวกันใน Copy-on-Write ฉันคิดว่าคำตอบของแอนดรูว์และคำตอบของชาร์ลีนั้นชัดเจนมากแล้ว ฉันจะยกตัวอย่างจาก OS world เพื่อพูดถึงวิธีการใช้แนวคิดนี้อย่างกว้างขวาง

เราสามารถใช้fork()หรือvfork()เพื่อสร้างกระบวนการใหม่ vfork เป็นไปตามแนวคิดของการคัดลอกเมื่อเขียน ตัวอย่างเช่นกระบวนการลูกที่สร้างขึ้นโดย vfork จะแชร์ข้อมูลและส่วนของโค้ดกับกระบวนการหลัก ทำให้เวลาในการตีเร็วขึ้น คาดว่าจะใช้ vfork หากคุณแสดง exec ตามด้วย vfork ดังนั้น vfork จะสร้างกระบวนการย่อยซึ่งจะแชร์ข้อมูลและส่วนโค้ดกับพาเรนต์ แต่เมื่อเราเรียก exec มันจะโหลดอิมเมจของไฟล์ปฏิบัติการใหม่ในพื้นที่แอดเดรสของโปรเซสลูก


3
"vfork เป็นไปตามแนวคิดของการคัดลอกเมื่อเขียน" โปรดพิจารณาเปลี่ยนบรรทัดนี้ vforkไม่ใช้วัว ในความเป็นจริงหากเด็กเขียนอะไรบางอย่างอาจส่งผลให้เกิดพฤติกรรมที่ไม่กำหนดและไม่คัดลอกหน้า !! ในความเป็นจริงคุณสามารถพูดในทางกลับกันว่าเป็นความจริงบ้าง COW ทำหน้าที่เหมือนvforkจนกว่าจะมีการแก้ไขบางอย่างในพื้นที่ที่ใช้ร่วมกัน!
Pavan Manjunath

เห็นด้วยอย่างยิ่งกับ Pavan ลบบรรทัด "vfork ตามแนวคิดของการคัดลอกเมื่อเขียน" วันนี้ COW ถูกใช้ในการแยกเป็นการเพิ่มประสิทธิภาพเพื่อให้ทำหน้าที่เหมือน vfork และไม่ทำสำเนาข้อมูลของผู้ปกครองสำหรับกระบวนการย่อย (ถ้าเราเรียกเฉพาะ exec * in child)
Shekhar Kumar

8

เพื่อให้เป็นอีกตัวอย่างหนึ่งMercurial ใช้ copy-on-writeเพื่อทำการโคลนที่เก็บในเครื่องเป็นการดำเนินการที่ "ถูก" จริงๆ

หลักการก็เหมือนกับตัวอย่างอื่น ๆ ยกเว้นว่าคุณกำลังพูดถึงไฟล์ทางกายภาพแทนที่จะเป็นวัตถุในหน่วยความจำ ในขั้นต้นโคลนจะไม่ซ้ำกัน แต่เป็นฮาร์ดลิงก์ไปยังต้นฉบับ เมื่อคุณเปลี่ยนไฟล์ในโคลนสำเนาจะถูกเขียนขึ้นเพื่อแสดงเวอร์ชันใหม่


2

ฉันพบนี้บทความที่ดีเกี่ยวกับ zval ใน PHP ซึ่งกล่าวถึง COW เกินไป:

Copy On Write (ย่อว่า 'COW') เป็นเคล็ดลับที่ออกแบบมาเพื่อบันทึกหน่วยความจำ มีการใช้โดยทั่วไปในวิศวกรรมซอฟต์แวร์ หมายความว่า PHP จะคัดลอกหน่วยความจำ (หรือจัดสรรพื้นที่หน่วยความจำใหม่) เมื่อคุณเขียนลงในสัญลักษณ์หากอันนี้ชี้ไปที่ zval แล้ว


1

การคัดลอกเมื่อเขียนเป็นเทคนิคในการลดการใช้หน่วยความจำของสำเนาทรัพยากรโดยการแบ่งปันหน่วยความจำจนกว่าจะมีการแก้ไขสำเนาใดสำเนาหนึ่ง กล่าวอีกนัยหนึ่งสำเนาจะเป็นสำเนาเสมือนในตอนแรกและจะกลายเป็นสำเนาจริงในการดำเนินการเขียนครั้งแรกเท่านั้นจึงมีชื่อว่า 'copy-on-write'

ที่นี่หลังจากที่เป็นการนำงูหลามของเทคนิคการสำเนาเมื่อเขียนโดยใช้รูปแบบการออกแบบพร็อกซี่ ValueProxyวัตถุ (คนพร็อกซี ) ใช้เทคนิคการคัดลอกเมื่อเขียนโดย:

  • มีแอตทริบิวต์ที่ผูกไว้กับValueวัตถุที่ไม่เปลี่ยนรูป( เรื่อง );
  • การส่งต่อคำขออ่านไปยังแอตทริบิวต์หัวเรื่อง
  • แปลคำขอเขียนเพื่อสร้างValueวัตถุที่ไม่เปลี่ยนรูปใหม่ด้วยสถานะใหม่และการเชื่อมโยงแอตทริบิวต์หัวเรื่องกับValueวัตถุที่ไม่เปลี่ยนรูปใหม่
  • แปลคำขอคัดลอกเป็นการสร้างValueProxyอ็อบเจ็กต์ใหม่ที่ใช้แอ็ตทริบิวต์เรื่องเดียวกันกับValueProxyอ็อบเจ็กต์ต้นฉบับ
import abc

class BaseValue(abc.ABC):
    @abc.abstractmethod
    def read(self):
        raise NotImplementedError
    @abc.abstractmethod
    def write(self, data):
        raise NotImplementedError

class Value(BaseValue):
    def __init__(self, data):
        self.data = data
    def read(self):
        return self.data
    def write(self, data):
        pass

class ValueProxy(BaseValue):
    def __init__(self, subject):
        self.subject = subject
    def read(self):
        return self.subject.read()
    def write(self, data):
        self.subject = Value(data)
    def clone(self):
        return ValueProxy(self.subject)

v1 = ValueProxy(Value('foo'))
v2 = v1.clone()  # shares the immutable Value object between the copies
assert v1.subject is v2.subject
v2.write('bar')  # creates a new immutable Value object with the new state
assert v1.subject is not v2.subject

0

นอกจากนี้ยังใช้ใน Ruby 'Enterprise Edition' เพื่อประหยัดหน่วยความจำ


2
ฉันไม่คิดว่าเขาหมายถึง "ใช้สำหรับ" ในความหมายนั้น
spydon

0

ตัวอย่างที่ดีคือ Git ซึ่งใช้กลยุทธ์ในการจัดเก็บ Blobs ทำไมจึงใช้แฮช? ส่วนหนึ่งเป็นเพราะสิ่งเหล่านี้ง่ายต่อการดำเนินการที่แตกต่างกัน แต่ยังเป็นเพราะทำให้การเพิ่มประสิทธิภาพกลยุทธ์ COW ทำได้ง่ายขึ้น เมื่อคุณทำการคอมมิตใหม่โดยมีไฟล์เพียงไม่กี่ไฟล์การเปลี่ยนแปลงอ็อบเจ็กต์และต้นไม้ส่วนใหญ่จะไม่เปลี่ยนแปลง ดังนั้นการกระทำจะผ่านพอยน์เตอร์ต่างๆที่ทำจากแฮชจะอ้างอิงถึงกลุ่มของวัตถุที่มีอยู่แล้วทำให้พื้นที่จัดเก็บข้อมูลที่จำเป็นในการจัดเก็บประวัติทั้งหมดมีขนาดเล็กลงมาก


0

เป็นแนวคิดการป้องกันหน่วยความจำ ในคอมไพเลอร์นี้สร้างสำเนาพิเศษเพื่อแก้ไขข้อมูลในลูกและข้อมูลที่อัปเดตนี้จะไม่แสดงในข้อมูลผู้ปกครอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.