ലിനക്സ് കുറിപ്പുകൾ/ഇന്റർപ്രോസസ് കമ്മ്യൂണിക്കേഷൻ

എല്ലാ മൾട്ടിപ്രോസസ്സ് ഓപ്പറേറ്റിങ് സിസ്റ്റങ്ങളിലും ഒന്നിലധികം പ്രോസസ്സുകൾ ഒരേ സമയത്ത് പ്രവർത്തിക്കുന്നുണ്ടാകും. ചില അവസരങ്ങളിൽ ഈ പ്രോസസ്സുകൾക്ക് പരസ്പരം വിവരങ്ങൾ കൈമാറേണ്ട ആവശ്യം വരും. ഇങ്ങനെയുള്ള വിവരക്കൈമാറ്റങ്ങളെ ഇന്റർപ്രോസസ്സ് കമ്യൂണിക്കേഷൻ അഥവാ ഐ.പി.സി. എന്ന് പറയുന്നു. ഇതിന് വിവിധ മാർഗ്ഗങ്ങൾ ഉണ്ട്. യൂണിക്സ് ഓപ്പറേറ്റിങ്ങ് സിസ്റ്റത്തിന്റെ ആദ്യകാല കമേർഷ്യൽ പതിപ്പുകളിൽ ഒന്നായ സിസ്റ്റം ഫൈവ് (System V) ആണ് ഇക്കാര്യത്തിൽ അടിസ്ഥാന മാനദണ്ഡമായി പരിഗണിക്കപ്പെടുന്നവയിൽ ഒന്ന്. SysV-ൽ ഉണ്ടായിരുന്ന ഐ.പി.സി. പ്രക്രിയകളെ SysV IPC Mechanisms എന്ന് വിളിക്കുന്നു. അതിൽ ഉണ്ടായിരുന്നതിലധികം സംവിധാനങ്ങൾ ഇന്ന് ലിനക്സിലടക്കം ഉണ്ട്.

രണ്ട് പ്രോഗ്രാമുകൾക്ക് പരസ്പരം വിവരങ്ങൾ കൈമാറാൻ അവയെ വെറുതെ ഒരു ഫയലിൽ എഴുതി അടുത്ത പ്രോഗ്രാമിന് വായിക്കാനായി കൊടുക്കാവുന്നതാണ്. എന്നാൽ ഇതിനൊന്നും പൊതുവായ മാനദണ്ഡങ്ങൾ ഇല്ല. കൂടാതെ ഒരു പ്രോസസ്സിനോട്‌ ഒരു പ്രത്യേക രീതിയിൽ പെരുമാറാൻ ആവശ്യപ്പെടുക, സിസ്റ്റത്തിൽ നടന്നിരിക്കുന്ന വിവിധ സംഭവങ്ങളെപ്പറ്റി അവക്ക് പൊതുവായ വിവരങ്ങൾ കൈമാറുക, ഒരു പ്രത്യേക സാഹചര്യം ഉണ്ടാകുന്നത് വരെ കാത്തിരിക്കാൻ ആവശ്യപ്പെടുക തുടങ്ങി വിവിധ കാര്യങ്ങൾ ചെയ്യേണ്ടതായി വരും. ലഭ്യമായ വിവിധ ഐപിസി മെക്കാനിസങ്ങൾ താഴെ ചേർക്കുന്നു.

  1. പൈപ്പുകൾ (pipe)
    1. പേരുള്ളവ (named pipes)
    2. പേരില്ലാത്തവ (unnamed pipes)
  2. സോക്കറ്റുകൾ (socket)
  3. സിഗ്നലുകൾ (signal)
  4. മെസ്സേജ് ക്യൂ (message queue)
  5. ഷെയേർഡ് മെമ്മറി (shared memory)
  6. സെമാഫോറുകൾ (semaphore)

പൈപ്പുകൾ: വെള്ളവും മറ്റും കൊണ്ടുപോകുന്ന പൈപ്പുകൾ പോലെ തന്നെയാണ് ഇവയുടെ പ്രവർത്തനം. ഇവക്ക് രണ്ട് അഗ്രങ്ങൾ ഉണ്ടായിരിക്കും. ഒരു വശത്തുകൂടി പോകുന്ന വിവരങ്ങൾ മറുവശത്ത് ലഭ്യമാകുന്നു. പേരുള്ള പൈപ്പുകൾ ആ പേര് അറിയാവുന്ന പ്രോസസ്സുകൾക്ക് എല്ലാം ഉപയോഗിക്കാം. എന്നാൽ പേരില്ലാത്തവ അതിനെ നിർമ്മിച്ച പ്രോസസ്സിനും അതിന്റെ ചൈൽഡ് പ്രോസസ്സുകൾക്കും മാത്രമേ ലഭ്യമാവുകയുള്ളു.

സോക്കറ്റുകൾ: ബിഎസ്‌‌ഡി യൂണിക്സിൽ ആണ് സോക്കറ്റുകൾ ആദ്യമായി വന്നത്. ഇവയൂടെ ഉപയോഗം കമ്പ്യൂട്ടറുകൾ തമ്മിൽ നെറ്റ്‌‌വർക്ക് ചെയ്യുന്നതിൽ ആണ്. ഒരു സോക്കറ്റിനെ വിവിധ വയറുകൾ ഘടിപ്പിച്ച് വയ്ക്കാവുന്ന ഒരു പ്ലഗ്ഗുമായി താരതമ്യം ചെയ്യാം. ഒരേ കമ്പ്യൂട്ടറിലെ വിവിധ പ്രോഗ്രാമുകൾക്ക് പരസ്പരം സംവദിക്കാൻ സോക്കറ്റുകൾ ഉപയോഗിക്കാവുന്നതാണ്. എന്നാൽ ഇതിന് കെർണലിലെ നെറ്റ്‌‌വർക്കിങ്ങ് ഭാഗത്തിന്റെ പിൻതുണ ആവശ്യമാണ്.

സിഗ്നലുകൾ: സിഗ്നലുകൾ വഴി പ്രോസസ്സുകൾക്ക് പരസ്പരം വിവരങ്ങൾകൈമാറാൻ സാധിക്കുകയില്ല. എന്നാൽ വിവിധ സാഹചര്യങ്ങളെക്കുറിച്ച് മറ്റ് പ്രോസസ്സുകൾക്ക് അറിയിപ്പ് കൊടുക്കാൻ സിഗ്നലുകൾ വഴി സാധിക്കും. ഒരു പ്രോഗ്രാമിന്റെ വിവിധ അവസ്ഥകളെക്കുറിച്ച് ആ പ്രോഗ്രാമിനെ അറിയിക്കാൻ കെർണലിനും സിഗ്നലുകൾ വഴി സാധിക്കും.

സിഗ്നൽ തിരുത്തുക

