পাঠ ০৬ · ৫৬-এর মধ্যে · মডিউল ২
Home / Courses / Operating Systems (OS) / PCB ও কনটেক্সট সুইচিং

প্রসেস কন্ট্রোল ব্লক (PCB) ও কনটেক্সট সুইচিং

Process Control Block (PCB) & context switching
৮ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • PCB-তে ঠিক কী কী তথ্য সংরক্ষিত থাকে এবং কেন প্রতিটি প্রসেসের নিজস্ব একটি PCB দরকার
  • কনটেক্সট সুইচ ঠিক কী করে — সেভ ও লোডের সঠিক ক্রম
  • কেন কনটেক্সট সুইচ "বিনামূল্যে" নয় — এটি কেন খাঁটি ওভারহেড
  • Python দিয়ে দুটি প্রসেসের মধ্যে একটি বাস্তব কনটেক্সট-সুইচ সিমুলেশন, ফিরে গিয়ে যাচাই করা রেজিস্টার স্টেট ঠিকভাবে সংরক্ষিত ছিল কি না

১ · PCB — OS-এর "প্রসেস ফাইল"

PCB (Process Control Block)Process Control BlockOS-এর ডেটা স্ট্রাকচার যা একটি নির্দিষ্ট প্রসেস পরিচালনার জন্য প্রয়োজনীয় সব তথ্য (স্টেট, রেজিস্টার, প্রায়োরিটি, মেমরি তথ্য, খোলা ফাইল) ধরে রাখে। হলো OS-এর একটি ডেটা স্ট্রাকচার — প্রতিটি প্রসেসের জন্য একটি করে — যেখানে সেই প্রসেস পরিচালনার জন্য প্রয়োজনীয় প্রায় সবকিছু থাকে: L05-এর প্রসেস স্টেট, প্রোগ্রাম কাউন্টার (পরবর্তী কোন নির্দেশ চালাতে হবে), CPU রেজিস্টারের মান, CPU শিডিউলিং তথ্য (প্রায়োরিটি — M3-এ বিস্তারিত), মেমরি ম্যানেজমেন্ট তথ্য, এবং I/O স্ট্যাটাস তথ্য। সব প্রসেসের PCB একসাথে মিলে OS-এর অভ্যন্তরীণ "প্রসেস টেবিল" গঠন করে — OS এই টেবিল দেখেই জানে সিস্টেমে এই মুহূর্তে কোন কোন প্রসেস আছে এবং প্রতিটির অবস্থা কী।

২ · কনটেক্সট সুইচ কী

কনটেক্সট সুইচContext SwitchOS যখন CPU-কে এক প্রসেস চালানো থেকে সরিয়ে আরেকটি প্রসেস চালানোয় নিয়ে যায় — বর্তমান প্রসেসের রেজিস্টার স্টেট সেভ করে এবং পরবর্তী প্রসেসের স্টেট লোড করে। ঘটে যখন OS CPU-কে একটি চলমান প্রসেস থেকে সরিয়ে অন্য একটি প্রসেসে নিয়ে যায়। এই প্রক্রিয়ায় দুটি ধাপ অবশ্যই সঠিক ক্রমে ঘটতে হবে — প্রথমে বর্তমান (আউটগোয়িং) প্রসেসের CPU রেজিস্টার স্টেট তার নিজের PCB-তে সেভ করতে হবে (নাহলে সেই কাজ চিরতরে হারিয়ে যাবে), তারপর পরবর্তী (ইনকামিং) প্রসেসের PCB থেকে তার আগে সেভ করা রেজিস্টার স্টেট CPU-তে লোড করতে হবে। এই সেভ/লোড ছাড়া CPU জানবেই না ইনকামিং প্রসেসটি ঠিক কোথায় থেমে ছিল।

P1-এর PCB CPU রেজিস্টার P2-এর PCB ১. সেভ (P1) ২. লোড (P2) P1: Running to Ready · P2: Ready to Running
সেভ সবসময় লোডের আগে — নাহলে P1-এর কাজ হারিয়ে যাবে।

৩ · কনটেক্সট সুইচ কেন "বিনামূল্যে" নয়

কনটেক্সট সুইচের সময় CPU কোনো ব্যবহারকারী প্রোগ্রামের জন্য দরকারি কাজ করছে না — এটি শুধু রেজিস্টার সেভ/লোড করছে। এই সময়টুকু সম্পূর্ণ ওভারহেড — কোনো প্রসেসের প্রকৃত অগ্রগতি হচ্ছে না। তাই যত ঘন ঘন কনটেক্সট সুইচ হবে, তত বেশি CPU সময় এই "হিসাব-নিকাশে" ব্যয় হবে, প্রকৃত কাজে কম। এই সরাসরি সম্পর্কই M3-এর Round Robin শিডিউলিংয়ের টাইম-কোয়ান্টাম বেছে নেওয়ার সবচেয়ে গুরুত্বপূর্ণ ট্রেড-অফ — কোয়ান্টাম খুব ছোট হলে বেশি সুইচ হয় (বেশি ওভারহেড), খুব বড় হলে রেসপন্সিভনেস কমে যায়।

