ইন্টার-প্রসেস কমিউনিকেশন (IPC)
এই পাঠে যা শিখবেন
- Shared memory মডেল — কী, কেন দ্রুত, এবং কেন কোঅর্ডিনেশন দরকার
- Message passing মডেল — কী, কেন নিরাপদ, এবং কেন ধীর
- দুই মডেলের সরাসরি তুলনা এবং কোনটি কখন উপযোগী
- Python দিয়ে দুটি সিমুলেশন — একটি আনপ্রোটেক্টেড শেয়ার্ড কাউন্টারে race condition, আর একটি deque-ভিত্তিক মেইলবক্স
১ · দুটি মৌলিক IPC মডেল
OS একটি মেমরি অঞ্চল একাধিক প্রসেসের কাছে ম্যাপ করে দেয়; সেটআপের পর প্রসেসগুলো সরাসরি সেই মেমরিতে read/write করে — কোনো OS ইনভলভমেন্ট ছাড়াই, তাই দ্রুত। কিন্তু কোঅর্ডিনেশনের দায়িত্ব প্রসেসদের নিজেদের।
প্রসেসরা OS-এর
send()/receive() কল দিয়ে discrete বার্তা আদান-প্রদান করে; OS প্রতিটি ট্রান্সফার সামলায় — সঠিকতা যুক্তি করা সহজ (কোনো shared-state race নেই), কিন্তু প্রতি বার্তায় OS ইনভলভমেন্টের কারণে ধীর। মাইক্রোকার্নেল ডিজাইনে (L04) এই মডেলই প্রধান।২ · শেয়ার্ড মেমরির ঝুঁকি — race condition
Shared memory-র সমস্যা হলো — OS শুধু মেমরি অঞ্চলটি সেটআপ করে দেয়, কিন্তু কোন প্রসেস কখন সেটি পড়বে/লিখবে তা নিয়ন্ত্রণ করে না। যদি দুটি প্রসেস কোনো কোঅর্ডিনেশন ছাড়াই একই শেয়ার্ড ডেটা একসাথে পড়তে/লিখতে যায়, ফলাফল নির্ভর করবে তাদের এক্সিকিউশনের অনির্দেশ্য টাইমিং/ইন্টারলিভিং-এর উপর — এটিই race condition, এবং এটি M5 (প্রসেস সিনক্রোনাইজেশন)-এর কেন্দ্রীয় সমস্যা।
৩ · সিমুলেশন — শেয়ার্ড মেমরি রেস কন্ডিশন বনাম মেসেজ পাসিং
নিচের কোড সেলে প্রথমে একটি আনপ্রোটেক্টেড শেয়ার্ড কাউন্টার দেখানো হয়েছে — দুটি "প্রসেস" কোনো লক ছাড়াই
কাউন্টার +1 করতে চায়, কিন্তু increment আসলে তিনটি সাব-স্টেপ (read, add, write) — ম্যানুয়ালি এই সাব-স্টেপগুলো
ইন্টারলিভ করে দেখানো হয়েছে একটি আপডেট কীভাবে হারিয়ে যায়। এরপর একটি collections.deque-ভিত্তিক
মেইলবক্স দিয়ে message passing দেখানো হয়েছে।
# সম্পূর্ণ ইন-মেমরি সিমুলেশন -- বাস্তব থ্রেড/প্রসেস বা OS shared-memory কল নয়
shared = {"counter": 0}
def read_counter(mem):
return mem["counter"]
def write_counter(mem, value):
mem["counter"] = value
print("--- Shared memory: দুটি প্রসেস কোনো কোঅর্ডিনেশন ছাড়াই counter += 1 করতে চায় ---")
# increment আসলে ৩টি সাব-স্টেপ: read -> add -> write। এখানে ম্যানুয়ালি ইন্টারলিভ করা হলো:
val_a = read_counter(shared) # প্রসেস A পড়লো
val_b = read_counter(shared) # প্রসেস B পড়লো (A এখনো লিখেনি!)
print(f"A পড়লো: {val_a} | B পড়লো: {val_b} (দুজনেই একই পুরনো মান পেলো)")
val_a += 1 # A হিসাব করলো
val_b += 1 # B হিসাব করলো
write_counter(shared, val_a) # A লিখলো
print(f"A লিখলো: {val_a} -> counter = {shared['counter']}")
write_counter(shared, val_b) # B লিখলো -- A-এর আপডেট মুছে গেলো!
print(f"B লিখলো: {val_b} -> counter = {shared['counter']} (A-এর আপডেট হারিয়ে গেলো!)")
print("\nপ্রত্যাশিত মোট (দুইবার +1) =", 2)
print("প্রকৃত counter =", shared["counter"])
print("রেস কন্ডিশনের কারণে একটি আপডেট হারিয়ে গেলো?", shared["counter"] < 2)
print("\n--- Message passing: deque-ভিত্তিক মেইলবক্স দিয়ে send()/receive() ---")
from collections import deque
mailbox = deque()
def send(mb, msg):
mb.append(msg)
print(f"send(): '{msg}' পাঠানো হলো, মেইলবক্সে এখন {len(mb)}টি বার্তা")
def receive(mb):
if not mb:
print("receive(): মেইলবক্স খালি, কিছু নেই")
return None
msg = mb.popleft()
print(f"receive(): '{msg}' গ্রহণ করা হলো, মেইলবক্সে এখন {len(mb)}টি বার্তা বাকি")
return msg
send(mailbox, "প্রসেস A থেকে: হ্যালো")
send(mailbox, "প্রসেস A থেকে: ডেটা রেডি")
receive(mailbox)
receive(mailbox)
receive(mailbox)
print("\n--- তুলনা ---")
print("Shared memory: দ্রুত, কিন্তু M5 (সিনক্রোনাইজেশন) ছাড়া উপরের মতো race condition ঘটে")
print("Message passing: প্রতি বার্তায় OS জড়িত থাকায় ধীর, কিন্তু কোনো shared state নেই বলেই নিরাপদ")
Shared memory ও message passing একে অপরের বিপরীত trade-off — গতি বনাম নিরাপত্তা। বাস্তব সিস্টেমে দুটোই ব্যবহৃত হয়: performance-critical জায়গায় shared memory (সাথে M5-এর সিনক্রোনাইজেশন টুল), আর simplicity/ isolation গুরুত্বপূর্ণ জায়গায় (যেমন মাইক্রোকার্নেল, L04) message passing।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ যদি প্রসেস A এবং B-এর সাব-স্টেপগুলো এমনভাবে ইন্টারলিভ হতো যে A সম্পূর্ণ read-add-write শেষ করার পরই B শুরু করতো, তাহলে কী হতো?
তাহলে কোনো race condition ঘটতো না — B যখন read করতো, ততক্ষণে A ইতিমধ্যে তার নতুন মান (1) লিখে ফেলেছে, তাই B সেই 1 পড়ে তাতে +1 করে 2 লিখতো, যা প্রত্যাশিত ফলাফল। এটিই দেখায় race condition কোনো নিশ্চিত ফলাফল নয় — এটি টাইমিং-নির্ভর এবং অনির্দেশ্য, যা একে ডিবাগ করা এত কঠিন করে তোলে।
প্র ০২ মেসেজ পাসিং মডেলে কি কখনো race condition ঘটতে পারে?
মেইলবক্সের নিজের অপারেশন (append/popleft) OS/রানটাইম দ্বারা atomic হিসেবে সামলানো হয় বলে ধরে নেওয়া হয়, তাই মূল IPC মেকানিজমে race থাকে না। তবে যদি একাধিক প্রসেস বার্তার ভেতরের কোনো shared রিসোর্স নিয়ে আলাদাভাবে সিদ্ধান্ত নেয় (যেমন দুটি প্রসেস একই বার্তা দেখে একই কাজ দুইবার করে ফেলা), সেটি একটি ভিন্ন ধরনের কোঅর্ডিনেশন বাগ — কিন্তু ক্লাসিক shared-memory lost-update সমস্যাটি নয়।
প্র ০৩ মাইক্রোকার্নেল ডিজাইন (L04) কেন সাধারণত message passing-কে প্রাধান্য দেয়, monolithic kernel-এর তুলনায়?
মাইক্রোকার্নেলে ফাইল সিস্টেম, ড্রাইভার ইত্যাদি আলাদা user-mode প্রসেস হিসেবে চলে (L04) — তারা একে অপরের মেমরিতে সরাসরি অ্যাক্সেস পায় না (fault isolation বজায় রাখতে)। তাই তাদের যোগাযোগের জন্য OS-নিয়ন্ত্রিত message passing স্বাভাবিক পছন্দ — এটি ধীর হলেও একটি component ক্র্যাশ করলে বাকিদের নিরাপদ রাখে, যা মাইক্রোকার্নেলের মূল লক্ষ্য।
অনুশীলন
-
চিন্তা করুন: দুই ধরনের বাস্তব অ্যাপ্লিকেশনের কথা ভাবুন — একটি যেখানে shared memory উপযোগী হবে (গতির জন্য) এবং একটি যেখানে message passing উপযোগী হবে (নিরাপত্তা/সরলতার জন্য)।
উদাহরণ: একটি ভিডিও এডিটিং সফটওয়্যার যেখানে একাধিক থ্রেড/প্রসেস একই বড় ফ্রেম বাফারে কাজ করে — shared memory (কপি করার খরচ বাঁচাতে, দ্রুততা জরুরি)। অন্যদিকে, একটি ব্রাউজারের ট্যাব-প্রসেস ও মূল প্রসেসের মধ্যে যোগাযোগ — message passing (একটি ট্যাব ক্র্যাশ করলেও ব্রাউজার বা অন্য ট্যাব যেন নিরাপদ থাকে)।
-
পরীক্ষা করুন: উপরের কোড সেলে
write_counter(shared, val_a)এবংwrite_counter(shared, val_b)-এর ক্রম উল্টে দিন (B আগে লিখুক)। চূড়ান্ত counter মান কী হবে, এবং কেন এখনো race condition-ই থাকবে?ক্রম উল্টালেও চূড়ান্ত মান এখনো 1 থাকবে (এবার A-এর লেখা মান শেষে থাকবে, B-এরটা হারাবে) — সমস্যাটি কোন প্রসেস শেষে লিখলো তা নয়, বরং উভয়েই একই পুরনো মান (0) পড়ে ফেলেছিল লেখার আগে। এটিই দেখায় সমাধান ক্রম পরিবর্তন নয়, বরং M5-এর মতো প্রকৃত কোঅর্ডিনেশন (lock/semaphore) দরকার।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরের পাঠ — CPU শিডিউলিং বেসিকস ও ক্রাইটেরিয়া L09 M2 শেষ, এখন M3 শুরু — কোন READY প্রসেস কখন CPU পাবে তা কীভাবে ঠিক হয়।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ M5 (প্রসেস সিনক্রোনাইজেশন) এই পাঠের race condition সমস্যার প্রকৃত সমাধান (mutex, semaphore) শেখাবে।
- Computer Networks কোর্স সহোদর কোর্স এক মেশিনের প্রসেসদের মধ্যে IPC থেকে ভিন্ন মেশিনের মধ্যে যোগাযোগ কীভাবে কাজ করে তা শিখতে দেখুন।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks ও Operating Systems — সব এক জায়গায়।