സിഗ്നലുകൾ ഇന്റർ പ്രോസസ്സ് കമ്മ്യൂണിക്കേഷനിൽ വളരെ പ്രധാനപ്പെട്ടവയാണെങ്കിലും അവയുടെ സാധ്യതകൾ പരിമിതമാണ്. എന്നാൽ സിസ്റ്റത്തിൽ നടക്കുന്ന വിവിധ സംഭവങ്ങളെക്കുറിച്ചുള്ള അറിയിപ്പുകൾ വിവിധ പ്രോസസ്സുകൾക്ക് നൽകാനും പ്രോസസ്സുകളുടെ പ്രവർത്തനത്തെ നിയന്ത്രിക്കാനും സിഗ്നലുകൾ ഉപയോഗിക്കാൻ സാധിക്കും. ഓരോ സിഗ്നലുകൾക്കും ഓരോ അർഥമാണുള്ളത്. ഈ സിഗ്നലുകളെ തിരിച്ചറിയാൻ പോസിക്സ് മാനദണ്ഡത്തിൽ ഓരോ സംഖ്യകൾ അവക്ക് നൽകപ്പെട്ടിരിക്കുന്നു. kill സിസ്റ്റം കോൾ, അല്ലെങ്കിൽ kill കമാന്റ് തുടങ്ങിയവ വഴി വിവിധ പ്രോസസ്സുകളിലേക്ക് സിഗ്നലുകൾ അയക്കാൻ സാധിക്കും. പോസിക്സ് നിർവ്വചിച്ചിരിക്കുന്നതിൽ കൂടുതൽ സിഗ്നലുകൾ പല സിസ്റ്റങ്ങളിലും ലഭ്യമാണ്. ലഭ്യമായ സിഗ്നലുകളുടെ പട്ടിക കാണുന്നതിന് kill -l എന്ന കമാന്റ് ഉപയോഗിക്കാവുന്നതാണ്.

സിസ്റ്റത്തിലെ ഓരോ പ്രോസസ്സിനും കെർണലിന്റെ പ്രോസസ്സ് ടേബിളിൽ ഒരു എൻട്രി ഉണ്ടായിരിക്കും എന്ന് പറഞ്ഞിരുന്നല്ലോ. ഓരോ പ്രോസസ്സിലേക്കും അയക്കപ്പെടുന്ന സിഗ്നലുകൾ ഇതിൽ രേഖപ്പെടുത്തിയിരിക്കുന്നു. ഒരു പ്രോസസ്സിന്റെ പ്രോസസ്സ് ടേബിൾ എൻട്രിയിൽ ഒരു സിഗ്നലിനെ സൂചിപ്പിക്കാൻ ഒരു ബിറ്റ് എന്ന കണക്കിലാണ് മെമ്മറി അനുവദിച്ചിരിക്കുക. അതിനാൽത്തന്നെ ഒരേ സിഗ്നൽ എത്ര തവണ ഒരു പ്രോസസ്സിലേക്ക് അയക്കപ്പെട്ടു എന്ന് അറീയാൻ സാധ്യമല്ല. എന്നാൽ ആ പ്രോസസ്സിലേക്ക് ഏതൊക്കെ സിഗ്നൽ അയക്കപ്പെട്ടിട്ടുണ്ട് എന്നറിയാൻ സാധിക്കും.

ഒരു പ്രോസസ്സ് ഒരു സിഗ്നൽ സ്വീകരിച്ച് കഴിഞ്ഞാൽ ആ സിഗ്നലിനോട് മൂന്നുതരത്തിലുള്ള പ്രതികരണങ്ങളാണ് സാധ്യമാകുക. ആ സിഗ്നൽ കണ്ടില്ലെന്ന് നടിക്കുക (Ignore), ആ സിഗ്നലിനെ കൈകാര്യം ചെയ്യുക (Handle), പ്രോസസ്സ് അവസാനിപ്പിക്കുക (Terminate) എന്നിവയാണ് അവ. എല്ലാ സിഗ്നലുകളെയും കണ്ടില്ലെന്ന് നടിക്കാൻ സാധിക്കുകയില്ല. എല്ലാ സിഗ്നലുകളെയും കൈകാര്യം ചെയ്യാനും സാധിക്കില്ല. ഒരു സിഗ്നലിനോട് നാമെഴുതുന്ന ഒരു പ്രോഗ്രാം എങ്ങനെ പ്രതികരിക്കണമെന്ന് നാം പരാമർശിക്കുന്നില്ലെങ്കിൽ കെർണൽ മുൻകൂട്ടി നിശ്ചയിച്ചിരിക്കുന്ന രീതിയിലുള്ള ഒരു നടപടി എടുക്കുന്നു. ഓരോ സിഗ്നലുകൾക്കും ഈ നടപടി വ്യത്യസ്തമായിരിക്കും. ഒരു സിഗ്നലിലെ നമ്മുടെ പ്രോഗ്രാമിൽ കൈകാര്യം ചെയ്യുന്നതിനായി ഒരു സിഗ്നൽ ഹാൻഡ്‌‌ലർ ഫങ്‌‌ഷൻ എഴുതുകയും അതിനെ ആ സിഗ്നലുമായി ബന്ധിപ്പിക്കുകയും ചെയ്യണം. നമ്മുടെ പ്രോഗ്രാം പിന്നീട് ആ സിഗ്നൽ സ്വീകരിക്കുകയാണെങ്കിൽ ബന്ധപ്പെട്ട ഫങ്ഷൻ പ്രവർത്തിക്കുന്നതായിരിക്കും. എന്നാൽ ഇക്കാര്യങ്ങളൊന്നും തന്നെ നാമെഴുതുന്ന പ്രോഗ്രാമിന്റെ നിയന്ത്രണത്തിൽ വരുന്ന കാര്യങ്ങളല്ല. ഇതൊക്കെ ചെയ്യുന്നത് കെർണൽ നേരിട്ടാണ്. നമ്മുടെ പ്രോസസ്സിന്റെ യു-ഏരിയയിൽ ആണ് അത് ഓരോ സിഗ്നലിനോടും എങ്ങനെ പ്രതികരിക്കുന്നു എന്നും സിഗ്നലുകൾ കൈകാര്യം ചെയ്യാനുള്ള ഫങ്ങ്ഷനുകൾ ഉണ്ടെങ്കിൽ അവയുടെ വിലാസം രേഖപ്പെടുത്തിയിരിക്കുന്നതും.