৪ · একটি কনটেক্সট-সুইচ সিমুলেশন

নিচের কোডে দুটি প্রসেসের PCB এবং একটি context_switch() ফাংশন তৈরি করে দেখানো হলো P1 থেকে P2-তে সুইচ করা, তারপর আবার P2 থেকে P1-এ ফিরে আসা — এবং যাচাই করা হলো P1-এর রেজিস্টার স্টেট এই দুই সুইচের মধ্যেও অবিকৃত ছিল কি না।

Python
# PCB ও কনটেক্সট সুইচ সিমুলেশন -- সিমুলেটেড, বাস্তব কোনো CPU রেজিস্টার নয়

pcb_table = {
    "P1": {"pid": 1, "state": "Running", "priority": 2, "saved_registers": None},
    "P2": {"pid": 2, "state": "Ready",   "priority": 1, "saved_registers": None},
}

def context_switch(cpu_registers, outgoing_pcb, incoming_pcb):
    # ধাপ ১: বর্তমান (আউটগোয়িং) প্রসেসের রেজিস্টার স্টেট তার নিজের PCB-তে সেভ করা
    outgoing_pcb["saved_registers"] = dict(cpu_registers)
    outgoing_pcb["state"] = "Ready"
    # ধাপ ২: ইনকামিং প্রসেসের PCB থেকে তার সেভ করা রেজিস্টার স্টেট লোড করা
    incoming_pcb["state"] = "Running"
    return dict(incoming_pcb["saved_registers"])

# শুরুতে P1 চলছে, CPU রেজিস্টারে তার state আছে
cpu_registers = {"PC": 1000, "ACC": 42, "SP": 9000}
pcb_table["P1"]["saved_registers"] = dict(cpu_registers)

# P2-এর আগে সেভ করা রেজিস্টার (এখনও কখনো চলেনি ধরে নিলে তার শুরুর মান)
pcb_table["P2"]["saved_registers"] = {"PC": 2000, "ACC": 0, "SP": 8000}

print("সুইচ ১: P1 -> P2")
cpu_registers = context_switch(cpu_registers, pcb_table["P1"], pcb_table["P2"])
print("CPU রেজিস্টার এখন (P2-এর):", cpu_registers)
print("P1-এর PCB-তে সেভ হওয়া রেজিস্টার:", pcb_table["P1"]["saved_registers"])

# P2 চলাকালীন কিছু কাজ করলো, তার রেজিস্টার বদলে গেলো
cpu_registers["ACC"] = 99
cpu_registers["PC"] = 2050

print("\nসুইচ ২: P2 -> P1 (ফিরে যাওয়া)")
cpu_registers = context_switch(cpu_registers, pcb_table["P2"], pcb_table["P1"])
print("CPU রেজিস্টার এখন (P1-এর):", cpu_registers)
print("P2-এর PCB-তে সেভ হওয়া রেজিস্টার:", pcb_table["P2"]["saved_registers"])

print("\nনিশ্চিতকরণ: P1-এর রেজিস্টার অবস্থা সঠিকভাবে সংরক্ষিত ছিল কি?",
      cpu_registers == {"PC": 1000, "ACC": 42, "SP": 9000})

    
লক্ষ্য করুন — সুইচ ২-এর সময় P1-এর PCB-তে থাকা saved_registers কেউ স্পর্শই করেনি (P2 চলাকালীন শুধু cpu_registers ভেরিয়েবল বদলেছে, P1-এর PCB নয়) — তাই যখন P1-এ ফিরে আসা হলো, তার ঠিক আগের অবস্থাই (PC=1000, ACC=42, SP=9000) নিখুঁতভাবে ফিরে এলো। এটিই কনটেক্সট সুইচের মূল প্রতিশ্রুতি।
মূল কথা · Key takeaway

PCB ও কনটেক্সট সুইচ একসাথে একটি বিভ্রম তৈরি করে — প্রতিটি প্রসেস মনে করে সে একাই, বাধাহীনভাবে চলছে, যদিও বাস্তবে CPU বারবার তার থেকে সরে গিয়ে অন্য প্রসেসে যাচ্ছে এবং ফিরে আসছে। L07 দেখাবে এই প্রসেসগুলো আসলে কীভাবে তৈরি ও শেষ হয়, এবং একটি টার্মিনেটেড প্রসেস কীভাবে সাময়িকভাবে একটি "জম্বি" হিসেবে টেবিলে থেকে যেতে পারে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ যদি কনটেক্সট সুইচের সময় "সেভ" ধাপটি "লোড" ধাপের আগে না হয়ে পরে করা হতো, তাহলে কী সমস্যা হতো?

