ฉันต้องการทราบรายละเอียดเกี่ยวกับ spinlock ของ Linux บางคนสามารถอธิบายให้ฉันได้ไหม
ฉันต้องการทราบรายละเอียดเกี่ยวกับ spinlock ของ Linux บางคนสามารถอธิบายให้ฉันได้ไหม
คำตอบ:
Spin lock เป็นวิธีในการปกป้องทรัพยากรที่ใช้ร่วมกันจากการถูกแก้ไขโดยกระบวนการสองกระบวนการขึ้นไปพร้อมกัน กระบวนการแรกที่พยายามแก้ไขทรัพยากร "ได้มา" ล็อคและดำเนินการต่อในทางที่ทำสิ่งที่จำเป็นต้องใช้กับทรัพยากร กระบวนการอื่นใดที่ในภายหลังพยายามรับการล็อคหยุด พวกเขาบอกว่าจะ "หมุนเข้าที่" รออยู่ในขั้นตอนแรกจะได้รับการปล่อยตัวดังนั้นล็อคหมุนชื่อ
เคอร์เนล Linux ใช้สปินล็อคสำหรับหลาย ๆ อย่างเช่นเมื่อส่งข้อมูลไปยังอุปกรณ์ต่อพ่วงโดยเฉพาะ อุปกรณ์ต่อพ่วงฮาร์ดแวร์ส่วนใหญ่ไม่ได้รับการออกแบบมาเพื่อจัดการกับการอัพเดทสถานะหลายรายการพร้อมกัน หากต้องมีการแก้ไขสองอย่างที่แตกต่างกันอย่างใดอย่างหนึ่งจะต้องปฏิบัติตามอย่างเคร่งครัดพวกเขาจะไม่ทับซ้อนกัน ล็อคแบบหมุนให้การป้องกันที่จำเป็นเพื่อให้แน่ใจว่าการปรับเปลี่ยนจะเกิดขึ้นทีละครั้ง
การล็อกแบบหมุนเป็นปัญหาเนื่องจากบล็อกการหมุนที่แกน CPU ของเธรดไม่สามารถทำงานอื่นได้ แม้ว่าเคอร์เนล Linux จะให้บริการมัลติทาสกิ้งแก่โปรแกรมพื้นที่ผู้ใช้ที่ทำงานภายใต้มันสิ่งอำนวยความสะดวกมัลติทาสก์อเนกประสงค์ที่ไม่ได้ขยายไปถึงรหัสเคอร์เนล
สถานการณ์นี้กำลังเปลี่ยนแปลงและเป็นส่วนใหญ่ของการมีอยู่ของ Linux จนถึง Linux 2.0 เคอร์เนลเกือบจะเป็นโปรแกรมแบบมอบหมายภารกิจเดียว: เมื่อใดก็ตามที่ CPU รันรหัสเคอร์เนลจะมีการใช้ CPU แกนเดียวเพียงอันเดียวเนื่องจากมีการล็อกการหมุนครั้งเดียวเพื่อป้องกันทรัพยากรที่ใช้ร่วมกันทั้งหมดที่เรียกว่า Big Kernel Lock (BKL) ) เริ่มต้นด้วย Linux 2.2, BKL ค่อยๆถูกแบ่งออกเป็นล็อกอิสระจำนวนมากซึ่งแต่ละตัวจะปกป้องคลาสที่เน้นทรัพยากรมากขึ้น ทุกวันนี้ด้วยเคอร์เนล 2.6, BKL ยังคงมีอยู่ แต่มันถูกใช้โดยรหัสเก่าจริงๆเท่านั้นที่ไม่สามารถย้ายไปยังล็อคที่ละเอียดยิ่งขึ้นได้ ตอนนี้เป็นไปได้ค่อนข้างที่กล่องแบบมัลติคอร์จะมี CPU ทุกตัวที่รันโค้ดเคอร์เนลที่มีประโยชน์
มีข้อ จำกัด สำหรับยูทิลิตี้ในการทำลาย BKL เนื่องจากเคอร์เนล Linux ขาดมัลติทาสก์ทั่วไป หากแกนประมวลผล CPU ถูกบล็อกการหมุนบนเคอร์เนลสปินล็อคจะไม่สามารถทำการติดตั้งใหม่เพื่อไปทำอย่างอื่นจนกว่าจะปลดล็อค มันตั้งอยู่และหมุนจนกว่าจะปลดล็อค
ระบบล็อคแบบหมุนได้อย่างมีประสิทธิภาพสามารถเปลี่ยนกล่องมอนสเตอร์ 16 คอร์เป็นกล่องแบบแกนเดียวได้อย่างมีประสิทธิภาพหากปริมาณงานเป็นเช่นนั้นทุกแกนหลักมักจะรอการล็อคแบบหมุนเพียงครั้งเดียว นี่เป็นข้อ จำกัด หลักสำหรับความสามารถในการขยายขีดความสามารถของเคอร์เนล Linux: การเพิ่มแกนประมวลผล CPU เป็นสองเท่าจาก 2 ถึง 4 อาจจะเพิ่มความเร็วเป็นสองเท่าของกล่อง Linux แต่เพิ่มขึ้นเป็นสองเท่าจาก 16 ถึง 32 อาจไม่เป็นเช่นนั้น
สปินล็อคคือเมื่อกระบวนการโพลอย่างต่อเนื่องเพื่อลบล็อค จะถือว่าไม่ดีเนื่องจากกระบวนการใช้รอบ (โดยปกติ) ไม่จำเป็น ไม่ใช่เฉพาะ Linux แต่เป็นรูปแบบการเขียนโปรแกรมทั่วไป และโดยทั่วไปถือว่าเป็นวิธีปฏิบัติที่ไม่ดี แต่ในความเป็นจริงแล้ววิธีแก้ปัญหาที่ถูกต้องคือ มีหลายกรณีที่ค่าใช้จ่ายของตัวกำหนดตารางเวลาใช้งานสูงกว่า (ในแง่ของรอบการทำงานของ CPU) กว่าค่าใช้จ่ายของรอบการใช้งานไม่กี่รอบที่คาดว่า spinlock จะมีอายุการใช้งาน
ตัวอย่างของ spinlock:
#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
sleep 1
done
#do stuff
มีวิธีหลีกเลี่ยงการล็อคแบบหมุนบ่อยครั้ง สำหรับตัวอย่างนี้มีเครื่องมือ Linux ที่เรียกว่าinotifywait (โดยปกติจะไม่ได้ติดตั้งตามค่าเริ่มต้น) ถ้าเขียนด้วยภาษา C คุณก็สามารถใช้inotify API ที่ Linux จัดเตรียมไว้ให้
ตัวอย่างเดียวกันการใช้ inotifywait แสดงวิธีการทำสิ่งเดียวกันให้สำเร็จโดยไม่ต้องหมุนล็อค:
#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff
เมื่อเธรดพยายามรับการล็อกสามสิ่งสามารถเกิดขึ้นได้หากล้มเหลวสามารถลองและบล็อกสามารถลองและดำเนินการต่อได้สามารถลองแล้วเข้าสู่โหมดสลีปเพื่อแจ้งให้ระบบปฏิบัติการปลุกเมื่อเหตุการณ์บางอย่างเกิดขึ้น
ตอนนี้ความพยายามและใช้เวลาน้อยลงอย่างต่อเนื่องจึงเป็นความพยายามและบล็อก สมมติว่าช่วงเวลาที่ "ลองและดำเนินต่อไป" จะใช้เวลาเป็นหน่วยของฉันและ "ลองและบล็อก" จะใช้เวลาเป็นร้อย
ตอนนี้ให้เราสมมติว่าโดยเฉลี่ยแล้วเธรดจะใช้เวลา 4 หน่วยในการล็อค เป็นการสิ้นเปลืองเวลาที่จะรอ 100 หน่วย ดังนั้นแทนที่จะเขียน "ลองแล้วทำต่อ" ในความพยายามครั้งที่สี่คุณมักจะได้รับการล็อค นี่คือล็อคการหมุน เรียกว่าเพราะด้ายยังคงหมุนอยู่จนกว่ามันจะล็อค
มาตรการความปลอดภัยที่เพิ่มเข้ามาคือการ จำกัด จำนวนครั้งในการวนซ้ำ ดังนั้นในตัวอย่างที่คุณสร้าง a สำหรับลูปรันเช่นหกครั้งหากล้มเหลวคุณจะ "ลองและบล็อก"
หากคุณรู้ว่าเธรดจะล็อคไว้เสมอสำหรับการพูด 200 หน่วยจากนั้นคุณจะเสียเวลากับคอมพิวเตอร์ในแต่ละครั้งและดำเนินการต่อ
ดังนั้นในที่สุดกุญแจหมุนอาจมีประสิทธิภาพมากหรือสิ้นเปลือง มันจะสิ้นเปลืองเมื่อเวลา "ทั่วไป" ในการระงับการล็อคนั้นสูงกว่าเวลาที่ใช้ในการ "ลองและบล็อก" จะมีประสิทธิภาพเมื่อเวลาทั่วไปในการล็อคมีขนาดเล็กลงมากแล้วเวลาในการ 'ลองและบล็อก'
Ps: หนังสือที่อ่านบนเธรดคือ "A Thread Primer" ถ้าคุณยังหามันเจอ
การล็อกเป็นวิธีสำหรับภารกิจสองอย่างหรือมากกว่านั้น (กระบวนการ, เธรด) เพื่อซิงโครไนซ์ โดยเฉพาะอย่างยิ่งเมื่องานทั้งสองต้องการการเข้าถึงทรัพยากรเป็นระยะ ๆ ที่สามารถใช้งานได้ครั้งละหนึ่งงานเท่านั้นมันเป็นหนทางสำหรับงานที่จะต้องจัดการไม่ให้ใช้ทรัพยากรในเวลาเดียวกัน ในการเข้าถึงทรัพยากรงานต้องทำตามขั้นตอนต่อไปนี้:
take the lock
use the resource
release the lock
ไม่สามารถทำการล็อคได้หากมีงานอื่นได้ดำเนินการไปแล้ว (คิดว่าการล็อกเป็นวัตถุโทเค็นทางกายภาพวัตถุนั้นอยู่ในลิ้นชักหรือมีคนอยู่ในมือของพวกเขาเฉพาะบุคคลที่ถือวัตถุเท่านั้นที่สามารถเข้าถึงทรัพยากรได้) ดังนั้น "เอากุญแจ" จริงๆหมายถึง "รอจนกระทั่ง ไม่มีใครล็อคแล้วเอามัน”
จากมุมมองระดับสูงมีสองวิธีที่สำคัญในการใช้งานล็อค: spinlocks และเงื่อนไข ด้วยspinlockการล็อคหมายถึงเพียงแค่“ หมุน” (เช่นไม่ทำอะไรเลยในวงวน) จนกว่าจะไม่มีใครล็อค ด้วยเงื่อนไขหากงานพยายามล็อค แต่ถูกบล็อกเพราะมีงานอื่นเก็บงานผู้ใช้ใหม่จะเข้าสู่คิวรอ การดำเนินการปล่อยสัญญาณไปยังงานรอใด ๆ ที่ล็อคอยู่ในขณะนี้
(คำอธิบายเหล่านี้ไม่เพียงพอที่จะให้คุณติดตั้งล็อคเพราะฉันไม่ได้พูดอะไรเกี่ยวกับอะตอมมิกซิตี้ แต่อะตอมมิกซิตี้ไม่สำคัญเลย)
Spinlocks นั้นสิ้นเปลืองอย่างเห็นได้ชัด: งานที่รอคอยคอยตรวจสอบว่ามีการล็อคหรือไม่ แล้วทำไมถึงใช้เมื่อไหร่? Spinlocks มักจะถูกมากในกรณีที่ไม่ได้ล็อค สิ่งนี้ทำให้น่าสนใจเมื่อมีโอกาสสำหรับล็อคที่จะจัดขึ้นมีขนาดเล็ก ยิ่งกว่านั้น spinlocks จะทำงานได้ก็ต่อเมื่อไม่ได้รับการล็อคซึ่งคาดว่าจะใช้เวลานาน ดังนั้นจึงมีแนวโน้มที่จะใช้ spinlock ในสถานการณ์ที่พวกเขาจะยังคงอยู่ในระยะเวลาอันสั้นดังนั้นความพยายามส่วนใหญ่คาดว่าจะประสบความสำเร็จในการลองครั้งแรกและผู้ที่ต้องรอไม่นาน
มีคำอธิบายที่ดีเกี่ยวกับ spinlocks และกลไกการทำงานพร้อมกันอื่น ๆ ของเคอร์เนลLinuxในไดรเวอร์อุปกรณ์ Linuxตอนที่ 5
synchronized
จะมีการใช้งานโดย spinlock: synchronized
บล็อกสามารถทำงานได้เป็นเวลานาน synchronized
เป็นโครงสร้างภาษาเพื่อให้ล็อคใช้งานง่ายในบางกรณีไม่ใช่แบบดั้งเดิมในการสร้างการซิงโครไนซ์แบบดั้งเดิมที่ใหญ่กว่า
Spinlock เป็นตัวล็อคที่ทำงานโดยปิดการใช้งานตัวกำหนดตารางเวลาและอาจขัดจังหวะ (ตัวแปร irqsave) บนแกนหลักนั้น ๆ ที่ล็อคได้มา มันแตกต่างจาก mutex ที่จะปิดใช้งานการกำหนดเวลาดังนั้นเฉพาะเธรดของคุณเท่านั้นที่สามารถทำงานได้ในขณะที่มีสปินล็อค mutex อนุญาตให้เธรดลำดับความสำคัญสูงกว่าอื่น ๆ ถูกกำหนดเวลาไว้ในขณะที่ถูกพักไว้ แต่ไม่อนุญาตให้เธรดดำเนินการในส่วนที่ได้รับการป้องกันในเวลาเดียวกัน เนื่องจาก spinlocks ปิดการใช้งานมัลติทาสกิ้งคุณไม่สามารถรับ spinlock จากนั้นเรียกรหัสอื่น ๆ ที่จะพยายามรับ mutex รหัสของคุณภายในส่วน spinlock ต้องไม่เคยหลับ (โดยทั่วไปรหัสจะหลับเมื่อพบการปิดกั้น mutex หรือเซมาฟอร์ที่ล็อคอยู่)
ความแตกต่างอีกอย่างกับ mutex คือเธรดโดยทั่วไปจะจัดคิวสำหรับ mutex ดังนั้น mutex ที่อยู่ด้านล่างจะมีคิว ในขณะที่ spinlock เพียงแค่ทำให้แน่ใจว่าไม่มีเธรดอื่นจะทำงานแม้ว่ามันจะต้อง ดังนั้นคุณจะต้องไม่ถือ spinlock เมื่อเรียกใช้ฟังก์ชันนอกไฟล์ที่คุณไม่แน่ใจว่าจะไม่เข้าสู่โหมดสลีป
เมื่อคุณต้องการแบ่งปัน spinlock ของคุณด้วยอินเทอร์รัปต์คุณต้องใช้ตัวแปร irqsave สิ่งนี้จะไม่เพียงปิดใช้งานตัวกำหนดตารางเวลา แต่จะปิดใช้งานการขัดจังหวะด้วย มันสมเหตุสมผลใช่ไหม? Spinlock ทำงานได้โดยทำให้แน่ใจว่าไม่มีสิ่งใดจะทำงาน หากคุณไม่ต้องการให้อินเทอร์รัปต์ปิดการใช้งานคุณจะปิดการใช้งานและดำเนินการต่อในส่วนที่สำคัญ
ในเครื่องมัลติคอร์สปินล็อคจะหมุนรอแกนหลักอีกอันที่เก็บล็อคเพื่อปลดล็อค การหมุนนี้เกิดขึ้นในเครื่องที่มีหลายแกนเพราะในแกนเดียวมันไม่สามารถเกิดขึ้นได้ (คุณถือ spinlock และดำเนินการต่อหรือคุณไม่เคยทำงานจนกว่าจะปล่อยล็อค)
Spinlock นั้นไม่สิ้นเปลืองในที่ที่เหมาะสม สำหรับส่วนที่สำคัญน้อยมากมันจะเป็นการสิ้นเปลืองในการจัดสรรคิวงาน mutex เมื่อเปรียบเทียบกับการหยุดตัวกำหนดตารางเวลาไว้สักสองสามไมโครวินาทีเพื่อให้งานสำคัญเสร็จ หากคุณต้องการเข้าสู่โหมดสลีปหรือกดปุ่มล็อคค้างไว้ตลอดการใช้งาน io (ซึ่งอาจเข้าสู่โหมดสลีป) ให้ใช้ mutex แน่นอนไม่เคยล็อค spinlock แล้วลองปล่อยมันในขัดจังหวะ ขณะนี้จะใช้งานได้มันจะเป็นเหมือนอึของ arduino ในขณะที่ (flagnotset); ในกรณีเช่นนี้ใช้สัญญาณ
หยิบ Spinlock เมื่อคุณต้องการการแยกซึ่งกันและกันอย่างง่าย ๆ สำหรับบล็อกการทำธุรกรรมของหน่วยความจำ หยิบ mutex เมื่อคุณต้องการให้เธรดหลายเธรดหยุดทำงานก่อนการล็อก mutex จากนั้นเธรดที่มีลำดับความสำคัญสูงสุดจะถูกเลือกเพื่อดำเนินการต่อเมื่อ mutex ว่างและเมื่อคุณล็อกและปล่อยในเธรดเดียวกัน หยิบเซมาฟอร์เมื่อคุณต้องการโพสต์ในหนึ่งเธรดหรือขัดจังหวะและนำไปใช้ในเธรดอื่น เป็นสามวิธีที่แตกต่างกันเล็กน้อยเพื่อให้แน่ใจว่ามีการยกเว้นซึ่งกันและกันและใช้เพื่อจุดประสงค์ที่แตกต่างกันเล็กน้อย