ഒരു പ്രോസസ്സിന് അത് ഏതവസ്ഥയിൽ ആയിരിക്കുമ്പോളും സിഗ്നലുകൾ സ്വീകരിക്കാൻ സാധിക്കും. എന്നാൽ ആ സിഗ്നൽ കൈകാര്യം ചെയ്യപ്പെടുന്നത് ആ പ്രോസസ്സിന്റെ അവസ്ഥയിൽ മാറ്റങ്ങൾ വരുമ്പോളാണ്.

  • ഒരു പ്രോസസ്സിന്റെ പ്രവർത്തനം കെർണൽ സ്പേസിൽ നിന്ന് യൂസർ സ്പേസിലേക്ക് മാറുക
  • സ്ലീപ്പ് അവസ്ഥയിൽ നിന്ന് പ്രോസസ്സ് പ്രവർത്തിക്കുന്ന അവസ്ഥയിലേക്ക് വരിക
  • പ്രോസസ്സ് ഷെഡ്യൂൾ ചെയ്യപ്പെടുക

തുടങ്ങിയ സാഹചര്യങ്ങളിലാണ് ആ പ്രോസസ്സിലേക്ക് അയക്കപ്പെട്ടിരിക്കുന്ന സിഗ്നലുകളെ കെർണൽ പരിശോധിക്കുകയും ആവശ്യമായ നടപടികൾ എടുക്കുകയും ചെയ്യുന്നത്. പ്രോസസ്സിന് കണ്ടില്ലെന്ന് നടിക്കാൻ അനുവാദമുള്ള സിഗ്നൽ ആണെങ്കിൽ ആ സിഗ്നലിനെ കൈകാര്യം ചെയ്യാനുള്ള ഫങ്ങ്ഷനുകൾ ഒന്നും പ്രോസസ്സ് രെജിസ്റ്റെർ ചെയ്തിട്ടില്ലെങ്കിൽ പ്രത്യേകിച്ചൊരു നടപടിയും ആവശ്യമില്ല. ആ സിഗ്നൽ കൈകാര്യം ചെയ്യപ്പെട്ടു എന്ന് പ്രോസസ്സിന്റെ പ്രോസസ്സ് ടേബിൾ എൻട്രിയിൽ രേഖപ്പെടുത്തുകയേ‌ വേണ്ടൂ. ഇനി പ്രോസസ്സിലേക്ക് അയക്കപ്പെട്ട സിഗ്നലിന്റെ സാമാന്യ പ്രതികരണം പ്രോസസ്സ് അവസാനിപ്പിക്കുക എന്നതാണെങ്കിൽ കെർണൽ ആ പ്രോസസ്സിന്റെ എക്സിറ്റ് ഹാൻഡ്‌‌ലർ പ്രവർത്തിപ്പിക്കുകയും ആ പ്രോഗ്രാമിന്റെ പ്രവർത്തനം അവസാനിപ്പിക്കുകയും ചെയ്യും. ഒരു സിഗ്നലിനെ കൈകാര്യം ചെയ്യാനുള്ള ഫങ്ങ്ഷൻ ഒരു പ്രോസസ്സ് രെജിസ്റ്റർ ചെയ്തിട്ടുണ്ടെങ്കിൽ കാര്യങ്ങൾ അൽപം കൂടി സങ്കീർണ്ണമാണ്. എല്ലാ ഫങ്ങ്ഷനുകൾക്കും പ്രവർത്തിക്കാൻ ഒരു സ്റ്റാക്ക് ആവശ്യമാണ്. പ്രോഗ്രാമിൽ തന്നെ വിളിക്കപ്പെടുന്ന ഫങ്ങ്ഷൻ ആണെങ്കിൽ ആ ഫങ്ങ്‌‌ഷനായുള്ള സ്റ്റാക്ക് പ്രോസസ്സിന്റെ സ്റ്റാക്ക് സെഗ്‌‌മെന്റിൽ നിർമ്മിക്കാനുള്ള നിർദ്ദേശങ്ങൾ സി കമ്പൈലർ ആ പ്രോഗ്രാമിൽ തന്നെ ഉൾപ്പെടുത്തിയിരിക്കും. എന്നാൽ ഇവിടെ ആ ഫങ്ങ്‌‌ഷനെ വിളിക്കുന്നത് ആ പ്രോസസ്സിൽ നിന്നല്ല എന്ന കാര്യം ഓർമ്മിക്കുക. ഇവിടെ കെർണൽ ആദ്യം ചെയ്യുന്നത് ആ പ്രോസസ്സിന്റെ യു-ഏരിയയിൽ നിന്ന് സിഗ്നൽ കൈകാര്യം ചെയ്യാനുള്ള ഫങ്ങ്ഷന്റെ വിലാസം കണ്ടെത്തുകയാണ്. പിന്നീട് ആ ഫങ്ങ്ഷന് പ്രവർത്തിക്കാൻ ആവശ്യമായ ഒരു സ്റ്റാക്ക് ഫ്രെയിം ആ പ്രോസസ്സിന്റെ സ്റ്റാക്ക് സെഗ്‌‌മെന്റിൽ സൃഷ്ടിക്കുന്നു. ആ ഫങ്ങ്‌‌ഷനുള്ള പരാമീറ്ററുകൾ ആ സ്റ്റാക്കിലേക്ക് ചേർക്കുകയും ആ പ്രോസസ്സിൽ അടുത്തതായി പ്രവർത്തിക്കേണ്ട നിർദ്ദേശത്തിന്റെ വിലാസം (ഇത് പ്രോഗ്രാം കൗണ്ടർ എന്നറിയപ്പെടുന്നു) സ്റ്റാക്കിലേക്ക് സിഗ്നൽ ഹാൻഡ്‌‌ലർ ഫങ്ങ്ഷന്റെ റിട്ടേൺ അഡ്രസ്സായി ചേർക്കുകയും ചെയ്യുന്നു. ആ ഫങ്ങ്ഷന്റെ പ്രവർത്തനം തീരുമ്പോൾ പ്രോഗ്രാം എവിടെനിന്ന് തുടരണം എന്നതാണ് ഫങ്ങ്‌‌ഷന്റെ റിട്ടേൺ അഡ്രസ്സ് എന്നറിയപ്പെടുന്നത്. ഇതിന് ശേഷം ആ ഫങ്ങ്ഷന്റെ സ്റ്റാർട്ടിങ്ങ് അഡ്രസ്സ് പ്രോഗ്രാം കൗണ്ടറിൽ എഴുതിയ ശേഷം ആ പ്രോസസ്സിനെ പ്രവർത്തിക്കാൻ അനുവദിക്കുന്നു. ചുരുക്കത്തിൽ നേരിട്ട് യൂസർ സ്പേസിലേക്ക് മടങ്ങി പ്രവർത്തനം തുടരേണ്ടിയിരുന്ന പ്രോസസ്സ് ആ ഫങ്ങ്ഷനിലൂടെ കടന്ന് പോകുകയും ആ ഫങ്ങ്ഷൻ പൂർത്തിയാക്കിയ ശേഷം അതിന്റെ പ്രവർത്തനം തുടരുകയും ചെയ്യും.

