(x | y) - y ทำไมมันไม่เป็นแค่ x หรือแม้แต่ `x | 0`


47

ฉันกำลังอ่านรหัสเคอร์เนลและที่เดียวที่ฉันเห็นนิพจน์ภายในifคำสั่งเช่น

if (value == (SPINLOCK_SHARED | 1) - 1) {
         ............
}

โดยที่SPINLOCK_SHARED = 0x80000000เป็นค่าคงที่ที่กำหนดไว้ล่วงหน้า

ฉันสงสัยว่าทำไมเราต้องใช้(SPINLOCK_SHARED | 1) - 1เพื่อการแปลงประเภท ผลลัพธ์ของนิพจน์จะเป็น 80000000 - เหมือนกับ 0x80000000 ใช่ไหม แต่ทำไม ORing 1 และ Subtracting 1 ถึงสำคัญ?

มีความรู้สึกเหมือนว่าฉันพลาดที่จะรับบางสิ่งบางอย่าง ..


3
#define SPINLOCK_SHARED 0x80000000
RaGa__M

1
ฉันสงสัยว่าไม่มีเหตุผล อาจเป็นสิ่งที่คัดลอกวาง คุณสามารถเพิ่มตำแหน่งที่คุณพบสิ่งนี้ได้อย่างแน่นอน (เวอร์ชันใดของเคอร์เนลไฟล์ใด ฯลฯ )
Sander De Dycker


2
if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED|0, 1))แฟ้มรหัสแหล่งเดียวกันนอกจากนี้ยังมี
Eric Postpischil

2
ถ้าอย่างนั้นฉันคิดว่าเราต้องถามผู้เขียนว่าทำไมมันถึงมีการเปลี่ยนแปลง
funnydman

คำตอบ:


1

มันเพิ่งทำแบบนั้นเพื่อความชัดเจนนั่นคือทั้งหมด เป็นเพราะ atomic_fetchadd_int () (ในเช่น sys / spinlock2.h) ส่งคืนค่า PRIOR ไปที่การเพิ่ม / การลบและค่านั้นจะถูกส่งไปยัง _spin_lock_contested ()

โปรดทราบว่าคอมไพเลอร์ C จะคำนวณนิพจน์คงที่ทั้งหมดล่วงหน้าอย่างสมบูรณ์ ในความเป็นจริงคอมไพเลอร์สามารถเพิ่มประสิทธิภาพโค้ดอินไลน์ตามเงื่อนไขที่ใช้อาร์กิวเมนต์โพรซีเดอร์แบบพาส - อินเมื่อโพรซีเดอร์ถูกส่งค่าคงที่ในอาร์กิวเมนต์เหล่านั้น นี่คือเหตุผลที่ lockmgr () แบบอินไลน์ใน sys / lock.h มีคำสั่ง case ... เนื่องจากว่าคำสั่ง case ทั้งหมดจะได้รับการปรับให้เหมาะสมและพัฒนาไปสู่การโทรโดยตรงไปยังฟังก์ชันที่เหมาะสม

นอกจากนี้ในฟังก์ชั่นการล็อคทั้งหมดค่าใช้จ่ายของปรมาณู ops แคระการคำนวณอื่น ๆ ทั้งหมดโดยสองหรือสามลำดับความสำคัญ

-Matt


คำตอบนี้มาจากผู้เขียน !!!
RaGa__M

31

พบรหัสใน_spin_lock_contestedซึ่งถูกเรียกจาก_spin_lock_quickเมื่อมีคนอื่นพยายามรับการล็อก:

count = atomic_fetchadd_int(&spin->counta, 1);
if (__predict_false(count != 0)) {
    _spin_lock_contested(spin, ident, count);
}

หากไม่มีการแข่งขันควรcount(ค่าก่อนหน้า) 0แต่ไม่ใช่ countค่านี้ถูกส่งเป็นพารามิเตอร์เพื่อ_spin_lock_contestedเป็นvalueพารามิเตอร์ นี่valueคือการตรวจสอบกับifจาก OP:

/*
 * WARNING! Caller has already incremented the lock.  We must
 *      increment the count value (from the inline's fetch-add)
 *      to match.
 *
 * Handle the degenerate case where the spinlock is flagged SHARED
 * with only our reference.  We can convert it to EXCLUSIVE.
 */
if (value == (SPINLOCK_SHARED | 1) - 1) {
    if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED | 1, 1))
        return;
}

