શૂન્યથી ઓપરેટિંગ સિસ્ટમ બનાવવી એ C માં લખતા કર્નલ હેકર્સનું કામ હોય તેવું લાગે છે. પરંતુ તમે એક બપોરના સમયમાં Python માં એક સરળ સિમ્યુલેશન તૈયાર કરી શકો છો, અને તમે ઝડપથી શોધી કાઢશો કે પ્રોસેસ મેનેજમેન્ટનું લોજિક હાઈ-લેવલ લેંગ્વેજમાં પણ એટલું જ કઠિન છે. મેં આ વાત કઠિન અનુભવ દ્વારા શીખી હતી. હું એક નાનું OS સિમ્યુલેટર લખવા બેઠો. ધ્યેય નમ્ર હતો: થોડી પ્રોસેસ બનાવવી, તેને શેડ્યૂલ કરવી અને કામ પૂરું થઈ જાય ત્યારે તેને 'finished' તરીકે માર્ક કરવી. કોડ ટૂંકો હતો. લોજિક અત્યંત સચોટ લાગતું હતું. પછી મેં તેને રન કર્યું, અને કંઈ જ બંધ થતું નહોતું.

Python માં મિની OS કેમ બનાવવું?

એક વાસ્તવિક ઓપરેટિંગ સિસ્ટમ મેમરી પેજિંગ, ફાઇલ સિસ્ટમ્સ, હાર્ડવેર ઇન્ટરપ્ટ્સ અને ડિવાઇસ ડ્રાઇવર્સનું સંચાલન કરે છે. સિમ્યુલેશન આ બધું દૂર કરે છે અને તમને મુખ્ય વિચાર પર ધ્યાન કેન્દ્રિત કરવા દે છે: સ્ટેટ (state). તમે એક પ્રોસેસ વ્યાખ્યાયિત કરો છો. તેમાં PID, બર્સ્ટ ટાઇમ (burst time) અને લાઇફસાયકલ સ્ટેટસ હોય છે. Ready. Running. Finished. એક શેડ્યૂલર લૂપ આગલા ઉમેદવારને પસંદ કરે છે, તેની સ્થિતિમાં ફેરફાર કરે છે, એક ટાઇમ સ્લાઇસનું સિમ્યુલેશન કરે છે, અને તેને 'done' માં પરિવર્તિત કરે છે.

આ પ્રકારના પ્રયોગ માટે Python એક ઉત્તમ માધ્યમ છે કારણ કે તે તમને પોઇન્ટર એરિથમેટિક અને મેમરી એલાઈનમેન્ટની ચિંતા કર્યા વગર કામ કરવા દે છે. ડિક્શનરીઓની એક લિસ્ટ તમારી પ્રોસેસ ટેબલ બની જાય છે. એક while લૂપ તમારો કર્નલ શેડ્યૂલર બની જાય છે. તમે સ્ટાન્ડર્ડ લાઇબ્રેરી ટૂલ્સનો ઉપયોગ કરીને રાઉન્ડ-રોબિન શેડ્યૂલિંગ અથવા પ્રાયોરિટી ક્યુ (priority queues) અમલમાં મૂકી શકો છો. તે સમજવામાં સરળ લાગે છે, અને બરાબર આ જ કારણ છે કે ત્યારબાદ આવેલી ભૂલ (bug) એટલી ગુસ્સા અપાવનારી હતી.

સેટઅપ

મારા સિમ્યુલેશનમાં process_table નામની લિસ્ટનો ઉપયોગ કરવામાં આવ્યો હતો. દરેક એન્ટ્રી આ મુજબની ડિક્શનરી હતી:

{
    "pid": 1,
    "burst_time": 3,
    "status": "ready"
}

શેડ્યૂલર એક સાદું while લૂપ ચલાવતું હતું. તે ટેબલમાં એવી પ્રથમ પ્રોસેસ શોધતું હતું જેનું સ્ટેટસ "finished" ન હોય. જ્યારે તેને આવી પ્રોસેસ મળતી, ત્યારે તે તે પ્રોસેસને એક સિમ્યુલેટેડ સાયકલ માટે ચલાવવા માટે execute_tick(p) નામનું હેલ્પર ફંક્શન કોલ કરતું હતું. execute_tick ની અંદર, મેં પ્રોસેસ સ્ટેટસને "running" પર સેટ કર્યું, બર્સ્ટ ટાઇમ ઘટાડ્યો, અને જો બાકીનું કામ શૂન્ય થઈ જાય તો તે તપાસ્યું. જો તે શૂન્ય થઈ જાય, તો મેં સ્ટેટસને "finished" માં અપડેટ કર્યું. દરેક પ્રોસેસ 'finished' સ્ટેટ પર પહોંચી જાય પછી બહારનું લૂપ બંધ થઈ જવું જોઈતું હતું.

કાગળ પર, પ્રક્રિયા (flow) સ્પષ્ટ હતી. તૈયાર પ્રોસેસ શોધો. તેને ચલાવો. કામ પૂરું થયું છે કે નહીં તે તપાસો. જ્યાં સુધી કામ પૂરું ન થાય ત્યાં સુધી આ પ્રક્રિયાનું પુનરાવર્તન કરો. શેડ્યૂલર પોતાનું કામ કેવી રીતે કરે છે તે જોવા માટે મેં પ્રિન્ટ સ્ટેટમેન્ટ્સ પણ ઉમેર્યા હતા. હું જોઈ શકતો હતો કે પ્રોસેસ પસંદ કરવામાં આવી રહી હતી. લૂપ ચાલતું જ હતું. તેમ છતાં, પ્રોસેસ જાણે અનંત વર્તમાનકાળમાં પ્રવેશી ગઈ હોય તેમ લાગતું હતું, જે કાયમ માટે ચાલતી જ રહેતી હતી, ક્યારેય આગળ વધતી નહોતી.

