<< العودة English

حياة متعددة: رحلة في عالم أنظمة التشغيل متعددة المهام

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

تخيل أن نظام التشغيل هو عمدة المدينة، وهو المسؤول عن تنظيم شؤونها. يُعرف كل برنامج يعمل على الحاسوب بـ "عملية" أو "عمل" (process)، وكل عملية تحتاج إلى "موارد" (resources) مثل المعالج (CPU) والذاكرة (RAM) للعمل.

يُمثل المعالج مثل "صانع الخبز" الذي يستطيع إعداد قطعة واحدة من الخبز في كل مرة، بينما تُمثل الذاكرة مثل "المخزن" الذي يُمكن للصانع أن يُحفظ فيه كل مكونات الخُبز التي سيُستخدمها في الوقت الحالي وتُشبه "الوصفات" التي يُحتفظ بها في الذاكرة هي "البرامج".

والآن، تخيل أن ثلاثة أشخاص يريدون طلب "خبز" من "صانع الخُبز" في نفس الوقت، وهنا يُصبح دور "العمدة" هامًا للغاية، فإنه يُقسم وقت "صانع الخُبز" بين جميع الطلبات، ويُحافظ على التوازن بين الطلبات المُختلفة بحيث لا يُصبح واحدٌ منهم "مُحظوظًا" أكثر من الآخر.

لإدارة هذه العمليات والموارد، يستخدم نظام التشغيل "جدولة العمليات" (process scheduling)، وهي تقنية تُمكن من توزيع الموارد بذكاء على العمليات المُختلفة، لضمان سلاسة عمل كل واحد منها.

هناك طرق عديدة لجدولة العمليات، مثل:

وهناك طرق أخرى أكثر تعقيدًا تُستخدم في أنظمة التشغيل الحديثة مثل "جدولة العمليات المُعدة للمُشاركة" (cooperative multitasking) و "جدولة العمليات الاستباقية" (preemptive multitasking) و "جدولة العمليات المُتعددة الأنوية" (multi-core scheduling).

مُثال عملي:

public class TaskManager {

    //  مُتغير  يُمثّل  جدول  العمليات  الذي  يُخزن  العمليات  و  أولوياتها.
    private PriorityQueue<Task> taskQueue;

    //  مُتغير  يُمثّل  المُعالج  (CPU)
    private CPU cpu;

    //  مُتغير  يُمثّل  الذاكرة  (RAM)
    private Memory memory;

    //  مُنشئ  لكلاس  TaskManager
    public TaskManager(CPU cpu, Memory memory) {
        this.cpu = cpu;
        this.memory = memory;
        //  إنشاء  جدول  العمليات  باستخدام  Queue  (FIFO)
        this.taskQueue = new PriorityQueue<>();
    }

    //  طريقة  لإضافة  عملية  جديدة  إلى  جدول  العمليات.
    public void addTask(Task task) {
        //  التحقق  من  توفر  الذاكرة  للعملية  الجديدة.
        if (memory.isAvailable(task.getMemorySize())) {
            taskQueue.offer(task);
        } else {
            //  تعامل  مع  نقص  الذاكرة.
            System.out.println("Not enough memory available!");
        }
    }

    //  طريقة  لإدارة  جدولة  العمليات.
    public void run() {
        //  التكرار  على  جميع  العمليات  في  جدول  العمليات.
        while (!taskQueue.isEmpty()) {
            //  استخراج  العملية  الأولى  من  جدول  العمليات.
            Task currentTask = taskQueue.poll();

            //  التحقق  من  توفر  المعالج  للأداء.
            if (cpu.isAvailable()) {
                //  تعيين  العملية  على  المعالج.
                cpu.run(currentTask);
            } else {
                //  التحقق  من  توفر  الذاكرة  للأداء.
                if (memory.isAvailable(currentTask.getMemorySize())) {
                    //  التحقق  من  توفر  الذاكرة  للأداء.
                    currentTask.wait();
                    //  إعادة  العملية  إلى  جدول  العمليات  بعد  انتظارها.
                    taskQueue.offer(currentTask);
                } else {
                    //  تعامل  مع  نقص  الذاكرة.
                    System.out.println("Not enough memory available!");
                }
            }
        }
    }

