Spinlock ใน Linux คืออะไร


32

ฉันต้องการทราบรายละเอียดเกี่ยวกับ spinlock ของ Linux บางคนสามารถอธิบายให้ฉันได้ไหม

คำตอบ:


34

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 อาจไม่เป็นเช่นนั้น


@Warren: สองข้อสงสัย - ฉันต้องการทราบเพิ่มเติมเกี่ยวกับ Big Kernel Lock นี้และความหมายของมัน ฉันยังอาศัยเข้าใจย่อหน้าสุดท้าย"เสแสร้งแกน CPU 2-4 อาจจะเกือบจะเป็นสองเท่าของความเร็วกล่อง Linux แต่เสแสร้งมัน 16-32 อาจจะไม่"
เซน

2
Re: ความหมายของ BKL: ฉันคิดว่าฉันได้อธิบายไว้อย่างชัดเจนแล้ว ด้วยการล็อกเพียงหนึ่งครั้งในเคอร์เนลเมื่อใดก็ตามที่แกนประมวลผลสองอันพยายามทำสิ่งที่ได้รับการปกป้องโดย BKL แกนหนึ่งจะถูกบล็อกในขณะที่การดำเนินการครั้งแรกเสร็จสิ้นโดยใช้ทรัพยากรที่มีการป้องกัน ยิ่งมีการล็อคที่ละเอียดยิ่งยิ่งมีโอกาสน้อยที่จะเกิดขึ้น
Warren Young

2
Re: การเสแสร้ง: ฉันหมายความว่ามีกฎของผลตอบแทนลดลงเมื่อเพิ่มแกนประมวลผลในคอมพิวเตอร์ เมื่อจำนวนแกนเพิ่มขึ้นโอกาสที่สองคนหรือมากกว่านั้นจะต้องเข้าถึงทรัพยากรที่ได้รับการป้องกันโดยการล็อคโดยเฉพาะ การเพิ่มการล็อคแบบละเอียดช่วยลดโอกาสในการชนเช่นนี้ แต่มีค่าใช้จ่ายในการเพิ่มจำนวนมากเกินไป คุณสามารถเห็นสิ่งนี้ได้อย่างง่ายดายในซูเปอร์คอมพิวเตอร์ซึ่งมักจะมีโปรเซสเซอร์หลายพันรายการในวันนี้: เวิร์กโหลดส่วนใหญ่ไม่มีประสิทธิภาพสำหรับพวกเขาเพราะพวกเขาไม่สามารถหลีกเลี่ยงการใช้งานโปรเซสเซอร์จำนวนมากเนื่องจากการแย่งทรัพยากรร่วมกัน
Warren Young

1
แม้ว่านี่จะเป็นคำอธิบายที่น่าสนใจ (+1 สำหรับเรื่องนั้น) ฉันไม่คิดว่ามันจะมีประสิทธิภาพในการถ่ายทอดความแตกต่างระหว่างสปิล็อคล็อคและล็อคประเภทอื่น
Gilles 'หยุดชั่วร้าย'

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

11

สปินล็อคคือเมื่อกระบวนการโพลอย่างต่อเนื่องเพื่อลบล็อค จะถือว่าไม่ดีเนื่องจากกระบวนการใช้รอบ (โดยปกติ) ไม่จำเป็น ไม่ใช่เฉพาะ 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

บทบาทของตัวจัดตารางเวลามีอะไรบ้าง หรือมีบทบาทอะไรบ้าง?
Sen

1
ในวิธีการล็อคแบบหมุนตัวกำหนดตารางเวลาดำเนินการต่อทุก ๆ 1 ~ 1 วินาทีเพื่อทำงาน (ซึ่งเพียงตรวจสอบการมีอยู่ของไฟล์) ในตัวอย่าง inotifywait ตัวจัดตารางเวลาดำเนินการต่อเฉพาะกระบวนการเมื่อกระบวนการลูก (inotifywait) ออกจาก Inotifywait ยังนอนหลับอยู่ ตัวกำหนดตารางเวลาดำเนินการต่อเมื่อเหตุการณ์ inotify เกิดขึ้นเท่านั้น
Shawn J. Goff

ดังนั้นสถานการณ์นี้จะจัดการกับระบบตัวประมวลผลหลักเดียวได้อย่างไร
Sen

@Sen: นี่คือคำอธิบายที่ค่อนข้างดีในลินุกซ์ไดรเวอร์ของอุปกรณ์
Gilles 'หยุดชั่วร้าย'

1
สคริปต์ทุบตีนั้นเป็นตัวอย่างที่ไม่ดีของ spinlock คุณกำลังหยุดกระบวนการชั่วคราวเพื่อให้หลับ spinlock ไม่เคยหลับ ในเครื่องหลักเพียงแค่หยุดตัวกำหนดตารางเวลาและดำเนินการต่อ (โดยไม่ต้องรอไม่ว่าง)
Martin

7

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

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

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

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

