Friday, December 07, 2012

ഇന്റര്‍ പ്രോസസ്സ് കമ്യൂണിക്കേഷന്‍ - 1 : സിഗ്നല്‍

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

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

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

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

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

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

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

കമാന്റ് ലൈനില്‍ നിന്നയക്കപ്പെടുന്ന SIGINT എങ്ങനെ കൈകാര്യം ചെയ്യാം എന്നൊരു ഉദാഹരണം നോക്കാം
 #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 എന്ന സിഗ്നലിനെ കണ്ടില്ലെന്ന് നടിക്കുന്നതെങ്ങനെ എന്ന് നോക്കാം
 #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 നു പകരം ഉപയോഗിച്ച് എന്ത് സംഭവിക്കുന്നു എന്ന് നോക്കൂ.. 

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

ഇന്റര്‍ പ്രോസസ്സ് കമ്യൂണിക്കേഷന്‍

എല്ലാ മള്‍ട്ടി പ്രോസസ്സ് ഓപ്പറേറ്റിങ്ങ് സിസ്റ്റങ്ങളിലും ഒന്നിലധികം പ്രോസസ്സുകള്‍ ഒരേ സമയത്ത് പ്രവര്‍ത്തിക്കുന്നുണ്ടാകും. ചില അവസരങ്ങളില്‍ ഈ പ്രോസസ്സുകള്‍ക്ക് പരസ്പരം വിവരങ്ങള്‍ കൈമാറേണ്ട ആവശ്യം വരും. ഇങ്ങനെയുള്ള വിവര കൈമാറ്റങ്ങളെ ഇന്റര്‍ പ്രോസസ്സ് കമ്യൂണിക്കേഷന്‍ എന്ന് പറയുന്നു. ഇതിന് വിവിധ മാര്‍ഗ്ഗങ്ങള്‍ ഉണ്ട്. യൂണിക്സ് ഓപ്പറേറ്റിങ്ങ് സിസ്റ്റത്തിന്റെ ആദ്യകാല കമേര്‍ഷ്യല്‍ പതിപ്പുകളില്‍ ഒന്നായ സിസ്റ്റം വി (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)
പൈപ്പുകള്‍
വെള്ളവും മറ്റും  കൊണ്ടുപോകുന്ന പൈപ്പുകള്‍ പോലെ തന്നെയാണ് ഇവയുടെ പ്രവര്‍ത്തനം. ഇവക്ക് രണ്ട് അഗ്രങ്ങള്‍ ഉണ്ടായിരിക്കും. ഒരു വശത്തുകൂടി പോകുന്ന വിവരങ്ങള്‍ മറുവശത്ത് ലഭ്യമാകുന്നു. പേരുള്ള പൈപ്പുകള്‍ ആ പേര് അറിയാവുന്ന പ്രോസസ്സുകള്‍ക്ക് എല്ലാം  ഉപയോഗിക്കാം. എന്നാല്‍ പേരില്ലാത്തവ അതിനെ നിര്‍മ്മിച്ച പ്രോസസ്സിനും അതിന്റെ ചൈല്‍ഡ് പ്രോസസ്സുകള്‍ക്കും  മാത്രമേ ലഭ്യമാവുകയുള്ളു.

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

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

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

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

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

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

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