    //  مُمثّل  للعملية  (Task)
    private class Task {

        //  اسم  العملية.
        private String name;

        //  حجم  الذاكرة  المُطلوبة  للأداء.
        private int memorySize;

        //  أولوية  العملية.
        private int priority;

        //  مُنشئ  لكلاس  Task
        public Task(String name, int memorySize, int priority) {
            this.name = name;
            this.memorySize = memorySize;
            this.priority = priority;
        }

        //  طريقة  للحصول  على  حجم  الذاكرة  المُطلوبة.
        public int getMemorySize() {
            return memorySize;
        }

        //  طريقة  للعملية  للانتظار  قبل  أداء.
        public void wait() {
            //  انتظار  العملية  قبل  أداء.
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //  تعامل  مع  الاستثناء.
                e.printStackTrace();
            }
        }

        //  طريقة  لأداء  العملية.
        public void run() {
            //  أداء  العملية  (مثال  فقط).
            System.out.println("Running task: " + name);
        }
    }

    //  مُمثّل  للمُعالج  (CPU)
    private class CPU {

        //  حالة  المُعالج  (متاح  أم  لا).
        private boolean available;

        //  مُنشئ  لكلاس  CPU
        public CPU() {
            this.available = true;
        }

        //  طريقة  للتحقق  من  توفر  المُعالج  للأداء.
        public boolean isAvailable() {
            return available;
        }

        //  طريقة  لتشغيل  العملية  على  المُعالج.
        public void run(Task task) {
            //  تعيين  حالة  المُعالج  إلى  غير  متاح.
            this.available = false;
            //  أداء  العملية  على  المُعالج.
            task.run();
            //  تعيين  حالة  المُعالج  إلى  متاح  مرة  أخرى.
            this.available = true;
        }
    }

    //  مُمثّل  للذاكرة  (RAM)
    private class Memory {

        //  حجم  الذاكرة  المُتاح.
        private int availableMemory;

        //  مُنشئ  لكلاس  Memory
        public Memory(int availableMemory) {
            this.availableMemory = availableMemory;
        }

        //  طريقة  للتحقق  من  توفر  الذاكرة  للأداء.
        public boolean isAvailable(int memorySize) {
            return availableMemory >= memorySize;
        }

        //  طريقة  للتخصيص  الذاكرة  للأداء.
        public void allocateMemory(int memorySize) {
            availableMemory -= memorySize;
        }

        //  طريقة  لتحرير  الذاكرة  بعد  أداء.
        public void releaseMemory(int memorySize) {
            availableMemory += memorySize;
        }
    }
}

تعليقات على الكود:

//  مُتغير  يُمثّل  جدول  العمليات  الذي  يُخزن  العمليات  و  أولوياتها.
private PriorityQueue<Task> taskQueue;

//  مُتغير  يُمثّل  المُعالج  (CPU)
private CPU cpu;

//  مُتغير  يُمثّل  الذاكرة  (RAM)
private Memory memory;

//  مُنشئ  لكلاس  TaskManager
public TaskManager(CPU cpu, Memory memory) {
    this.cpu = cpu;
    this.memory = memory;
    //  إنشاء  جدول  العمليات  باستخدام  Queue  (FIFO)
    this.taskQueue = new PriorityQueue<>();
}

//  طريقة  لإضافة  عملية  جديدة  إلى  جدول  العمليات.
public void addTask(Task task) {
    //  التحقق  من  توفر  الذاكرة  للعملية  الجديدة.
    if (memory.isAvailable(task.getMemorySize())) {
        taskQueue.offer(task);
    } else {
        //  تعامل  مع  نقص  الذاكرة.
        System.out.println("Not enough memory available!");
    }
}

