ทำไม“ กระบวนการต้องไม่แยก” สำหรับบริการประเภทธรรมดาใน systemd?

Aug 15 2020

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

Type=simple(ค่าเริ่มต้น): systemd ถือว่าบริการเริ่มต้นทันที กระบวนการนี้จะต้องไม่แยก อย่าใช้ประเภทนี้หากจำเป็นต้องสั่งซื้อบริการอื่นในบริการนี้เว้นแต่จะเปิดใช้งานซ็อกเก็ต

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


1ฉันไม่ต้องการ tmux / tmux send-keysจอเพราะผมต้องการวิธีที่สง่างามมากขึ้นในการตรวจสอบสถานะและรีสตาร์ทบริการโดยไม่ต้อง

คำตอบ

41 Gilles'SO-stopbeingevil' Aug 15 2020 at 17:29

บริการได้รับอนุญาตให้โทรเข้าforkระบบ Systemd จะไม่ป้องกันหรือแม้กระทั่งสังเกตว่าเป็นเช่นนั้น ประโยคนี้อ้างอิงโดยเฉพาะถึงการฝึกฝนการตีที่จุดเริ่มต้นของ daemon เพื่อแยก daemon ออกจากกระบวนการพาเรนต์ “ กระบวนการต้องไม่แยก [และออกจากพาเรนต์ในขณะที่เรียกใช้บริการในกระบวนการย่อย]”

หน้าคนอธิบายมากกว่านี้ verbosely และด้วยถ้อยคำที่ไม่นำไปสู่ความสับสนนี้โดยเฉพาะอย่างยิ่ง

โปรแกรมจำนวนมากที่ใช้เป็น daemons มีโหมด (มักเป็นโหมดเริ่มต้น) ซึ่งเมื่อเริ่มต้นโปรแกรมจะแยกตัวเองออกจากผู้ปกครอง daemon เริ่มต้นการเรียกใช้fork()และพาเรนต์ออก กระบวนการลูกเรียกsetsid()เพื่อให้รันในกลุ่มกระบวนการและเซสชันของตนเองและเรียกใช้บริการ จุดประสงค์คือหากเรียกใช้ daemon จากบรรทัดคำสั่งเชลล์ daemon จะไม่ได้รับสัญญาณใด ๆ จากเคอร์เนลหรือจากเชลล์แม้ว่าจะมีบางอย่างเกิดขึ้นกับเทอร์มินัลเช่นการปิดเทอร์มินัล (ในกรณีนี้เชลล์จะส่ง SIGHUP ไปยังกลุ่มกระบวนการทั้งหมดที่รู้จัก) นอกจากนี้ยังทำให้กระบวนการให้บริการถูกนำมาใช้โดย init ซึ่งจะเก็บเกี่ยวเมื่อมันออกโดยหลีกเลี่ยงซอมบี้หาก daemon เริ่มต้นด้วยสิ่งที่ไม่wait()ต้องการ (สิ่งนี้จะไม่เกิดขึ้นหาก daemon เริ่มต้นโดยเชลล์ ).

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

15 JdeBP Aug 16 2020 at 11:59

ละเว้นหน้า Arch wiki นี้

มันมีสิ่งผิดปกติอย่างมากเกี่ยวกับการTypeตั้งค่า สิ่งนี้ไม่ได้ จำกัด เพียงแค่คำอธิบายsimpleเท่านั้นยิ่งไปกว่านั้น สิ่งที่กล่าวถึงforkingก็ผิดเช่นกัน

คำแนะนำที่ถูกต้องสำหรับสิ่งประเภทนี้มีมานานหลายทศวรรษแล้วกว่าที่ systemd จะมีอยู่และย้อนกลับไปอย่างน้อยก็ต้นปี 1990 ตามที่ฉันสังเกตเห็นhttps://unix.stackexchange.com/a/476608/5132ใน systemd doco มีคำแนะนำสำหรับdæmonsรุ่น Johnny-come-เมื่อเร็ว ๆ นี้ซึ่งส่วนใหญ่จะทำซ้ำสิ่งที่ผู้ใช้ daemontools, IBM, ผู้คนใช้inittabและ ... ดี ... ฉันพูดมาหลายสิบปีแล้ว (เป็นคำตอบที่ได้รับบ่อยอยู่แล้วเมื่อฉันเขียนขึ้นในปี 2544)

