บนระบบ Unix ทำไมเราต้อง `open () 'และ` close () `ไฟล์อย่างชัดเจนเพื่อให้สามารถอ่านไฟล์ read ()` `หรือ` write ()' ได้


50

ทำไมopen()และclose()มีอยู่ในการออกแบบระบบไฟล์ Unix

ระบบปฏิบัติการไม่สามารถตรวจพบในครั้งแรกread()หรือwrite()ถูกเรียกและทำสิ่งใดopen()ตามปกติ


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

18
ฉันคิดว่านี่เป็นสาเหตุที่open()มีอยู่จริง "ระบบปฏิบัติการไม่สามารถตรวจพบในครั้งแรกที่อ่าน () หรือเขียน () และทำสิ่งใดที่เปิดได้ () โดยปกติแล้วจะทำอย่างไร" มีคำแนะนำที่สอดคล้องกันสำหรับการปิดเมื่อใดจะเกิดขึ้น?
Joshua Taylor

7
คุณจะบอกread()หรือwrite()ไฟล์ใดให้เข้าถึง? สันนิษฐานได้โดยผ่านเส้นทาง จะเกิดอะไรขึ้นถ้าเส้นทางของไฟล์เปลี่ยนไปในขณะที่คุณกำลังเข้าถึง (ระหว่างสองสายread()หรือwrite()เรียก)
user253751

2
นอกจากนี้คุณมักจะไม่ได้ทำในการควบคุมการเข้าถึงread()และเพียงแค่บนwrite() open()
Pavel Šimerda

6
@ จอห์นนี่: บางทีคุณอาจลืมว่าฮาร์ดแวร์มีข้อ จำกัด ในสมัยนั้นอย่างไร PDP-7 ที่ Unix ถูกนำมาใช้ครั้งแรกมี (ต่อ Google) สูงสุด 64K of RAM และนาฬิกา 0.333 MHz - น้อยกว่าไมโครคอนโทรลเลอร์ที่เรียบง่ายในปัจจุบัน การทำเช่นนี้การรวบรวมขยะหรือการใช้รหัสระบบเพื่อตรวจสอบการเข้าถึงไฟล์จะนำระบบไปที่หัวเข่า
jamesqf

คำตอบ:


60

เดนนิสริตชี่กล่าวถึงใน«วิวัฒนาการของการแบ่งเวลา Unix ระบบ»ที่openและcloseพร้อมด้วยread, writeและcreatได้รับอยู่ในระบบตั้งแต่เริ่มต้น

ฉันเดาระบบที่ไม่มีopenและcloseจะไม่นึกไม่ถึงอย่างไรก็ตามฉันเชื่อว่ามันจะทำให้การออกแบบซับซ้อนขึ้น โดยทั่วไปคุณต้องการทำการโทรแบบอ่านและเขียนหลายครั้งไม่ใช่เพียงครั้งเดียวและอาจเป็นจริงโดยเฉพาะอย่างยิ่งในคอมพิวเตอร์เครื่องเก่าที่มี RAM จำกัด ที่ UNIX กำเนิด การมีหมายเลขอ้างอิงที่รักษาตำแหน่งไฟล์ปัจจุบันของคุณจะทำให้สิ่งนี้ง่ายขึ้น ถ้าreadหรือwriteพวกเขาจะต้องส่งคืนคู่จับและสถานะการส่งคืนของตนเอง ส่วนที่จับของทั้งคู่จะไร้ประโยชน์สำหรับการโทรอื่น ๆ ทั้งหมดซึ่งจะทำให้การจัดเรียงที่น่าอึดอัดใจ การปล่อยสถานะของเคอร์เซอร์ไปที่เคอร์เนลช่วยให้สามารถปรับปรุงประสิทธิภาพไม่เพียง แต่การบัฟเฟอร์เท่านั้น นอกจากนี้ยังมีค่าใช้จ่ายที่เกี่ยวข้องกับการค้นหาเส้นทาง - การมีจุดจับช่วยให้คุณจ่ายได้เพียงครั้งเดียว นอกจากนี้บางไฟล์ในมุมมองของ UNIX ไม่ได้มีเส้นทางระบบแฟ้ม (หรือไม่ - ตอนนี้พวกเขาทำกับสิ่งที่ต้องการ/proc/self/fd)