ഇതിൽനിന്ന് മനസ്സിലാക്കാനുള്ള കാര്യം സിഗ്നലുകൾ സ്വീകരിക്കുകയോ അവയെ കൈകാര്യം ചെയ്യുകയോ ചെയ്യുന്നത് ഒരു പ്രോസസ്സിന്റെ അധികാര പരിധിയിൽ ഉള്ള കാര്യമല്ല. അവ നടക്കുന്നത് ആ പ്രോസസ്സ് അറിയുകയും ഇല്ല. ഒരു പ്രോസസ്സ് ഫോർക്ക് ഉപയോഗിച്ച് അതിന്റെ ചൈൽഡ് പ്രോസസ്സിനെ സൃഷ്ടിക്കുമ്പോൾ അത് രെജിസ്റ്റർ ചെയ്തിരുന്ന ഹാൻഡ്‌‌ലർ ഫങ്ങ്ഷനുകൾ ഒക്കെ ചൈൽഡ് പ്രോസസ്സിലും അതേ നിലയിൽ തുടരും. എന്നാൽ ചൈൽഡ് പ്രോസസ്സ് സൃഷ്ടിക്കപ്പെടുന്നതിനു മുൻപ് പേരന്റ് പ്രോസസ്സ് സ്വീകരിച്ചതും കൈകാര്യം ചെയ്യപ്പെട്ടിട്ടില്ലാത്തതുമായ സിഗ്നലുകൾ ഒന്നും ചൈൽഡ് പ്രോസസ്സിന് ബാധകമാകില്ല.

ഒരു പ്രോസസ്സിന് വേറൊരു പ്രോസസ്സിലേക്ക് സിഗ്നൽ അയക്കാൻ kill() സിസ്റ്റം കോൾ ഉപയോഗിക്കാമെന്ന് പറഞ്ഞു. അതേ പ്രോസസ്സിലേക്ക് തന്നെ സിഗ്നൽ അയക്കാൻ (സ്വയം) ഒരു പ്രോസസ്സിന് raise() എന്ന ഫങ്ങ്ഷൻ ഉപയോഗിക്കാവുന്നതാണ്. ഇതല്ലാതെ പ്രത്യേക സാഹചര്യങ്ങളിൽ പ്രോസസ്സുകൾ സിഗ്നലുകൾ സ്വീകരിക്കാറുണ്ട്. ഉദാഹരണത്തിന് കമാന്റ് ലൈനിൽ പ്രവർത്തിച്ചുകൊണ്ടിരിക്കുന്ന ഒരു പ്രോസസ്സിലേക്ക് ഉപയോക്താവ് കണ്ട്രോൾ + സി എന്ന കീബോർഡ് കോമ്പിനേഷൻ ഉപയോഗിക്കുമ്പോൾ SIGINT എന്ന സിഗ്നൽ അയക്കപ്പെടുന്നു. പ്രോസസ്സ് അതിന്റെ അഡ്രസ്സ് സ്പേസിനു പുറത്തുള്ള ഒരു അഡ്രസ്സിലെ വിവരങ്ങൾ വായിക്കാനോ എഴുതാനോ ശ്രമിക്കുമ്പോൾ SIGSEGV എന്ന സിഗ്നൽ, പ്രോസസ്സിൽ ഒരു സംഖ്യയെ പൂജ്യം കൊണ്ട് ഹരിക്കാൻ ശ്രമിച്ചാൽ SIGFPE, ഒരു പ്രോസസ്സിന്റെ ചൈൽഡ് പ്രോസസ്സ് പ്രവർത്തനം അവസാനിപ്പിച്ചാൽ SIGCHILD എന്നിങ്ങനെ. ഇത് കൂടാതെ പോസിക്സ് ഓപ്പറേറ്റിങ്ങ് സിസ്റ്റത്തിൽ ആപ്ലിക്കേഷൻ പ്രോഗ്രാമുകൾക്ക് ലഭ്യമായ ടൈമർ/കൗണ്ടർ സർവ്വീസുകൾ ഒക്കെ സിഗ്നലുകൾ ഉപയോഗപ്പെടുത്തിയാണ് പ്രവർത്തിക്കുന്നത്.

പോസിക്സിൽ നിർവ്വചിക്കപ്പെട്ടിരിക്കുന്ന സിഗ്നലുകളുടെ വിശദാംശങ്ങൾ ഈ വിക്കിപീഡിയ ലേഖനത്തിൽ കാണാം. പ്രോസസ്സിലെ സിഗ്നൽ ഹാൻഡ്ലിങ്ങിന്റെ വിശദാംശത്തിനും ഓരോ സിഗ്നലും എങ്ങനെ പെരുമാറുന്നു എന്നുമുള്ള വിശദാംശങ്ങൾക്കായി man 7 signal എന്ന് ടെർമിനലിൽ ടൈപ്പ് ചെയ്ത് നോക്കുക.

കമാന്റ് ലൈനിൽ നിന്നയക്കപ്പെടുന്ന SIGINT എങ്ങനെ കൈകാര്യം ചെയ്യാം എന്നൊരു ഉദാഹരണം നോക്കാം

  1. include <stdio.h>
#include <signal.h>  
#include <unistd.h>  
  
volatile sig_atomic_t terminated = 0;  
  
void sigint_handler(int signum)  
{  
    printf("\nSignal %d received..\n", signum);  
    terminated = 1;  
}  
  
int main(void)  
{  
    unsigned long counter = 0;  
  
    signal(SIGINT, sigint_handler);  
  
    printf("Entering an infinit loop..\n");  
  
    while (!terminated) {  
        counter++;  
        sleep(1);  
        printf("\r%ld seconds without signal..", counter);  
        fflush(stdout);  
    }  
  
    printf("\nLoop terminated\n");  
  
    return 0;  
}

