بسم الله الرحمن الرحيم.
هذا المقال خاص بالعمليات والخيوط (Process & Threads) الموجودتين في جميع أنظمة التشغيل.
العمليات (Processes)
باختصار، العملية (Process) هي برنامج قيد التنفيذ (A Program in Execution).
للعملية بنية داخل النواة Kernel اسمها KProcess أو كتلة التحكم بالعملية (Process Control Block – PCB)، وتستخدمها النواة للتحكم بعمليات هذه البروسيس.
وتحتوي الـ PCB على حقول كثيرة، أهمها:
- Process State: حالة العملية.
- Process Number: رقم العملية.
- Program Counter: عداد البرنامج.
- Registers: السجلات الخاصة بالعملية.
شكل (1): بنية كتلة التحكم بالعملية
على مستوى الذاكرة: EProcess
على مستوى الذاكرة يختلف الأمر: يكون للعمليات بنية مختلفة اسمها EProcess، وهي مختلفة تماماً عن الـ KProcess.
ويحتوي الـ EProcess أيضاً على حقول كثيرة أهمها:
- KProcess Structure.
- Process ID.
- EXE File Name.
وتوجد الـ EProcesses في الذاكرة مرتبطة بقائمة مزدوجة (Doubly-Linked List) — أي أن كل عملية تحمل مؤشراً أمامياً Flink للعنصر التالي ومؤشراً خلفياً Blink للعنصر السابق:
شكل (2): ربط العمليات بقائمة مزدوجة في الذاكرة
وهناك كثير من البرامج تصل إلى هذه الـ EProcesses مثل Process Explorer الذي يمكنك تحميله من موقع Microsoft مباشرة.
الخيوط مقابل العمليات (Threads vs Processes)
- Thread: هي ما ينفذه نظام التشغيل.
- Process: هي الحاوية التي يحدث فيها التنفيذ.
فالعملية ليست هي التي تشغل الكود؛ بل يجب أن تحتوي على خيط واحد على الأقل ليشغل هذا الكود على الـ CPU (لكن لا يمكن أن يعمل الخيط منفرداً دون أن يكون تحت مظلة عملية).
وللخيط ثلاث حالات أساسية:
- Running: يعني أن الخيط يعمل حالياً على المعالج.
- Ready: في انتظار أن يفرغ المعالج ليشغلها.
- Blocked/Suspended: متوقفة بانتظار حدث ما؛ وعند اكتمال ذلك الحدث يستعيد المجدول تنفيذها.
شكل (3): حالات الخيط
نستنتج مما سبق أن العملية الواحدة يمكن أن تحتوي على خيط أو أكثر.
طيب، بما أن العملية قد تحتوي على أكثر من خيط، فهل تكون هذه الخيوط معزولة (Isolated)؟
لا؛ فهذه الخيوط تتشارك الذاكرة — أي أن الخيوط تتشارك فضاء العناوين (Address Space) وهو ببساطة أقسام الذاكرة مثل .data section و.code section. لكن يبقى لكل خيط Stack وسجلات Registers خاصة به.
فضاء العناوين الافتراضي (Virtual Address Space)
ما هو الـ Virtual Address Space؟ أي ملف exe نشغّله سيكون له عملية خاصة به، وهذه العملية لها فضاء عناوين خاص بها.
يأخذ الـ Address Space مدى يبدأ من 00000000 وينتهي بـ FFFFFFFF (في النظام 32-bit).
وينقسم هذا المدى إلى جزأين كل قسم 2GB: قسم للـ User Mode وقسم للـ Kernel Mode:
شكل (4): تقسيم الـ 4GB بين وضعي المستخدم والنواة
العنوان الافتراضي مقابل العنوان الفيزيائي (Virtual vs Physical Address)
لنأخذ مثالاً: لدينا برنامجان مختلفان:
- MyApp1
- MyApp2
كل برنامج له عملية، وكل عملية لها ذاكرة افتراضية تبدأ من 0x00000000 وتنتهي بـ 0x80000000، ولبرامجنا هذه أقسام Sections في الذاكرة.
عند تشغيل MyApp1 ستوضع أقسام .text و.data بعناوين معينة في الذاكرة، وكذلك MyApp2. وللصدفة تشابهت عناوين قسم .data في MyApp1 مع عناوين MyApp2. هل يعني هذا أن قسم .data سيكون مشتركاً؟
لا؛ لأن هذه العناوين ليست هي نفسها الموجودة في الـ RAM — فالعناوين الخاصة بالعمليات تكون افتراضية (Virtual).
لكن عندما يريد المعالج قراءة هذه البيانات يحدث ما يسمى الترجمة من الافتراضي إلى الفيزيائي (Virtual-to-Physical Translation): يتدخل نظام التشغيل في الأمر ويصل كل قسم بعنوان معين في الـ RAM الفعلية.
التزامن (Synchronization)
آخر نقطة سنتحدث عنها هي كيفية التزامن؟
• الـ Mutex — اختصار Mutual Exclusion ويطلق عليه Lock:
كائن (Object) نستخدمه في البرمجة عموماً لمنع حدوث وصول متزامن (Simultaneous Access) إلى أي مورد (Resource) سواء كان متغيراً أو ملفاً أو عنوان ذاكرة.
والـ Simultaneous Access يعني أن عدة برامج أو خيوط تستعمل الملف نفسه أو الذاكرة نفسها في الوقت ذاته.
مثال:
لو كان لدينا خيطان مختلفان مهمتهما الكتابة في الذاكرة المشتركة في الوقت نفسه، فماذا سيحدث هنا؟ يمكننا الاتفاق على أن هذين الخيطين لن نسمح لأي منهما بالكتابة في الذاكرة إلا إذا كان بحوزته كائن الـ Mutex — والـ Mutex كائن واحد فقط. أي: يأتي خيط يحمل الـ Mutex فيكتب في الذاكرة، ثم يتركه للخيط الآخر فيأخذه هذا الأخير ويكتب بدوره، وهكذا دواليك.
بهذا نكون قد غطينا أكبر قدر من المفاهيم المهمة الموجودة في كل أنظمة التشغيل إن شاء الله.
إن أحسنت فمن الله، وإن أسأت فمن نفسي والشيطان. لا تنسونا من صالح دعائكم.