เป็นไปได้อย่างไรที่จะทำการอัพเดทสดขณะที่โปรแกรมกำลังทำงานอยู่?


15

ฉันสงสัยว่าแอปพลิเคชันนักฆ่าเช่น Thunderbird หรือ Firefox สามารถอัปเดตผ่านตัวจัดการแพคเกจของระบบในขณะที่ยังทำงานอยู่ได้ จะเกิดอะไรขึ้นกับรหัสเก่าในขณะที่กำลังอัปเดต ฉันต้องทำอย่างไรเมื่อฉันต้องการเขียนโปรแกรม a.out ที่ปรับปรุงตัวเองในขณะที่มันกำลังทำงานอยู่?



@derobert ไม่ถูกต้อง: เธรดนั้นไม่ได้เข้าสู่ลักษณะเฉพาะของไฟล์ปฏิบัติการ มันให้พื้นหลังที่เกี่ยวข้อง แต่ไม่ซ้ำกัน
Gilles 'หยุดความชั่วร้าย'

@Gilles ดีถ้าคุณปล่อยให้สิ่งที่เหลือมันกว้างเกินไป มีคำถามสองข้อ (อย่างน้อย) ที่นี่ และคำถามอื่น ๆ ก็ค่อนข้างใกล้เคียงมันถามว่าทำไมการแทนที่ไฟล์เพื่ออัปเดตทำงานบน Unix
Derobert

หากคุณต้องการทำสิ่งนี้ด้วยรหัสของคุณเองคุณอาจต้องการดูภาษาการเขียนโปรแกรม Erlang ซึ่งการอัปเดตรหัส "ร้อน" เป็นคุณสมบัติที่สำคัญ learnyousomeerlang.com/relups
mattdm

1
@Gilles BTW: เมื่อเห็นการเขียนเรียงความที่คุณเพิ่มเข้าไปในการตอบกลับฉันได้ถอนการโหวตอย่างใกล้ชิดของฉันแล้ว คุณได้เปลี่ยนคำถามนี้ให้เป็นจุดที่ดีที่จะชี้ให้ทุกคนที่ต้องการทราบว่าการอัปเดตทำงานอย่างไร
Derobert

คำตอบ:


21

การแทนที่ไฟล์โดยทั่วไป

ก่อนอื่นมีกลยุทธ์หลายวิธีในการแทนที่ไฟล์:

  1. เปิดไฟล์ที่มีอยู่เพื่อเขียนตัดให้เหลือ 0 ความยาวและเขียนเนื้อหาใหม่ (ตัวแปรที่พบได้น้อยคือการเปิดไฟล์ที่มีอยู่เขียนทับเนื้อหาเก่าด้วยเนื้อหาใหม่ตัดทอนไฟล์ให้มีความยาวใหม่ถ้ามันสั้นกว่า) ในเงื่อนไขของเชลล์:

    echo 'new content' >somefile
    
  2. ลบไฟล์เก่าและสร้างไฟล์ใหม่ด้วยชื่อเดียวกัน ในเงื่อนไขของเชลล์:

    rm somefile
    echo 'new content' >somefile
    
  3. เขียนไปยังไฟล์ใหม่ภายใต้ชื่อชั่วคราวจากนั้นย้ายไฟล์ใหม่ไปยังชื่อที่มีอยู่ การย้ายจะลบไฟล์เก่า ในเงื่อนไขของเชลล์:

    echo 'new content' >somefile.new
    mv somefile.new somefile
    