ทำซ้ำ:

ถ้าโปรแกรมของคุณมีบางส่วน "dæmonization" กลไกว่าในงาโดยเฉพาะอย่างยิ่งเด็กและออกจากการปกครองที่จะปิดและไม่ได้ใช้มัน ขอบคุณข้อมูล daemontools et al ซึ่งเป็นข้อกำหนดมาเป็นเวลานานหลายโปรแกรมได้เพิ่มความสามารถที่จะไม่มีกลไกดังกล่าวในช่วงกว่า 20 ปีที่ผ่านมาและอื่น ๆ ก็ไม่ได้ตั้งต้นที่จะ "dæmonizing" ตั้งแต่แรกเพื่อให้สามารถใช้ใน โหมดการทำงานเริ่มต้น

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

ต้องการType=simpleด้วยการเปิดซ็อกเก็ตในช่วงต้น (ซึ่งการจัดการบริการเปิดซ็อกเก็ตเซิร์ฟเวอร์และส่งต่อเป็นตัวบอกไฟล์ที่เปิดอยู่แล้วไปยังโปรแกรมบริการ) หรือType=notify.

  • Type=simple ถือว่าบริการพร้อม (เพื่อให้บริการที่สั่งซื้อตามสามารถเริ่ม / หยุดได้) ทันทีที่กระบวนการบริการเริ่มต้นด้วยการเปิดซ็อกเก็ตก่อนกำหนดโดยใช้ความหมายการเชื่อมต่อซ็อกเก็ตเพื่อชะลอการให้บริการไคลเอ็นต์ ณ จุดที่พยายามเชื่อมต่อกับเซิร์ฟเวอร์สำหรับ บริการจนกว่าเซิร์ฟเวอร์จะพร้อมใช้งานจริง
  • Type=notifyมีข้อเสียที่แปลกประหลาดสำหรับ systemd และ Linux (ควบคู่ไปกับปัญหาการใช้งานไม่ได้จากกระบวนการที่มีอายุสั้นเช่นการวางไข่ของเชลล์systemd-notifyและการใช้การแยกวิเคราะห์รูปแบบที่มนุษย์สามารถอ่านได้ไปยังรูปแบบที่เครื่องอ่านได้ในกระบวนการที่มีสิทธิพิเศษโดยที่ ปัญหาการแยกวิเคราะห์ได้เกิดขึ้นแล้วในอดีต) แต่มีข้อได้เปรียบในการให้การควบคุมที่ละเอียดยิ่งขึ้น (จากมุมมองของโปรแกรมบริการ) เมื่อบริการได้รับการพิจารณาว่าพร้อมใช้งานจริง นอกจากนี้ยังช่วยให้สามารถปรับแต่งเอาต์พุตสถานะได้

โปรแกรมบริการทั้งสองประเภทสามารถแยก เป็นการฟอร์คแล้วออกจากกระบวนการเดิมที่เป็นปัญหา

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

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

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

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

  • โจนาธานเดอบอยน์พอลลาร์ด (2544) ข้อผิดพลาดที่ควรหลีกเลี่ยงเมื่อออกแบบโปรแกรมภูติยูนิกซ์ คำตอบที่ได้รับบ่อยครั้ง
  • โจนาธานเดอบอยน์พอลลาร์ด (2015). คุณไม่จำเป็นต้องแสดงผล จริงๆ. . บ้านแห่งความสยองขวัญ systemd
  • โจนาธานเดอบอยน์พอลลาร์ด (2015). ปัญหาโปรโตคอลการเตรียมพร้อมกับdæmonsยูนิกซ์ คำตอบที่ได้รับบ่อยครั้ง
  • https://unix.stackexchange.com/a/401611/5132