หากคุณรู้ว่าเธรดจะล็อคไว้เสมอสำหรับการพูด 200 หน่วยจากนั้นคุณจะเสียเวลากับคอมพิวเตอร์ในแต่ละครั้งและดำเนินการต่อ

ดังนั้นในที่สุดกุญแจหมุนอาจมีประสิทธิภาพมากหรือสิ้นเปลือง มันจะสิ้นเปลืองเมื่อเวลา "ทั่วไป" ในการระงับการล็อคนั้นสูงกว่าเวลาที่ใช้ในการ "ลองและบล็อก" จะมีประสิทธิภาพเมื่อเวลาทั่วไปในการล็อคมีขนาดเล็กลงมากแล้วเวลาในการ 'ลองและบล็อก'

Ps: หนังสือที่อ่านบนเธรดคือ "A Thread Primer" ถ้าคุณยังหามันเจอ


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

หมุนในคำสั่ง 3-4 ในวงโดยทั่วไป
Paul Stelian

5

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

take the lock
use the resource
release the lock

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

จากมุมมองระดับสูงมีสองวิธีที่สำคัญในการใช้งานล็อค: spinlocks และเงื่อนไข ด้วยspinlockการล็อคหมายถึงเพียงแค่“ หมุน” (เช่นไม่ทำอะไรเลยในวงวน) จนกว่าจะไม่มีใครล็อค ด้วยเงื่อนไขหากงานพยายามล็อค แต่ถูกบล็อกเพราะมีงานอื่นเก็บงานผู้ใช้ใหม่จะเข้าสู่คิวรอ การดำเนินการปล่อยสัญญาณไปยังงานรอใด ๆ ที่ล็อคอยู่ในขณะนี้

(คำอธิบายเหล่านี้ไม่เพียงพอที่จะให้คุณติดตั้งล็อคเพราะฉันไม่ได้พูดอะไรเกี่ยวกับอะตอมมิกซิตี้ แต่อะตอมมิกซิตี้ไม่สำคัญเลย)

Spinlocks นั้นสิ้นเปลืองอย่างเห็นได้ชัด: งานที่รอคอยคอยตรวจสอบว่ามีการล็อคหรือไม่ แล้วทำไมถึงใช้เมื่อไหร่? Spinlocks มักจะถูกมากในกรณีที่ไม่ได้ล็อค สิ่งนี้ทำให้น่าสนใจเมื่อมีโอกาสสำหรับล็อคที่จะจัดขึ้นมีขนาดเล็ก ยิ่งกว่านั้น spinlocks จะทำงานได้ก็ต่อเมื่อไม่ได้รับการล็อคซึ่งคาดว่าจะใช้เวลานาน ดังนั้นจึงมีแนวโน้มที่จะใช้ spinlock ในสถานการณ์ที่พวกเขาจะยังคงอยู่ในระยะเวลาอันสั้นดังนั้นความพยายามส่วนใหญ่คาดว่าจะประสบความสำเร็จในการลองครั้งแรกและผู้ที่ต้องรอไม่นาน

มีคำอธิบายที่ดีเกี่ยวกับ spinlocks และกลไกการทำงานพร้อมกันอื่น ๆ ของเคอร์เนลLinuxในไดรเวอร์อุปกรณ์ Linuxตอนที่ 5


อะไรจะเป็นวิธีที่ดีในการปรับใช้การซิงโครไนซ์ดั้งเดิมอื่น ๆ ใช้ spinlock ตรวจสอบว่ามีคนอื่นกำลังใช้งาน primitive จริงอยู่หรือไม่จากนั้นใช้ scheduler หรือ grant access? เราสามารถพิจารณารูปแบบการซิงโครไนซ์ () ใน Java ในรูปแบบของ spinlock และในการใช้งานพื้นฐานอย่างเหมาะสมเพียงแค่ใช้ spinlock?
Paul Stelian

@ PaulStelian มันเป็นเรื่องธรรมดาที่จะใช้ "ช้า" ล็อคด้วยวิธีนี้ ฉันไม่รู้ Java พอที่จะตอบส่วนนั้น แต่ฉันสงสัยว่าsynchronizedจะมีการใช้งานโดย spinlock: synchronizedบล็อกสามารถทำงานได้เป็นเวลานาน synchronizedเป็นโครงสร้างภาษาเพื่อให้ล็อคใช้งานง่ายในบางกรณีไม่ใช่แบบดั้งเดิมในการสร้างการซิงโครไนซ์แบบดั้งเดิมที่ใหญ่กว่า
Gilles 'หยุดความชั่วร้าย'

3

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 ว่างและเมื่อคุณล็อกและปล่อยในเธรดเดียวกัน หยิบเซมาฟอร์เมื่อคุณต้องการโพสต์ในหนึ่งเธรดหรือขัดจังหวะและนำไปใช้ในเธรดอื่น เป็นสามวิธีที่แตกต่างกันเล็กน้อยเพื่อให้แน่ใจว่ามีการยกเว้นซึ่งกันและกันและใช้เพื่อจุดประสงค์ที่แตกต่างกันเล็กน้อย

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