โปรดทราบว่าvalueเป็นค่าก่อนหน้าของspin->countaและหลังได้เพิ่มขึ้น 1 เราคาดว่าspin->countaจะเท่ากันvalue + 1(ยกเว้นบางสิ่งที่มีการเปลี่ยนแปลงในระหว่างนี้)

ดังนั้นการตรวจสอบว่าspin->counta == SPINLOCK_SHARED | 1(เงื่อนไขเบื้องต้นatomic_cmpset_int) ตรงกับการตรวจสอบหรือvalue + 1 == SPINLOCK_SHARED | 1ไม่ซึ่งสามารถเขียนใหม่เป็นvalue == (SPINLOCK_SHARED | 1) - 1(อีกครั้งหากไม่มีการเปลี่ยนแปลงใด ๆ ในระหว่างนี้)

ในขณะที่value == (SPINLOCK_SHARED | 1) - 1สามารถเขียนใหม่เป็นvalue == SPINLOCK_SHAREDมันเป็นซ้ายเพื่อชี้แจงความตั้งใจของการเปรียบเทียบ (เช่น. เพื่อเปรียบเทียบค่าที่เพิ่มขึ้นก่อนหน้านี้กับค่าทดสอบ)

หรือ iow คำตอบดูเหมือนจะเป็น: เพื่อความชัดเจนและความสอดคล้องของรหัส


ขอบคุณสำหรับการตอบกลับของคุณทุกอย่างยกเว้น(SPINLOCK_SHARED | 1) - 1ส่วนหนึ่งเป็นที่เข้าใจและvalue == SPINLOCK_SHAREDเป็นความคิดของฉันเช่นกันเพราะเรากำลังตรวจสอบว่าค่าก่อนหน้านี้มีการตั้งค่าสถานะที่ใช้ร่วมกันถ้าใช่เปลี่ยนล็อคเป็นเอกสิทธิ์ .........
RaGa__M

1
@RaGa__M: ความตั้งใจของifการตรวจสอบเพื่อตรวจสอบว่าvalue + 1(ซึ่งควรจะมีค่าเช่นเดียวกับspin->countaถ้ามีการเปลี่ยนแปลงอะไรในขณะเดียวกัน) SPINLOCK_SHARED | 1เท่ากับ หากคุณเขียนifเช็คเป็นvalue == SPINLOCK_SHAREDความตั้งใจนี้ไม่ชัดเจนและมันจะยากมากที่จะคิดออกว่าหมายถึงการตรวจสอบ การรักษาทั้งสองSPINLOCK_SHARED | 1และ- 1ชัดเจนในifการตรวจสอบคือเจตนา
Sander De Dycker

แต่มันทำให้เกิดความสับสน
RaGa__M

ทำไมif (value + 1 == (SPINLOCK_SHARED | 1) )ล่ะ
Pablo H

อืม .... มันอาจเป็นvalue & SPINLOCK_SHAREDสิ่งที่อ่านได้ง่ายกว่า
RaGa__M

10

ฉันคิดว่าเป้าหมายน่าจะเพิกเฉยต่อนัยสำคัญต่ำสุด:

  • ถ้า SPINLOCK_SHARED แสดงเป็นไบนารี่คือ xxx0 -> ผลลัพธ์คือ xxx0
  • ถ้า SPINLOCK_SHARED = xxx1 -> ผลลัพธ์ก็คือ xxx0

อาจจะชัดเจนกว่านี้หากใช้การแสดงออกของหน้ากากเล็กน้อย


8
นั่นคือสิ่งที่รหัสทำ แต่คำถามคือทำไมคุณต้องทำเช่นนั้นสำหรับค่าคงที่ที่กำหนดซึ่งไม่มีบิตที่มีนัยสำคัญน้อยที่สุด
Sander De Dycker

4
@SanderDeDycker เพราะเคอร์เนล Linux?
Lundin

9
@Lundin เคอร์เนล linux ไม่ได้รับการยกเว้นจากวิธีการเข้ารหัสที่เข้าใจได้ ค่อนข้างตรงกันข้าม
Qix - MONICA ถูกยกเลิกเมื่อ

2
@Qix ถ้าคุณพูดอย่างนั้น ฉันเป็นแฟนตัวยงของ Linux จนกว่าฉันจะอ่านรหัสและอ่านเอกสารสไตล์การเข้ารหัสเคอร์เนล ทุกวันนี้ฉันรักษาระยะห่างจากคอมพิวเตอร์ Linux ไว้ 10 เมตร
Lundin