ഇവിടെ SIGINT എന്ന സിഗ്നൽ ഹാൻഡിൽ ചെയ്യാൻ sigint_handler എന്ന ഫങ്ങ്ഷനെ രെജിസ്റ്റർ ചെയ്യുകയാണ് signal ഫങ്ങ്ഷൻ ഉപയോഗിച്ച് ചെയ്യുന്നത്. terminated എന്ന വേരിയബിളിന്റെ മൂല്യം പൂജ്യമല്ലാതാകുന്നത് വരെ മെയിൻ ഫങ്ങ്ഷനിനെ while ലൂപ്പ് പ്രവർത്തിച്ച് കൊണ്ടിരിക്കുന്നു. ഇവിടെ terminated എന്ന വേരിയബിളിന്റെ ഡാറ്റ ടൈപ്പ് ശ്രദ്ധിക്കുക. volatile sig_atomic_t എന്ന പ്രത്യേക ഡാറ്റാ ടൈപ്പ് ആണ് ഉപയോഗിച്ചിരിക്കുന്നത്. ഇതിലെ volatile എന്ന് കീവേർഡ് ആ ചരത്തിന് ചില പ്രത്യേകതകൾ നൽകുന്നതാണ്. ആ ചരത്തിന്റെ മൂല്യം പ്രോഗ്രാമിൽ എവിടെ വേണമെങ്കിലും മാറ്റം വരാവുന്നതാണെന്നും അതിനാൽ തന്നെ ഒരു രീതിയിലുള്ള ഓപ്‌‌റ്റിമൈസേഷനും ആ വേരിയബിളിന്റെ മേൽ നടത്തരുതെന്നും ആ വേരിയബിൾ ആവശ്യമാകുമ്പോളൊക്കെ അതിനെ പ്രത്യേകം വായിക്കണം എന്നും സി കമ്പൈലറിനോട് പറയുന്നതാണത്. സിഗ്നലുകൾ റേസ് കണ്ടീഷൻസ് എന്ന പ്രശ്നത്തിൽ നിന്ന് മുക്തമല്ലെന്ന് മാത്രമല്ല ആ പ്രശ്നത്തിന്റെ കാഠിന്യം നന്നായി അനുഭവിക്കുന്നവയും ആണ്. സ്വീകരിക്കപ്പെട്ട ഒരു സിഗ്നൽ കൈകാര്യം ചെയ്യപ്പെടുന്ന അവസരത്തിൽ മറ്റൊരു സിഗ്നൽ സ്വീകരിക്കപ്പെട്ടാൽ സംഭവിക്കാവുന്ന പല പ്രശ്നങ്ങളും ഉണ്ട്. ഉദാഹരണത്തിന് ഒരു സിഗ്നൽ എത്ര തവണ പ്രോഗ്രാം കൈകാര്യം ചെയ്തു എന്നറിയാൻ ഒരു കൗണ്ടർ ഉപയോഗിക്കുന്നു എന്ന് കരുതുക. ഓരോ കൈകാര്യം ചെയ്യലിലും അതിന്റെ മൂല്യം ഒന്ന് വീതം വർദ്ധിക്കുകയും ഒരു പ്രത്യേക മൂല്യം എത്തുമ്പോൾ ഒരു കാര്യം ചെയ്യേണ്ടതും ആണെന്ന് കരുതൂ. അതായത് ആ കൗണ്ടറിന്റെ മൂല്യം നാലിന്റെ ഗുണിതങ്ങളാകുമ്പോൾ ഒരു സന്ദേശം ഉപയോക്താവിനെ കാണിക്കണം. കൗണ്ടറിലെ മൂല്യം 3 ആയിരിക്കുമ്പോൾ പ്രോഗ്രാം സിഗ്നൽ സ്വീകരിക്കുകയും അതിന്റെ മൂല്യം 4 ആക്കുകയും ചെയ്തു എന്ന് കരുതുക. അതിന്റെ മൂല്യം നാലിന്റെ ഗുണിതമാണോ എന്ന് പരിശോധിക്കാനുള്ള നിർദ്ദേശത്തിന്റെ തൊട്ട് മുൻപേ കെർണൽ ഷെഡ്യൂളിങ്ങ് നടത്തുകയാണെങ്കിൽ ആ പ്രോസസ്സ് താൽക്കാലികമായി പ്രവർത്തനം നിർത്തും. വീണ്ടും പ്രവർത്തനം തുടങ്ങുന്നതിനു മുൻപേ ഒരിക്കൽ കൂടി ആ സിഗ്നൽ സ്വീകരിക്കപ്പെട്ടാൽ വീണ്ടും ആ നിർദ്ദേശത്തിലെത്തുമ്പോൾ ആ കൗണ്ടറിന്റെ മൂല്യം 5 ആയിട്ടുണ്ടാകും. ഷെഡ്യൂളിങ്ങ് നടന്നില്ലെങ്കിൽ പോലും മൂല്യം വർദ്ധിപ്പിക്കുന്ന നിർദ്ദേശം പ്രവർത്തിക്കുമ്പോൾ അടുത്ത സിഗ്നൽ വന്നാലും ഈ പ്രശ്നം ഉണ്ടാകാം. sig_atomic_t ആ ചരത്തിന്റെ ഉപയോഗത്തെ ഒരു ആറ്റോമിക് ഓപ്പറേഷൻ ആയി നടത്തണം എന്ന് ജിസിസി കമ്പൈലറിനോട് ആവശ്യപ്പെടും. ഒരു പരിധി വരെ പ്രശ്നങ്ങൾ ഇതുവഴി പരിഹരിക്കാം.

ഇനി SIGINT എന്ന സിഗ്നലിനെ കണ്ടില്ലെന്ന് നടിക്കുന്നതെങ്ങനെ എന്ന് നോക്കാം

  1. include <stdio.h>
#include <signal.h>  
#include <unistd.h>  
  
volatile sig_atomic_t terminated = 0;  
  
int main(void)  
{  
    unsigned long counter = 0;  
  
    signal(SIGINT, SIG_IGN);  
  
    printf("Entering an infinit loop..\n");  
  
    while (!terminated) {  
        counter++;  
        sleep(1);  
        printf("\r%ld seconds without signal..", counter);  
        fflush(stdout);  
    }  
  
    printf("\nLoop terminated\n");  
  
    return 0;  
}  
ഇവിടെ SIG_IGN എന്നതാണ് signal ഫങ്ങ്ഷന്റെ രണ്ടാമത്തെ ആർഗ്യുമെന്റ്. പരാമർശിക്കപ്പെട്ട സിഗ്നലിനെ ഇഗ്നോർ ചെയ്യുക എന്നതാണ് ഇതിന്റെ അർഥം. ഈ പ്രോഗ്രാം പ്രവർത്തിക്കുമ്പോൾ CTRL+C അമർത്തിയാൽ ഒന്നും സംഭവിക്കില്ലെന്ന് കാണാം. എന്നാൽ ആദ്യത്തെ പ്രോഗ്രാം അതോടെ ലൂപ്പിൽ നിന്ന് പുറത്ത് വരുമായിരുന്നു. ഇനി ഈ പ്രോഗ്രാമിന്റെ പ്രവർത്തനം അവസാനിപ്പിക്കാൻ ഒരു പുതിയ ടെർമിനൽ തുറന്ന് ps -e കമാന്റ് ഉപയോഗിച്ച് അതിന്റെ പിഐഡി കണ്ടുപിടിക്കുക. അതിന് ശേഷം kill -9 <pid> ഉപയോഗിച്ച് SIGKILL എന്ന സിഗ്നൽ അതിലേക്ക് അയക്കാം. അപ്പോൾ ആ പ്രോസസ്സ് killed എന്ന് ടെർമിനലിൽ കാണിക്കും. SIGKILL സിഗ്നലിന്റെ സംഖ്യ 9 ആണ്. kill -l വഴി വിവിധ സിഗ്നലുകളുടെ സംഖ്യകൾ കണ്ട് പിടിച്ച് 9 നു പകരം ഉപയോഗിച്ച് എന്ത് സംഭവിക്കുന്നു എന്ന് നോക്കൂ.. 

