กระบวนการแบบไม่หยุดชะงักคืออะไร


156

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

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

เป็นไปได้ไหมว่าโปรแกรมสามารถเขียนขึ้นเพื่อเริ่มต้นกระบวนการที่เข้าสู่TASK_UNINTERUPTIBLEสถานะเมื่อใดก็ตามที่ระบบไม่ได้อยู่ในสถานะว่างจึงบังคับให้รวบรวมข้อมูลรอส่งเมื่อผู้ใช้ระดับสูงออกจากการทำงานหรือไม่ นี่จะเป็นทองคำสำหรับแฮ็กเกอร์ที่จะดึงข้อมูลกลับสู่สถานะซอมบี้และส่งข้อมูลผ่านเครือข่ายที่ว่าง บางคนสามารถโต้แย้งว่านี่เป็นวิธีหนึ่งในการสร้างBlackdoorพลังที่จะเข้าและออกจากระบบใด ๆ ตามที่ต้องการ ฉันเชื่ออย่างยิ่งว่าช่องโหว่นี้สามารถถูกปิดผนึกได้อย่างดีโดยกำจัด `TASK_UNINTERUPTIB
Nuuwski

2
จะโปรดแบ่งปันรหัสหรือไม่
อีกครั้ง

คำตอบ:


198

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

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

ในขณะที่กระบวนการกำลังหลับอยู่ในการเรียกของระบบมันสามารถรับสัญญาณ Unix asynchronous (พูด, SIGTERM) จากนั้นเกิดเหตุการณ์ต่อไปนี้:

  • ระบบเรียกออกก่อนเวลาอันควรและตั้งค่าให้ส่งคืน -EINTR ไปยัง userspace
  • ตัวจัดการสัญญาณดำเนินการ
  • หากกระบวนการยังคงทำงานอยู่จะได้รับค่าส่งคืนจากการเรียกของระบบและสามารถทำการโทรเดียวกันอีกครั้ง

การกลับมาก่อนจากการเรียกของระบบทำให้รหัสพื้นที่ผู้ใช้สามารถเปลี่ยนพฤติกรรมการตอบสนองต่อสัญญาณได้ทันที ตัวอย่างเช่นการยกเลิกอย่างสมบูรณ์ในการตอบสนองต่อ SIGINT หรือ SIGTERM

ในทางกลับกันการเรียกของระบบบางอย่างไม่ได้รับอนุญาตให้ถูกขัดจังหวะด้วยวิธีนี้ หากระบบเรียกแผงลอยด้วยเหตุผลบางอย่างกระบวนการสามารถอยู่ในสถานะไม่มีทักษะได้โดยไม่มีกำหนด

LWN เป็นบทความที่ดีที่แตะหัวข้อนี้ในเดือนกรกฎาคม

ในการตอบคำถามเดิม:

  • วิธีการป้องกันไม่ให้เกิดขึ้น: หาว่าไดรเวอร์ตัวใดทำให้คุณเดือดร้อนและหยุดใช้หรือกลายเป็นแฮกเกอร์เคอร์เนลแล้วซ่อมมัน

  • วิธีฆ่ากระบวนการที่ไม่สามารถขัดจังหวะได้โดยไม่ต้องเริ่มระบบใหม่: ทำให้การเรียกของระบบสิ้นสุดลง บ่อยครั้งที่วิธีที่มีประสิทธิภาพที่สุดในการทำเช่นนี้โดยไม่ได้กดปุ่มเปิด / ปิดเครื่องคือการดึงสายไฟ คุณยังสามารถเป็นแฮกเกอร์เคอร์เนลและทำให้ไดรเวอร์ใช้ TASK_KILLABLE ตามที่อธิบายในบทความ LWN


31
ฉันดึงสายไฟบนแล็ปท็อปของฉันและมันไม่ทำงานเศร้า ;-)
thecarpy

1
ไม่ใช่ EINTR แทนที่จะเป็น EAGAIN ใช่ไหม ยังอ่าน () ส่งคืน -1 และ errno ถูกตั้งค่าเป็นข้อผิดพลาด
ตาย

2
@ ด้านขวา: คุณพลาดจุดแน่นอน อ่านบทความ LWN นี้: lwn.net/Articles/288056 ปัญหาเหล่านั้นเกิดจากโปรแกรมเมอร์โปรแกรมควบคุมอุปกรณ์ที่ขี้เกียจและพวกเขาจำเป็นต้องได้รับการแก้ไขในรหัสไดรเวอร์อุปกรณ์
ddaa

4
@ddaa "ประเพณี Unix (และแอปพลิเคชั่นเกือบทั้งหมด) เชื่อว่าที่เก็บไฟล์เขียนเป็นสัญญาณที่ไม่ถูกขัดจังหวะมันจะไม่ปลอดภัยหรือสามารถเปลี่ยนการรับประกันได้" -> นี่เป็นส่วนที่ผิดพลาดมากที่สุดของ IMO นี้ทั้งหมด เพียงขัดจังหวะการร้องขอการอ่าน / เขียนของไดรเวอร์และเมื่ออุปกรณ์จริง (ฮาร์ดดิสก์ / การ์ดเครือข่าย / ฯลฯ ) ส่งข้อมูลให้เพิกเฉย เคอร์เนลระบบปฏิบัติการควรทำในลักษณะที่ไม่มีนักพัฒนาสามารถทำให้พลาด
เด็กซ์เตอร์

2
@ddaa ฉันรู้ว่า Linux ไม่ใช่ microkernel ถึงแม้ว่าฉันไม่แน่ใจว่าส่วนใดของความคิดเห็นของฉันเกี่ยวข้องกับมัน ... จากนั้นความคิดเห็นของคุณหมายความว่า microkernel OS ไม่มีปัญหากับกระบวนการ "ไม่หยุดชะงัก" เหล่านั้นหรือไม่ เพราะถ้ามันไม่ได้อาจเป็นเวลาที่ฉันจะกลายเป็นแฟน microkernel ... : D
Dexter

49

เมื่อกระบวนการอยู่ในโหมดผู้ใช้สามารถถูกขัดจังหวะได้ตลอดเวลา (เปลี่ยนเป็นโหมดเคอร์เนล) เมื่อเคอร์เนลกลับสู่โหมดผู้ใช้จะตรวจสอบว่ามีสัญญาณใด ๆ ที่ค้างอยู่ (รวมถึงสัญญาณที่ใช้ในการฆ่ากระบวนการเช่นSIGTERMและSIGKILL) นี่หมายความว่ากระบวนการสามารถถูกฆ่าได้เมื่อกลับสู่โหมดผู้ใช้เท่านั้น

เหตุผลที่กระบวนการไม่สามารถฆ่าได้ในโหมดเคอร์เนลคืออาจทำให้โครงสร้างเคอร์เนลที่ใช้โดยกระบวนการอื่น ๆ ทั้งหมดในเครื่องเดียวกันเสียหายได้ (เช่นเดียวกับการฆ่าเธรดอาจทำให้โครงสร้างข้อมูลเสียหายที่ใช้โดยเธรดอื่นในกระบวนการเดียวกัน) .

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

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

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

TASK_KILLABLE (กล่าวถึงในบทความ LWN ที่เชื่อมโยงกับคำตอบของ dda) เป็นตัวแปรใหม่

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

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


1
ข้อผิดพลาดในการล็อกระบบไฟล์ยังเป็นสาเหตุที่ทำให้เกิด IME
Tobu

3
ฉันไม่เข้าใจทั้งหมดนี้ "คุณไม่สามารถหลีกเลี่ยงการนอนหลับไม่สามารถหยุดชะงัก" ได้ - ระบบปฏิบัติการไม่สามารถทำในลักษณะที่การนอนหลับแบบต่อเนื่องไม่ได้อยู่ในสถานะใช่หรือไม่? จากนั้นส่วนที่เกี่ยวกับความเสียหาย - ส่วนเคอร์เนลของกระบวนการไม่สามารถดำเนินการเองได้ (หรือสิ่งใดที่ทำให้เกิดความเสียหาย) หรือยกเลิกการแก้ไขโค้ดในหน่วยความจำเพื่อส่งคืนหรือไม่ โปรดอธิบายว่าทำไมการทำเช่นนี้จึงเป็นเรื่องยาก / เป็นไปไม่ได้แม้แต่ Linux ก็ยังไม่ได้ทำ (ฉันคิดว่าปัญหานี้มีอยู่ใน Windows เท่านั้น)
Dexter

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

@Dexter คิดว่าเคอร์เนลราวกับว่ามันเป็นกระบวนการแบบมัลติเธรดเดียวโดยที่ส่วนโหมดเคอร์เนลของแต่ละกระบวนการเป็นเธรดภายในเคอร์เนล ข้อเสนอแนะของคุณอาจไม่ดีเท่ากับการฆ่าเธรดเดี่ยวในโปรแกรมแบบมัลติเธรด: มันอาจปล่อยให้ล็อกห้อยต่องแต่งโครงสร้างข้อมูลชั่วคราวหรือในระหว่างการแก้ไขและอื่น ๆ
CesarB

@ CesarB คุณถูกต้องเกี่ยวกับการฆ่าเธรด ... แต่เธรด "หลัก" ไม่สามารถ (เช่นเคอร์เนลระบบปฏิบัติการและหัวข้ออื่น ๆ จะเป็นไดรเวอร์) อย่างใดจัดการกับมันได้หรือไม่ แม้ว่าโครงสร้างเหล่านั้น "ในช่วงกลางของการแก้ไข" ดูเหมือนจะเป็นปัญหาที่ยากมากจริงๆ ... บางทีเราอาจไม่เคยเห็นระบบปฏิบัติการที่กระบวนการสำรองจะเป็นไปไม่ได้ :(
Dexter

23

กระบวนการที่ไม่สามารถขัดจังหวะได้โดยปกติแล้วเราจะรอ I / O ตามข้อบกพร่องของหน้า

พิจารณาสิ่งนี้:

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

กระบวนการ / ภารกิจไม่สามารถถูกขัดจังหวะในสถานะนี้เนื่องจากไม่สามารถจัดการสัญญาณใด ๆ ถ้าเป็นเช่นนั้นข้อผิดพลาดของหน้าอื่นจะเกิดขึ้นและมันจะกลับมาที่เดิม

เมื่อฉันพูดว่า "กระบวนการ" ฉันหมายถึง "งาน" จริงๆซึ่งภายใต้ Linux (2.6) แปลโดยประมาณเป็น "กลุ่มข้อความ" ซึ่งอาจหรืออาจไม่มีรายการ "กลุ่มกลุ่มข้อความ" ใน / proc

ในบางกรณีอาจรอเป็นเวลานาน ตัวอย่างทั่วไปของสิ่งนี้จะเป็นที่ที่ไฟล์เรียกทำงานหรือไฟล์ mmap'd อยู่ในระบบไฟล์เครือข่ายที่เซิร์ฟเวอร์ล้มเหลว หาก I / O สำเร็จในที่สุดงานจะดำเนินการต่อ หากในที่สุดมันล้มเหลวงานมักจะได้รับ SIGBUS หรือบางสิ่งบางอย่าง


1
หากในที่สุดมันล้มเหลวงานมักจะได้รับ SIGBUS หรือบางสิ่งบางอย่าง รอไม่สามารถสร้างเคอร์เนลได้ดังนั้นเมื่อฆ่ากระบวนการ "uninterruptible" เหล่านั้นมันเพียงแค่บอกพวกเขาว่าการดำเนินการ I / O ล้มเหลว จากนั้นกระบวนการจะกลับไปที่โหมดผู้ใช้และหายไป? มีวิธีที่จะฆ่ากระบวนการสถานะ 'D' เหล่านั้นได้อย่างปลอดภัย ฉันเดาว่ามันไม่ง่ายเลยและนั่นก็เป็นเหตุผลว่าทำไมทั้ง Windows หรือ Linux ก็ไม่มีโอกาสนั้น ในอีกด้านหนึ่งฉันต้องการที่จะฆ่ากระบวนการเหล่านั้นอย่างน้อยไม่ปลอดภัย ฉันไม่สนใจความผิดพลาดของระบบที่อาจเกิดขึ้นหรืออะไรก็ตาม ...
Dexter

@ ด้านล่างอืมฉันไม่เคยพบปัญหานี้กับ Windows วิธีการทำซ้ำที่นั่นคืออะไร? อย่างน้อยตามโพสต์นี้คำขอ I / O ทั้งหมดสามารถถูกขัดจังหวะใน Windows
Ruslan

1

คำถามที่ 3 ของคุณ: sudo kill -HUP 1ผมคิดว่าคุณสามารถฆ่ากระบวนการแบบต่อเนื่องโดยการเรียกใช้ มันจะเริ่มต้นใหม่โดยไม่ต้องจบกระบวนการทำงานและหลังจากใช้งานไปกระบวนการที่ไม่หยุดชะงักของฉันก็หายไป


-3

หากคุณกำลังพูดถึงกระบวนการ "zombie" (ซึ่งถูกกำหนดให้เป็น "zombie" ในเอาต์พุต ps) นี่เป็นเร็กคอร์ดที่ไม่เป็นอันตรายในรายการกระบวนการที่รอให้ใครบางคนเก็บรหัสส่งคืนและอาจถูกละเว้นได้อย่างปลอดภัย

คุณช่วยอธิบายสิ่งที่ได้และ "กระบวนการที่ไม่หยุดชะงัก" ให้คุณได้หรือไม่? มันรอดชีวิตจาก "kill -9" และมีความสุขไหม? หากเป็นเช่นนั้นแสดงว่าติดอยู่บน syscall บางตัวซึ่งติดอยู่ในไดรเวอร์บางตัวและคุณติดอยู่กับกระบวนการนี้จนกระทั่งรีบูต (และบางครั้งก็เป็นการดีกว่าที่จะรีบูทในเร็ว ๆ นี้) หรือยกเลิกการโหลดไดรเวอร์ที่เกี่ยวข้อง . คุณสามารถลองใช้ "strace" เพื่อค้นหาว่ากระบวนการของคุณติดอยู่ที่ใดและหลีกเลี่ยงในอนาคต


ไม่สามารถยกเลิกการโหลดไดรเวอร์อย่างรุนแรงเช่นเดียวกับที่กระบวนการถูกฆ่าได้หรือไม่? ฉันรู้ว่าโหมดเคอร์เนลมีการเข้าถึงที่มีสิทธิพิเศษมากกว่าโหมดผู้ใช้ แต่มันไม่สามารถมีสิทธิพิเศษมากกว่านั้นได้จากระบบปฏิบัติการ สิ่งที่ดำเนินการในโหมดเคอร์เนลสามารถยุ่งกับสิ่งอื่นใดที่ดำเนินการในโหมดเคอร์เนล - ไม่มีการควบคุมเพียง
เด็กซ์เตอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.