2
@Qix Nah ฉันค่อนข้างตัดสินตามรหัสแหล่งที่มาของมัน ...
Lundin

4

ผลของการ

(SPINLOCK_SHARED | 1) - 1

valueเพื่อให้แน่ใจว่าบิตต่ำลำดับของผลที่ได้จะถูกล้างออกก่อนที่จะเปรียบเทียบกับ ฉันเห็นด้วยว่ามันค่อนข้างไร้สาระ แต่เห็นได้ชัดว่าบิตลำดับต่ำมีการใช้งานเฉพาะหรือความหมายที่ไม่ชัดเจนในรหัสนี้และฉันคิดว่าเราต้องสมมติว่า devs มีเหตุผลที่ดีสำหรับการทำเช่นนี้ คำถามที่น่าสนใจก็คือ - ใช้รูปแบบเดียวกันนี้ ( | 1) -1) ตลอดทั้งฐานข้อมูลที่คุณกำลังดูหรือไม่?


2

มันเป็นวิธีการเขียนหน้ากากที่สับสน รุ่นที่อ่านได้: value == (SPINLOCK_SHARED & ~1u).


5
แต่ก็ใช่ว่าทำไม OP กำลังถามว่าทำไมจึงเป็นเช่นนี้ถ้าSPINLOCK_SHAREDเป็นค่าคงที่ที่ทราบ หากพวกเขาเพียงแค่ทดสอบSPINLOCK_SHAREDว่าอยู่ในหน้ากากทำไมif (value & SPINLOCK_SHARED)ล่ะ?
Qix - MONICA ถูกยกเลิกเมื่อ

4
value == (SPINLOCK_SHARED & ~1u)จะไม่เทียบเท่าเพราะvalue == (SPINLOCK_SHARED | 1) - 1ทำงานแม้ว่าจะเป็นชนิดของกว้างกว่าSPINLOCK_SHARED unsigned
Eric Postpischil

4
สุจริตฉันไม่แน่ใจว่า& ~1uมีความชัดเจนใด ๆ ฉันคิดว่าจะแนะนำ& 0xFFFFFFFEคำตอบของฉัน แต่รู้ว่านั่นก็ไม่ชัดเจนเช่นกัน ข้อเสนอแนะของคุณมีข้อดีของความกะทัดรัด :-)
Bob Jarvis - Reinstate Monica

6
@Lundin: 0x80000000เราไม่ทราบว่ามันจะเป็น OP ระบุว่ามันถูกกำหนดด้วย#define SPINLOCK_SHARED 0x80000000แต่อาจอยู่ข้างใน#if…#endifและมีการใช้คำจำกัดความที่แตกต่างกันในสถานการณ์อื่น ๆ หรือผู้เขียนรหัสนี้อาจตั้งใจให้มันทำงานแม้ว่าคำจำกัดความจะถูกแก้ไขหรือมีการคอมไพล์ด้วยส่วนหัวอื่น ๆ กำหนดมันแตกต่างกัน ไม่ว่าโค้ดสองชิ้นนั้นจะไม่เทียบเท่ากัน
Eric Postpischil

2
@ BobJarvis-ReinstateMonica มันชัดเจนกว่าสำหรับคนที่ทำงานกับผู้ประกอบการระดับบิตทุกวัน การผสม bitwise กับเลขคณิตปกติทำให้เกิดความสับสน
Lundin

0

ส่วนใหญ่เช่นนี้จะทำเพื่อจัดการกรณีเพิ่มเติมหลายประการ ตัวอย่างเช่นในกรณีนี้เราบอกว่าSPINLOCK_SHAREDไม่สามารถเป็น 1:

int SPINLOCK_SHARED = 0x01

int res = (SPINLOCK_SHARED | 1) - 1 // 0

2
มันจะทำให้รู้สึก แต่แม้ว่ามันจะไม่จริงที่ชัดเจนจากคำถามที่ดูเหมือนเป็นค่าคงที่กำหนดและตัวแปรการทดสอบคือSPINLOCK_SHARED valueในกรณีนี้ยังคง mistery
Roberto Caboni

ขอบคุณสำหรับการตอบกลับของคุณ แต่ฉันคิดว่าในกรณีดั้งเดิม SPINLOCK_SHARED ไม่ใช่ 0x01 คุณเก็บ| 1) - 1ส่วนนี้ไว้เมื่อ SPINLOCK_SHARED ถือ0x80000000สิ่งที่จะได้รับผลกระทบ | 1) - 1หรือไม่
RaGa__M

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