เมื่อไหร่ที่เหมาะสำหรับการคัดลอกข้อมูล (หรือเมื่อมีการอ่าน () และเขียน () บางส่วน)


60

ฉบับย่อ:ในสถานการณ์ใดที่ddปลอดภัยที่จะใช้สำหรับการคัดลอกข้อมูลหมายความว่าปลอดภัยไม่มีความเสี่ยงจากการทุจริตเนื่องจากการอ่านหรือเขียนบางส่วน?

รุ่นยาว - บทนำ: ddมักใช้เพื่อคัดลอกข้อมูลโดยเฉพาะจากหรือไปยังอุปกรณ์ ( ตัวอย่าง ) บางครั้งก็นำมาประกอบคุณสมบัติลึกลับของความสามารถในการเข้าถึงอุปกรณ์ในระดับที่ต่ำกว่าเครื่องมืออื่น ๆ (เมื่ออยู่ในความเป็นจริงมันเป็นแฟ้มอุปกรณ์ที่ทำมายากล) - ยังเป็นสิ่งเดียวกับdd if=/dev/sda บางครั้งคิดจะเร็ว แต่สามารถเอาชนะมันในทางปฏิบัติ อย่างไรก็ตามมีคุณสมบัติที่เป็นเอกลักษณ์ที่ทำให้มันมีประโยชน์อย่างแท้จริงบางครั้งcat /dev/sdaddcatdd

ปัญหา: ไม่ได้ในความเป็นจริงเช่นเดียวกับdd if=foo of=bar cat <foo >barเมื่อวันที่unices¹ที่สุดทำให้สายเดียวที่จะdd read()(ฉันพบว่าPOSIXคลุมเครือในสิ่งที่ถือเป็นการ "อ่านบล็อกอินพุต" ในdd) หากread()ส่งคืนผลลัพธ์บางส่วน (ซึ่งตาม POSIX และเอกสารอ้างอิงอื่น ๆ จะอนุญาตให้ยกเว้นว่าเอกสารการใช้งานระบุไว้เป็นอย่างอื่น) บล็อกบางส่วนจะถูกคัดลอก write()ว่าปัญหาเดียวกันที่มีอยู่สำหรับ

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

yes | dd of=out bs=1024k count=10

และตรวจสอบขนาดของoutไฟล์ (น่าจะต่ำกว่า 10MB)

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

( GNU ddมีการfullblockตั้งค่าสถานะเพื่อบอกให้เรียกread()หรือwrite()วนซ้ำเพื่อถ่ายโอนบล็อกเต็มดังนั้นdd iflag=fullblockปลอดภัยเสมอคำถามของฉันเกี่ยวกับกรณีที่ไม่มีการตั้งค่าสถานะเหล่านี้ (ซึ่งไม่มีอยู่ในการใช้งานอื่น ๆ ) .)

¹ ฉันตรวจสอบแล้วใน OpenBSD, GNU coreutils และ BusyBox


ฉันไม่เคยเห็นระบบ Unixy ใด ๆ ที่สามารถอ่าน MiB สองสามตัวในการอ่านครั้งเดียว (2) ...
vonbrand

3
เมื่อใช้countงานiflag=fullblockจำเป็นต้องมี (หรืออีกวิธีหนึ่งiflag=count_bytes) oflag=fullblockไม่มี
frostschutz

คำตอบ:


10

จากสเป็ค :

  • หากbs=exprดำเนินการระบุและไม่มีการแปลงอื่น ๆ กว่าsync, noerrorหรือnotruncมีการขอข้อมูลกลับมาจากบล็อกการป้อนข้อมูลแต่ละจะต้องจารึกไว้เป็นบล็อกแยกออก; หากread()ผลตอบแทนน้อยกว่าบล็อกเต็มและsyncไม่ได้ระบุการแปลงบล็อกผลลัพธ์ที่ได้จะต้องมีขนาดเท่ากับบล็อกอินพุต

ดังนั้นนี่อาจเป็นสิ่งที่ทำให้คุณสับสน ใช่เพราะddถูกออกแบบมาสำหรับการปิดกั้นโดยค่าเริ่มต้นบางส่วนread()s จะถูกแมป 1: 1 ถึงบางส่วนwrite()ของหรืออื่น ๆsyncวันที่ออกบนหาง padding NUL หรือตัวอักษรพื้นที่ในการbs=ขนาดเมื่อconv=syncมีการระบุ