7
ค่าใช้จ่ายในการค้นหาเส้นทางและการตรวจสอบการอนุญาต ฯลฯ เป็นสิ่งสำคัญมาก หากคุณต้องการสร้างระบบที่ไม่มีopen/ closeคุณต้องแน่ใจว่าใช้สิ่งที่ต้องการ/dev/stdoutอนุญาตให้มีการวางท่อ
Peter Cordes

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

2
ฉันออกแบบโดยไม่ปิด (); คุณผ่านหมายเลขไอโหนดและชดเชยการอ่าน () และเขียน () ฉันไม่สามารถทำได้โดยไม่เปิด () ได้ง่ายมากเพราะนั่นคือที่การแก้ไขชื่อ
Joshua

3
@ โจชัว: ระบบดังกล่าวมีความหมายที่แตกต่างกันโดยพื้นฐานเพราะคำอธิบายไฟล์ยูนิกซ์ไม่ได้อ้างถึงไฟล์ (inodes) แต่เพื่อเปิดคำอธิบายไฟล์ซึ่งอาจมีจำนวนมากสำหรับไฟล์ที่กำหนด (inode)
..

@Joshua คุณเพียงแค่เปลี่ยนชื่อopen()ไปget_inode()และทำให้ทั้งระบบเข้มงวดมากขึ้น (เป็นไปไม่ได้ที่จะอ่าน / เขียนไฟล์เดียวกันในหลายตำแหน่งพร้อมกัน)
vonbrand

53

จากนั้นทั้งหมดreadและการwriteโทรจะต้องผ่านข้อมูลนี้ในการดำเนินการแต่ละครั้ง:

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

ไม่ว่าคุณจะพิจารณาเป็นอิสระโทร open , read, writeและcloseที่จะง่ายกว่าเดียววัตถุประสงค์ของ I / O ข้อความอยู่บนพื้นฐานของปรัชญาการออกแบบของคุณ ผู้พัฒนา Unix เลือกที่จะใช้การทำงานแบบง่ายและโปรแกรมที่สามารถรวมกันได้หลายวิธีมากกว่าการทำงานเพียงครั้งเดียว (หรือโปรแกรม) ที่ทำทุกอย่าง


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

5
"ไฟล์" อาจไม่มีชื่อหรือการอนุญาตในตอนแรก readและwriteไม่ จำกัด เฉพาะไฟล์ที่อยู่บนระบบไฟล์และนั่นเป็นการตัดสินใจขั้นพื้นฐานใน Unix ตามที่ pjc50 อธิบาย
reinierpost

1
นอกจากนี้ที่ที่ไฟล์จะอ่าน / เขียน - จุดเริ่มต้นจุดสิ้นสุดหรือตำแหน่งโดยพลการ (โดยทั่วไปจะอยู่หลังสิ้นสุดการอ่าน / เขียนครั้งสุดท้าย) - เคอร์เนลจะติดตามสิ่งนี้ให้คุณ (ด้วยโหมด นำการเขียนทั้งหมดไปยังจุดสิ้นสุดของไฟล์หรือมิฉะนั้นไฟล์จะถูกเปิดด้วยตำแหน่งที่จุดเริ่มต้นและขั้นสูงโดยแต่ละการอ่าน / เขียนและสามารถย้ายได้lseek)
Random832

51

แนวคิดของการจัดการไฟล์มีความสำคัญเนื่องจากตัวเลือกการออกแบบของ UNIX ที่ "ทุกอย่างเป็นไฟล์" รวมถึงสิ่งที่ไม่ได้เป็นส่วนหนึ่งของระบบไฟล์ เช่นไดรฟ์เทปแป้นพิมพ์และหน้าจอ (หรือโทรพิมพ์!) เครื่องอ่านการ์ด / เทปที่ต่อยการเชื่อมต่อแบบอนุกรมการเชื่อมต่อเครือข่ายและ (การประดิษฐ์ UNIX สำคัญ) เชื่อมต่อโดยตรงกับโปรแกรมอื่น ๆ ที่เรียกว่า "ไพพ์"

ถ้าคุณดูที่หลายสาธารณูปโภค UNIX ง่ายมาตรฐานเช่นgrepโดยเฉพาะอย่างยิ่งในรุ่นเดิมของพวกเขาคุณจะสังเกตเห็นว่าพวกเขาไม่ได้รวมถึงการโทรไปopen()และclose()แต่เพียงและread writeตัวจัดการไฟล์ถูกตั้งค่านอกโปรแกรมโดยเชลล์และส่งต่อเมื่อเริ่มทำงาน ดังนั้นโปรแกรมไม่จำเป็นต้องสนใจว่าจะเขียนไปยังไฟล์หรือไปยังโปรแกรมอื่น