લક્ષણ

આ નિષ્ફળતાનો સૌથી ખરાબ પ્રકાર છે: શાંત નિષ્ફળતા. ટર્મિનલમાં કોઈ સ્ટેક ટ્રેસ (stack trace) દેખાયો નહીં. કોઈ IndexError કે KeyError એ મને આગળ વધવા માટે કોઈ સંકેત આપ્યો નહીં. ઇન્ટરપ્રિટર (interpreter) એકદમ બરાબર કામ કરી રહ્યું હતું. પ્રોગ્રામ ફક્ત યોગ્ય રીતે વર્તતો નહોતો. પ્રોસેસ શરૂ થઈ, પણ તે ક્યારેય પૂરી થઈ નહીં. મેં કલાકો સુધી આખી પ્રક્રિયા ફરીથી તપાસી.

શું લૂપની કન્ડિશન ખોટી હતી? કદાચ બર્સ્ટ ટાઇમની ગણતરીમાં મારી ભૂલ હતી. શું પ્રોસેસ ટેબલ ઇન-પ્લેસ અપડેટ થવાને બદલે કોપી થઈ રહ્યું હતું? શું મારી ટેર્મિનેશન કન્ડિશન ખોટી કી (key) તપાસી રહી હતી? મેં વધુ પ્રિન્ટ સ્ટેટમેન્ટ્સ ઉમેર્યા. મેં દરેક બુલિયન એક્સપ્રેશનનું ઓડિટ કર્યું. મેં તે એક લાઇન સિવાય બધું જ શંકાના દાયરામાં રાખ્યું જે ખરેખર મહત્વની હતી.

દોષિત

ત્યારે મને તે દેખાયું. execute_tick ની અંદર, મેં આ લખ્યું હતું:

p["status"] == "running"

બે સમાન ચિહ્નો. તે સરખામણી (comparison) હતી, એસાઇનમેન્ટ (assignment) નહીં. સુધારો માત્ર એક કેરેક્ટર દૂર હતો:

p["status"] = "running"

Python માં, p["status"] == "running" એ એક સંપૂર્ણ રીતે માન્ય એક્સપ્રેશન છે. તે True અથવા False રિટર્ન કરે છે, અને પછી ઇન્ટરપ્રિટર પરિણામને અવગણે છે કારણ કે મેં તેને ક્યાંય એસાઇન કર્યું નહોતું. આ લાઇન કંઈ જ ઉપયોગી કામ કરતી નથી. ડિક્શનરી એન્ટ્રી અસ્પૃશ્ય રહી ગઈ, જે સ્ટેટસ પહેલાં હતું તે જ જળવાઈ રહ્યું, અને પ્રોસેસ તેના લાઇફસાયકલ દ્વારા ક્યારેય આગળ વધી શકી નહીં.

મેં તેને સિંગલ ઇક્વલ (single equals) ચિહ્નમાં બદલી નાખ્યું. મેં સ્ક્રિપ્ટ ફરીથી રન કરી. સિમ્યુલેશન જીવંત થઈ ગયું. પ્રોસેસ પ્લાન મુજબ જ ready, running, અને finished સ્ટેટમાંથી પસાર થઈ. માત્ર એક વધારાના કીસ્ટ્રોકને કારણે મારા કલાકો બગડી ગયા હતા.

આ બગ્સ કેમ છુપાયેલા રહે છે

આ એટલું દુઃખદ એટલા માટે છે કારણ કે Python એક્સપ્રેશન સ્ટેટમેન્ટને ભૂલ તરીકે ત્યારે જ દર્શાવે છે જ્યારે તે સીધું જ અમાન્ય સિન્ટેક્સ (syntax) હોય. આ બગ એક સેમેન્ટિક ટાઈપો (semantic typo) હતો. પ્રોગ્રામે સ્ટેટસની સરખામણી કરી, બુલિયન પરિણામ આપ્યું અને તેને ફેંકી દીધું. કારણ કે સરખામણી પોતે False રિટર્ન કરી શકતી હતી, પ્રોસેસ તેની અગાઉની સ્થિતિમાં જ અટકી રહી, અને બહારના લૂપ પાસે તેને તોડવાનું (break કરવાનું) કોઈ કારણ નહોતું.

You compound this with confirmation bias. You know you typed an assignment because you intended an assignment. When you read the code for the fifth time, your brain autocorrects the symbol. This is why rubber ducking works. It forces you to articulate each line slowly enough that the gap between what is written and what you meant becomes visible.

Small bugs like this are harder to find than dramatic crashes. A segfault or a syntax error announces itself immediately. A silent no-op simply corrupts the state and lets the program limp forward. The failure is downstream, and your instinct is to debug the symptom rather than the cause.

A Better Defense

You cannot trust your eyes alone. After this episode, I changed a few habits that would have caught the mistake earlier.

First, if you are maintaining state in a dictionary, consider using a dataclass or an enum.Enum for process states. Define your statuses as constants or enum members:

from enum import Enum

class ProcessState(Enum):
    READY = "ready"
    RUNNING = "running"
    FINISHED = "finished"

With explicit types, tools like mypy can flag suspicious comparisons during static analysis. An accidental comparison where an assignment should live becomes much easier to spot when the types do not align with expectations.

Second, write unit tests for state transitions before you write the scheduler logic. A simple test that creates a process with one tick of work, runs the scheduler, and asserts the final state is FINISHED would have failed immediately. That failure would have narrowed the search to the state update logic rather than letting me wander through the entire loop