যদি আগে লোড করা হতো (ইনকামিং প্রসেসের রেজিস্টার CPU-তে বসিয়ে দেওয়া), তাহলে আউটগোয়িং প্রসেসের এখনো-না-সেভ-করা বর্তমান রেজিস্টার স্টেট চিরতরে হারিয়ে যেত — CPU রেজিস্টার ওভাররাইট হয়ে যাওয়ার পর আর সেই ডেটা ফিরে পাওয়ার উপায় নেই। এই কারণেই ক্রম কঠোরভাবে "প্রথমে সেভ, তারপর লোড" হতে হবে — উপরের কোডেও context_switch() ফাংশনে ঠিক এই ক্রমেই কাজ দুটি হয়েছে।

প্র ০২ PCB-তে "CPU শিডিউলিং তথ্য" (যেমন প্রায়োরিটি) কেন রাখা দরকার — এটি কি রেজিস্টার স্টেটের সাথে সম্পর্কিত নয়?

প্রায়োরিটি রেজিস্টার স্টেটের অংশ নয় (এটি হার্ডওয়্যার-স্তরের CPU ডেটা নয়), কিন্তু OS-এর শিডিউলারের (M3) জন্য অপরিহার্য — পরবর্তী কোন প্রসেসকে CPU দেওয়া হবে তা ঠিক করতে শিডিউলারকে প্রতিটি Ready প্রসেসের প্রায়োরিটি জানতে হয়। যেহেতু PCB ইতিমধ্যেই "প্রতি প্রসেসের সব প্রাসঙ্গিক তথ্যের একক জায়গা," প্রায়োরিটির মতো শিডিউলিং-সম্পর্কিত তথ্যও যৌক্তিকভাবে সেখানেই রাখা হয়, রেজিস্টার স্টেটের পাশাপাশি একটি আলাদা ফিল্ড হিসেবে।

প্র ০৩ উপরের কোড সেলে "নিশ্চিতকরণ" লাইনটি কীভাবে প্রমাণ করছে যে কনটেক্সট সুইচ সঠিকভাবে কাজ করেছে?

সুইচ ১-এর পর P2 চলাকালীন cpu_registers-এ ইচ্ছাকৃতভাবে পরিবর্তন আনা হয়েছিল (ACC=99, PC=2050) — যদি P1-এর আসল অবস্থা কোনোভাবে P2-এর পরিবর্তনের দ্বারা প্রভাবিত হতো, তাহলে সুইচ ২-এর পর ফিরে পাওয়া রেজিস্টার আসল {"PC": 1000, "ACC": 42, "SP": 9000}-এর সাথে মিলত না। শেষ লাইনে সরাসরি এই দুটি তুলনা করে True/False প্রিন্ট করে দেখানো হয়েছে যে P1-এর PCB সঠিকভাবে তার নিজস্ব, অবিকৃত অবস্থা ধরে রেখেছিল।

অনুশীলন

  1. চিন্তা করুন: আপনার কম্পিউটারে একসাথে ৫০টির বেশি প্রসেস চলছে এমন একটি অবস্থা কল্পনা করুন — একটি একক-কোর CPU সবগুলোর PCB নিয়ে কী করবে বলে মনে করেন?

    সবগুলো প্রসেসের PCB OS-এর process table-এ থাকবে, কিন্তু যেকোনো মুহূর্তে মাত্র একটি প্রসেসের রেজিস্টার স্টেট আসল CPU-তে লোড থাকবে (Running স্টেট) — বাকি প্রায় ৪৯টির (Ready বা Waiting) রেজিস্টার স্টেট তাদের নিজস্ব PCB-তে সেভ করা অবস্থায় থাকবে। শিডিউলার (M3) দ্রুত, ঘন ঘন কনটেক্সট সুইচ করে একে একে প্রতিটিকে সংক্ষিপ্ত সময়ের জন্য CPU দেবে, যা মানুষের চোখে "সবগুলো একসাথে চলছে" বলে মনে হয়।

  2. পরীক্ষা করুন: উপরের কোড সেলে সুইচ ২-এর আগে pcb_table["P1"]["saved_registers"]["ACC"] = 999 লাইন যোগ করে (P1-এর PCB সরাসরি বদলে দিয়ে) Run চাপুন — শেষ "নিশ্চিতকরণ" লাইনটি এখন কী প্রিন্ট করবে?

    এখন সুইচ ২-এ context_switch() P1-এর PCB থেকে {"PC": 1000, "ACC": 999, "SP": 9000} লোড করবে (কারণ আমরা সরাসরি PCB-র মধ্যেই ACC বদলে দিয়েছি), যা মূল {"PC": 1000, "ACC": 42, "SP": 9000}-এর সাথে মেলে না — তাই নিশ্চিতকরণ লাইনটি এবার False প্রিন্ট করবে। এটি দেখায় যে PCB নিজেই যদি বাইরে থেকে পরিবর্তিত হয়, কনটেক্সট সুইচ সেই (সম্ভবত অনিচ্ছাকৃত) পরিবর্তনসহই ডেটা লোড করবে — সুইচ ফাংশনটি নিজে কোনো "সঠিকতা" যাচাই করে না।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
প্রসেস কনসেপ্ট ও প্রসেস স্টেট