ซึ่งหมายความว่าddเป็นที่ปลอดภัยที่จะใช้สำหรับการคัดลอกข้อมูล (w / ความเสี่ยงของการทุจริตไม่มีเนื่องจากการอ่านบางส่วนหรือเขียน)ในทุกกรณี แต่อย่างหนึ่งที่มันจะถูก จำกัด โดยพลการโดยcount=โต้แย้งเพราะมิฉะนั้นddจะมีความสุขwrite()ผลผลิตของตนในบล็อกขนาดเหมือนกัน สำหรับผู้ที่มีการป้อนข้อมูลread()จนกว่าจะread()ผ่านอย่างสมบูรณ์ และแม้กระทั่งข้อแม้นี้เป็นจริงเท่านั้นเมื่อbs=มีการระบุหรือobs=จะไม่ได้ระบุว่าเป็นประโยคถัดไปมากในสเป็ฯ :

  • ถ้าbs=exprตัวถูกดำเนินการไม่ได้ระบุหรือแปลงอื่น ๆ กว่าsync, noerrorหรือnotruncมีการร้องขอการป้อนข้อมูลจะถูกประมวลผลและเก็บรวบรวมลงในบล็อกเอาท์พุทขนาดเต็มจนกว่าจะสิ้นสุดของการป้อนข้อมูลที่จะถึง

หากไม่มีibs=และ / หรือมีobs=ข้อโต้แย้งสิ่งนี้ไม่สำคัญ - เพราะibsและobsมีขนาดเท่ากันตามค่าเริ่มต้น แต่คุณจะได้รับอย่างชัดเจนเกี่ยวกับการกำหนดบัฟเฟอร์อินพุตโดยการระบุขนาดที่แตกต่างกันอย่างใดอย่างหนึ่งและไม่ระบุ(เพราะมันจะมีความสำคัญ)bs=

ตัวอย่างเช่นหากคุณ:

IN| dd ibs=1| OUT

... จากนั้น POSIX ddจะมีwrite()หน่วยเป็น 512 ไบต์โดยรวบรวมทุกread()ไบต์เดี่ยว ๆลงในบล็อกเอาต์พุตเดี่ยว

มิฉะนั้นถ้าคุณทำ ...

IN| dd obs=1kx1k| OUT

ที่ ... POSIX ddจะread() ได้สูงสุด 512 ไบต์ในเวลา แต่write()ทุกเมกะไบต์ขนาดเอาท์พุทบล็อก(เคอร์เนลอนุญาตและการยกเว้นอาจจะเป็นครั้งสุดท้าย - เพราะนั่นคือ EOF)ในเต็มรูปแบบโดยการเก็บรวบรวมใส่ลงในบล็อกเอาท์พุทขนาดเต็ม

นอกจากนี้จากสเป็คแล้ว:

  • count=n
    • คัดลอกเฉพาะบล็อกอินพุตnเท่านั้น

count=แผนที่ไปยังi?bs=บล็อกและอื่น ๆ เพื่อจัดการขีด จำกัด โดยพลการในcount=พอร์ตคุณจะต้องมีสองddตัว วิธีปฏิบัติที่เป็นไปได้มากที่สุดในการทำสองdds คือการส่งออกไปยังอินพุตของอีกอันหนึ่งซึ่งทำให้เราอยู่ในขอบเขตของการอ่าน / เขียนไฟล์พิเศษโดยไม่คำนึงถึงชนิดอินพุตต้นฉบับ

ไปป์ IPC หมายความว่าเมื่อระบุ[io]bs=args ที่จะทำอย่างปลอดภัยคุณต้องเก็บค่าดังกล่าวภายในPIPE_BUFขีด จำกัด ที่กำหนดของระบบ POSIX ระบุว่าเคอร์เนลระบบจะต้องรับประกันอะตอมread()และwrite()s ภายในขอบเขตของตามที่กำหนดในPIPE_BUF limits.hPOSIX รับประกันว่าPIPE_BUFจะเป็นอย่างน้อย ...

  • {_POSIX_PIPE_BUF}
    • จำนวนไบต์สูงสุดที่รับประกันว่าเป็นแบบปรมาณูเมื่อเขียนไปยังไพพ์
    • ค่า: 512

... (ซึ่งเกิดขึ้นเป็นค่าเริ่มต้นddบล็อก i / o)แต่ค่าจริงมักจะเป็นอย่างน้อย 4k บนระบบ Linux ที่ทันสมัยคือ 64k โดยค่าเริ่มต้น

ดังนั้นเมื่อคุณตั้งค่าโปรเซสของddคุณคุณควรทำในบล็อคแฟเตอร์ตามค่าสามค่า:

  1. bs = (obs = PIPE_BUFหรือน้อยกว่า)
  2. n = จำนวนไบต์ที่ต้องการอ่านทั้งหมด
  3. count = n / bs

ชอบ:

yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s

