ท่อส่ง จำกัด การใช้หน่วยความจำอย่างไร


36

Brian Kernighan อธิบายในวิดีโอนี้ถึงการดึงดูดเบลล์แล็บในช่วงต้นให้กับภาษา / โปรแกรมเล็ก ๆ

เครื่องจักรขนาดใหญ่จะเป็น 64 k-bytes - K ไม่ใช่ M หรือ G - และนั่นหมายความว่าโปรแกรมแต่ละโปรแกรมไม่สามารถมีขนาดใหญ่มากดังนั้นจึงมีแนวโน้มที่จะเขียนโปรแกรมขนาดเล็กและกลไกของท่อ โดยทั่วไปการเปลี่ยนเส้นทางอินพุตเอาต์พุตทำให้สามารถเชื่อมโยงโปรแกรมหนึ่งไปยังอีกโปรแกรมหนึ่งได้

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

จากวิกิพีเดีย :

ในระบบที่เหมือนยูนิกซ์ส่วนใหญ่กระบวนการทั้งหมดของไปป์ไลน์จะเริ่มพร้อมกันมีการเชื่อมต่อกับสตรีมอย่างเหมาะสมและจัดการโดยตัวกำหนดตารางเวลาร่วมกับกระบวนการอื่น ๆ ทั้งหมดที่ทำงานบนเครื่อง สิ่งสำคัญในเรื่องนี้คือการตั้งค่าท่อ Unix นอกเหนือจากการใช้งานไปป์อื่น ๆ คือแนวคิดของการบัฟเฟอร์: ตัวอย่างเช่นโปรแกรมส่งอาจสร้าง 5,000 ไบต์ต่อวินาทีและโปรแกรมรับอาจยอมรับได้ 100 ไบต์ต่อวินาทีเท่านั้น แต่ไม่มี ข้อมูลสูญหาย แต่ผลลัพธ์ของโปรแกรมส่งจะถูกเก็บไว้ในบัฟเฟอร์แทน เมื่อโปรแกรมรับข้อมูลพร้อมที่จะอ่านข้อมูลโปรแกรมถัดไปในไปป์ไลน์จะอ่านจากบัฟเฟอร์ ใน Linux ขนาดของบัฟเฟอร์คือ 65536 ไบต์ (64KB) ตัวกรองบุคคลที่สามโอเพนซอร์สที่เรียกว่า bfr พร้อมให้บริการบัฟเฟอร์ที่มีขนาดใหญ่ขึ้นหากจำเป็น

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

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

ทั้งหมดนี้จะช่วยได้มากถ้าใช้ไฟล์ temp แต่ฉันเข้าใจว่าไพพ์ไม่ได้เขียนลงดิสก์ (ยกเว้นว่าใช้ swap)

ตัวอย่าง:

sed 'simplesubstitution' file | sort | uniq > file2

เป็นที่ชัดเจนสำหรับฉันที่sedกำลังอ่านไฟล์และแยกออกเป็นรายบรรทัด แต่sortเป็น BK ระบุในวิดีโอที่เชื่อมโยงเป็นหยุดเต็มดังนั้นข้อมูลทั้งหมดจะต้องมีการอ่านในหน่วยความจำ (หรือไม่ได้?) แล้วก็ผ่านไปuniqซึ่ง (ในใจของฉัน) จะเป็นหนึ่ง โปรแกรมออนไลน์ แต่ระหว่างไพพ์แรกและท่อที่สองข้อมูลทั้งหมดจะต้องอยู่ในหน่วยความจำใช่ไหม?


1
unless swap is usedแลกเปลี่ยนนั้นจะใช้เมื่อมีไม่เพียงพอ RAM
edc65

คำตอบ:


44