പോസിക്സ് സിഗ്നലുകളെ വികസിപ്പിച്ചാണ് ജിറ്റികെ പോലെയുള്ള യുഐ ലൈബ്രറികൾ ഈവന്റ് മെക്കാനിസം പ്രായോഗികമാക്കിയതെന്ന് പറയപ്പെടുന്നു..

മെസ്സേജ് ക്യൂ: അധികം വലിപ്പമില്ലാത്ത വിവരങ്ങൾ പ്രോസസ്സുകൾക്ക് പരസ്പരം കൈമാറ്റം ചെയ്യാൻ മെസ്സേജ് ക്യൂ വഴി സാധിക്കും. പ്രോസസ്സുകൾ ക്യൂവിലേക്ക് മെസ്സേജുകൾ അയക്കുകയും ക്യൂവിൽ നിന്ന് മെസ്സേജുകൾ സ്വീകരിക്കുകയും ചെയ്യും.

ഷെയേർഡ് മെമ്മറി: പ്രധാന മെമ്മറിയിലെ ഒരു ഭാഗം ഒന്നോ അതിലധികമോ പ്രോസസ്സുകൾ പങ്കിട്ട് ഉപയോഗിക്കുന്ന രീതിയിലാണ് ഇതിന്റെ രൂപകൽപ്പന. ഇത് വിവരങ്ങൾ പരസ്പരം കൈമാറാൻ ഏറ്റവും വേഗതയുള്ള രീതിയാണ്.

സെമാഫോറുകൾ: ഒരു പൊതുവായ വിഭവം വിവിധ പ്രോസസ്സുകൾക്ക് ഒരേ സമയം ഉപയോഗിക്കേണ്ട അവസ്ഥയുണ്ടായാൽ ഒന്നിനു പുറകെ ഒന്നായി അവയ്ക്ക് ആ വിഭവത്തെ ഉപയോഗിക്കാനുള്ള അവസരം കൊടുക്കുന്നതിനുവേണ്ടിയുള്ള ഒരു സംവിധാനമാണിത്. ഉപയോഗിക്കേണ്ടിവരുന്ന ഒരു വിഭവത്തെ മറ്റൊരു പ്രോസസ്സ് ഉപയോഗിക്കുന്നുണ്ടോ എന്ന് പരിശോധിക്കാനും പ്രോസസ്സുകൾക്ക് ഇവ ഉപയോഗിക്കാം. ബൈനറി സെമാഫോർ, കൗണ്ടിങ്ങ് സെമാഫോർ എന്ന് രണ്ട്‌ തരത്തിലുള്ള സെമാഫോറുകൾ ഉണ്ട്.

ലിനക്സ് അധിഷ്ഠിത സിസ്റ്റങ്ങൾ ഉപയോഗിക്കുന്നവർക്ക് ipcs നിർദ്ദേശം ഉപയോഗിച്ച് സിസ്റ്റത്തിൽ വിവിധ പ്രോസസ്സുകൾ ഉപയോഗിച്ചുകൊണ്ടിരിക്കുന്ന ഐ.പി.സി. മെക്കാനിസങ്ങളെക്കുറിച്ചുള്ള വിവരങ്ങൾ കാണാൻ സാധിക്കും. ഈ നിർദ്ദേശത്തെക്കുറിച്ച കൂടുതൽ വിവരങ്ങൾക്ക് man ipcs ഉപയോഗിക്കുക.

മുകളിൽ പരാമർശിച്ച ഐപിസി മെക്കാനിസങ്ങളിൽ മെസ്സേജ് ക്യൂ, ഷെയേർഡ് മെമ്മറി, സെമാഫോറുകൾ എന്നിവ സിസ്റ്റം ഫൈവ് ഐ.പി.സി. മെക്കാനിസങ്ങളാണ്. സിഗ്നലുകൾ, പൈപ്പുകൾ എന്നിവ പോസിക്സും സോക്കറ്റുകൾ ബിഎസ്ഡി സ്റ്റാൻഡേർഡും ആണ്. ലിനക്സ് പിൻതുടരുന്നത് പോസിക്സ് മാനദണ്ഡമാണ്. സിസ്റ്റം ഫൈവ് ഐപിസി മെക്കാനിസങ്ങളെ പൂർണ്ണമായും പോസിക്സ് നിർവ്വചിക്കുന്നില്ലെങ്കിലും ഇവയെല്ലാം ലിനക്സിൽ ലഭ്യമാണ്. ഇതിൽ സോക്കറ്റുകൾ വളരെ വിശാലമായ ഒരു വിഷയമായതിനാൽ അവയെ മാത്രമായി മറ്റൊരു വിഭാഗത്തിൽ പരിചയപ്പെടാം.

പൈപ്പുകൾ തിരുത്തുക

ഒന്നിലധികം പ്രോസസ്സുകൾക്ക് ഒരു കുഴലിലൂടെ എന്നവണ്ണം വിവരങ്ങൾ കൈമാറാൻ സാധിക്കുന്ന ഒരുപാധിയാണ് പൈപ്പുകൾ. ഇവക്ക് രണ്ട് അറ്റങ്ങൾ ഉണ്ടാകും. ഒരറ്റത്ത് എഴുതപ്പെടുന്ന വിവരങ്ങൾ അടുത്ത അറ്റത്തുനിന്ന് വായിച്ചെടുക്കാം. പൈപ്പുകൾ കമാന്റ്‌‌ ലൈൻ ഇന്റർഫേസുകളിൽ നല്കുന്ന സൗകര്യങ്ങൾ ചില്ലറയല്ല. ഒരു കമാന്റിന്റെ ഔട്ട്പുട്ട് മറ്റൊരു കമാന്റിന്റെ ഇൻപുട്ടായി നൽകാൻ സാധിക്കുന്നത് വഴി ഒന്നിലധികം ലളിതമായ കമാന്റുകൾ ഉപയോഗിച്ച് ഒരു സങ്കീർണ്ണമായ ജോലി ചെയ്യാൻ സാധിക്കുമല്ലോ.

