سؤال

أنا أكتب رمزًا لوحدة Linux kernel وأواجه سلوكًا غريبًا فيه. ها هو رمزتي:

int data = 0;
void threadfn1()
{
    int j;
    for( j = 0; j < 10; j++ )
        printk(KERN_INFO "I AM THREAD 1 %d\n",j);   
    data++;
}

void threadfn2()
{
    int j;
    for( j = 0; j < 10; j++ )
        printk(KERN_INFO "I AM THREAD 2 %d\n",j);
    data++; 
}
static int __init abc_init(void)
{
        struct task_struct *t1 = kthread_run(threadfn1, NULL, "thread1");
        struct task_struct *t2 = kthread_run(threadfn2, NULL, "thread2");
        while( 1 )
        {
        printk("debug\n"); // runs ok
            if( data >= 2 )
            {
                kthread_stop(t1);
                kthread_stop(t2);
                break;
            }
        }
        printk(KERN_INFO "HELLO WORLD\n");

 }

في الأساس ، كنت أحاول الانتظار حتى تنتهي المواضيع ثم طباعة شيء بعد ذلك. الكود أعلاه يحقق هذا الهدف ولكن مع "printk("debug\n");" لم يعلق. بمجرد التعليق printk("debug\n"); لتشغيل الكود دون تصحيح الأخطاء وتحميل الوحدة النمطية من خلال أمر insmod ، يتم تعليق الوحدة النمطية ويبدو أنها تضيع في عودة. أنا لا لماذا طباعة تأثيرات الكود الخاص بي بطريقة كبيرة؟

سيكون موضع تقدير أي مساعدة.

مع تحياتي.

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

المحلول

مع دعوة إلى printk() إزالة المترجم يعمل على تحسين الحلقة إلى while (1);. عند إضافة المكالمة إلى printk() المترجم غير متأكد من ذلك data لم يتم تغييره ، وبالتالي يتحقق من القيمة في كل مرة من خلال الحلقة.

يمكنك إدراج حاجز في الحلقة ، مما يجبر المترجم على إعادة تقييم data على كل تكرار. على سبيل المثال:

while (1) {
        if (data >= 2) {
                kthread_stop(t1);
                kthread_stop(t2);
                break;
        }

        barrier();
}

نصائح أخرى

أنت لا تتم مزامنة الوصول إلى متغير البيانات. ما يحدث هو أن المترجم سيولد حلقة لا حصر لها. هنا لماذا:

  while( 1 )
        {
            if( data >= 2 )
            {
                kthread_stop(t1);
                kthread_stop(t2);
                break;
            }
        }

يمكن للمترجم أن يكتشف أن قيمة البيانات لا تتغير أبدًا خلال الحلقة. لذلك يمكنه تحريك الاختيار من الحلقة تمامًا وستنتهي ببسيط

 while (1) {} 

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

كيف يمكن اصلاح هذا:

استخدام بدائل مزامنة الخيط المناسبة. إذا قمت بإنهاء الوصول إلى البيانات إلى قسم رمز محمي بواسطة Mutex ، فسيعمل الرمز. يمكنك أيضًا استبدال البيانات المتغيرة واستخدام Semaphore المحسوب بدلاً من ذلك.

يحرر:

يشرح هذا الرابط كيف يعمل Linux-Kernel:

http://www.linuxgrill.com/anonymous/fire/netfilter/kernel-hacking-howto-5.html

ربما يجب إعلان البيانات المتقلبة؟ يمكن أن يكون المترجم لا يذهب إلى الذاكرة للحصول على البيانات في الحلقة.

إجابة Nils Pipenbrinck على الفور. سأضيف فقط بعض المؤشرات.

دليل Rusty غير موثوق به إلى قفل kernel (يجب على كل قراصنة kernel قراءة هذا).
وداعا semaphores؟, واجهة برمجة تطبيقات Mutex (lwn.net مقالات على واجهة برمجة تطبيقات Mutex الجديدة التي تم تقديمها في أوائل عام 2006 ، قبل أن تستخدم Kernel Linux Semaphores كقوائم).

أيضًا ، نظرًا لأن بياناتك المشتركة هي عداد بسيط ، يمكنك فقط استخدام واجهة برمجة التطبيقات الذرية (في الأساس ، إعلان عدادك كأذرات Atomic_T والوصول إليها باستخدام وظائف Atomic_*).

قد لا يكون التقلب دائمًا "فكرة سيئة". يحتاج المرء إلى فصل حالة عندما تكون هناك حاجة إلى متقلبة وعندما تكون هناك حاجة إلى آلية الاستبعاد المتبادل. إنه غير مثالي عندما يستخدم المرء أو يسيء استخدام آلية واحدة للآخر. في الحالة أعلاه. أود أن أقترح على الحل الأمثل ، أن كلتا الآليتين مطلوبة: Mutex لتوفير استبعاد متبادل ، متقلبة للإشارة إلى المترجم أن "المعلومات" يجب قراءة جديدة من الأجهزة. خلاف ذلك ، في بعض الحالة (التحسين -o2 ، -o3) ، قد يترك المجمعون عن غير قصد الرموز المطلوبة.

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