ข้อมูลไม่จำเป็นต้องเก็บไว้ใน RAM ท่อบล็อกนักเขียนของพวกเขาหากผู้อ่านไม่ได้อยู่ที่นั่นหรือติดตามไม่ได้ ภายใต้ Linux (และการใช้งานอื่น ๆ ส่วนใหญ่ฉันจินตนาการ) มีการกำหนดบัฟเฟอร์บางอย่าง แต่ไม่จำเป็น ดังกล่าวโดยmtraceurและJdeBP (ดูคำตอบหลัง) รุ่นแรก ๆ ของ Unix ไปป์บัฟเฟอร์ไปยังดิสก์และนี่คือวิธีที่พวกเขาช่วย จำกัด การใช้หน่วยความจำ: ไปป์ไลน์การประมวลผลสามารถแบ่งออกเป็นโปรแกรมขนาดเล็กซึ่งแต่ละอันจะประมวลผลข้อมูลบางอย่างภายในขอบเขตของดิสก์บัฟเฟอร์ โปรแกรมขนาดเล็กใช้หน่วยความจำน้อยลงและการใช้ไพพ์หมายความว่าการประมวลผลอาจเป็นอนุกรม: โปรแกรมแรกจะรันเติมบัฟเฟอร์เอาต์พุตถูกระงับจากนั้นโปรแกรมที่สองจะถูกกำหนดเวลาประมวลผลบัฟเฟอร์ ฯลฯ ระบบสมัยใหม่เป็นคำสั่ง มีขนาดใหญ่กว่าระบบ Unix รุ่นแรกและสามารถใช้งานท่อหลายแบบพร้อมกันได้ แต่สำหรับข้อมูลจำนวนมากคุณยังคงเห็นผลที่คล้ายกัน (และตัวแปรของเทคนิคประเภทนี้ใช้สำหรับการประมวลผล "ข้อมูลขนาดใหญ่")

ในตัวอย่างของคุณ

sed 'simplesubstitution' file | sort | uniq > file2

sedอ่านข้อมูลจากfileเท่าที่จำเป็นจากนั้นเขียนมันตราบเท่าที่sortพร้อมที่จะอ่าน หากsortยังไม่พร้อมบล็อกการเขียน ในที่สุดข้อมูลจะอยู่ในหน่วยความจำจริง ๆ แล้ว แต่ก็เฉพาะเจาะจงsortและsortพร้อมที่จะจัดการกับปัญหาใด ๆ (มันจะใช้ไฟล์ชั่วคราวจำนวนข้อมูลที่จะเรียงลำดับมีขนาดใหญ่เกินไป)

คุณสามารถดูพฤติกรรมการปิดกั้นได้โดยเรียกใช้

strace seq 1000000 -1 1 | (sleep 120; sort -n)

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


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

6
@malan: สามารถเริ่มกระบวนการหลายกระบวนการได้และสามารถอยู่ในสถานะที่รันได้ในเวลาเดียวกัน แต่อย่างน้อยที่สุดหนึ่งโพรเซสสามารถเรียกใช้งานบน CPU จริงแต่ละตัวในเวลาใดก็ได้และเป็นหน้าที่ของตัวกำหนดตารางกระบวนการของเคอร์เนลเพื่อจัดสรร "ส่วน" ของเวลา CPU ให้กับแต่ละกระบวนการที่รันได้ ในระบบที่ทันสมัยกระบวนการที่รันได้ แต่ไม่ได้กำหนดเวลาซีพียูมักจะยังคงอยู่ในหน่วยความจำในขณะที่รอชิ้นถัดไป แต่เคอร์เนลได้รับอนุญาตให้หน้าหน่วยความจำของกระบวนการใด ๆ ออกไปยังดิสก์และกลับเข้าไปในหน่วยความจำอีกครั้ง มันสะดวก (ส่งรายละเอียดมาที่นี่)
Daniel Pryden

5
กระบวนการที่ด้านใดด้านหนึ่งของท่อสามารถทำงานได้อย่างมีประสิทธิภาพเช่นร่วมกิจวัตร: ด้านใดด้านหนึ่งเขียนจนกว่าจะเติมบัฟเฟอร์และบล็อกการเขียนที่จุดที่กระบวนการไม่สามารถทำอะไรกับส่วนที่เหลือของเวลาและมันจะเข้าสู่ โหมดรอ IO จากนั้นระบบปฏิบัติการให้ส่วนที่เหลือของ timeslice (หรืออีกครั้ง timeslice ที่จะเกิดขึ้น) ไปยังด้านการอ่านซึ่งอ่านจนกว่าจะไม่มีอะไรเหลืออยู่ในบัฟเฟอร์และบล็อกอ่านต่อไปที่จุดที่กระบวนการอ่านไม่สามารถทำอะไรกับส่วนที่เหลือของ ระยะเวลาและอัตราผลตอบแทนกลับสู่ระบบปฏิบัติการ ข้อมูลต้องผ่านบัฟเฟอร์มูลค่าหนึ่งท่อในเวลาเดียวกัน
Daniel Pryden