ഒരു പൈപ്പിന്റെ എഴുതാനുള്ള അറ്റത്ത് എഴുതപ്പെടുന്ന കാര്യങ്ങൾ മറ്റേ അറ്റത്തുനിന്ന് വായിക്കപ്പെടുന്നത് വരെ കെർണൽ അതിന്റെ മെമ്മറിയിൽ സൂക്ഷിച്ചു വയ്ക്കുന്നു. ഇങ്ങനെ സൂക്ഷിക്കപ്പെടാൻ സാധിക്കുന്ന വിവരങ്ങളുടെ അളവ് സംബന്ധിച്ച് ചില നിബന്ധനകളോ നിയന്ത്രണങ്ങളോ ഉണ്ടെങ്കിലും വിവിധ സിസ്റ്റങ്ങളിൽ അവ വ്യത്യസ്തമായിരിക്കും. പോസിക്സ് മാനദണ്ഡമനുസരിച്ച് ഇതിന്റെ വലിപ്പം കുറഞ്ഞത് 512 ബൈറ്റുകൾ ആയിരിക്കണം എന്നതാണ്. മെമ്മറിയുടെ ലഭ്യത അനുസരിച്ച് കൂടിയ വലിപ്പം എത്രവേണമെങ്കിലും ആകാം. ലിനക്സിൽ ഇത് 4096 ബൈറ്റുകൾ ആണ്. അതായത് ലിനക്സിലെ ഒരു പൈപ്പിന് സംഭരിക്കാൻ സാധിക്കുന്ന ഡാറ്റയുടെ പരമാവധി വലിപ്പം 4096 ബൈറ്റുകൾ ആണ്. ഇതിൽ കൂടുതൽ ഡാറ്റ കൈമാറ്റം ചെയ്യേണ്ടത് ആവശ്യമാണെങ്കിൽ എഴുതുന്ന പ്രോസസ്സിന് തുടർച്ചയായി എഴുതുന്നതിൽ നിയന്ത്രണങ്ങൾ ഒന്നും ഇല്ല. എന്നാൽ പൈപ്പ് ഒരിക്കൽ നിറഞ്ഞ് കഴിഞ്ഞാൽ മറ്റൊരു പ്രോസസ്സ് അതിൽ നിന്ന് ഡാറ്റ വായിക്കുന്നത് വരെ ഈ എഴുത്തിന് തുടരാൻ സാധിക്കുകയില്ല. വായിക്കേണ്ട അറ്റത്ത് നിന്നും ഒരു പ്രോസസ്സ് ഡാറ്റ വായിച്ചെടുത്ത് കഴിഞ്ഞാൽ ആ വായിച്ച അത്ര വിവരങ്ങൾ പൈപ്പിൽ നിന്ന് നീക്കം ചെയ്യപ്പെടുന്നു. കൂടുതൽ വിവരങ്ങൾ എഴുതാൻ പ്രോസസ്സ് കാത്തുനിൽക്കുന്നുണ്ടെങ്കിൽ അതിന് ഈ കാലിലായ സ്ഥലം നിറയുന്നത് വരെ വീണ്ടും എഴുതാവുന്നതാണ്.

പൈപ്പിലേക്ക് വിവരങ്ങൾ എഴുതുകയോ പൈപ്പിൽ നിന്ന് വായിക്കുകയോ ചെയ്യുന്ന സമയത്ത് എഴുതാൻ സ്ഥലമില്ലാതെ വരിക/വായിക്കാൻ പൈപ്പിൽ വിവരങ്ങൾ ഇല്ലാതെ വരിക എന്ന രണ്ട് സാഹചര്യങ്ങൾ ഉണ്ടാകാം. ഈ അവസരങ്ങളിൽ അതിനായി ശ്രമിക്കുന്ന പ്രോസസ്സ് എങ്ങനെ പെരുമാറും എന്നതിന്റെ അടിസ്ഥാനത്തിൽ ഇൻപുട്ട്/ഔട്ട്പുട്ട് പ്രക്രിയകൾ രണ്ട് തരത്തിൽ ആകാം. (ഇവ ബാക്കി ഫയൽ ഇൻപുട്ട്/ഔട്ട്പുട്ടുകൾക്കും ബാധകമാണ്)

1. ബ്ലോക്കിങ്ങ് ഐഒ: പൈപ്പിലേക്ക്/ഫയലിലേക്ക് എഴുതാനോ അവയിൽ നിന്ന് വായിക്കാനോ ശ്രമിക്കുമ്പോൾ വിവരം/സ്ഥലം ലഭ്യമല്ലെങ്കിൽ കെർണൽ ആ പ്രോസസ്സിനെ താൽക്കാലികമായി സസ്പെൻഡ് ചെയ്യുന്നു. പ്രോസസ്സ് വെയിറ്റ് അവസ്ഥയിലേക്ക് പോകും. വിവരങ്ങൾ ലഭ്യമാകുമ്പോൾ പഴയ നിലയിലേക്ക് തിരിച്ച് വരികയും പ്രവർത്തനം തുടരുകയും ചെയ്യും. ശരിക്കും ഈ അവസ്ഥാ മാറ്റങ്ങളൊക്കെ പ്രോസസ്സിന്റെ അറിവോടെ അല്ല നടക്കുന്നത്.

2. നോൺ ബ്ലോക്കിങ്ങ് ഐഒ: ഈ രീതിയിലാണെങ്കിൽ വിവരങ്ങൾ/സ്ഥലം ലഭ്യമല്ലാതെ വരുമ്പോൾ ആ റൈറ്റ്/റീഡ് പ്രോസസ്സ് താൽക്കാലികമായി ആ പ്രവർത്തനം സാധ്യമല്ല എന്ന എറർ കോഡോടെ അവസാനിപ്പിക്കപ്പെടുന്നു. ഇവിടെ പ്രോസസ്സ് വിവര/സ്ഥല ലഭ്യതക്കായി കാത്തു നിൽക്കുന്നില്ല.

ആദ്യമേ‌ തന്നെ സൂചിപ്പിച്ചതുപോലെ പൈപ്പുകൾ രണ്ട് വിധത്തിലുണ്ട്. പേരുള്ളവയും ഇല്ലാത്തവയും. ആദ്യം പേരില്ലാത്ത പൈപ്പുകളെക്കുറിച്ച് വിശദീകരിക്കാം.

പേരില്ലാത്ത പൈപ്പുകൾ (Unnamed Pipes) തിരുത്തുക

ഇത്തരം പൈപ്പുകളുടെ സവിശേഷത അവയെ പരസ്പരം ബന്ധപ്പെട്ടിട്ടില്ലാത്ത പ്രോസസ്സുകൾക്ക് ഉപയോഗിക്കാൻ സാധിക്കില്ല എന്നതാണ്‌. ഈ പൈപ്പിനെ സൃഷ്ടിക്കുന്ന പ്രോസസ്സിനും അതിന്റെ ചൈൽഡ് പ്രോസസ്സുകൾക്കും മാത്രമേ ഈ പൈപ്പ് ഉപയോഗിക്കാൻ സാധിക്കുകയുള്ളു.ഒരു പ്രോസസ്സ് അതിൽ തുറന്ന് വച്ചിരിക്കുന്ന ഫയലുകളൊക്കെ തുറന്നതിനു ശേഷം സൃഷ്ടിക്കപ്പെടുന്ന ചൈൽഡ് പ്രോസസ്സുകൾക്ക് കൂടി ഉപയോഗിക്കാൻ സാധിക്കും എന്നതാണ് ഇതിന്റെ അടിസ്ഥാനം. എന്നാൽ മറ്റൊരു പ്രോസസ്സിന് തുറക്കാൻ പാകത്തിൽ ഈ പൈപ്പുമായി ബന്ധപ്പെട്ട ഫയൽ ഡിസ്കിലോ ഫയൽ സിസ്റ്റത്തിലോ കാണില്ല. അതിനാൽത്തന്നെ അതിനെ സൃഷ്ടിച്ച പ്രോസസ്സിന് മാത്രമേ അതിനെക്കുറിച്ചുള്ള വിവരങ്ങളും ലഭ്യമാവുകയുള്ളു. ഇവയുടെ ഉപയോഗത്തിന് താഴെക്കൊടുത്തിരിക്കുന്ന ഉദാഹരണം നോക്കൂ,