คุณต้องซิงโครไนซ์ i / ow / ddเพื่อจัดการอินพุตที่ไม่สามารถค้นหาได้ กล่าวอีกนัยหนึ่งทำให้ไปป์ไลน์บัฟเฟอร์อย่างชัดเจนและพวกเขาจะหยุดเป็นปัญหา นั่นคือสิ่งที่ddมีไว้เพื่อ ปริมาณที่ไม่รู้จักที่นี่คือyes's ขนาดบัฟเฟอร์ - แต่ถ้าคุณปิดกั้นออกไปเป็นที่รู้จักในปริมาณอีกddแล้วคูณมีข้อมูลเล็ก ๆ น้อย ๆ สามารถทำให้dd ปลอดภัยที่จะใช้สำหรับการคัดลอกข้อมูล (w / ความเสี่ยงของการทุจริตไม่มีเนื่องจากการอ่านบางส่วนหรือเขียน)แม้ว่าจะ จำกัด อินพุตโดยไม่ จำกัด ชนิด w / count=w / อินพุตใดก็ได้บนระบบ POSIX และไม่ขาดไบต์เดียว

นี่เป็นตัวอย่างข้อมูลจากPOSIX spec :

  • ibs=expr
    • ระบุขนาดบล็อกใส่ไบต์โดย(ค่าปกติคือ 512)expr
  • obs=expr
    • ระบุขนาดของบล็อกการส่งออกในไบต์โดย(ค่าปกติคือ 512)expr
  • bs=expr
    • ตั้งค่าทั้งอินพุทและเอาท์พุทบล็อกขนาดexprไบต์แทนและibs= obs=หากไม่มีการแปลงนอกเหนือจากsync, noerrorและnotruncระบุไว้แต่ละบล็อคอินพุตจะถูกคัดลอกไปยังเอาต์พุตเป็นบล็อกเดียวโดยไม่มีการรวมบล็อกสั้น ๆ

นอกจากนี้คุณยังจะได้พบกับบางนี้อธิบายที่ดีกว่าที่นี่


5

ด้วยซ็อกเก็ตไพพ์หรือ ttys, read () และ write () สามารถถ่ายโอนได้น้อยกว่าขนาดที่ร้องขอดังนั้นเมื่อใช้ dd กับสิ่งเหล่านี้คุณจำเป็นต้องใช้ค่าสถานะ fullblock ด้วยไฟล์และอุปกรณ์บล็อกปกติอย่างไรก็ตามมีเพียงสองครั้งเมื่อพวกเขาสามารถอ่าน / เขียนสั้น ๆ : เมื่อคุณไปถึง EOF หรือหากมีข้อผิดพลาด นี่คือสาเหตุที่การใช้งานแบบเก่าของ dd โดยไม่มีการตั้งค่าสถานะบล็อกเต็มปลอดภัยที่จะใช้สำหรับการทำสำเนาดิสก์


นั่นเป็นความจริงของสิ่งมีชีวิตสมัยใหม่ทั้งหมดหรือไม่? (ฉันรู้ว่ามันไม่เป็นความจริงสำหรับ Linux ในบางจุดอาจสูงถึง 2.0.x หรือ 2.2.x ฉันจำได้ว่าmke2fsล้มเหลวอย่างเงียบ ๆ เนื่องจากเรียกว่าwrite()มีขนาดที่ไม่กินไฟ (3kB IIRC) และเคอร์เนลถูกปัดเศษ ลงไปที่พลังของ 2)
Gilles

@Gilles ที่ฟังดูเหมือนเป็นปัญหาต่างออกไปโดยสิ้นเชิง คุณต้องใช้ขนาดบล็อกที่เหมาะสมกับอุปกรณ์บล็อกหลายขนาด ฉันค่อนข้างแน่ใจว่ามันเป็นความจริงของ unicies ทั้งหมดและมันก็เป็นจริงสำหรับ Windows
psusi

นอกเหนือจากเทปแล้วขนาดบล็อกของอุปกรณ์นั้นสำหรับเคอร์เนลที่จะสนใจหรือไม่ cat </dev/sda >/dev/sdbใช้งานได้ดีในการโคลนดิสก์
Gilles

@Gilles นั้นเป็นเพราะแมวใช้ขนาดบล็อกที่เหมาะสมตามที่ OrbWeaver ระบุไว้ในคำตอบของเขา
psusi

ไม่ไม่มี "ขนาดบล็อกที่เหมาะสม" catเลือกขนาดบัฟเฟอร์สำหรับประสิทธิภาพ ไม่ได้รับข้อมูลที่เกี่ยวข้องกับอุปกรณ์ใด ๆ จากเคอร์เนล นอกเหนือจากเทปคุณสามารถread()และwrite()อุปกรณ์บล็อกได้ทุกขนาด อย่างน้อยบน Linux st_blksizeขึ้นอยู่กับระบบไฟล์ที่มีอุปกรณ์บล็อก inode ตั้งอยู่ไม่ใช่ในอุปกรณ์ที่รองรับ
Gilles
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.