6
@malan โปรแกรมจะเริ่มต้น "ในเวลาเดียวกัน" แนวคิดในทุกระบบปฏิบัติการ Unix เพียงในระบบมัลติโปรเซสเซอร์ที่ทันสมัยด้วย RAM พอที่จะถือพวกเขาว่าพวกเขากำลังหมายถึงตัวอักษรทั้งหมดที่จัดขึ้นในแรมในเวลาเดียวกันขณะที่ในระบบที่กระป๋อง ไม่ต้องถือพวกมันทั้งหมดใน RAM ในเวลาเดียวกันบางตัวก็ถูกสลับไปยังดิสก์ นอกจากนี้โปรดทราบว่า "หน่วยความจำ" ในบริบทจำนวนมากหมายถึงหน่วยความจำเสมือนซึ่งเป็นผลรวมของพื้นที่แรมและพื้นที่สว็อปบนดิสก์ Wikipedia มุ่งเน้นที่แนวคิดมากกว่ารายละเอียดการใช้งานโดยเฉพาะอย่างยิ่งเพราะ Unix ที่เก่าแก่ที่สุดทำสิ่งต่าง ๆ มีความเกี่ยวข้องน้อยลงในขณะนี้
mtraceur

2
@malan ความขัดแย้งที่คุณเห็นมาจากสองความหมายที่แตกต่างกันของ "หน่วยความจำ" (RAM vs RAM + swap) ฉันกำลังพูดถึง RAM ฮาร์ดแวร์เท่านั้นและในบริบทนั้นเฉพาะรหัสที่ถูกเรียกใช้โดย CPU ในปัจจุบันต้องพอดีกับ RAM (ซึ่งเป็นสิ่งที่มีผลต่อการตัดสินใจที่ Kernighan กำลังพูดถึง) ในขณะที่ในบริบทของโปรแกรมทั้งหมดที่ถูกดำเนินการทางตรรกะ โดยระบบปฏิบัติการในเวลาที่กำหนด (ในระดับนามธรรมที่มีให้ด้านบนของการแบ่งเวลา) โปรแกรมเพียงแค่ต้องการให้พอดีกับหน่วยความจำเสมือนทั้งหมดที่มีให้กับระบบปฏิบัติการซึ่งรวมถึงพื้นที่ swap บนดิสก์
mtraceur

34

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

นี่เป็นข้อผิดพลาดพื้นฐานของคุณ Unix เวอร์ชันก่อนหน้าไม่ได้เก็บข้อมูลไพพ์ไว้ใน RAM พวกเขาเก็บไว้ในแผ่นดิสก์ ไพพ์มี i-nodes; บนอุปกรณ์ดิสก์ที่ถูกแสดงอุปกรณ์ท่อ ผู้ดูแลระบบวิ่งโปรแกรมชื่อ/etc/configเพื่อระบุ (ในหมู่สิ่งอื่น ๆ ) ซึ่งปริมาณที่แผ่นดิสก์เป็นอุปกรณ์ท่อซึ่งปริมาณเป็นอุปกรณ์รากและที่อุปกรณ์การถ่ายโอนข้อมูล

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

กลไกนี้ถูกแทนที่โดยคนอื่น ๆ ในช่วงกลางถึงปลายปี 1980 SCO XENIX ได้รับ "ระบบท่อประสิทธิภาพสูง" ซึ่งแทนที่ i-nodes ด้วยบัฟเฟอร์ในแกน 4BSD ทำท่อที่ไม่มีชื่อในซ็อกเก็ตคู่ AT&T นำท่อไปใช้อีกครั้งโดยใช้กลไก STREAMS