/* Creating an unnamed pipe */  
#include <unistd.h>  
#include <stdio.h>  
  
int main(void)  
{  
    int fd[2];  
    pid_t pid;  
  
    char hello[] = "Hello child";  
    char reply[] = "Got it parent..";  
    char buffer[100];  
  
    if (pipe(fd) == -1) {  
        perror("pipe");  
        return;  
    }  
  
    pid = fork();  
    if (pid < 0) {  
        perror("fork");  
        return;  
    }  
  
    if (pid != 0) {  
        printf("\nParent: writing hello to pipe\n");  
        printf("Wrote %d bytes\n",write(fd[1],hello,sizeof(hello)));  
        sleep(2);  
        printf("\nParent: Reading pipe\n");  
        printf("Read %d bytes\n",read(fd[0],buffer,sizeof(buffer)));  
        printf("Data: %s\n\n",buffer);  
        return;  
    } else {  
        sleep(1);  
        printf("\nChild: Reading pipe\n");  
        printf("Read %d bytes\n",read(fd[0],buffer,sizeof(buffer)));  
        printf("Data: %s\n",buffer);  
        printf("\nChild: writing reply to pipe\n");  
        printf("Wrote %d bytes\n",write(fd[1],reply,sizeof(reply)));  
        return;  
    }  
  
    return 0;  
}  
  

പേരില്ലാത്ത പൈപ്പുകൾ സൃഷ്ടിക്കാൻ pipe() സിസ്റ്റം കോളാണ് ഉപയോഗിക്കുന്നത്. ഇതിന്റെ ആർഗ്യുമെന്റായി നൽകുന്നത് രണ്ട് ഫയൽ ഡിസ്ക്രിപ്റ്ററുകൾ ഉള്ള ഒരു അറേ ആണ്. ഇതിൽ ഒരെണ്ണം ആ പൈപ്പിന്റെ റീഡ് എൻഡും മറ്റേത് റൈറ്റ് എൻഡും ആണ്. fd[0] പൈപ്പിൽ നിന്ന് വായിക്കാനും fd[1] പൈപ്പിലേക്ക് എഴുതാനും ഉപയോഗിക്കുന്നു. ഒരു ചൈൽഡ് പ്രോസസ്സിന് അതിന്റെ പേരന്റ് പ്രോസസ്സിൽ തുറക്കപ്പെട്ട എല്ലാ ഫയൽ ഡിസ്ക്രിപ്റ്ററുകളും ലഭ്യമാകും. അതിനാൽ പൈപ്പ് സൃഷ്ടിക്കപ്പെടുന്നത് fork നടക്കുന്നതിന്റെ മുൻപ് ആയിരിക്കണം. പ്രോഗ്രാമിലെ fork ഉപയോഗത്തെ പറ്റി സംശയമുണ്ടെങ്കിൽ പ്രോസസ്സ് മാനേജ്മെന്റ് പോസ്റ്റിലെ ഫോർക്കിന്റെ ഉപയോഗം പരാമർശിച്ചിരിക്കുന്ന ലേഖനം വായിക്കൂ.

പേരുള്ള പൈപ്പുകൾ (Named Pipes) തിരുത്തുക

ഈ പൈപ്പുകൾ ഫയൽ സിസ്റ്റത്തിൽ സാധാരണ ഫയലുകളെ പോലെ തന്നെ പ്രവർത്തിക്കുന്നു. അതിനാൽ ഈ ഫയലുകളുടെ പേരറിയുന്ന ഏത് പ്രോസസ്സിനും അതറിയാവുന്ന മറ്റ് പ്രോസസ്സുകളുമായുള്ള ആശയവിനിമയത്തിന് ഈ പൈപ്പ് ഉപയോഗിക്കാം. പേരുള്ള പൈപ്പുകൾ സൃഷ്ടിക്കാൻ ഉപയോഗിക്കുന്ന സിസ്റ്റം കോൾ mkfifo ആണ്. ഇതിന്റെ ഉപയോഗം താഴെപ്പറയുന്നത് പോലെ ആണ്,

int mkfifo(const char *pathname, mode_t mode);

ആദ്യത്തെ പരാമീറ്റർ പൈപ്പിന്റെ പാത്ത് ആണ്. ഫയൽ സിസ്റ്റത്തിലെ ഏത് പാത്ത് വേണമെങ്കിലും നല്കാവുന്നതാണ്. ഒരു സാധാരണ ഫയൽ നിർമ്മിക്കപ്പെടുമ്പോളുള്ള പരിമിതികൾ ഒക്കെ ഇവിടെയും ബാധകമായിരിക്കും. രണ്ടാമത്തെ പരാമീറ്റർ ആ പൈപ്പിന്റെ അനുമതികൾ നിർണ്ണയിക്കുന്നു. (ഫയൽ അനുമതികളെക്കുറിച്ചുള്ള പോസ്റ്റ് കാണുക). വിജയകരമായി ഒരു പൈപ്പ് നിർമ്മിക്കപ്പെട്ടാൽ ഈ സിസ്റ്റം കോൾ 0 റിട്ടേൺ ചെയ്യുന്നു. വിജയകരമല്ലെങ്കിൽ -1. ഉദാഹരണത്തിന്

mkfifo("myfifo", 0644);

എന്ന രീതിയിൽ ഉപയോഗിച്ചാൽ അപ്പോളത്തെ വർക്കിങ്ങ് ഡയറക്ടറിയിൽ myfifo എന്ന പേരിൽ ഈ പൈപ്പ് സൃഷ്ടിക്കപ്പെടും. ഉപഭോക്താവിന് വായിക്കാനും എഴുതാനും ബാക്കിയുള്ളവർക്ക് എഴുതാൻ മാത്രവും ഉള്ള അനുമതികളായിരിക്കും ഇതിന് നൽകപ്പെടുക. സൃഷ്ടിക്കപ്പെട്ടതിന് ശേഷം open, read, write, close സിസ്റ്റം കോളൂകളുപയോഗിച്ച് മറ്റ് ഫയലുകളെ കൈകാര്യം ചെയ്യുന്നത് പോലെ തന്നെ ഇവയേയും കൈകാര്യം ചെയ്യാവുന്നതാണ്.