เช่นเดียวกับopenที่วิธีการอื่น ๆ ในการได้รับการอธิบายไฟล์มีsocket, listen, pipe, dupและมากกลไก Heath โรบินสันสำหรับการส่งอธิบายไฟล์ผ่าน pipes: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -เบ้า

แก้ไข: บันทึกการบรรยายบางบทที่อธิบายถึงเลเยอร์ของการอ้อมและวิธีนี้ทำให้ O_APPEND ทำงานได้อย่างสมเหตุสมผล โปรดทราบว่าการเก็บข้อมูล inode ในหน่วยความจำรับประกันว่าระบบไม่จำเป็นต้องไปและดึงข้อมูลอีกครั้งสำหรับการเขียนครั้งต่อไป


1
นอกจากนี้creatและlistenไม่สร้าง fd แต่เมื่อ (และถ้า) คำขอเข้ามาในขณะที่ฟังacceptสร้างและส่งคืน fd สำหรับซ็อกเก็ต (เชื่อมต่อ) ใหม่
dave_thompson_085

18
นี่คือคำตอบที่ถูกต้อง ชุดการดำเนินการที่มีชื่อเสียง (เล็ก) บนตัวให้คำอธิบายไฟล์เป็น API ที่รวมเข้าด้วยกันสำหรับทรัพยากรทุกประเภทที่ผลิตหรือใช้ข้อมูล แนวคิดนี้ประสบความสำเร็จอย่างมาก สตริงอาจมีไวยากรณ์ที่กำหนดประเภททรัพยากรพร้อมกับตำแหน่งที่แท้จริง (URL ใคร?) แต่การคัดลอกสตริงรอบ ๆ ซึ่งครอบครองหลายเปอร์เซ็นต์ของ RAM ที่มีอยู่ (สิ่งที่อยู่ใน PDP 7? 16 kB?) ดูเหมือนมากเกินไป .
ปีเตอร์ - Reinstate Monica

อาจเป็นไปได้ว่าหากการโทรระดับต่ำและเชลล์ได้รับการพัฒนาในเวลาเดียวกัน แต่pipeถูกแนะนำไม่กี่ปีหลังจากการพัฒนาบน Unix เริ่มต้น
Thomas Dickey

1
@Thomas Dickey: ซึ่งแสดงให้เห็นเพียงว่าการออกแบบดั้งเดิมนั้นดีเพียงใดเนื่องจากได้รับอนุญาตให้ขยายส่วนต่อไปยังท่อ & c :-) ได้อย่างง่ายดาย
jamesqf

แต่ตามอาร์กิวเมนต์บรรทัดคำตอบนี้ไม่ได้ให้อะไรใหม่
Thomas Dickey

10

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

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


7

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

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


5

เนื่องจากเส้นทางของไฟล์อาจเคลื่อนไหวในขณะที่คุณสมมติว่ามันจะยังคงเหมือนเดิม


4

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

หากระบบปฏิบัติการไม่ได้เปิด / ปิดจากนั้นด้วยreadหรือwriteการกระทำของไฟล์เหล่านั้นจะยังคงต้องดำเนินการเริ่มต้นใด ๆ การล้าง / จัดการบัฟเฟอร์ ฯลฯ ทุกครั้ง นั่นเป็นค่าใช้จ่ายมากมายในการกำหนดให้มีการอ่านและเขียนซ้ำ ๆ


อย่าลืมว่า open () และ close () ยังคงตำแหน่งในไฟล์ (สำหรับการอ่านครั้งถัดไปหรือการเขียนครั้งถัดไป) ดังนั้นในตอนท้ายหรือ read () และ write () จะต้องมี struct เพื่อจัดการพารามิเตอร์ทั้งหมดหรือต้องการอาร์กิวเมนต์สำหรับแต่ละพารามิเตอร์ การสร้างโครงสร้างนั้นเทียบเท่า (โปรแกรมเมอร์ไซต์) กับ open ดังนั้นหาก OS ทราบเกี่ยวกับ open เรามีข้อดีมากกว่าเท่านั้น
Giacomo Catenazzi

1

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

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