//  طريقة  لإدارة  جدولة  العمليات.
public void run() {
    //  التكرار  على  جميع  العمليات  في  جدول  العمليات.
    while (!taskQueue.isEmpty()) {
        //  استخراج  العملية  الأولى  من  جدول  العمليات.
        Task currentTask = taskQueue.poll();

        //  التحقق  من  توفر  المعالج  للأداء.
        if (cpu.isAvailable()) {
            //  تعيين  العملية  على  المعالج.
            cpu.run(currentTask);
        } else {
            //  التحقق  من  توفر  الذاكرة  للأداء.
            if (memory.isAvailable(currentTask.getMemorySize())) {
                //  التحقق  من  توفر  الذاكرة  للأداء.
                currentTask.wait();
                //  إعادة  العملية  إلى  جدول  العمليات  بعد  انتظارها.
                taskQueue.offer(currentTask);
            } else {
                //  تعامل  مع  نقص  الذاكرة.
                System.out.println("Not enough memory available!");
            }
        }
    }
}

//  مُمثّل  للعملية  (Task)
private class Task {

    //  اسم  العملية.
    private String name;

    //  حجم  الذاكرة  المُطلوبة  للأداء.
    private int memorySize;

    //  أولوية  العملية.
    private int priority;

    //  مُنشئ  لكلاس  Task
    public Task(String name, int memorySize, int priority) {
        this.name = name;
        this.memorySize = memorySize;
        this.priority = priority;
    }

    //  طريقة  للحصول  على  حجم  الذاكرة  المُطلوبة.
    public int getMemorySize() {
        return memorySize;
    }

    //  طريقة  للعملية  للانتظار  قبل  أداء.
    public void wait() {
        //  انتظار  العملية  قبل  أداء.
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            //  تعامل  مع  الاستثناء.
            e.printStackTrace();
        }
    }

    //  طريقة  لأداء  العملية.
    public void run() {
        //  أداء  العملية  (مثال  فقط).
        System.out.println("Running task: " + name);
    }
}

//  مُمثّل  للمُعالج  (CPU)
private class CPU {

    //  حالة  المُعالج  (متاح  أم  لا).
    private boolean available;

    //  مُنشئ  لكلاس  CPU
    public CPU() {
        this.available = true;
    }

    //  طريقة  للتحقق  من  توفر  المُعالج  للأداء.
    public boolean isAvailable() {
        return available;
    }

    //  طريقة  لتشغيل  العملية  على  المُعالج.
    public void run(Task task) {
        //  تعيين  حالة  المُعالج  إلى  غير  متاح.
        this.available = false;
        //  أداء  العملية  على  المُعالج.
        task.run();
        //  تعيين  حالة  المُعالج  إلى  متاح  مرة  أخرى.
        this.available = true;
    }
}

//  مُمثّل  للذاكرة  (RAM)
private class Memory {

    //  حجم  الذاكرة  المُتاح.
    private int availableMemory;

    //  مُنشئ  لكلاس  Memory
    public Memory(int availableMemory) {
        this.availableMemory = availableMemory;
    }

    //  طريقة  للتحقق  من  توفر  الذاكرة  للأداء.
    public boolean isAvailable(int memorySize) {
        return availableMemory >= memorySize;
    }

    //  طريقة  للتخصيص  الذاكرة  للأداء.
    public void allocateMemory(int memorySize) {
        availableMemory -= memorySize;
    }

    //  طريقة  لتحرير  الذاكرة  بعد  أداء.
    public void releaseMemory(int memorySize) {
        availableMemory += memorySize;
    }
}

يُمكن أن تُرى هذه الأنظمة كأنها "سُفينة فضائية" تُحمل برامج متعددة تُشغل في نفس الوقت، ويقوم نظام التشغيل بإدارة "الطاقة" و "الموارد" بين جميع البرامج لضمان "الرحلة السلسة" للجميع.

تُعتبر أنظمة التشغيل متعددة المهام أساسًا لكل حاسوب حديث، من الهاتف المُحمول إلى الحاسوب الشخصي إلى الخوادم الكبيرة. وهي تُمكن من زيادة كفاءة الحاسوب وإنجاز مهام متعددة في وقت واحد، و تُسهل من استخدام الحاسوب في جميع مجالات الحياة.

هل تُريد أن تُصبح "مُبرمج أنظمة تشغيل" ؟ لا تُفكر في هذا الاختيار على أنه "معركة" صعبة، بل اعتبره "مُغامرة" جديدة لإكتشاف عالم التكنولوجيا المُثير!