และแน่นอนsortโปรแกรมดำเนินการจัดเรียง จำกัด ภายในของชิ้น 32KiB ของท่าน (หรือสิ่งที่มีขนาดเล็กจำนวนหน่วยความจำก็อาจจัดสรรถ้า 32KiB ก็ไม่สามารถใช้ได้) เขียนผลการเรียงลำดับการกลางstmX??ไฟล์ใน/usr/tmp/ที่ที่มันแล้วภายนอกผสานเรียงเพื่อให้สุดท้าย เอาท์พุต

อ่านเพิ่มเติม

  • Steve D. Pate (1996) "การสื่อสารระหว่างกระบวนการ" Internals ยูนิกซ์: วิธีการปฏิบัติ Addison-Wesley ไอ 9780201877212
  • Maurice J. Bach (1987) msgstr "ระบบเรียกใช้ระบบไฟล์". การออกแบบของระบบปฏิบัติการยูนิกซ์ ศิษย์ฮอลล์. ไอ 0132017571
  • Steven V. Earhart (1986) " config(1M)" Unix Programmer ของคู่มือการใช้งาน: 3. การบริหารระบบอำนวยความสะดวก Holt, Rinehart และ Winston ไอ 0030093139. pp. 23–28

1

คุณถูกต้องบางส่วน แต่โดยอุบัติเหตุ

ในตัวอย่างของคุณข้อมูลทั้งหมดจะต้องถูกอ่าน "ในระหว่าง" ไพพ์ แต่ไม่จำเป็นต้องอยู่ในหน่วยความจำ (รวมถึงหน่วยความจำเสมือน) การใช้งานตามปกติของsortสามารถเรียงชุดข้อมูลที่จะไม่พอดีกับ RAM โดยทำ partials เรียงลำดับเพื่อ tempfiles และการผสาน อย่างไรก็ตามมันเป็นความจริงที่กำหนดว่าคุณไม่สามารถส่งออกเรียงลำดับก่อนที่จะอ่านแต่ละองค์ประกอบ เห็นได้ชัดว่าสวย ใช่แล้วsortสามารถเริ่มส่งออกไปยังท่อที่สองหลังจากอ่าน (และทำสิ่งใด ๆ ซึ่งอาจเป็นเพียงการเรียงลำดับเทมเพลต) ทุกอย่างตั้งแต่แรก แต่ไม่จำเป็นต้องเก็บทุกอย่างไว้ใน RAM

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

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

หากท่อมีขนาดเล็กและมีขนาด จำกัด ดังนั้นวิธีนี้จะทำงานกับข้อมูลที่ไม่รู้จัก (อาจมีขนาดใหญ่) ได้อย่างไร? ง่ายมาก: เมื่อไม่มีสิ่งใดที่เหมาะกับการเขียนบล็อกมากขึ้นจนกว่าจะมีที่ว่างอีกครั้ง

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


เมื่อคุณพูดว่า 'ท่อถูกตั้งชื่อ' (JdeBP ดูเหมือนว่าจะบอกว่ามีเป็นหนึ่งใน 'อุปกรณ์ท่อ') หมายความว่ามีการ จำกัด จำนวนของท่อที่สามารถนำมาใช้ในเวลาที่กำหนด (เช่นมีขีด จำกัด กับ คุณสามารถใช้|คำสั่งได้กี่ครั้ง)
malan

2
ฉันไม่เคยเห็นขีด จำกัด ดังกล่าวและฉันไม่คิดว่าในทางทฤษฎีจะมีอยู่ครั้งหนึ่ง ในทางปฏิบัติสิ่งใดก็ตามที่มีชื่อไฟล์จำเป็นต้องมี inode และจำนวนของ inodes นั้นแน่นอนแน่นอน เช่นเดียวกับจำนวนหน้าฟิสิคัลในระบบหากไม่มีสิ่งอื่นใด ระบบที่ทันสมัยรับประกันการเขียนแบบอะตอมมิก 4k ดังนั้นแต่ละท่อต้องมีหน้า 4k ที่สมบูรณ์หนึ่งหน้าอย่างน้อยหนึ่งหน้า แต่ลองพิจารณามี RAM สักสองกิกะไบต์ ... ในทางปฏิบัตินั่นเป็นข้อ จำกัด ที่คุณจะไม่มีวันประสบ ลองพิมพ์สองสามล้านท่อบนเทอร์มินัล ... :)
Damon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.