~/writing/os-grand-conductor $
    06/08
    intro ⏻
    LEVEL 3 — THE SOFTWAREপর্ব ০৬/০৮~২০ মিনিট

    অপারেটিং সিস্টেম: মহাব্যবস্থাপক

    process, scheduling, virtual memory, syscall — OS কীভাবে সব চালায়

    আপনার laptop-এ এই মুহূর্তে কতগুলো program চলছে?

    সহজে যা মনে পড়ে — browser, code editor, terminal, Spotify। কিন্তু task manager বা system monitor খুলে দেখলে দেখা যায়, এর বাইরেও অনেক process আর background task চলছে।

    কিন্তু laptop-এ কি ততগুলো CPU core আছে? না। হাতে গোনা কয়েকটা। আর প্রতিটা core একই সময়ে সীমিত সংখ্যক instruction stream চালাতে পারে।

    তাহলে এতগুলো program একসাথে চলে কীভাবে?

    মূল কৌশলের নাম concurrency। CPU খুব দ্রুত বিভিন্ন runnable কাজের মধ্যে সময় ভাগ করে দেয় — এত দ্রুত যে আপনার চোখে "একসাথে" মনে হয়। আর একাধিক core থাকলে কিছু কাজ সত্যিই একই সময়ে চলে।

    এই ভাগাভাগির পুরো ব্যবস্থাপনা যে করে, সে আপনার laptop-এর সবচেয়ে গুরুত্বপূর্ণ software — Operating System। আর সে শুধু CPU ভাগ করে না। Memory, file, device, network access, permission — এসবও application-গুলোর মধ্যে managed আর isolated রাখে।

    আজকের গল্প OS-কে ঘিরে।

    // একটা কথা আগে বলে রাখি

    এই সিরিজে এতদিন সব কথা ছিল hardware নিয়ে। Transistor, gate, CPU, register, cache, RAM। আজ প্রথম software-এর দুনিয়ায় পা রাখা।

    কিন্তু এই software সাধারণ কোনো app না। Operating System এমন একটা system software, যেটা hardware-এর resource পরিচালনা করে আর application-কে সেগুলো ব্যবহারের জন্য abstraction ও controlled access দেয়। Linux, Windows, macOS, Android, iOS — এদের implementation আলাদা, কিন্তু কাজের তালিকা মোটামুটি একই: process আর thread management, memory management, device management, filesystem, networking, protection।

    আজকে যেসব প্রশ্নের উত্তর খুঁজব:

    • অনেকগুলো program বা thread কীভাবে সীমিত CPU resource ভাগ করে নেয়?
    • একটা program-এর memory অন্য program থেকে আলাদা থাকে কীভাবে?
    • আপনার লেখা app hardware-এ সরাসরি access পায় না, তাহলে file বা network ব্যবহার করে কীভাবে?
    01

    Program আর Process — এক জিনিস না

    শুরুতেই একটা distinction পরিষ্কার করে নেওয়া দরকার। এই দুইটা শব্দ প্রায়ই মিশিয়ে ব্যবহার হয়, কিন্তু আসলে দুই জিনিস।

    Program হলো instruction আর data-র একটা নিষ্ক্রিয় বর্ণনা — যেমন disk-এ পড়ে থাকা একটা executable file বা program image। এটা নিজে কোনো কাজ করছে না।

    ধরেন, সেই file-এ double-click করলেন। এখন সেটা "চলতে" শুরু করেছে — সেটাই process। অর্থাৎ, Process হলো সেই program-এর একটা running instance — চলতে থাকা কাজটা, আর তার জন্য যা যা state ও resource লাগে সেগুলো সহ।

    সহজ কথায় — একটা recipe (program) আর সেই recipe দেখে করা রান্না (process) দুই জিনিস। একই recipe দিয়ে দশজন রাঁধুনি দশটা আলাদা রান্না করতে পারেন; ঠিক তেমনি একই program থেকে একাধিক process চলতে পারে।

    02

    একটা process-এর ভেতরে কী থাকে?

    OS যখন একটা process তৈরি করে, তখন শুধু program-এর code memory-তে তুলে দেয় না। Process-এর সঙ্গে তার execution আর resource নিয়ে বেশ কিছু state জড়িয়ে থাকে:

    PID (Process ID): Process-কে চিহ্নিত করার একটা identifier — যাতে OS বুঝতে পারে কোন process-এর কথা বলা হচ্ছে।

    Memory space: Process-এর একটা virtual address space থাকে, যেখানে তার code, data, stack, heap আর অন্যান্য mapped region বসে। এই এলাকা কীভাবে "নিজস্ব" হয়, সেটা একটু পরের section-এর গল্প।

    সেই address space-এর ভেতরে কয়েকটা গুরুত্বপূর্ণ অংশ:

    Code section: Program-এর executable instruction-গুলো সাধারণত এখানে থাকে। সাধারণ protection-এর অধীনে এই অংশ read-only হিসেবে map করা হতে পারে, যাতে চলতি code সহজে নিজেকে পাল্টে ফেলতে না পারে।

    Data section: Global আর static variable-এর মতো data এখানে থাকে। যেমন C-তে function-এর বাইরে declare করা int counter = 0; — এই ধরনের global variable এখানে বসে থাকে। Program যতক্ষণ চলবে, ততক্ষণ এই variable-ও থাকবে।

    Stack: Function call-এর সময় parameter, local variable আর return information-এর জন্য জায়গা লাগে; সেটা আসে stack থেকে।
    কল্পনা করুন একটা tray-তে একের পর এক কাগজ রাখছেন — সবশেষ কাগজটাই সবার ওপরে, সেটাই আগে সরাতে হবে। Function call-ও তেমনই: যে function সবশেষ call হয়েছে, সে-ই আগে শেষ হয়। এই "শেষে এসে আগে যায়" pattern-এর নামই stack।

    Heap: Program চলার সময় dynamically allocate করা memory-র জন্য heap ব্যবহার হয়। C-তে malloc() করলে বা JavaScript runtime-এ new Array(1000) লিখলে memory আসে এখান থেকেই।

    File descriptors: Process কোনো file, socket, pipe বা অন্য OS resource ব্যবহার করলে OS তাকে একটা handle দেয়। Unix-এর মতো system-এ সেই handle একটা ছোট integer — file descriptor। ওই নম্বর দেখিয়েই process বলে, "এই resource-টার সাথে কাজ করো।"

    Execution state: এই process যখন CPU-তে চলছিল, তখন CPU-র register-এর মান, program counter — এই ধরনের execution state লাগে। কেন এটা track করে রাখতে হয়? কারণ context switch-এর সময় এই state সংরক্ষণ করে পরে আবার restore করা যায়। Context Switching নিয়ে আমরা একটু পরে জানবো।

    Process হলো address space আর resource-এর একটা context; CPU-তে যে execution state save আর restore হয়, সেটা মূলত thread-এর সঙ্গে জড়িত। এক process-এ একাধিক thread থাকলে address space এক, কিন্তু register state আর stack প্রত্যেকের নিজের।

    এই সব তথ্য kernel তার নিজের data structure-এ track করে। Classic OS বইয়ে প্রতি process-এর এই record-টাকে বলা হয় PCB (Process Control Block) — তবে বাস্তব kernel-এ এটা একটামাত্র literal "central table" হতেই হবে এমন নয়।

    নিচের যন্ত্রে process-এর memory কীভাবে সাজানো থাকে, তার একটা conceptual ছবি:

    যন্ত্র ০১ — PROCESS ANATOMY
    HIGH ADDR ↑
    STACK↓ grows down
    main()
    · unused ·
    obj
    HEAP↑ grows up
    DATA / BSS · globals
    CODE · read-only
    LOW ADDR ↓
    stack: ১ frame · heap: ১ block। call/return stack নাড়ায় (উপর থেকে নিচে), malloc/free heap নাড়ায় (নিচ থেকে উপরে) — দুই দিক একে অপরের দিকে এগোয়।
    এক process-এর address space-এর একটা conceptual ছবি: উপরে stack, নিচে heap, মাঝে ফাঁকা জায়গা যা দুই দিক ভাগ করে নেয়, আর নিচে code ও global data। Exact বিন্যাস architecture, OS আর runtime ভেদে আলাদা।

    একটা কথা মনে রাখবেন — এটা একটা conceptual layout। Exact বিন্যাস architecture, OS, executable format আর runtime-এর ওপর নির্ভর করে; "stack সবসময় নিচের দিকে নামে, heap উপরে ওঠে" কোনো universal physical নিয়ম নয়।

    03

    এক CPU-তে অনেক কাজ: পালা-বদলের গল্প

    এবার আসল প্রশ্ন। CPU core সীমিত, কিন্তু runnable process আর thread অনেকগুলো। তাহলে?

    OS-এর scheduler ঠিক করে দেয় কোন runnable thread কোন core-এ কখন চলার সুযোগ পাবে। একটা thread কিছুক্ষণ চলে; তারপর হয় preemption-এর কারণে তাকে সরে যেতে হয়, নয়তো সে নিজেই অপেক্ষায় চলে যায় — যেমন কোনো I/O শেষ হওয়ার অপেক্ষায়।

    এই execution context বদলে যাওয়ার ঘটনার নাম context switch।

    কল্পনা করুন আপনি ৫টা আলাদা subject-এ homework করছেন — গণিত, বাংলা, ইংরেজি, বিজ্ঞান, সমাজ। একসাথে পাঁচটা খাতায় লেখা যায় না। তাই একটা করে করছেন। কিন্তু একটা ছেড়ে আরেকটায় যাওয়ার আগে কয়েকটা কাজ করতে হয়:

    • এখন যে subject-এ আছেন, সেটার page কোথায় ছিল তা bookmark দিয়ে রাখা।
    • কোন চিন্তা কোথায় ছিল, রাফখাতায় টুকে রাখা।
    • বই বন্ধ করা।
    • পরের subject-এর বই বের করা।
    • আগেরবার যে page-এ থেমেছিলেন সেখানে ফেরত যাওয়া।
    • কোথায় কী ভাবছিলেন, মনে করা।

    তারপরই কাজ শুরু করা যায়।

    CPU-তেও ধারণাটা একই:

    context switch
    current thread's execution state
                  ↓
                saved
                  ↓
    other thread's saved state
                  ↓
               restored
                  ↓
           execution resumes

    Save করা state-এর মধ্যে থাকে register-এর মান, program counter — এই ধরনের execution information। পরে সেই state আবার restore করলে কাজটা মোটামুটি যেখানে থেমেছিল সেখান থেকেই এগোতে পারে।

    Context switch মানেই পুরো process বদলে যাওয়া নয় — একই process-এর দুইটা thread-এর মধ্যেও context switch হতে পারে।

    CPU নিজে ঠিক করে না এখন কোন application "গুরুত্বপূর্ণ"। সে শুধু তার সামনে থাকা instruction চালিয়ে যায়। OS-এর scheduler runnable কাজের তথ্য আর নিজের policy দেখে পরেরজনকে বেছে নেয়। নিচে এক ধাপ এক ধাপ করে switch-টা ঘটিয়ে দেখুন:

    যন্ত্র ০২ — THE SWITCH
    P1 · chrome
    ● running
    PCB1 saved
    PC — / —
    CPU REGISTERS
    PC 0x0040
    ACC 17
    ◉
    P2 · spotify
    ○ ready
    PCB2 saved
    PC 0x0088 / 05
    P1 (chrome) CPU-তে চলছে, register-এ তার live state। এক টুকরো সময় শেষ হতে চলেছে।
    Context switch: চলতি কাজের execution state সেভ, পরেরজনের state restore। CPU-র "মাথা" বদলে যায়, CPU নিজে জানেও না। এখানে দুই process দেখানো হয়েছে, তবে একই process-এর দুই thread-এর মধ্যেও এটা ঘটে।

    এই পালা-বদলের একটা দাম আছে। প্রতিবার save আর restore-এ কিছু সময় যায় — অর্থাৎ switch যত ঘনঘন হবে, CPU-র কিছুটা সময় ততই bookkeeping-এ খরচ হবে। তাই OS-কে একটা balance খুঁজতে হয়: এত ঘনঘন নয় যে overhead-ই বড় হয়ে ওঠে, আবার এত কম নয় যে user "hang" টের পায়।

    04

    Scheduling: কার পালা এখন?

    আরেকটা প্রশ্ন। যদি একই সময়ে অনেকগুলো thread runnable থাকে, OS কীভাবে ঠিক করে পরের বার CPU কে পাবে?

    এই সিদ্ধান্ত নেওয়ার নাম scheduling, আর যে নেয় তার নাম scheduler।

    Scheduler-কে একসাথে কয়েকটা জিনিস সামলাতে হয় — সবাই যেন fair chance পায়, জরুরি কাজ যেন সময়মতো হয়, system যেন responsive থাকে। এই তিনটা একসাথে মেলানো কঠিন, তাই ইতিহাসে আর বিভিন্ন system-এ নানা algorithm ব্যবহার হয়েছে:

    • Round-Robin: runnable কাজগুলোকে পালা করে সময় দাও। ক্লাসে teacher যেমন সবাইকে পালা করে বলার সুযোগ দেন। সরল আর fair — কিন্তু এখানে কোন কাজটা বেশি জরুরি, সেই পার্থক্য ধরা পড়ে না।
    • Priority-based: কিছু কাজকে বেশি priority দেওয়া যায়, যাতে তারা আগে CPU পায়। এখানে ঝুঁকি একটাই — কোনো low-priority কাজ দীর্ঘ সময় CPU না-ও পেতে পারে। একে বলে starvation।
    • Fair-share scheduling: যে এখন পর্যন্ত সবচেয়ে কম CPU time পেয়েছে, পরের সুযোগটা তার। Linux বহু বছর এই ধারায় CFS (Completely Fair Scheduler) ব্যবহার করেছে; kernel 6.6 থেকে সেই জায়গায় এসেছে EEVDF, যেটা virtual runtime-এর পাশাপাশি deadline-ও হিসাব করে — যাতে latency-sensitive কাজ শুধু fairly নয়, সময়মতোও চলে।

    সব algorithm-এর ভেতরের হিসাব জানার দরকার নেই। এতটুকু মনে রাখলেই চলবে:

    Scheduler হলো OS-এর সেই রেফারি, যে ঠিক করে runnable কাজগুলোর মধ্যে CPU time কীভাবে বণ্টন হবে।
    যন্ত্র ০৩ — THE SCHEDULER
    UIpri: উচ্চ০ms
    compilepri: নিম্ন০ms
    musicpri: মাঝ০ms
    bg syncpri: নিম্ন০ms
    "run slice" চাপুন — নীতিভেদে দেখুন কে CPU পায়।
    চার process, একটাই CPU। প্রতিটা "run slice" নির্বাচিত নীতি অনুযায়ী একজনকে ১০ms দেয়। bar = মোট পাওয়া CPU time।
    05

    Virtual Memory: প্রতিটা process-এর নিজস্ব address space

    Multitasking-এর আরেকটা বড় সমস্যা — memory isolation।

    আপনার laptop-এ এখন Chrome, VS Code, Spotify — সবাই একই physical RAM ব্যবহার করছে। একটা program যেন ইচ্ছামতো আরেকটার private memory পড়তে বা লিখতে না পারে, সেটা নিশ্চিত হয় কীভাবে?

    এখানেই আসে virtual memory। সহজ কথায়: প্রতিটা process পায় নিজের একটা আলাদা virtual address space।

    Program যখন কোনো memory address ব্যবহার করে, সেটাকে সরাসরি physical RAM-এর ঘর ভাবার দরকার নেই। সেটা একটা virtual address। Hardware-এর MMU (Memory Management Unit) আর OS-এর তৈরি mapping information মিলে সেই virtual address-কে corresponding physical location-এর সঙ্গে মিলিয়ে দেয়।

    address mapping
    Process A
    virtual address 100
            ↓
    physical location X
    
    Process B
    virtual address 100
            ↓
    physical location Y

    দুই process একই virtual address number ব্যবহার করতে পারে, কিন্তু তাদের address space আলাদা বলে physical mapping আলাদা হয়। কেউ কারো এলাকায় ঢুকছে না।

    এই mapping-এর তথ্য page table-এর মতো data structure-এ থাকে, আর MMU সেটা ব্যবহার করে translation সেরে ফেলে। নিচে দুই process টগল করে দেখুন একই virtual address কীভাবে ভিন্ন জায়গায় যায়:

    যন্ত্র ০৪ — THE TRANSLATOR
    chrome
    vaddr 0x100
    MMU
    page table
    0x100 → 0x842
    PHYSICAL RAM
    phys 0x842E0
    chrome-এর "0x100" আসলে physical 0x842E0-তে। একই virtual address, ভিন্ন আসল জায়গা।
    দুই process একই virtual address 0x100 ব্যবহার করে, কিন্তু MMU + page table তাদের RAM-এর ভিন্ন cell-এ পাঠায়। এটাই isolation।

    Isolation

    একটা সাধারণ user process নিজের অনুমোদিত virtual memory-র বাইরে গিয়ে অন্য process-এর private memory সরাসরি পড়তে বা লিখতে পারে না। আধুনিক system security-র একটা বড় ভিত্তি এটাই।

    এটাকে এভাবে ভাবতে পারেন — একই শহরে ৫০ জন থাকছে, কিন্তু প্রত্যেকের হাতে আলাদা মানচিত্র। কারো মানচিত্রে অন্যের ঘরের রাস্তাটা আঁকাই নেই।

    Virtual memory আর swap এক জিনিস না

    এই পার্থক্যটা জরুরি।

    Virtual memory হলো address space abstraction, protection আর virtual-to-physical mapping-এর পুরো ব্যবস্থাটা।

    Swap হলো সেই ব্যবস্থার ভেতরের একটা mechanism, যেখানে memory-র কিছু content RAM থেকে সরিয়ে secondary storage-এ রাখা হতে পারে, যাতে RAM অন্য কাজে লাগে। সেই page পরে আবার দরকার হলে storage থেকে ফিরিয়ে আনতে হয় — আর তখন page fault-এর মধ্য দিয়ে সেই কাজটা হয়।

    virtual memory vs swap
    Virtual Memory
    ├── address space abstraction
    ├── protection / isolation
    ├── virtual → physical mapping
    └── other mechanisms
    
    Swap
    └── one mechanism: move memory contents
        out to secondary storage

    তাই virtual memory-কে শুধু "RAM-এর চেয়ে বেশি memory পাওয়ার কৌশল" ভাবা ঠিক নয়। Swap ছাড়াও virtual memory থাকে।

    আর একটা কথা — program physical location জানে না ঠিকই, কিন্তু swap হলে সে টের পায় না তা নয়। Performance-এ সেটা স্পষ্ট ধরা পড়ে।

    Physical RAM সবাই মিলে ভাগ করে, কিন্তু প্রতিটা process দেখে নিজের আলাদা virtual address space।
    06

    Kernel Mode আর User Mode: দুই স্তরের দরজা

    আরেকটা fundamental separation আছে — এবার privilege আর security-র দিক থেকে।

    Windows-এ Admin account আর regular user account-এর পার্থক্যটা মনে করুন। Admin সব করতে পারে — settings বদলানো, software install, system file edit। Regular user restricted — নিজের কাজ করতে পারে, কিন্তু system ভাঙতে পারে না।

    CPU-র execution-কেও একটা simplified model হিসেবে দুই ধরনের privilege level-এ ভাবা যায়:

    Kernel Mode: এখানে চলা kernel code privileged operation করতে পারে — memory-management configuration, কিছু hardware-related control, এই ধরনের কাজ।

    User Mode: সাধারণ application এই restricted mode-এ চলে। privileged operation তারা সরাসরি করতে পারে না — আপনার browser, editor, game, সবাই এখানেই।

    বাস্তব CPU architecture-এ privilege mechanism-এর implementation ভিন্ন হতে পারে; কোথাও দুইয়ের বেশি level-ও থাকে। এই article-এর জন্য মূল কথাটা এইটুকু:

    Application code আর trusted OS code একই privilege level-এ চলে না।

    কোনো application যদি এমন কিছু করতে যায় যার অনুমতি তার নেই, CPU সেটা নিঃশব্দে হতে দেয় না — একটা exception বা trap ঘটে, আর নিয়ন্ত্রণ চলে যায় kernel-এর হাতে। এরপর কী হবে (process terminate, signal, error return) সেটা OS ঠিক করে।

    কেন এই বিভাজন? সহজ কারণ — প্রতিটা app যদি যা খুশি করতে পারত, একটা খারাপ app পুরো system-এ ছড়িয়ে পড়তে পারত।

    কিন্তু app-এর তো file পড়তে হবে, network ব্যবহার করতে হবে, নতুন process বানাতে হবে। তাহলে সে করে কীভাবে? OS-এর কাছে অনুরোধ করে — আর সেই অনুরোধের রাস্তার নাম system call।

    07

    System Call: OS-এর কাছে অনুরোধের দরজা

    System call হলো user mode-এ থাকা application আর kernel-এর মধ্যে একটা controlled interface। Application নিজে privileged কাজটা না করে OS-কে বলে — "আমার হয়ে এই কাজটা করে দাও।"

    C-তে একটা সহজ উদাহরণ:

    read_file.c
    #include <fcntl.h> #include <unistd.h> int main() { int fd = open("file.txt", O_RDONLY); // library wrapper → syscall char buffer[100]; read(fd, buffer, 100); // library wrapper → syscall close(fd); // library wrapper → syscall return 0; }

    একটা সূক্ষ্ম কিন্তু গুরুত্বপূর্ণ কথা: open(), read(), close() নিজেরা system call নয় — এরা library/API function। Linux-এ C library-র এই wrapper-গুলো প্রয়োজন হলে kernel-এর system call interface ব্যবহার করে। x86-64 Linux-এ সেই কাজটা হয় syscall instruction দিয়ে, যা user mode থেকে kernel-এর নির্ধারিত entry point-এ নিয়ে যায়।

    request path
    Application
        ↓
    library / API function
        ↓
    system call interface
        ↓
    kernel
        ↓
    requested operation
        ↓
    result
        ↓
    Application

    পুরো ব্যাপারটা সংক্ষেপে:

    1. Application system call-এর argument গুলো নির্দিষ্ট জায়গায় প্রস্তুত করে।
    2. System call mechanism ব্যবহার করে kernel-এ entry নেওয়া হয়।
    3. CPU privilege transition করে kernel-এর নির্দিষ্ট entry point-এ যায়।
    4. Kernel অনুরোধটা যাচাই করে আর কাজটা করে।
    5. Result বা error information ফেরত বসানো হয়।
    6. Execution আবার user mode-এ ফিরে আসে।

    OS-এর প্রায় সব service-এর জন্যই এমন system call আছে:

    • File: open(), read(), write(), close()
    • Network: socket(), send(), recv()
    • Process: fork() (নতুন process), exit() (শেষ করা)
    • Memory: mmap() (address space-এ region map করা)
    প্রতিটা high-level function call কিন্তু system call নয়। অনেক library function পুরো কাজটাই user space-এ সেরে ফেলতে পারে; দরকার হলে তবেই kernel-এ যায়।

    এই kernel transition-এর একটা overhead আছে। তাই performance-sensitive code অপ্রয়োজনে বারবার kernel-এ যাওয়া এড়ায়। নিচের যন্ত্রে দরজাটা এক ধাপ এক ধাপে পার হয়ে দেখুন:

    যন্ত্র ০৫ — THE DOORWAY
    MODE: USER
    USER MODE
    app library-র read(fd) কল করল
    │
    KERNEL MODE · OS
    —
    app user mode-এ চলছে — hardware-এ সরাসরি হাত নেই। Library-র wrapper এখন kernel-এ যাওয়ার প্রস্তুতি নিচ্ছে।
    System call = user আর kernel-এর মাঝের নিয়ন্ত্রিত দরজা। শুধু এই দরজা দিয়েই app hardware-এর কাজ OS-কে দিয়ে করায় — mode বদলে, আবার ফিরে।
    08

    Thread: এক Process-এর ভেতরে অনেক execution path

    এতক্ষণ process-এর কথা বললাম। এবার thread।

    একটা process-এর ভেতরে একাধিক thread থাকতে পারে। তারা একই process-এর virtual address space আর অনেক process-level resource share করে, কিন্তু প্রত্যেকের নিজের stack আর নিজের execution state থাকে।

    Process যদি একটা কারখানা হয়, thread হলো সেই কারখানার শ্রমিক। এক কারখানায় অনেক শ্রমিক একসাথে কাজ করে, একই মেশিন আর একই কাঁচামাল ব্যবহার করে — কিন্তু প্রত্যেকের নিজের কাজের ধারা আছে।

    inside a process
    Process
    ├── Thread A → own stack + execution state
    ├── Thread B → own stack + execution state
    └── Thread C → own stack + execution state
    
    shared between them:
    virtual address space
    files and other process resources

    একই process-এর দুইটা thread একই memory access করতে পারে — এতে data ভাগাভাগি সহজ হয়, আবার coordination-এর দায়িত্বও এসে পড়ে। কে কখন কী লিখছে সেটা ঠিকঠাক না সামলালে ফল অনিশ্চিত হয়ে যেতে পারে।

    Concurrency ≠ parallelism

    Concurrency মানে একাধিক কাজের অগ্রগতি overlap করা। Parallelism মানে একাধিক কাজ সত্যিই একই সময়ে আলাদা core-এ চলা। Single-core CPU-তেও concurrency সম্ভব; একাধিক core থাকলে কিছু thread সত্যিই parallel-এ চলতে পারে।

    দিকPROCESSTHREAD
    Address spaceসাধারণত আলাদাprocess-এর মধ্যে share করে
    Execution stateprocess contextনিজের register state আর stack
    Resource sharingবেশি isolatedবেশি shared
    Context switchবেশি state জড়িতএকই process হলে কিছু state shared
    Crash হলেঅন্য process সাধারণত বেঁচে থাকেfatal হলে পুরো process যেতে পারে

    বাস্তব software-এ process আর thread — দুটোই একসাথে ব্যবহার হয়। যেমন একটা modern browser একাধিক process ব্যবহার করে (UI, renderer, GPU), আর সেই process-গুলোর ভেতরেও একাধিক thread চলে।

    09

    পুরো ছবিটা একবার: Keyboard-এর 'A' Screen-এ যাওয়ার গল্প

    এবার সব একসাথে। ধরুন আপনি keyboard-এ 'A' চাপলেন।

    নিচের পথটা একটা simplified conceptual model — ভিন্ন operating system, input stack, window system আর graphics architecture-এ আসল ধাপগুলো আলাদা হতে পারে।

    conceptual path
    Keyboard
       ↓
    input controller
       ↓
    interrupt / input event
       ↓
    kernel + device driver
       ↓
    input subsystem
       ↓
    window system → focused application
       ↓
    application updates its state
       ↓
    rendering / graphics system
       ↓
    display

    ধাপে ধাপে ধারণাটা:

    1. Keyboard hardware থেকে input-এর signal বা data তৈরি হয়।
    2. Hardware-এর মাধ্যমে CPU-কে জানানো হয় যে input এসেছে।
    3. OS-এর privileged code আর device driver সেই input process করে।
    4. Input subsystem সেটাকে একটা higher-level input event হিসেবে প্রকাশ করে।
    5. Window system বা application সেই event পায়।
    6. Application নিজের state update করে — যেমন text field-এ 'A' যোগ করা।
    7. UI আবার render করতে হয়।
    8. Rendering আর display pipeline-এর মধ্য দিয়ে পরিবর্তনটা শেষ পর্যন্ত screen-এ পৌঁছায়।

    নিচের যন্ত্রে step চেপে পুরো relay-টা দেখুন:

    যন্ত্র ০৬ — THE RELAY ('A' → screen)
    context switch: ০
    HWkeyboard-এ 'A' চাপা হলো
    HWinput controller CPU-কে জানায়
    KCPU privileged code-এ যায়: interrupt handler
    Kkernel + keyboard driver input process করে
    Kinput subsystem একটা input event বানায়
    Kwindow system event-টা focused app-এর দিকে পাঠায়
    Kscheduler সেই app-কে CPU time দেয়
    Uapp নিজের state update করে — text-এ 'A'
    Uapp graphics system-কে re-render করতে বলে
    HWdisplay pipeline নতুন frame দেখায় → 'A' দৃশ্যমান
    SCREEN
    অপেক্ষায়…
    badge: ■ kernel · ■ user · ■ hardware। ধাপ ০/৯
    একটা keypress-এর simplified conceptual path: hardware থেকে interrupt, kernel আর driver, input event, window system, scheduler-এর সিদ্ধান্ত, app-এর state update, তারপর rendering। আসল ধাপগুলো OS, input stack আর graphics architecture ভেদে আলাদা।

    শুধু একটা keypress-এর জন্য এতগুলো ধাপ, কয়েকটা privilege transition, কয়েকটা component-এর হাতবদল। আর মাঝখানে OS scheduling, protection, device access আর এই সব component-এর মধ্যে coordination সামলাচ্ছে।

    এই কারণেই OS-কে "Grand Conductor" বলা — hardware আর application-এর মাঝখানে বসে পুরো orchestra-টা চালানো।

    তবে মনে রাখবেন — এটা একটা conceptual flow, কোনো নির্দিষ্ট OS-এর exact instruction-by-instruction sequence নয়। বাস্তবে interrupt handling, driver, event queue, window system, rendering আর GPU pipeline অনেক বেশি জটিল।
    // এই আর্টিকেলে কী শিখলাম
    • Program আর process এক জিনিস না। Program হলো instruction আর data-র নিষ্ক্রিয় বর্ণনা; process হলো তার একটা running execution context।
    • অনেক runnable thread সীমিত CPU resource ভাগ করে নেয়। Scheduler ঠিক করে কে কখন CPU time পাবে।
    • Context switch-এ execution state save আর restore হয় — একই process-এর দুই thread-এর মধ্যেও সেটা ঘটতে পারে।
    • Virtual memory প্রতিটা process-কে আলাদা virtual address space দেয়। Hardware আর OS-এর mapping ও protection mechanism মিলে physical memory access নিয়ন্ত্রণ করে — আর virtual memory মানেই swap নয়।
    • Kernel mode আর user mode privilege আলাদা রাখে। সাধারণ application privileged operation সরাসরি করতে পারে না; চেষ্টা করলে trap হয়।
    • System call হলো kernel service ব্যবহারের controlled interface — তবে প্রতিটা library function call system call নয়।
    • Thread হলো process-এর ভেতরের আলাদা execution path। Memory আর resource shared, কিন্তু stack আর execution state যার যার নিজের।
    এই পাতা খোলার পর থেকে আপনার device-এ আনুমানিক ২৩৩.১ কোটি বার transistor switch হয়েছে।
    cd ~  # back to terminal