سؤال

على جهاز SMP يجب أن نستخدمه spin_lock_irqsave و لا spin_lock_irq من سياق المقاطعة.

لماذا نريد حفظ الأعلام (التي تحتوي على IF)؟

هل هناك روتين آخر يمكن أن يقطعنا؟

هل كانت مفيدة؟

المحلول

أنا جديد في kernel ، لكن من ما جمعته من كتاب روبرت لوف "Linux kernel Development" ، إذا تم تعطيل المقاطعات بالفعل على المعالج قبل أن يبدأ الكود في القفل ، عند الاتصال بـ Spin_unlock_irq ، فسوف تصدر القفل بطريقة خاطئة. إذا قمت بحفظ الأعلام وأصدرتها باستخدام الأعلام ، فستقوم الدالة Spin_lock_irqsave بإعادة المقاطعة إلى حالتها السابقة.

مثال مع spin_lock_irqsave

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // save the state, if locked already it is saved in flags
// Critical section
spin_unlock_irqrestore(&mLock, flags); // return to the formally state specified in flags

مثال مع spin_lock_irq(بدون irqsave):

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if already locked
// Critical section
spin_unlock_irq(&mLock); // Could result in an error unlock...

نصائح أخرى

spin_lock_irqsave يستخدم بشكل أساسي لحفظ حالة المقاطعة قبل أخذ قفل الدوران ، وذلك لأن قفل الدوران يعطل المقاطعة ، عندما يتم أخذ القفل في سياق المقاطعة ، وإعادة تهيئة ذلك أثناء فتحه. يتم حفظ حالة المقاطعة بحيث يجب إعادة المقاطعات مرة أخرى.

مثال:

  1. دعنا نقول أن المقاطعة تم تعطيلها قبل الحصول على قفل الدوران
  2. spin_lock_irq سوف يعطل المقاطعة x وأخذ القفل
  3. spin_unlock_irq سوف تمكين المقاطعة x.

لذلك في الخطوة الثالثة أعلاه بعد إصدار القفل ، سنقاطع تمكين X الذي تم تعطيله في وقت سابق قبل الحصول على القفل.

لذلك فقط عندما تكون متأكدًا من عدم تعطيل المقاطعات فقط ، يجب عليك فقط spin_lock_irq وإلا يجب استخدامك دائمًا spin_lock_irqsave.

الحاجة ل spin_lock_irqsave بجانب spin_lock_irq يشبه إلى حد بعيد السبب local_irq_save(flags) مطلوب إلى جانب ذلك local_irq_disable. فيما يلي تفسير جيد لهذا المطلب مأخوذ من طبعة Linux Kernel Development Second من قبل Robert Love.

يكون روتين Local_IRQ_DISABLE () خطيرًا إذا تم تعطيل المقاطعات بالفعل قبل الاحتجاج بها. تتيح الدعوة المقابلة لـ LOCAL_IRQ_ENABLE () بشكل غير مشروط المقاطعات ، على الرغم من حقيقة أنها كانت في البداية للبدء. بدلاً من ذلك ، هناك حاجة إلى آلية لاستعادة المقاطعات إلى حالة سابقة. هذا مصدر قلق مشترك لأنه يمكن الوصول إلى مسار رمز معين في kernel مع تمكين أو بدون مقاطعات ، اعتمادًا على سلسلة المكالمات. على سبيل المثال ، تخيل أن مقتطف الكود السابق هو جزء من وظيفة أكبر. تخيل أن هذه الوظيفة تسمى وظيفتين أخريين ، واحدة تعطل المقاطعات والواحدة التي لا. نظرًا لأنه أصبح أكثر صعوبة مع نمو النواة في الحجم والتعقيد لمعرفة جميع مسارات التعليمات البرمجية التي تؤدي إلى وظيفة ما ، فمن الأكثر أمانًا لتوفير حالة نظام المقاطعة قبل تعطيلها. بعد ذلك ، عندما تكون مستعدًا لمقاطعات قابلة للإنشاء ، يمكنك ببساطة استعادتها إلى حالتها الأصلية:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */

لاحظ أن هذه الأساليب يتم تنفيذها جزئيًا على الأقل كوحدات وحشية ، وبالتالي فإن معلمة الأعلام (التي يجب تعريفها على أنها طويلة غير موقعة) يتم تمريرها على ما يبدو حسب القيمة. تحتوي هذه المعلمة على بيانات خاصة بالهندسة المعمارية التي تحتوي على حالة أنظمة المقاطعة. نظرًا لأن الهندسة المعمارية المدعومة واحدة على الأقل تتضمن معلومات المكدس في القيمة (مهم ، SPARC) ، لا يمكن نقل الأعلام إلى وظيفة أخرى (على وجه التحديد ، يجب أن تظل على نفس إطار المكدس). لهذا السبب ، يجب أن تحدث مكالمة الحفظ والمكالمة لاستعادة المقاطعات في نفس الوظيفة.

يمكن استدعاء جميع الوظائف السابقة من كل من سياق المقاطعة والعملية.

قراءة لماذا لا يمكن تنفيذ رمز kernel/مؤشر ترابط في سياق المقاطعة؟ الذي يرتبط بروبرت يحب مقالة - سلعة, ، انا اقرأ هذا :

تعمل بعض معالجات المقاطعة (المعروفة في Linux باسم معالجات المقاطعة السريعة) مع جميع المقاطعات على المعالج المحلي معاق. يتم ذلك لضمان تشغيل معالج المقاطعة دون انقطاع في أسرع وقت ممكن. أكثر من ذلك ، تعمل جميع معالجات المقاطعة مع تعطيل خط المقاطعة الحالي على جميع المعالجات. هذا يضمن عدم تشغيل اثنين من معالجات المقاطعة لنفس خط المقاطعة بشكل متزامن. كما أنه يمنع كتاب سائق الجهاز من الاضطرار إلى التعامل مع المقاطعات العودية ، مما يعقد البرمجة.

يوجد أدناه جزء من الكود في Linux kernel 4.15.18 ، مما يدل على أن spiin_lock_irq () سوف يتصل __raw_spin_lock_irq (). ومع ذلك ، فإنه لن يحفظ أي أعلام كما ترون أدناه جزء من الكود ولكن تعطيل المقاطعة.

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

يعرض الرمز أدناه Spin_lock_irqsave () الذي يحفظ المرحلة الحالية من العلم ومن ثم تعطيل التعطيل.

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}

يبدأ هذا السؤال من التأكيد الخاطئ: On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

لا ينبغي استخدام أي من هذه من سياق المقاطعة ، على SMP أو على أعلى. هكذا قال، spin_lock_irqsave() مايو يتم استخدامها من سياق المقاطعة ، باعتبارها أكثر عالمية (يمكن استخدامها في كل من المقاطعة والسياقات العادية) ، لكن من المفترض أن تستخدم spin_lock() من سياق المقاطعة ، و spin_lock_irq() أو spin_lock_irqsave() من السياق الطبيعي. استخدام spin_lock_irq() هو دائمًا ما يكون الشيء الخطأ الذي يجب القيام به في سياق المقاطعة ، كونه SMP أو أعلى. قد ينجح ذلك لأن معظم معالجات المقاطعة تعمل مع تمكين IRQs محليًا ، لكن يجب ألا تحاول ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top