ฉันจะไม่แสดงความแตกต่างระหว่างกลยุทธ์ฉันจะพูดถึงสิ่งที่สำคัญที่นี่ ด้วย stategy 1 หากกระบวนการใดกำลังใช้ไฟล์อยู่กระบวนการจะเห็นเนื้อหาใหม่เมื่อกำลังมีการอัปเดต สิ่งนี้อาจทำให้เกิดความสับสนหากกระบวนการคาดว่าเนื้อหาไฟล์จะยังคงเหมือนเดิม โปรดทราบว่านี่เป็นเพียงเกี่ยวกับกระบวนการที่เปิดไฟล์ (เท่าที่เห็นในlsofหรือในแอปพลิเคชันแบบโต้ตอบที่มีการเปิดเอกสาร (เช่นการเปิดไฟล์ในโปรแกรมแก้ไข) โดยปกติจะไม่เปิดไฟล์ไว้ การดำเนินการ“ เปิดเอกสาร” และพวกเขาแทนที่ไฟล์ (ใช้หนึ่งในกลยุทธ์ด้านบน) ในระหว่างการดำเนินการ“ บันทึกเอกสาร”/proc/PID/fd/

ด้วยกลยุทธ์ 2 และ 3 หากกระบวนการบางอย่างsomefileเปิดไฟล์ไฟล์เก่าจะยังคงเปิดอยู่ในระหว่างการอัปเกรดเนื้อหา ด้วยกลยุทธ์ที่ 2 ขั้นตอนการลบไฟล์โดยข้อเท็จจริงแล้วจะลบรายการไฟล์ในไดเรกทอรีเท่านั้น ไฟล์จะถูกลบออกเมื่อไม่มีรายการไดเร็กทอรีที่นำไปสู่ ​​(บนระบบไฟล์ Unix ทั่วไปอาจมีรายการไดเรกทอรีมากกว่าหนึ่งรายการสำหรับไฟล์เดียวกัน ) และไม่มีกระบวนการใดเปิดไว้ ต่อไปนี้เป็นวิธีในการสังเกตสิ่งนี้ - ไฟล์จะถูกลบออกเฉพาะเมื่อsleepกระบวนการหยุดทำงาน ( rmลบเฉพาะรายการไดเรกทอรี)

echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .

ด้วยกลยุทธ์ 3 ขั้นตอนในการย้ายไฟล์ใหม่ไปยังชื่อที่มีอยู่จะลบรายการไดเรกทอรีที่นำไปสู่เนื้อหาเก่าและสร้างรายการไดเรกทอรีที่นำไปสู่เนื้อหาใหม่ สิ่งนี้ทำในการปฏิบัติการแบบอะตอมเดียวดังนั้นกลยุทธ์นี้มีข้อได้เปรียบที่สำคัญ: หากกระบวนการเปิดไฟล์ได้ตลอดเวลามันจะเห็นเนื้อหาเก่าหรือเนื้อหาใหม่ - ไม่มีความเสี่ยงในการผสมเนื้อหาหรือไฟล์ที่ไม่ได้ ที่มีอยู่เดิม.

แทนที่ executables

หากคุณลองกลยุทธ์ที่ 1 ด้วยการเรียกใช้งานได้บน Linux คุณจะได้รับข้อผิดพลาด

cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy

A“แฟ้มข้อความ” หมายความว่าไฟล์ที่มีรหัสปฏิบัติการด้วยเหตุผลทางประวัติศาสตร์ปิดบัง Linux เช่นเดียวกับรุ่นอื่น ๆ ของยูนิกซ์ปฏิเสธที่จะเขียนทับรหัสของโปรแกรมที่กำลังทำงานอยู่ unix บางตัวยอมให้สิ่งนี้นำไปสู่การล่มสลายยกเว้นว่ารหัสใหม่เป็นการดัดแปลงรหัสเก่าที่ดีมาก

บน Linux คุณสามารถเขียนทับรหัสของไลบรารีที่โหลดแบบไดนามิก มีแนวโน้มที่จะนำไปสู่ข้อผิดพลาดของโปรแกรมที่ใช้งานอยู่ (คุณอาจไม่สามารถสังเกตสิ่งนี้ได้sleepเพราะมันจะโหลดรหัสห้องสมุดทั้งหมดที่จำเป็นเมื่อเริ่มใช้งานลองใช้โปรแกรมที่ซับซ้อนกว่าซึ่งทำสิ่งที่มีประโยชน์หลังจากนอนหลับเช่นperl -e 'sleep 9; print lc $ARGV[0]')

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

กลยุทธ์ที่ 2 และ 3 มีความปลอดภัยสำหรับไฟล์ที่เรียกใช้งานเช่นกัน: แม้ว่าการเรียกใช้ไฟล์ปฏิบัติการ (และไลบรารีที่โหลดแบบไดนามิก) จะไม่เปิดไฟล์ในกรณีที่มีตัวให้คำอธิบายไฟล์ แต่จะทำงานในลักษณะเดียวกัน ตราบใดที่บางโปรแกรมกำลังรันโค้ดไฟล์จะยังคงอยู่บนดิสก์แม้ไม่มีรายการไดเรกทอรี

การอัพเกรดแอปพลิเคชัน

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

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

  1. อินสแตนซ์ของแอปพลิเคชันเริ่มต้นแล้ว
  2. แอปพลิเคชันได้รับการอัพเกรด
  3. แอปพลิเคชันอินสแตนซ์ที่ทำงานอยู่เปิดไฟล์ข้อมูลหนึ่งไฟล์

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

หลังจากอัปเกรดคุณจะทราบว่าโปรแกรมเดิมยังคงทำงานอยู่ หากคุณต้องการรันเวอร์ชันใหม่คุณจะต้องออกจากโปรแกรมเดิมและรันเวอร์ชันใหม่ ผู้จัดการแพคเกจมักจะฆ่าและรีสตาร์ท daemons ในการอัปเกรด แต่ปล่อยให้แอปพลิเคชันผู้ใช้ปลายทางเพียงอย่างเดียว

daemons สองสามตัวมีโพรซีเดอร์พิเศษเพื่อจัดการกับการอัพเกรดโดยไม่ต้องฆ่า daemon และรอให้อินสแตนซ์ใหม่เริ่มต้นใหม่ (ซึ่งทำให้บริการขัดข้อง) นี่เป็นสิ่งจำเป็นในกรณีของinitซึ่งไม่สามารถฆ่าได้ ระบบ init จัดเตรียมวิธีในการร้องขอให้การเรียกใช้อินสแตนซ์ที่ทำงานexecveเพื่อแทนที่ตัวเองด้วยเวอร์ชันใหม่


"จากนั้นย้ายไฟล์ใหม่ไปยังชื่อที่มีอยู่การย้ายจะลบไฟล์เก่า" นั่นเป็นเพียงความสับสนเล็กน้อยเพราะมันเป็นเพียงแค่ในunlinkขณะที่คุณครอบคลุมในภายหลัง อาจจะ "แทนที่ชื่อเดิม" แต่ก็ยังค่อนข้างสับสน
Derobert

@derobert ฉันไม่ต้องการหนักลงบนคำศัพท์ "ยกเลิกการเชื่อมโยง" ฉันใช้ "ลบ" ในการอ้างอิงถึงรายการไดเรกทอรีความละเอียดซึ่งจะอธิบายในภายหลัง มันสับสนในขั้นตอนนั้นหรือไม่?
Gilles 'หยุดความชั่วร้าย'

อาจไม่สับสนพอที่จะรับประกันย่อหน้าพิเศษหรือสองอันบนอธิบายการยกเลิกการเชื่อมโยง ฉันหวังว่าจะใช้ถ้อยคำที่ไม่สับสน แต่ก็มีความถูกต้องทางเทคนิค อาจจะเพียงแค่ใช้ "ลบ" อีกครั้งซึ่งคุณได้ใส่ลิงค์เพื่ออธิบาย?
Derobert

3

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

คำอธิบาย:บนระบบ Linux ไฟล์เป็นเพียงไอโหนดซึ่งสามารถมีลิงค์ได้หลายลิงค์ เช่น. ที่/bin/bashคุณเห็นเป็นเพียงลิงค์ไปยังinode 3932163ระบบของฉัน คุณสามารถค้นหาสิ่งที่ไอโหนดทำลิงก์ไปโดยออกls --inode /pathลิงค์ ไฟล์ (inode) จะถูกลบออกหากไม่มีลิงค์เชื่อมโยงที่ชี้ไปและไม่ได้ใช้งานโดยโปรแกรมใด ๆ เมื่อตัวจัดการแพ็คเกจอัพเกรดเช่น /usr/bin/firefoxมันเป็นการยกเลิกการเชื่อมโยงครั้งแรก (ลบฮาร์ดลิงก์/usr/bin/firefox) จากนั้นสร้างไฟล์ใหม่ที่เรียก/usr/bin/firefoxว่า hardlink ไปยัง inode อื่น (อันที่มีfirefoxเวอร์ชั่นใหม่) ขณะนี้ไอโหนดเก่าถูกทำเครื่องหมายว่าว่างและสามารถนำมาใช้ซ้ำเพื่อจัดเก็บข้อมูลใหม่ แต่ยังคงอยู่บนดิสก์ (ไอโหนดจะถูกสร้างขึ้นเฉพาะเมื่อคุณสร้างระบบไฟล์ของคุณและจะไม่ถูกลบ) ในการเริ่มต้นต่อไปของfirefoxใหม่จะถูกนำมาใช้

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


1
ที่จริงแล้วมันเป็นเพราะการลบ (unlinking) ไฟล์ที่ทำงานบน Unix; ดูunix.stackexchange.com/questions/49299/ …อย่างน้อยบน Linux คุณไม่สามารถเขียนไปยังไบนารีที่กำลังทำงานอยู่ได้จริงคุณจะได้รับข้อผิดพลาด "ไฟล์ข้อความไม่ว่าง"
Derobert

แปลก ... แล้วจะเป็นเช่นไร การaptอัปเกรดของ Debian ทำงานอย่างไร ฉันสามารถอัพเกรดโปรแกรมที่รันอยู่โดยไม่มีปัญหารวมถึงIceweasel( Firefox)
psimon

2
APT (หรือค่อนข้างdpkg) จะไม่เขียนทับไฟล์ มันจะยกเลิกการเชื่อมโยงพวกเขาและวางใหม่ภายใต้ชื่อเดียวกัน ดูคำถาม & คำตอบที่ฉันเชื่อมโยงเพื่อขอคำอธิบาย
Derobert

2
ไม่ใช่เพียงเพราะยังอยู่ใน RAM แต่ยังอยู่ในดิสก์ ไฟล์จะไม่ถูกลบออกไปจนกว่าอินสแตนซ์สุดท้ายของโปรแกรมจะออก
Derobert

2
ไซต์จะไม่ให้ฉันแนะนำการแก้ไข (เมื่อตัวแทนของคุณสูงพอคุณเพิ่งทำการแก้ไขคุณไม่สามารถแนะนำได้อีกต่อไป) ดังนั้นตามความเห็น: ไฟล์ (inode) ในระบบ Unix มักจะมีชื่อเดียว แต่อาจมีมากกว่านี้ถ้าคุณเพิ่มชื่อด้วยln(ฮาร์ดลิงก์) คุณสามารถลบชื่อด้วยrm(unlink) คุณไม่สามารถลบไฟล์ได้โดยตรงเพียงลบชื่อออกเท่านั้น เมื่อไฟล์ไม่มีชื่อและไม่ได้เปิดเพิ่มเติมเคอร์เนลจะลบออก โปรแกรมที่กำลังทำงานมีไฟล์ที่เปิดอยู่จากการเปิดดังนั้นแม้หลังจากลบชื่อทั้งหมดแล้วไฟล์ก็ยังคงอยู่
Derobert

0

ฉันสงสัยว่าแอปพลิเคชันนักฆ่าเช่น Thunderbird หรือ Firefox สามารถอัปเดตผ่านตัวจัดการแพคเกจของระบบในขณะที่ยังทำงานอยู่ได้อย่างไร ฉันสามารถบอกคุณได้ว่านี่ใช้งานไม่ได้จริง ๆ ... ฉันมี Firefox ทำให้ฉันค่อนข้างแย่ถ้าเปิดทิ้งไว้ในขณะที่การอัปเดตแพคเกจทำงานอยู่ ฉันต้องฆ่ามันอย่างแรงในบางครั้งและทำการรีสตาร์ทเพราะมันเสียจนฉันไม่สามารถปิดได้อย่างถูกต้อง

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

ฉันต้องทำอย่างไรเมื่อฉันต้องการเขียนโปรแกรม a.out ที่อัพเดตตัวเองในขณะที่มันกำลังทำงานอยู่? มีคำตอบสำหรับคำถามของคุณอยู่แล้วจำนวนมาก ลองดูสิ: /programming/232347/how-should-i-implement-an-auto-updater โดยวิธีการคำถามเกี่ยวกับการเขียนโปรแกรมจะดีกว่าใน StackOverflow


2
ไฟล์ที่เรียกใช้งานได้นั้นเป็นเพจที่ต้องการจริง (สลับสับเปลี่ยน) พวกเขาไม่ได้โหลดเข้าสู่หน่วยความจำทั้งหมดและอาจถูกทิ้งจากหน่วยความจำเมื่อใดก็ตามที่ระบบต้องการ RAM สำหรับอย่างอื่น เวอร์ชั่นเก่ายังคงอยู่บนดิสก์ ดูunix.stackexchange.com/questions/49299/... อย่างน้อยใน Linux คุณไม่สามารถเขียนลงไฟล์ที่รันได้จริงคุณจะได้รับข้อผิดพลาด "ไฟล์ข้อความไม่ว่าง" แม้แต่รากไม่สามารถทำได้ (คุณค่อนข้างถูกต้องเกี่ยวกับ Firefox)
Derobert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.