കേർണൽ സിസ്റ്റം കോളുകളുടെ ലിനക്സ് വിവരണം. Man syscalls (2): Linux സിസ്റ്റം കോളുകൾ. സിദ്ധാന്തം. എന്താണ് സിസ്റ്റം കോളുകൾ


സിസ്റ്റം കോളുകൾ

ഇതുവരെ, ഞങ്ങൾ ഉണ്ടാക്കിയ എല്ലാ പ്രോഗ്രാമുകൾക്കും /പ്രോക് ഫയലുകളും ഡിവൈസ് ഡ്രൈവറുകളും രജിസ്റ്റർ ചെയ്യുന്നതിന് നന്നായി നിർവചിക്കപ്പെട്ട കേർണൽ മെക്കാനിസങ്ങൾ ഉപയോഗിക്കേണ്ടതുണ്ട്. കേർണൽ പ്രോഗ്രാമർമാർ ഇതിനകം നൽകിയിട്ടുള്ള ഒരു ഡിവൈസ് ഡ്രൈവർ എഴുതുന്നതുപോലുള്ള എന്തെങ്കിലും ചെയ്യാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ ഇത് വളരെ നല്ലതാണ്. എന്നാൽ അസാധാരണമായ എന്തെങ്കിലും ചെയ്യാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, സിസ്റ്റത്തിന്റെ സ്വഭാവം ഏതെങ്കിലും വിധത്തിൽ മാറ്റുക?

ഇവിടെയാണ് കേർണൽ പ്രോഗ്രാമിംഗ് അപകടകരമാകുന്നത്. ചുവടെയുള്ള ഉദാഹരണം എഴുതുമ്പോൾ, ഞാൻ തുറന്ന സിസ്റ്റം കോൾ നശിപ്പിച്ചു. ഇതിനർത്ഥം എനിക്ക് ഫയലുകളൊന്നും തുറക്കാൻ കഴിയില്ല, എനിക്ക് പ്രോഗ്രാമുകളൊന്നും പ്രവർത്തിപ്പിക്കാൻ കഴിഞ്ഞില്ല, ഷട്ട്ഡൗൺ കമാൻഡ് ഉപയോഗിച്ച് എനിക്ക് സിസ്റ്റം ഷട്ട്ഡൗൺ ചെയ്യാൻ കഴിഞ്ഞില്ല. അത് നിർത്താൻ എനിക്ക് പവർ ഓഫ് ചെയ്യണം. ഭാഗ്യവശാൽ, ഫയലുകളൊന്നും നശിച്ചില്ല. നിങ്ങൾക്ക് ഫയലുകൾ നഷ്‌ടപ്പെടുന്നില്ലെന്ന് ഉറപ്പാക്കാൻ, insmod, rmmod കമാൻഡുകൾ നൽകുന്നതിന് മുമ്പ് ദയവായി ഒരു സമന്വയം നടത്തുക.

/proc ഫയലുകളെയും ഉപകരണ ഫയലുകളെയും കുറിച്ച് മറക്കുക. അവ ചെറിയ വിശദാംശങ്ങൾ മാത്രമാണ്. എല്ലാ പ്രക്രിയകളും ഉപയോഗിക്കുന്ന കേർണലുമായുള്ള ആശയവിനിമയത്തിന്റെ യഥാർത്ഥ പ്രക്രിയ സിസ്റ്റം കോളുകളാണ്. ഒരു പ്രോസസ്സ് കേർണലിൽ നിന്ന് സേവനം അഭ്യർത്ഥിക്കുമ്പോൾ (ഒരു ഫയൽ തുറക്കുക, ഒരു പുതിയ പ്രോസസ്സ് ആരംഭിക്കുക, അല്ലെങ്കിൽ കൂടുതൽ മെമ്മറി ആവശ്യപ്പെടുന്നത് പോലെ), ഈ സംവിധാനം ഉപയോഗിക്കുന്നു. നിങ്ങൾക്ക് രസകരമായ രീതിയിൽ കേർണൽ സ്വഭാവം മാറ്റണമെങ്കിൽ, ഇതാണ് ഏറ്റവും അനുയോജ്യമായ സ്ഥലം. വഴിയിൽ, ഒരു പ്രോഗ്രാം ഏതൊക്കെ സിസ്റ്റം കോളുകളാണ് ഉപയോഗിക്കുന്നതെന്ന് നിങ്ങൾക്ക് കാണണമെങ്കിൽ, റൺ ചെയ്യുക: സ്‌ട്രേസ് .

പൊതുവേ, പ്രക്രിയയ്ക്ക് കേർണൽ ആക്സസ് ചെയ്യാൻ കഴിയില്ല. ഇതിന് കേർണൽ മെമ്മറി ആക്സസ് ചെയ്യാൻ കഴിയില്ല കൂടാതെ കേർണൽ ഫംഗ്ഷനുകളെ വിളിക്കാനും കഴിയില്ല. CPU ഹാർഡ്‌വെയർ ഈ അവസ്ഥയെ അനുശാസിക്കുന്നു (ഇതിനെ `സംരക്ഷിത മോഡ്' എന്ന് വിളിക്കാൻ ഒരു കാരണമുണ്ട്). സിസ്റ്റം കോളുകൾ ഈ പൊതു നിയമത്തിന് അപവാദമാണ്. ഈ പ്രക്രിയ രജിസ്റ്ററുകളിൽ ഉചിതമായ മൂല്യങ്ങൾ നിറയ്ക്കുകയും തുടർന്ന് ഒരു പ്രത്യേക നിർദ്ദേശം വിളിക്കുകയും ചെയ്യുന്നു. കേർണലിലെ ഒരു മുൻനിശ്ചയിച്ച ലൊക്കേഷൻ (തീർച്ചയായും , ഇത് ഉപയോക്തൃ പ്രക്രിയകളാൽ വായിക്കപ്പെടുന്നു, പക്ഷേ അവ പുനരാലേഖനം ചെയ്യപ്പെടുന്നില്ല.) Intel CPU-കളിൽ, ഇന്ററപ്റ്റ് 0x80 വഴിയാണ് ഇത് നടപ്പിലാക്കുന്നത്. ഒരിക്കൽ നിങ്ങൾ ആ ലൊക്കേഷനിലേക്ക് ചാടുമ്പോൾ, നിങ്ങൾ ഇനി പ്രവർത്തിക്കില്ലെന്ന് ഹാർഡ്‌വെയറിന് അറിയാം. ഉപയോക്തൃ നിയന്ത്രിത മോഡിൽ പകരം, നിങ്ങൾ ഓപ്പറേറ്റിംഗ് സിസ്റ്റത്തിന്റെ കേർണലായി പ്രവർത്തിക്കുന്നു, അതിനാൽ നിങ്ങൾ ചെയ്യാൻ ആഗ്രഹിക്കുന്നതെന്തും ചെയ്യാൻ നിങ്ങളെ അനുവദിച്ചിരിക്കുന്നു.

ഒരു പ്രോസസ്സിന് വിളിക്കാൻ കഴിയുന്ന കേർണലിലെ സ്ഥാനത്തെ system_call എന്ന് വിളിക്കുന്നു. അവിടെയുള്ള നടപടിക്രമം സിസ്റ്റം കോൾ നമ്പർ പരിശോധിക്കുന്നു, ഇത് പ്രോസസിന് കൃത്യമായി എന്താണ് വേണ്ടതെന്ന് കേർണലിനോട് പറയുന്നു. പിന്നീട് അത് വിളിക്കാനുള്ള കേർണൽ ഫംഗ്‌ഷന്റെ വിലാസം കണ്ടെത്താൻ സിസ്റ്റം കോൾ ടേബിൾ (sys_call_table) നോക്കുന്നു. ആവശ്യമുള്ള ഫംഗ്ഷൻ പിന്നീട് വിളിക്കപ്പെടുന്നു, അത് ഒരു മൂല്യം നൽകിയ ശേഷം, നിരവധി സിസ്റ്റം പരിശോധനകൾ നടത്തുന്നു. ഫലം പിന്നീട് പ്രോസസിലേക്ക് (അല്ലെങ്കിൽ പ്രോസസ്സ് അവസാനിപ്പിച്ചെങ്കിൽ മറ്റൊരു പ്രക്രിയയിലേക്ക്) തിരികെ നൽകും. ഇതെല്ലാം ചെയ്യുന്ന കോഡ് നിങ്ങൾക്ക് കാണണമെങ്കിൽ, അത് ആർച്ച്/സോഴ്സ് ഫയലിലുണ്ട്< architecture >/kernel/entry.S , വരിക്ക് ശേഷം ENTRY(system_call) .

അതിനാൽ, ചില സിസ്റ്റം കോൾ എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്നത് മാറ്റണമെങ്കിൽ, ആദ്യം ചെയ്യേണ്ടത് ഉചിതമായ കാര്യം ചെയ്യാൻ സ്വന്തം ഫംഗ്‌ഷൻ എഴുതുക എന്നതാണ് (സാധാരണയായി ഞങ്ങളുടെ സ്വന്തം കോഡിന്റെ ഒരു ബിറ്റ് ചേർത്ത്, തുടർന്ന് യഥാർത്ഥ ഫംഗ്‌ഷനിലേക്ക് വിളിക്കുക), തുടർന്ന് ഞങ്ങളുടെ ഫംഗ്‌ഷനിലേക്ക് പോയിന്റർ sys_call_table എന്നതിലേക്ക് മാറ്റുക. ഞങ്ങൾ പിന്നീട് ഇല്ലാതാക്കപ്പെടാനിടയുള്ളതിനാലും സിസ്റ്റത്തെ പൊരുത്തമില്ലാത്ത അവസ്ഥയിൽ വിടാൻ ആഗ്രഹിക്കാത്തതിനാലും, ടേബിളിനെ അതിന്റെ യഥാർത്ഥ അവസ്ഥയിലേക്ക് പുനഃസ്ഥാപിക്കുന്നത് cleanup_module-ന് പ്രധാനമാണ്.

ഇവിടെ നൽകിയിരിക്കുന്ന സോഴ്സ് കോഡ് അത്തരമൊരു മൊഡ്യൂളിന്റെ ഉദാഹരണമാണ്. ഒരു നിശ്ചിത ഉപയോക്താവിനെ "ചാരൻ" ചെയ്യാനും ആ ഉപയോക്താവ് ഒരു ഫയൽ തുറക്കുമ്പോഴെല്ലാം printk വഴി ഒരു സന്ദേശം അയയ്ക്കാനും ഞങ്ങൾ ആഗ്രഹിക്കുന്നു. ഫയൽ ഓപ്പൺ സിസ്റ്റം കോളിന് പകരം, our_sys_open എന്ന ഞങ്ങളുടെ സ്വന്തം ഫംഗ്‌ഷൻ ഉപയോഗിച്ച് ഞങ്ങൾ മാറ്റിസ്ഥാപിക്കുന്നു. ഈ ഫംഗ്‌ഷൻ നിലവിലെ പ്രക്രിയയുടെ uid (ഉപയോക്തൃ ഐഡി) പരിശോധിക്കുന്നു, അത് നമ്മൾ ചാരപ്പണി ചെയ്യുന്ന uid-ന് തുല്യമാണെങ്കിൽ, തുറക്കുന്ന ഫയലിന്റെ പേര് പ്രദർശിപ്പിക്കാൻ printk-ലേക്ക് വിളിക്കുന്നു. അപ്പോൾ അത് യഥാർത്ഥ ഓപ്പൺ ഫംഗ്ഷനെ അതേ പാരാമീറ്ററുകൾ ഉപയോഗിച്ച് വിളിക്കുന്നു, യഥാർത്ഥത്തിൽ ഫയൽ തുറക്കുന്നു.

init_module ഫംഗ്‌ഷൻ sys_call_table-ലെ അനുബന്ധ സ്ഥാനം മാറ്റുകയും യഥാർത്ഥ പോയിന്റർ ഒരു വേരിയബിളിൽ സംഭരിക്കുകയും ചെയ്യുന്നു. എല്ലാം സാധാരണ നിലയിലാക്കാൻ cleanup_module ഫംഗ്‌ഷൻ ഈ വേരിയബിൾ ഉപയോഗിക്കുന്നു. രണ്ട് മൊഡ്യൂളുകൾ ഒരേ സിസ്റ്റം കോളിൽ മാറ്റം വരുത്താനുള്ള സാധ്യത കാരണം ഈ സമീപനം അപകടകരമാണ്. നമുക്ക് A, B എന്നീ രണ്ട് മൊഡ്യൂളുകൾ ഉണ്ടെന്ന് സങ്കൽപ്പിക്കുക. മൊഡ്യൂളിന്റെ ഓപ്പൺ സിസ്റ്റം കോളിനെ നമുക്ക് A A_open എന്നും അതേ കോളിനെ മൊഡ്യൂളിലേക്ക് B B_open എന്നും വിളിക്കാം. ഇപ്പോൾ കേർണൽ തിരുകിയ syscal ന് പകരം A_open എന്ന് വരുന്നു, അത് ചെയ്യേണ്ടത് ചെയ്യുമ്പോൾ യഥാർത്ഥ sys_open എന്ന് വിളിക്കും. തുടർന്ന്, കെർണലിലേക്ക് ബി ചേർക്കും, കൂടാതെ സിസ്റ്റം കോളിന് പകരം ബി_ഓപ്പൺ നൽകും, അത് യഥാർത്ഥ സിസ്റ്റം കോൾ എന്ന് കരുതുന്നതിനെ വിളിക്കും, എന്നാൽ യഥാർത്ഥത്തിൽ A_open ആണ്.

ഇപ്പോൾ ആദ്യം ബി നീക്കം ചെയ്താൽ, എല്ലാം ശരിയാകും: ഇത് ഒറിജിനലിനെ വിളിക്കുന്ന A_open-ലെ സിസ്റ്റം കോൾ പുനഃസ്ഥാപിക്കും. എന്നിരുന്നാലും, A നീക്കം ചെയ്യുകയും തുടർന്ന് B നീക്കം ചെയ്യുകയും ചെയ്താൽ, സിസ്റ്റം തകരും. A നീക്കം ചെയ്യുന്നത് സിസ്റ്റം കോൾ യഥാർത്ഥമായ sys_open എന്നതിലേക്ക് പുനഃസ്ഥാപിക്കും, ലൂപ്പിൽ നിന്ന് B മുറിക്കുന്നു. തുടർന്ന്, ബി നീക്കം ചെയ്യുമ്പോൾ, അത് യഥാർത്ഥമായതായി കരുതുന്ന സിസ്റ്റം കോൾ പുനഃസ്ഥാപിക്കും. കോൾ യഥാർത്ഥത്തിൽ മെമ്മറിയിൽ ഇല്ലാത്ത A_open എന്നതിലേക്ക് നയിക്കപ്പെടും. ഒറ്റനോട്ടത്തിൽ, സിസ്റ്റം കോൾ നമ്മുടെ ഓപ്പൺ ഫംഗ്‌ഷന് തുല്യമാണോ എന്ന് പരിശോധിച്ച് ഈ പ്രത്യേക പ്രശ്‌നം പരിഹരിക്കാൻ കഴിയുമെന്ന് തോന്നുന്നു, അങ്ങനെയാണെങ്കിൽ, ആ കോളിന്റെ മൂല്യം മാറ്റാതിരിക്കുക (അതിനാൽ അത് ഇല്ലാതാക്കപ്പെടുമ്പോൾ ബി സിസ്റ്റം കോൾ മാറ്റില്ല. ), എന്നാൽ അത് മറ്റൊരു മോശം പ്രശ്നം ഉണ്ടാക്കും. A നീക്കം ചെയ്യുമ്പോൾ, സിസ്റ്റം കോൾ B_open എന്നതിലേക്ക് മാറ്റിയതായി കാണുന്നു, അതിനാൽ അത് A_open-ലേക്ക് പോയിന്റ് ചെയ്യില്ല, അതിനാൽ മെമ്മറിയിൽ നിന്ന് നീക്കം ചെയ്യുന്നതിനുമുമ്പ് ഇത് പോയിന്റർ sys_open-ലേക്ക് പുനഃസ്ഥാപിക്കില്ല. നിർഭാഗ്യവശാൽ, B_open ഇപ്പോഴും മെമ്മറിയിൽ ഇല്ലാത്ത A_open എന്ന് വിളിക്കാൻ ശ്രമിക്കും, അതിനാൽ B നീക്കം ചെയ്യാതെ തന്നെ സിസ്റ്റം ക്രാഷ് ചെയ്യും.

ഈ പ്രശ്നം തടയാൻ ഞാൻ രണ്ട് വഴികൾ കാണുന്നു. ആദ്യം: sys_open-ന്റെ യഥാർത്ഥ മൂല്യത്തിലേക്കുള്ള ആക്സസ് പുനഃസ്ഥാപിക്കുക. നിർഭാഗ്യവശാൽ, sys_open /proc/ksyms-ലെ കേർണൽ പട്ടികയുടെ ഭാഗമല്ല, അതിനാൽ ഞങ്ങൾക്ക് അത് ആക്സസ് ചെയ്യാൻ കഴിയില്ല. മൊഡ്യൂൾ അൺലോഡ് ചെയ്യുന്നത് തടയാൻ ഒരു റഫറൻസ് കൗണ്ടർ ഉപയോഗിക്കുക എന്നതാണ് മറ്റൊരു പരിഹാരം. ഇത് സാധാരണ മൊഡ്യൂളുകൾക്ക് നല്ലതാണ്, എന്നാൽ "വിദ്യാഭ്യാസ" മൊഡ്യൂളുകൾക്ക് മോശമാണ്.

/* syscall.c * * സിസ്‌റ്റം കോൾ "സ്റ്റീലിംഗ്" സാമ്പിൾ */ /* പകർപ്പവകാശം (സി) 1998-99 ഓറി പോമറന്റ്‌സ് */ /* ആവശ്യമായ ഹെഡർ ഫയലുകൾ */ /* കേർണൽ മൊഡ്യൂളുകളിലെ സ്റ്റാൻഡേർഡ് */ #ഉൾപ്പെടുന്നു /* ഞങ്ങൾ കേർണൽ വർക്ക് ചെയ്യുന്നു */ #ഉൾപ്പെടുത്തുക /* പ്രത്യേകമായി, ഒരു മൊഡ്യൂൾ */ /* CONFIG_MODVERSIONS കൈകാര്യം ചെയ്യുക */ #CONFIG_MODVERSIONS==1 #MODVERSIONS നിർവ്വചിക്കുക #ഉൾപ്പെടുത്തുക #endif #ഉൾപ്പെടുന്നു /* സിസ്റ്റം കോളുകളുടെ ലിസ്റ്റ് */ /* നിലവിലെ (പ്രോസസ്സ്) ഘടനയ്ക്ക്, നിലവിലെ ഉപയോക്താവ് ആരാണെന്ന് അറിയാൻ ഞങ്ങൾക്ക് ഇത് ആവശ്യമാണ്. */ #ഉൾപ്പെടുന്നു /* 2.2.3 /usr/include/linux/version.h-ൽ ഇതിനായി ഒരു * മാക്രോ ഉൾപ്പെടുന്നു, എന്നാൽ 2.0.35-ൽ ഇല്ല - അതിനാൽ ആവശ്യമെങ്കിൽ ഞാൻ അത് * ഇവിടെ ചേർക്കുന്നു. */ #ifndef KERNEL_VERSION #define KERNEL_VERSION(a ,b,c) ((a)*65536+(b)*256+(c)) #endif #LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) #ഉൾപ്പെടുന്നു #endif /* സിസ്റ്റം കോൾ ടേബിൾ (ഫംഗ്ഷനുകളുടെ ഒരു പട്ടിക). ഞങ്ങൾ * ഇത് ബാഹ്യമായി നിർവചിക്കുന്നു, ഞങ്ങൾ ഇൻസ്‌മോഡ് ചെയ്യുമ്പോൾ */ എക്‌സ്‌റ്റേൺ അസാധുവായ *sys_call_table; /* യുഐഡി ഞങ്ങൾ ചാരപ്പണി ചെയ്യാൻ ആഗ്രഹിക്കുന്നു - * കമാൻഡ് ലൈനിൽ നിന്ന് പൂരിപ്പിക്കും */ int uid; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) MODULE_PARM(uid, "i"); #endif /* യഥാർത്ഥ സിസ്റ്റം കോളിലേക്കുള്ള ഒരു പോയിന്റർ. കാരണം * യഥാർത്ഥ ഫംഗ്‌ഷനെ വിളിക്കുന്നതിനുപകരം ഞങ്ങൾ ഇത് നിലനിർത്തുന്നു * (sys_open), നമ്മുടെ മുമ്പിലുള്ള സിസ്റ്റം കോൾ മറ്റാരെങ്കിലും മാറ്റിസ്ഥാപിച്ചിരിക്കാം. ഇത് * 100% സുരക്ഷിതമല്ലെന്നത് ശ്രദ്ധിക്കുക, കാരണം മറ്റൊരു മൊഡ്യൂൾ * sys_open മാറ്റിസ്ഥാപിക്കുകയാണെങ്കിൽ, ഞങ്ങൾ ചേർക്കുമ്പോൾ * ഞങ്ങൾ വിളിക്കും. ആ മൊഡ്യൂളിലെ ഫംഗ്‌ഷൻ - അത് * നമ്മൾ ആകുന്നതിന് മുമ്പ് നീക്കം ചെയ്‌തേക്കാം. * * ഇതിനുള്ള മറ്റൊരു കാരണം നമുക്ക് sys_open ചെയ്യാൻ കഴിയില്ല എന്നതാണ്. * ഇതൊരു സ്റ്റാറ്റിക് വേരിയബിളാണ്, അതിനാൽ ഇത് എക്‌സ്‌പോർട്ട് ചെയ്യപ്പെടുന്നില്ല. */ asmlinkage int (*original_call)(const char *, int, int); /* ചില കാരണങ്ങളാൽ, 2.2.3 കറന്റ്->uid എനിക്ക് * പൂജ്യം നൽകി, യഥാർത്ഥ ഉപയോക്തൃ ഐഡി അല്ല. എന്താണ് തെറ്റ് സംഭവിച്ചതെന്ന് കണ്ടെത്താൻ ഞാൻ ശ്രമിച്ചു, പക്ഷേ എനിക്ക് അത് കുറച്ച് സമയത്തിനുള്ളിൽ ചെയ്യാൻ കഴിഞ്ഞില്ല, കൂടാതെ * ഞാൻ മടിയനാണ് - അതിനാൽ ഞാൻ * uid ലഭിക്കാൻ സിസ്റ്റം കോൾ ഉപയോഗിക്കും, ഒരു പ്രക്രിയ ആയിരിക്കും. * * ചില കാരണങ്ങളാൽ, ഞാൻ കേർണൽ വീണ്ടും കംപൈൽ ചെയ്‌തതിനുശേഷം ഈ * പ്രശ്‌നം ഇല്ലാതായി. */ asmlinkage int (*getuid_call)(); /* ഞങ്ങൾ sys_open (നിങ്ങൾ ഓപ്പൺ സിസ്റ്റം കോളിനെ വിളിക്കുമ്പോൾ വിളിക്കുന്ന ഫംഗ്‌ഷൻ * എന്ന് വിളിക്കുന്ന ഫംഗ്‌ഷൻ) ഉപയോഗിച്ച് മാറ്റിസ്ഥാപിക്കും. * കൃത്യമായ പ്രോട്ടോടൈപ്പ് കണ്ടെത്തുന്നതിന്, ആർഗ്യുമെന്റുകളുടെ നമ്പറും തരവും * ഉപയോഗിച്ച്, ഞങ്ങൾ ആദ്യം യഥാർത്ഥ ഫംഗ്ഷൻ കണ്ടെത്തുന്നു * (ഇത്" s-ൽ fs/open.c). * * സിദ്ധാന്തത്തിൽ, ഇതിനർത്ഥം ഞങ്ങൾ കേർണലിന്റെ * നിലവിലെ പതിപ്പുമായി ബന്ധിപ്പിച്ചിരിക്കുന്നു എന്നാണ്. പ്രായോഗികമായി, * സിസ്റ്റം കോളുകൾ ഒരിക്കലും മാറില്ല (ഇത് നശിപ്പിക്കും * കൂടാതെ പ്രോഗ്രാമുകൾ വീണ്ടും കംപൈൽ ചെയ്യേണ്ടതുണ്ട്, കാരണം സിസ്റ്റം * കോളുകൾ കേർണലിനും * പ്രോസസ്സുകൾക്കും ഇടയിലുള്ള ഇന്റർഫേസ്). */ if (uid == getuid_call()) ( /* getuid_call എന്നത് getuid സിസ്റ്റം കോളാണ്, * ഇത് സിസ്റ്റം * കോൾ എന്ന് വിളിക്കുന്ന പ്രോസസ്സ് പ്രവർത്തിപ്പിച്ച ഉപയോക്താവിന്റെ uid നൽകുന്നു */ /* ഫയൽ റിപ്പോർട്ടുചെയ്യുക, പ്രസക്തമാണെങ്കിൽ */ printk("%d പ്രകാരം തുറന്ന ഫയൽ: ", uid); ചെയ്യുക ( #If LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) get_user(ch, filename+i); #else ch = get_user(filename+ i ). ഫയലുകൾ */ റിട്ടേൺ original_call(ഫയലിന്റെ പേര്, ഫ്ലാഗുകൾ, മോഡ്); ) /* മൊഡ്യൂൾ സമാരംഭിക്കുക - സിസ്റ്റം കോൾ മാറ്റിസ്ഥാപിക്കുക */ int init_module() ( /* മുന്നറിയിപ്പ് - ഇപ്പോൾ വളരെ വൈകി, പക്ഷേ * അടുത്ത തവണ. .. */ printk("ഞാൻ" അപകടകാരിയാണ്. നിങ്ങൾ ഒരു " ചെയ്തുവെന്ന് ഞാൻ പ്രതീക്ഷിക്കുന്നു); printk("Sync before you insmod"ed me.\n"); printk("എന്റെ എതിരാളി, cleanup_module(), is even"); printk("കൂടുതൽ അപകടകരമാണ്. എങ്കിൽ\n"); printk ("നിങ്ങൾ നിങ്ങളുടെ ഫയൽ സിസ്റ്റത്തെ വിലമതിക്കുന്നു, അത് "); printk("be \"sync; rmmod\" \n"); printk("നിങ്ങൾ ഈ മൊഡ്യൂൾ നീക്കം ചെയ്യുമ്പോൾ.\n"); /* ഒറിജിനൽ_കോളിൽ യഥാർത്ഥ ഫംഗ്‌ഷനിലേക്ക് ഒരു പോയിന്റർ സൂക്ഷിക്കുക, തുടർന്ന് സിസ്റ്റം കോൾ ടേബിളിലെ സിസ്റ്റം കോൾ * പകരം our_sys_open */ original_call = sys_call_table[__NR_open]; sys_call_table[__NR_open] = our_sys_open; /* സിസ്റ്റം * കോൾ ഫൂവിനുള്ള ഫംഗ്‌ഷന്റെ വിലാസം ലഭിക്കുന്നതിന്, sys_call_table[__NR_foo] എന്നതിലേക്ക് പോകുക. */ printk("യുഐഡിയിൽ ചാരപ്പണി:%d\n", uid); /* getuid */ getuid_call = sys_call_table[__NR_getuid] എന്നതിനായുള്ള സിസ്റ്റം കോൾ നേടുക; തിരികെ 0; ) /* ക്ലീനപ്പ് - /proc */ void cleanup_module() എന്നതിൽ നിന്ന് ഉചിതമായ ഫയൽ രജിസ്റ്റർ ചെയ്യുക "); printk("ഓപ്പൺ സിസ്റ്റം കോൾ\n"); printk("സിസ്റ്റം "" എന്നതിൽ അവശേഷിച്ചേക്കാം); printk("ഒരു അസ്ഥിരമായ അവസ്ഥ.\n"); ) sys_call_table[__NR_open] = original_call; )

മിക്കപ്പോഴും, __NR_xxx എന്ന നമ്പറുള്ള സിസ്റ്റം കോൾ കോഡ് നിർവചിച്ചിരിക്കുന്നു /usr/include/asm/unistd.h, ഫംഗ്ഷനിലെ ലിനക്സ് കേർണൽ സോഴ്സ് കോഡിൽ കാണാം sys_xxx(). (i386-നുള്ള കോൾ ടേബിൾ ഇതിൽ കാണാം /usr/src/linux/arch/i386/kernel/entry.S.) ഈ നിയമത്തിന് നിരവധി ഒഴിവാക്കലുകൾ ഉണ്ട്, പ്രധാനമായും പഴയ മിക്ക സിസ്റ്റം കോളുകളും ഒരു സിസ്റ്റവുമില്ലാതെ പുതിയവ ഉപയോഗിച്ച് മാറ്റിസ്ഥാപിക്കുന്നു എന്ന വസ്തുത കാരണം. പാരിസ്‌ക്, സ്പാർക്, സ്പാർക്64, ആൽഫ തുടങ്ങിയ പ്രൊപ്രൈറ്ററി ഒഎസ് എമുലേഷൻ പ്ലാറ്റ്‌ഫോമുകളിൽ, നിരവധി അധിക സിസ്റ്റം കോളുകൾ ഉണ്ട്; mips64 ന് 32-ബിറ്റ് സിസ്റ്റം കോളുകളുടെ ഒരു പൂർണ്ണ സെറ്റും ഉണ്ട്.

കാലക്രമേണ, ആവശ്യമെങ്കിൽ ചില സിസ്റ്റം കോളുകളുടെ ഇന്റർഫേസിൽ മാറ്റങ്ങൾ സംഭവിച്ചു. ഈ മാറ്റങ്ങളുടെ ഒരു കാരണം സിസ്റ്റം കോളിലേക്ക് കൈമാറിയ ഘടനകളുടെ വലുപ്പം അല്ലെങ്കിൽ സ്കെയിലർ മൂല്യങ്ങൾ വർദ്ധിപ്പിക്കേണ്ടതിന്റെ ആവശ്യകതയാണ്. ഈ മാറ്റങ്ങൾ കാരണം, ചില ആർക്കിടെക്ചറുകളിൽ (അതായത് പഴയ 32-ബിറ്റ് i386) സമാനമായ സിസ്റ്റം കോളുകളുടെ വിവിധ ഗ്രൂപ്പുകൾ പ്രത്യക്ഷപ്പെട്ടു (ഉദാഹരണത്തിന്, വെട്ടിച്ചുരുക്കുക(2) ഒപ്പം വെട്ടിച്ചുരുക്കുക64(2)), ഒരേ ടാസ്‌ക്കുകൾ ചെയ്യുന്നു, എന്നാൽ അവയുടെ വാദങ്ങളുടെ വലുപ്പത്തിൽ വ്യത്യാസമുണ്ട്. (ശ്രദ്ധിച്ചത് പോലെ, ഇത് ആപ്ലിക്കേഷനുകളെ ബാധിക്കില്ല: ശരിയായ സിസ്റ്റം കോൾ ട്രിഗർ ചെയ്യുന്നതിന് glibc റാപ്പർ ഫംഗ്‌ഷനുകൾ ചില പ്രവർത്തനങ്ങൾ ചെയ്യുന്നു, ഇത് പഴയ ബൈനറികൾക്ക് ABI അനുയോജ്യത ഉറപ്പാക്കുന്നു.) ഒന്നിലധികം പതിപ്പുകളുള്ള സിസ്റ്റം കോളുകളുടെ ഉദാഹരണങ്ങൾ:

*നിലവിൽ മൂന്ന് വ്യത്യസ്ത പതിപ്പുകളുണ്ട് സ്ഥിതിവിവരക്കണക്ക്(2): sys_stat() (സ്ഥലം __NR_oldstat), sys_newsstat() (സ്ഥലം __NR_stat) ഒപ്പം sys_stat64() (സ്ഥലം __NR_stat64), രണ്ടാമത്തേത് നിലവിൽ ഉപയോഗത്തിലാണ്. സമാനമായ ഒരു സാഹചര്യം lstat(2) ഒപ്പം fstat(2). * സമാനമായി നിർവചിച്ചിരിക്കുന്നു __NR_oldoldname, __NR_oldnameഒപ്പം __NR_നാമംകോളുകൾക്കായി sys_oldname(), sys_name() ഒപ്പം sys_newname(). * Linux 2.0 ന് ഒരു പുതിയ പതിപ്പുണ്ട് vm86(2), ആണവ നടപടിക്രമങ്ങളുടെ പുതിയതും പഴയതുമായ പതിപ്പുകളെ വിളിക്കുന്നു sys_vm86old() ഒപ്പം sys_vm86(). * Linux 2.4 ന് ഒരു പുതിയ പതിപ്പുണ്ട് പരിധി(2) ആണവ നടപടിക്രമങ്ങളുടെ പുതിയതും പഴയതുമായ പതിപ്പുകളെ വിളിക്കുന്നു sys_old_getrlimit() (സ്ഥലം __NR_getrlimit) ഒപ്പം sys_getrlimit() (സ്ഥലം __NR_ugetrlimit). * Linux 2.4-ൽ, ഉപയോക്താവിന്റെയും ഗ്രൂപ്പ് ഐഡി ഫീൽഡുകളുടെയും വലുപ്പം 16-ൽ നിന്ന് 32 ബിറ്റുകളായി വർദ്ധിപ്പിച്ചു. ഈ മാറ്റത്തെ പിന്തുണയ്ക്കുന്നതിനായി നിരവധി സിസ്റ്റം കോളുകൾ ചേർത്തിട്ടുണ്ട് (ഉദാ. ചൗൺ32(2), getuid32(2), ഗെറ്റ്ഗ്രൂപ്പുകൾ32(2), setresuid32(2)), "32" എന്ന പ്രത്യയം കൂടാതെ, സമാന പേരുകളുള്ള മുൻ കോളുകൾ ഒഴിവാക്കുന്നു. * 32-ബിറ്റ് ആർക്കിടെക്ചറുകളിലെ ആപ്ലിക്കേഷനുകളിൽ വലിയ ഫയലുകൾ (അതിന്റെ വലുപ്പങ്ങളും ഓഫ്‌സെറ്റുകളും 32 ബിറ്റുകളിലേക്ക് യോജിക്കുന്നില്ല) ആക്‌സസ് ചെയ്യുന്നതിനുള്ള പിന്തുണ Linux 2.4 ചേർത്തു. ഫയൽ വലുപ്പത്തിലും ഓഫ്‌സെറ്റുകളിലും പ്രവർത്തിക്കുന്ന സിസ്റ്റം കോളുകളിൽ ഇതിന് മാറ്റങ്ങൾ ആവശ്യമാണ്. ഇനിപ്പറയുന്ന സിസ്റ്റം കോളുകൾ ചേർത്തു: fcntl64(2), getdents64(2), സ്ഥിതി64(2), statfs64(2), വെട്ടിച്ചുരുക്കുക64(2) ഫയൽ വിവരണങ്ങളോ പ്രതീകാത്മക ലിങ്കുകളോ കൈകാര്യം ചെയ്യുന്ന അവയുടെ അനലോഗുകളും. ഈ സിസ്റ്റം കോളുകൾ പഴയ സിസ്റ്റം കോളുകളെ ഇല്ലാതാക്കുന്നു, അവയ്ക്ക് "സ്റ്റാറ്റ്" കോളുകൾ ഒഴികെ, അതേ പേരിട്ടിട്ടുണ്ടെങ്കിലും "64" പ്രത്യയം ഇല്ല.

64-ബിറ്റ് ഫയൽ ആക്‌സസും 32-ബിറ്റ് യുഐഡി/ജിഐഡിയും മാത്രമുള്ള പുതിയ പ്ലാറ്റ്‌ഫോമുകളിൽ (ഉദാ. ആൽഫ, IA64, s390x, x86-64), UID/GID, ഫയൽ ആക്‌സസ് എന്നിവയ്‌ക്കായി സിസ്റ്റം കോളുകളുടെ ഒരു പതിപ്പ് മാത്രമേയുള്ളൂ. *64, *32 കോളുകൾ ലഭ്യമാകുന്ന പ്ലാറ്റ്‌ഫോമുകളിൽ (സാധാരണയായി 32-ബിറ്റ് പ്ലാറ്റ്‌ഫോമുകൾ), മറ്റ് പതിപ്പുകൾ കാലഹരണപ്പെട്ടതാണ്.

* വെല്ലുവിളികൾ rt_sig*അധിക തത്സമയ സിഗ്നലുകൾ പിന്തുണയ്ക്കുന്നതിനായി കേർണൽ 2.2-ലേക്ക് ചേർത്തു (കേർണൽ 2.2 കാണുക). സിഗ്നൽ(7)). ഈ സിസ്റ്റം കോളുകൾ പഴയ സിസ്റ്റം കോളുകളെ അസാധുവാക്കുന്നു, എന്നാൽ "rt_" പ്രിഫിക്സ് ഇല്ലാതെ. * സിസ്റ്റം കോളുകളിൽ തിരഞ്ഞെടുക്കുക(2) ഒപ്പം mmap(2) അഞ്ചോ അതിലധികമോ ആർഗ്യുമെന്റുകൾ ഉപയോഗിക്കുന്നു, ഇത് i386-ൽ ആർഗ്യുമെന്റുകൾ എങ്ങനെ കടന്നുപോയി എന്ന് നിർണ്ണയിക്കുന്നതിൽ പ്രശ്നങ്ങൾ സൃഷ്ടിച്ചു. ഇതിന്റെ അനന്തരഫലമായി, മറ്റ് ആർക്കിടെക്ചറുകളിൽ വിളിക്കുന്നു sys_select() ഒപ്പം sys_mmap() പൊരുത്തം __NR_selectഒപ്പം __NR_mmap, i386-ൽ അവർ യോജിക്കുന്നു പഴയ_തിരഞ്ഞെടുപ്പ്() ഒപ്പം പഴയ_മാപ്പ്() (ആർഗ്യുമെന്റുകളുടെ ഒരു ബ്ലോക്കിലേക്ക് ഒരു പോയിന്റർ ഉപയോഗിക്കുന്ന നടപടിക്രമങ്ങൾ). നിലവിൽ അഞ്ചിൽ കൂടുതൽ ആർഗ്യുമെന്റുകൾ പാസാക്കുന്നതിൽ ഒരു പ്രശ്നവുമില്ല __NR__newsselect, അത് കൃത്യമായി യോജിക്കുന്നു sys_select(), ഒപ്പം അതേ സാഹചര്യവും __NR_mmap2.

വാൽറസ് പല കാര്യങ്ങളെ കുറിച്ചും പറഞ്ഞു, "സംസാരിക്കാനുള്ള സമയം വന്നിരിക്കുന്നു."
എൽ. കരോൾ (ബി. സ്ട്രോസ്ട്രാപ്പിന്റെ പുസ്തകത്തിൽ നിന്നുള്ള ഉദ്ധരണി)

ഒരു ആമുഖത്തിന് പകരം.

പൊതുവായി ലിനക്സ് കേർണലിന്റെ ആന്തരിക ഘടന, അതിന്റെ വിവിധ സബ്സിസ്റ്റങ്ങൾ, സിസ്റ്റം കോളുകൾ എന്നിവയെക്കുറിച്ചുള്ള വിഷയത്തിൽ ധാരാളം എഴുതുകയും മാറ്റിയെഴുതുകയും ചെയ്തിട്ടുണ്ട്. ഒരുപക്ഷേ, ആത്മാഭിമാനമുള്ള ഓരോ എഴുത്തുകാരനും ഇതിനെക്കുറിച്ച് ഒരിക്കലെങ്കിലും എഴുതണം, ഓരോ സ്വയം ബഹുമാനിക്കുന്ന പ്രോഗ്രാമറും തീർച്ചയായും സ്വന്തം ഫയൽ മാനേജർ എഴുതണം :) ഞാൻ ഒരു പ്രൊഫഷണൽ ഐടി എഴുത്തുകാരനല്ലെങ്കിലും, പൊതുവേ, ഞാൻ എന്റെ കുറിപ്പുകൾ ആദ്യം തന്നെ എഴുതുന്നു. എല്ലാറ്റിനുമുപരിയായി, നിങ്ങൾ പഠിച്ച കാര്യങ്ങൾ പെട്ടെന്ന് മറക്കാതിരിക്കാൻ. പക്ഷേ, എന്റെ യാത്രാ കുറിപ്പുകൾ ആർക്കെങ്കിലും ഉപകാരപ്രദമാണെങ്കിൽ, തീർച്ചയായും ഞാൻ സന്തോഷവാനായിരിക്കും. ശരി, പൊതുവേ, നിങ്ങൾക്ക് എണ്ണ ഉപയോഗിച്ച് കഞ്ഞി നശിപ്പിക്കാൻ കഴിയില്ല, അതിനാൽ ആരും പരാമർശിക്കാൻ മെനക്കെടാത്ത എന്തെങ്കിലും എഴുതാനോ വിവരിക്കാനോ എനിക്ക് കഴിഞ്ഞേക്കും.

സിദ്ധാന്തം. സിസ്റ്റം കോളുകൾ എന്തൊക്കെയാണ്?

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

അതിനാൽ, ആദ്യത്തെ ഏകദേശ കണക്കിൽ ഞങ്ങൾ ഒരു സിസ്റ്റം കോളിന്റെ നിർവചനത്തിലേക്ക് എത്തി: ഒരു സിസ്റ്റം കോൾ എന്നത് ഒരു ഉപയോക്തൃ ആപ്ലിക്കേഷന്റെ അഭ്യർത്ഥന പ്രകാരം OS കേർണൽ നൽകുന്ന ഒരു നിശ്ചിത സേവനമാണ്. അത്തരമൊരു സേവനം ഒരു ഫയലിന്റെ ഇതിനകം സൂചിപ്പിച്ച ഓപ്പണിംഗ്, അതിന്റെ നിർമ്മാണം, വായന, എഴുത്ത്, ഒരു പുതിയ പ്രോസസ്സ് സൃഷ്ടിക്കൽ, പ്രോസസ്സ് ഐഡന്റിഫയർ (പിഡ്) നേടൽ, ഫയൽ സിസ്റ്റം മൌണ്ട് ചെയ്യുക, സിസ്റ്റം നിർത്തുക, ഒടുവിൽ. യഥാർത്ഥ ജീവിതത്തിൽ ഇവിടെ ലിസ്റ്റുചെയ്തിരിക്കുന്നതിനേക്കാൾ കൂടുതൽ സിസ്റ്റം കോളുകൾ ഉണ്ട്.

ഒരു സിസ്റ്റം കോൾ എങ്ങനെയിരിക്കും, അത് എന്താണ്? ശരി, മുകളിൽ പറഞ്ഞതിൽ നിന്ന്, ഒരു സിസ്റ്റം കോൾ അനുബന്ധ രൂപമുള്ള ഒരു കേർണൽ സബ്റൂട്ടീനാണെന്ന് വ്യക്തമാകും. Win9x/DOS-ന് കീഴിൽ പ്രോഗ്രാമിംഗ് അനുഭവം ഉള്ളവർ, അതിന്റെ എല്ലാ (അല്ലെങ്കിൽ കുറഞ്ഞത് ചിലത്) ഫംഗ്‌ഷനുകളുമായും int 0x21 ഇന്ററപ്റ്റ് ഓർത്തിരിക്കാം. എന്നിരുന്നാലും, എല്ലാ Unix സിസ്റ്റം കോളുകൾക്കും ബാധകമായ ഒരു ചെറിയ വിചിത്രതയുണ്ട്. കൺവെൻഷൻ പ്രകാരം, ഒരു സിസ്റ്റം കോൾ നടപ്പിലാക്കുന്ന ഒരു ഫംഗ്‌ഷന് N ആർഗ്യുമെന്റുകൾ എടുക്കാം അല്ലെങ്കിൽ ഒന്നുമില്ല, എന്നാൽ ഒരു തരത്തിലല്ലെങ്കിൽ മറ്റൊരു തരത്തിൽ, ഫംഗ്‌ഷൻ ഒരു ഇൻറ്റ് മൂല്യം നൽകണം. ഏതൊരു നോൺ-നെഗറ്റീവ് മൂല്യവും സിസ്റ്റം കോൾ ഫംഗ്‌ഷന്റെ വിജയകരമായ നിർവ്വഹണമായി വ്യാഖ്യാനിക്കപ്പെടുന്നു, അതിനാൽ സിസ്റ്റം കോൾ തന്നെ. പൂജ്യത്തിൽ താഴെയുള്ള മൂല്യം ഒരു പിശകിന്റെ അടയാളമാണ്, അതേ സമയം ഒരു പിശക് കോഡ് അടങ്ങിയിരിക്കുന്നു (എറർ കോഡുകൾ ഉൾപ്പെടുന്ന/asm-generic/errno-base.h എന്നതിൽ നിർവചിച്ചിരിക്കുന്നു കൂടാതെ/asm-generic/errno.h തലക്കെട്ടുകളും ഉൾപ്പെടുന്നു) . Linux-ൽ, അടുത്തിടെ വരെ സിസ്റ്റം കോളുകൾക്കുള്ള ഗേറ്റ്‌വേ int 0x80 തടസ്സമായിരുന്നു, വിൻഡോസിൽ (പതിപ്പ് XP സർവീസ് പാക്ക് 2 വരെ, ഞാൻ തെറ്റിദ്ധരിച്ചിട്ടില്ലെങ്കിൽ) ഗേറ്റ്‌വേ 0x2e തടസ്സമാണ്. വീണ്ടും, ലിനക്സ് കേർണലിൽ, അടുത്തിടെ വരെ എല്ലാ സിസ്റ്റം കോളുകളും കൈകാര്യം ചെയ്തത് system_call() ഫംഗ്ഷനാണ്. എന്നിരുന്നാലും, പിന്നീട് തെളിഞ്ഞതുപോലെ, ഗേറ്റ്‌വേ 0x80 വഴിയുള്ള സിസ്റ്റം കോളുകൾ പ്രോസസ്സ് ചെയ്യുന്നതിനുള്ള ക്ലാസിക് മെക്കാനിസം ഇന്റൽ പെന്റിയം 4 പ്രോസസറുകളിലെ പ്രകടനത്തിൽ ഗണ്യമായ കുറവുണ്ടാക്കുന്നു. അതിനാൽ, ക്ലാസിക് മെക്കാനിസത്തിന് പകരം വെർച്വൽ ഡൈനാമിക് പങ്കിട്ട ഒബ്‌ജക്റ്റുകളുടെ (DSO - ഡൈനാമിക്) രീതി മാറ്റി. പങ്കിട്ട ഒബ്‌ജക്റ്റ് ഫയൽ, ശരിയായ വിവർത്തനത്തിനായി എനിക്ക് ഉറപ്പുനൽകാൻ കഴിയില്ല, പക്ഷേ DSO എന്നത് വിൻഡോസ് ഉപയോക്താക്കൾക്ക് DLL - ഡൈനാമിക്കലി ലോഡഡ്, ലിങ്ക്ഡ് ലൈബ്രറി) - VDSO എന്നാണ് അറിയുന്നത്. പുതിയ രീതിയും ക്ലാസിക് രീതിയും തമ്മിലുള്ള വ്യത്യാസം എന്താണ്? ആദ്യം, ഗേറ്റ് 0x80 വഴി പ്രവർത്തിക്കുന്ന ക്ലാസിക് രീതി നോക്കാം.

ലിനക്സിൽ സിസ്റ്റം കോളുകൾ സർവ്വീസ് ചെയ്യുന്നതിനുള്ള ക്ലാസിക് മെക്കാനിസം.

x86 ആർക്കിടെക്ചറിലെ തടസ്സങ്ങൾ.

മുകളിൽ സൂചിപ്പിച്ചതുപോലെ, മുമ്പ്, ഗേറ്റ്‌വേ 0x80 (int 0x80) ഉപയോക്തൃ ആപ്ലിക്കേഷനുകളിൽ നിന്നുള്ള സേവന അഭ്യർത്ഥനകൾക്കായി ഉപയോഗിച്ചിരുന്നു. IA-32 ആർക്കിടെക്ചറിനെ അടിസ്ഥാനമാക്കിയുള്ള ഒരു സിസ്റ്റത്തിന്റെ പ്രവർത്തനം നിയന്ത്രിക്കുന്നത് തടസ്സങ്ങളാൽ ആണ് (കർശനമായി പറഞ്ഞാൽ, ഇത് പൊതുവെ എല്ലാ x86-അധിഷ്ഠിത സിസ്റ്റങ്ങൾക്കും ബാധകമാണ്). ചില ഇവന്റ് സംഭവിക്കുമ്പോൾ (ഒരു പുതിയ ടൈമർ ടിക്ക്, ചില ഉപകരണത്തിലെ ചില പ്രവർത്തനം, പിശകുകൾ - പൂജ്യം കൊണ്ട് വിഭജനം മുതലായവ), ഒരു തടസ്സം സൃഷ്ടിക്കപ്പെടുന്നു. കോഡിന്റെ സാധാരണ ഒഴുക്കിനെ തടസ്സപ്പെടുത്തുന്നതിനാലാണ് ഒരു തടസ്സത്തിന് അങ്ങനെ പേര് നൽകിയിരിക്കുന്നത്. തടസ്സങ്ങളെ സാധാരണയായി ഹാർഡ്‌വെയർ, സോഫ്റ്റ്‌വെയർ തടസ്സങ്ങൾ എന്നിങ്ങനെ തിരിച്ചിരിക്കുന്നു. സിസ്റ്റവും പെരിഫറൽ ഉപകരണങ്ങളും സൃഷ്ടിക്കുന്ന തടസ്സങ്ങളാണ് ഹാർഡ്‌വെയർ തടസ്സങ്ങൾ. ഒരു ഉപകരണത്തിന് OS കേർണലിന്റെ ശ്രദ്ധ ആകർഷിക്കേണ്ടിവരുമ്പോൾ, അത് (ഉപകരണം) അതിന്റെ ഇന്ററപ്റ്റ് റിക്വസ്റ്റ് ലൈനിൽ (IRQ - Interrupt ReQuest line) ഒരു സിഗ്നൽ സൃഷ്ടിക്കുന്നു. ചില പ്രോസസർ ഇൻപുട്ടുകളിൽ അനുബന്ധ സിഗ്നൽ സൃഷ്ടിക്കപ്പെടുന്നു എന്ന വസ്തുതയിലേക്ക് ഇത് നയിക്കുന്നു, അതിന്റെ അടിസ്ഥാനത്തിൽ ഇൻസ്ട്രക്ഷൻ സ്ട്രീമിന്റെ നിർവ്വഹണത്തെ തടസ്സപ്പെടുത്താനും ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലറിലേക്ക് നിയന്ത്രണം കൈമാറാനും പ്രോസസ്സർ തീരുമാനിക്കുന്നു, ഇത് എന്താണ് സംഭവിച്ചതെന്നും എന്താണ് ചെയ്യേണ്ടതെന്നും ഇതിനകം കണ്ടെത്തുന്നു. ചെയ്തിരിക്കണം. ഹാർഡ്‌വെയർ തടസ്സങ്ങൾ പ്രകൃതിയിൽ അസമന്വിതമാണ്. ഇതിനർത്ഥം ഒരു തടസ്സം എപ്പോൾ വേണമെങ്കിലും സംഭവിക്കാം എന്നാണ്. പെരിഫറൽ ഉപകരണങ്ങൾക്ക് പുറമേ, പ്രോസസ്സറിന് തന്നെ തടസ്സങ്ങൾ സൃഷ്ടിക്കാൻ കഴിയും (അല്ലെങ്കിൽ, കൂടുതൽ കൃത്യമായി, ഹാർഡ്‌വെയർ ഒഴിവാക്കലുകൾ - ഹാർഡ്‌വെയർ ഒഴിവാക്കലുകൾ - ഉദാഹരണത്തിന്, ഇതിനകം സൂചിപ്പിച്ച വിഭജനം പൂജ്യം). അസാധാരണമായ ഒരു സാഹചര്യം സംഭവിച്ചുവെന്ന് OS-നെ അറിയിക്കുന്നതിനാണ് ഇത് ചെയ്യുന്നത്, അങ്ങനെ ഒരു സാഹചര്യം ഉണ്ടായാൽ OS-ന് ചില നടപടികളെടുക്കാൻ കഴിയും. ഇന്ററപ്റ്റ് പ്രോസസ്സ് ചെയ്ത ശേഷം, തടസ്സപ്പെട്ട പ്രോഗ്രാം എക്സിക്യൂട്ട് ചെയ്യുന്നതിലേക്ക് പ്രൊസസർ മടങ്ങുന്നു. ഒരു ഉപയോക്തൃ ആപ്ലിക്കേഷൻ വഴി തടസ്സം ആരംഭിക്കാൻ കഴിയും. ഇത്തരത്തിലുള്ള തടസ്സത്തെ സോഫ്‌റ്റ്‌വെയർ തടസ്സം എന്ന് വിളിക്കുന്നു. സോഫ്റ്റ്‌വെയർ തടസ്സങ്ങൾ, ഹാർഡ്‌വെയറിൽ നിന്ന് വ്യത്യസ്തമായി, സിൻക്രണസ് ആണ്. അതായത്, ഒരു ഇന്ററപ്റ്റ് വിളിക്കുമ്പോൾ, ഇന്ററപ്റ്റ് സർവീസ് ചെയ്യുന്നതുവരെ അതിനെ വിളിച്ച കോഡ് താൽക്കാലികമായി നിർത്തുന്നു. ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലറിൽ നിന്ന് പുറത്തുകടക്കുമ്പോൾ, അത് സ്റ്റാക്കിൽ നേരത്തെ സംഭരിച്ചിരിക്കുന്ന ഏറ്റവും ദൂരെയുള്ള വിലാസത്തിലേക്ക് (ഇന്ററപ്റ്റ് വിളിക്കുമ്പോൾ), ഇന്ററപ്റ്റ് കോളിംഗ് നിർദ്ദേശത്തിന് (int) ശേഷമുള്ള അടുത്ത നിർദ്ദേശത്തിലേക്ക് മടങ്ങുന്നു. ഒരു റസിഡന്റ് (സ്ഥിരമായി മെമ്മറിയിൽ സ്ഥിതിചെയ്യുന്ന) കോഡിന്റെ ഒരു ഭാഗമാണ് ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലർ. ചട്ടം പോലെ, ഇതൊരു ചെറിയ പ്രോഗ്രാമാണ്. എന്നിരുന്നാലും, നമ്മൾ ലിനക്സ് കേർണലിനെക്കുറിച്ചാണ് സംസാരിക്കുന്നതെങ്കിൽ, അവിടെയുള്ള ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലർ എല്ലായ്പ്പോഴും അത്ര ചെറുതായിരിക്കില്ല. ഒരു വെക്റ്റർ ഉപയോഗിച്ചാണ് ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലർ നിർവചിച്ചിരിക്കുന്നത്. ഒരു വെക്റ്റർ എന്നത് ഒരു നിശ്ചിത സൂചികയിൽ തടസ്സങ്ങൾ കൈകാര്യം ചെയ്യേണ്ട കോഡിന്റെ ആരംഭത്തിന്റെ വിലാസം (സെഗ്‌മെന്റും ഓഫ്‌സെറ്റും) അല്ലാതെ മറ്റൊന്നുമല്ല. ഇന്ററപ്റ്റുകൾ ഉപയോഗിച്ച് പ്രവർത്തിക്കുന്നത് യഥാർത്ഥ (റിയൽ മോഡ്), പരിരക്ഷിത (സംരക്ഷിത മോഡ്) പ്രോസസ്സർ ഓപ്പറേറ്റിംഗ് മോഡുകളിൽ കാര്യമായ വ്യത്യാസമുണ്ട് (ഇനി മുതൽ ഞങ്ങൾ ഉദ്ദേശിക്കുന്നത് ഇന്റൽ പ്രോസസ്സറുകളും അവയുമായി പൊരുത്തപ്പെടുന്നവയുമാണ് എന്ന് ഞാൻ നിങ്ങളെ ഓർമ്മിപ്പിക്കട്ടെ). യഥാർത്ഥ (സുരക്ഷിതമല്ലാത്ത) പ്രോസസ്സർ പ്രവർത്തനരീതിയിൽ, ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലറുകൾ നിർണ്ണയിക്കുന്നത് അവയുടെ വെക്‌റ്ററുകളാണ്, അവ എപ്പോഴും മെമ്മറിയുടെ തുടക്കത്തിൽ സൂക്ഷിക്കുന്നു; വെക്‌റ്റർ പട്ടികയിൽ നിന്ന് ആവശ്യമായ വിലാസം ഇൻഡെക്‌സ് പ്രകാരം തിരഞ്ഞെടുക്കുന്നു, അത് ഇന്ററപ്റ്റ് നമ്പർ കൂടിയാണ്. ഒരു നിശ്ചിത സൂചിക ഉപയോഗിച്ച് വെക്‌ടറിനെ പുനരാലേഖനം ചെയ്യുന്നതിലൂടെ, നിങ്ങളുടെ സ്വന്തം ഹാൻഡ്‌ലറെ ഇന്ററപ്റ്റിലേക്ക് നിയോഗിക്കാവുന്നതാണ്.

സംരക്ഷിത മോഡിൽ, വെക്റ്റർ ടേബിൾ ഉപയോഗിച്ച് ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലറുകൾ (ഗേറ്റുകൾ, ഗേറ്റുകൾ അല്ലെങ്കിൽ ഗേറ്റുകൾ) ഇനി നിർവചിക്കില്ല. ഈ പട്ടികയ്ക്ക് പകരം, ഒരു ഗേറ്റ് ടേബിൾ അല്ലെങ്കിൽ, കൂടുതൽ ശരിയായി, ഒരു ഇന്ററപ്റ്റ് ടേബിൾ - IDT (ഇന്ററപ്റ്റ് ഡിസ്ക്രിപ്റ്റേഴ്സ് ടേബിൾ) ഉപയോഗിക്കുന്നു. ഈ ടേബിൾ കെർണൽ ആണ് ജനറേറ്റ് ചെയ്യുന്നത്, അതിന്റെ വിലാസം പ്രോസസർ idtr രജിസ്റ്ററിൽ സംഭരിച്ചിരിക്കുന്നു. ഈ രജിസ്റ്റർ നേരിട്ട് ആക്സസ് ചെയ്യാൻ കഴിയില്ല. ലിഡ് / സിഡ് നിർദ്ദേശങ്ങൾ ഉപയോഗിച്ച് മാത്രമേ ഇത് ഉപയോഗിച്ച് പ്രവർത്തിക്കാൻ കഴിയൂ. അവയിൽ ആദ്യത്തേത് (lidt) idtr രജിസ്റ്ററിനെ ഓപ്പറണ്ടിൽ വ്യക്തമാക്കിയ മൂല്യം ലോഡുചെയ്യുന്നു, ഇത് ഇന്ററപ്റ്റ് ഡിസ്ക്രിപ്റ്റർ ടേബിളിന്റെ അടിസ്ഥാന വിലാസമാണ്, രണ്ടാമത്തേത് (sidt) idtr-ൽ സ്ഥിതിചെയ്യുന്ന പട്ടിക വിലാസം നിർദ്ദിഷ്ട ഓപ്പറണ്ടിലേക്ക് സംഭരിക്കുന്നു. ഒരു സെലക്ടർ ഉപയോഗിച്ച് ഒരു സെഗ്‌മെന്റിനെക്കുറിച്ചുള്ള വിവരങ്ങൾ ഡിസ്‌ക്രിപ്‌റ്റർ ടേബിളിൽ നിന്ന് വീണ്ടെടുക്കുന്നതുപോലെ, പരിരക്ഷിത മോഡിൽ ഒരു തടസ്സം നൽകുന്ന ഒരു സെഗ്‌മെന്റ് ഡിസ്‌ക്രിപ്‌റ്ററും വീണ്ടെടുക്കുന്നു. CPU i80286 (286 ഒരു 16-ബിറ്റ് പ്രോസസർ ആയിരുന്നതിനാൽ മാത്രം ഇപ്പോൾ അവതരിപ്പിച്ചിരിക്കുന്ന രൂപത്തിലല്ല - അതിനാൽ Linux ഈ പ്രോസസറുകളിൽ പ്രവർത്തിക്കാൻ കഴിയില്ല) i80386-ൽ ആരംഭിക്കുന്ന Intel പ്രോസസറുകൾ മെമ്മറി സംരക്ഷണത്തെ പിന്തുണയ്ക്കുന്നു. പ്രോസസർ തന്നെ ആവശ്യമായ എല്ലാ തിരഞ്ഞെടുപ്പുകളും നടത്തുന്നു, അതിനാൽ, സംരക്ഷിത മോഡിന്റെ എല്ലാ സങ്കീർണതകളിലേക്കും ഞങ്ങൾ ആഴത്തിൽ പരിശോധിക്കില്ല (അതായത്, ലിനക്സ് പരിരക്ഷിത മോഡിൽ പ്രവർത്തിക്കുന്നു). നിർഭാഗ്യവശാൽ, സംരക്ഷിത മോഡിൽ തടസ്സങ്ങൾ കൈകാര്യം ചെയ്യുന്നതിനുള്ള സംവിധാനത്തിൽ ദീർഘനേരം താമസിക്കാൻ സമയമോ കഴിവുകളോ ഞങ്ങളെ അനുവദിക്കുന്നില്ല. അതെ, ഈ ലേഖനം എഴുതുമ്പോൾ ഇതായിരുന്നില്ല ലക്ഷ്യം. x86 ഫാമിലി പ്രൊസസറുകളുടെ പ്രവർത്തനവുമായി ബന്ധപ്പെട്ട് ഇവിടെ നൽകിയിരിക്കുന്ന എല്ലാ വിവരങ്ങളും ഉപരിപ്ലവമാണ്, കൂടാതെ കേർണൽ സിസ്റ്റം കോളുകൾ എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്നതിന്റെ മെക്കാനിസം കുറച്ചുകൂടി നന്നായി മനസ്സിലാക്കാൻ സഹായിക്കുന്നതിന് മാത്രമാണ് നൽകിയിരിക്കുന്നത്. ചില കാര്യങ്ങൾ കേർണൽ കോഡിൽ നിന്ന് നേരിട്ട് പഠിക്കാൻ കഴിയും, എന്നിരുന്നാലും എന്താണ് സംഭവിക്കുന്നതെന്ന് പൂർണ്ണമായി മനസ്സിലാക്കാൻ, സംരക്ഷിത മോഡിന്റെ തത്വങ്ങൾ സ്വയം പരിചയപ്പെടുത്തുന്നത് ഇപ്പോഴും ഉചിതമാണ്. IDT ആരംഭിക്കുന്ന (എന്നാൽ സജ്ജീകരിച്ചിട്ടില്ല!) കോഡിന്റെ വിഭാഗം arch/i386/kernel/head.S-ൽ സ്ഥിതി ചെയ്യുന്നു: /* * setup_idt * * 256 എൻട്രികളുള്ള ഒരു idt സജ്ജീകരിക്കുന്നു, *ignign_int, interrupt gates. ഇത് യഥാർത്ഥത്തിൽ * idt ലോഡ് ചെയ്യുന്നില്ല - പേജിംഗ് പ്രവർത്തനക്ഷമമാക്കിയതിന് ശേഷം മാത്രമേ ഇത് ചെയ്യാൻ കഴിയൂ * കൂടാതെ കേർണൽ PAGE_OFFSET-ലേക്ക് നീക്കി. എല്ലാം ശരിയാണെന്ന് ഞങ്ങൾക്ക് താരതമ്യേന * ഉറപ്പുണ്ടായിരിക്കുമ്പോൾ തടസ്സങ്ങൾ * മറ്റെവിടെയെങ്കിലും പ്രവർത്തനക്ഷമമാക്കും. * * മുന്നറിയിപ്പ്: %esi ഈ ഫംഗ്‌ഷനിലുടനീളം തത്സമയമാണ്. */ 1.setup_idt: 2. leaignor_int,%edx 3. movl $(__KERNEL_CS<< 16),%eax 4. movw %dx,%ax /* selector = 0x0010 = cs */ 5. movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 6. lea idt_table,%edi 7. mov $256,%ecx 8.rp_sidt: 9. movl %eax,(%edi) 10. movl %edx,4(%edi) 11. addl $8,%edi 12. dec %ecx 13. jne rp_sidt 14..macro set_early_handler handler,trapno 15. lea \handler,%edx 16. movl $(__KERNEL_CS << 16),%eax 17. movw %dx,%ax 18. movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 19. lea idt_table,%edi 20. movl %eax,8*\trapno(%edi) 21. movl %edx,8*\trapno+4(%edi) 22..endm 23. set_early_handler handler=early_divide_err,trapno=0 24. set_early_handler handler=early_illegal_opcode,trapno=6 25. set_early_handler handler=early_protection_fault,trapno=13 26. set_early_handler handler=early_page_fault,trapno=14 28. ret കോഡിലെ ചില കുറിപ്പുകൾ: മുകളിലെ കോഡ് AT&T അസംബ്ലറിന്റെ ഒരു പതിപ്പിലാണ് എഴുതിയിരിക്കുന്നത്, അതിനാൽ അതിന്റെ സാധാരണ ഇന്റൽ നൊട്ടേഷനിൽ അസംബ്ലറിനെ കുറിച്ചുള്ള നിങ്ങളുടെ അറിവ് ആശയക്കുഴപ്പത്തിലാക്കാം. ഏറ്റവും പ്രധാനപ്പെട്ട വ്യത്യാസം ഓപ്പറണ്ടുകളുടെ ക്രമമാണ്. ഇന്റൽ നൊട്ടേഷനായി ഓർഡർ നിർവചിച്ചിട്ടുണ്ടെങ്കിൽ - "അക്യുമുലേറ്റർ"< "источник", то для ассемблера AT&T порядок прямой. Регистры процессора, как правило, должны иметь префикс "%", непосредственные значения (константы) префиксируются символом доллара "$". Синтаксис AT&T традиционно используется в Un*x-системах.

മുകളിലെ ഉദാഹരണത്തിൽ, 2-4 വരികൾ എല്ലാ തടസ്സങ്ങൾക്കും ഡിഫോൾട്ട് ഇന്ററപ്റ്റ് ഹാൻഡ്‌ലറിന്റെ വിലാസം സജ്ജമാക്കുന്നു. ഡിഫോൾട്ട് ഹാൻഡ്‌ലർ ഇഗ്നോർ_ഇന്റ് ഫംഗ്‌ഷനാണ്, അത് ഒന്നും ചെയ്യുന്നില്ല. ഈ ഘട്ടത്തിലെ എല്ലാ തടസ്സങ്ങളുടെയും ശരിയായ പ്രോസസ്സിംഗിന് അത്തരമൊരു സ്റ്റബിന്റെ സാന്നിധ്യം ആവശ്യമാണ്, കാരണം ഇതുവരെ മറ്റുള്ളവ ഒന്നുമില്ല (എന്നിരുന്നാലും, കോഡിൽ കെണികൾ അൽപ്പം താഴെയായി ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ട് - കെണികൾക്കായി, ഇന്റൽ ആർക്കിടെക്ചർ മാനുവൽ റഫറൻസ് അല്ലെങ്കിൽ മറ്റെന്തെങ്കിലും കാണുക. അതുപോലെ, കെണിയിൽ സ്പർശിക്കുന്നതിനെക്കുറിച്ച് ഞങ്ങൾ ഇവിടെ ചർച്ച ചെയ്യുന്നില്ല). ലൈൻ 5 ഗേറ്റ് തരം സജ്ജമാക്കുന്നു. ലൈൻ 6-ൽ ഞങ്ങൾ ഇൻഡക്‌സ് രജിസ്‌റ്റർ ലോഡുചെയ്യുന്നത് നമ്മുടെ IDT ടേബിളിന്റെ വിലാസത്തിലാണ്. പട്ടികയിൽ 255 റെക്കോർഡുകൾ അടങ്ങിയിരിക്കണം, ഓരോന്നിനും 8 ബൈറ്റുകൾ. 8-13 വരികളിൽ, eax, edx രജിസ്റ്ററുകളിൽ നേരത്തെ സജ്ജമാക്കിയ അതേ മൂല്യങ്ങൾ ഉപയോഗിച്ച് ഞങ്ങൾ മുഴുവൻ പട്ടികയും പൂരിപ്പിക്കുന്നു - അതായത്, ഇഗ്നോർ_ഇന്റ് ഹാൻഡ്‌ലറിനെ പരാമർശിക്കുന്ന ഇന്ററപ്റ്റ് ഗേറ്റാണിത്. കെണികൾ സജ്ജീകരിക്കുന്നതിനുള്ള ഒരു മാക്രോ ഞങ്ങൾ ചുവടെ നിർവചിക്കുന്നു - വരികൾ 14-22. 23-26 വരികളിൽ, മുകളിലുള്ള മാക്രോ ഉപയോഗിച്ച്, ഇനിപ്പറയുന്ന ഒഴിവാക്കലുകൾക്കായി ഞങ്ങൾ ട്രാപ്പുകൾ സജ്ജമാക്കി: നേരത്തെ_ഡിവിഡ്_എർർ - പൂജ്യത്താൽ വിഭജിക്കുക (0), നേരത്തെ_ഇല്ലെഗൽ_ഓപ്‌കോഡ് - അജ്ഞാത പ്രോസസ്സർ നിർദ്ദേശം (6), നേരത്തെ_പ്രൊട്ടക്ഷൻ_ഫാൾട്ട് - മെമ്മറി സംരക്ഷണ പരാജയം (13), നേരത്തെ_പേജ്_ഫാൾട്ട് - പേജ് വിവർത്തനം പരാജയം (14) ഒരു അസാധാരണ സാഹചര്യം ഉണ്ടാകുമ്പോൾ ഉണ്ടാകുന്ന "തടസ്സങ്ങളുടെ" സംഖ്യകൾ പരാൻതീസിസിൽ നൽകിയിരിക്കുന്നു. arch/i386/kernel/head.S-ൽ പ്രോസസർ തരം പരിശോധിക്കുന്നതിന് മുമ്പ്, setup_idt എന്ന് വിളിച്ച് IDT പട്ടിക സജ്ജീകരിച്ചിരിക്കുന്നു: /* * സിസ്റ്റം 32-ബിറ്റ് സജ്ജീകരണം ആരംഭിക്കുക. "യഥാർത്ഥ" പ്രവർത്തനങ്ങൾക്കായി 16-ബിറ്റ് മോഡിൽ * ചെയ്ത ചില കാര്യങ്ങൾ ഞങ്ങൾ വീണ്ടും ചെയ്യേണ്ടതുണ്ട്. */ 1. call setup_idt ... 2. call check_x87 3. lgdt early_gdt_descr 4. lidt idt_descr(കോ) പ്രോസസറിന്റെ തരം കണ്ടെത്തി 3, 4 വരികളിലെ എല്ലാ തയ്യാറെടുപ്പ് ഘട്ടങ്ങളും നടപ്പിലാക്കിയ ശേഷം, ഞങ്ങൾ GDT, IDT പട്ടികകൾ ലോഡ് ചെയ്യുന്നു, അത് കേർണലിന്റെ ആദ്യ ഘട്ടങ്ങളിൽ ഉപയോഗിക്കും.

സിസ്റ്റം കോളുകളും int 0x80.

തടസ്സങ്ങളിൽ നിന്ന്, നമുക്ക് സിസ്റ്റം കോളുകളിലേക്ക് മടങ്ങാം. അതിനാൽ, ചില സേവനം അഭ്യർത്ഥിക്കുന്ന ഒരു പ്രോസസ്സ് നൽകുന്നതിന് എന്താണ് വേണ്ടത്? ആരംഭിക്കുന്നതിന്, നിങ്ങൾ റിംഗ് 3 (പ്രിവിലേജ് ലെവൽ CPL=3) ൽ നിന്ന് ഏറ്റവും പ്രിവിലേജ്ഡ് ലെവൽ 0 ലേക്ക് (റിംഗ് 0, CPL=0) മാറേണ്ടതുണ്ട്, കാരണം ഏറ്റവും ഉയർന്ന പ്രത്യേകാവകാശങ്ങളുള്ള സെഗ്‌മെന്റിലാണ് കേർണൽ കോഡ് സ്ഥിതി ചെയ്യുന്നത്. കൂടാതെ, പ്രോസസ്സിന് സേവനം നൽകുന്ന ഒരു ഹാൻഡ്‌ലർ കോഡ് ആവശ്യമാണ്. ഇതിന് തന്നെയാണ് 0x80 ഗേറ്റ്‌വേ ഉപയോഗിക്കുന്നത്. കുറച്ച് സിസ്റ്റം കോളുകൾ ഉണ്ടെങ്കിലും, അവയെല്ലാം ഒരൊറ്റ എൻട്രി പോയിന്റ് ഉപയോഗിക്കുന്നു - int 0x80. arch/i386/kernel/traps.c::trap_init(): എന്ന ഫംഗ്‌ഷൻ വിളിക്കുമ്പോൾ ഹാൻഡ്‌ലർ തന്നെ ഇൻസ്റ്റാൾ ചെയ്യപ്പെടുന്നു. void __init trap_init(void) (... set_system_gate(SYSCALL_VECTOR,&system_call); ... ) trap_init() ലെ ഈ വരിയിൽ ഞങ്ങൾക്ക് ഏറ്റവും താൽപ്പര്യമുണ്ട്. മുകളിലുള്ള അതേ ഫയലിൽ നിങ്ങൾക്ക് set_system_gate() ഫംഗ്‌ഷന്റെ കോഡ് നോക്കാം: സ്റ്റാറ്റിക് ശൂന്യത __init set_system_gate (അൺ സൈൻ ചെയ്യാത്ത int n, void * addr) ( _set_gate(n, DESCTYPE_TRAP | DESCTYPE_DPL3, addr, __KERNEL_CS); )തടസ്സപ്പെടുത്തുന്നതിനുള്ള ഗേറ്റ് 0x80 (അതായത്, ഈ മൂല്യം നിർവചിച്ചിരിക്കുന്നത് SYSCALL_VECTOR മാക്രോ - നിങ്ങൾക്ക് എന്റെ വാക്ക് എടുക്കാം :)) എന്നത് ഇവിടെ നിങ്ങൾക്ക് കാണാം. യൂസർ സ്‌പെയ്‌സിൽ നിന്ന് വിളിക്കുമ്പോൾ ഈ തടസ്സം പിടിക്കപ്പെടും. റിംഗ് 3 ൽ നിന്ന് റിംഗ് 0 ലേക്ക് മാറുന്നതിലെ പ്രശ്നം അതായത്. പരിഹരിച്ചു. ഉൾപ്പെടുത്തിയ/asm-i386/desc.h ഹെഡർ ഫയലിൽ _set_gate() ഫംഗ്‌ഷൻ നിർവചിച്ചിരിക്കുന്നു. പ്രത്യേകിച്ചും ജിജ്ഞാസയുള്ളവർക്കായി, ദൈർഘ്യമേറിയ വിശദീകരണങ്ങളില്ലാതെ കോഡ് ചുവടെ നൽകിയിരിക്കുന്നു, എന്നിരുന്നാലും: സ്റ്റാറ്റിക് ഇൻലൈൻ ശൂന്യമായ _set_gate (int ഗേറ്റ്, ഒപ്പിടാത്ത int തരം, void * addr, unsigned short seg) ( __u32 a, b; pack_gate(&a, &b, (signed long)addr, seg, type, 0); write_idt_entry(idt_table, gate , a, b);)നമുക്ക് trap_init() ഫംഗ്ഷനിലേക്ക് മടങ്ങാം. init/main.c-ലെ start_kernel() ഫംഗ്ഷനിൽ നിന്നാണ് ഇതിനെ വിളിക്കുന്നത്. നിങ്ങൾ trap_init() കോഡ് നോക്കുകയാണെങ്കിൽ, ഈ ഫംഗ്‌ഷൻ IDT ടേബിളിന്റെ ചില മൂല്യങ്ങൾ വീണ്ടും എഴുതുന്നതായി നിങ്ങൾക്ക് കാണാൻ കഴിയും - കേർണൽ ഇനീഷ്യലൈസേഷന്റെ പ്രാരംഭ ഘട്ടത്തിൽ ഉപയോഗിച്ചിരുന്ന ഹാൻഡ്‌ലറുകൾ (early_page_fault, early_divide_err, early_illegal_opcode, early_protection_fault) മാറ്റിസ്ഥാപിക്കപ്പെടുന്നു. പ്രോസസ്സ് കേർണൽ വർക്കിന്റെ സമയത്ത് ഇതിനകം ഉപയോഗിക്കുന്നവയ്‌ക്കൊപ്പം. അതിനാൽ, ഞങ്ങൾ മിക്കവാറും പോയിന്റിലെത്തി, എല്ലാ സിസ്റ്റം കോളുകളും ഒരേപോലെ പ്രോസസ്സ് ചെയ്യപ്പെടുന്നുവെന്ന് ഇതിനകം തന്നെ അറിയാം - int 0x80 ഗേറ്റ്‌വേ വഴി. arch/i386/kernel/traps.c::trap_init() എന്ന കോഡിന്റെ മുകളിലെ ഭാഗത്തിൽ നിന്ന് വീണ്ടും കാണാൻ കഴിയുന്നതുപോലെ, system_call() ഫംഗ്‌ഷൻ int 0x80-നുള്ള ഒരു ഹാൻഡ്‌ലറായി ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ട്.

system_call().

system_call() ഫംഗ്‌ഷനുള്ള കോഡ് arch/i386/kernel/entry.S എന്ന ഫയലിലാണ് സ്ഥിതി ചെയ്യുന്നത്, ഇതുപോലെ കാണപ്പെടുന്നു: # സിസ്റ്റം കോൾ ഹാൻഡ്ലർ സ്റ്റെബ് എൻട്രി (സിസ്റ്റം_കോൾ) റിംഗ് 0_ഇന്_ഫ്റ്റ്_ഫ്രെയിം # യാത്രാമധ്യേ keftels cfi_ade_info (% ebp) , അതിനാൽ ഇതിന് testw ആവശ്യമാണ്, കൂടാതെ testb */ testw അല്ല $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) jnz syscall_trace_entry %syscall_trace_entry cmsybayss_jnz) scall_call: call *sys_call_table (,%eax, 4) movl %eax,PT_EAX(%esp) # റിട്ടേൺ മൂല്യം സംഭരിക്കുക ...കോഡ് പൂർണ്ണമായി കാണിച്ചിട്ടില്ല. നിങ്ങൾക്ക് കാണാനാകുന്നതുപോലെ, ആദ്യം system_call() റിംഗ് 0-ൽ പ്രവർത്തിക്കാൻ സ്റ്റാക്ക് കോൺഫിഗർ ചെയ്യുന്നു, EAx വഴി അതിലേക്ക് കൈമാറിയ മൂല്യം സ്റ്റാക്കിലേക്ക് സംരക്ഷിക്കുന്നു, എല്ലാ രജിസ്റ്ററുകളും സ്റ്റാക്കിലേക്ക് സംരക്ഷിക്കുന്നു, കോളിംഗ് ത്രെഡിനെക്കുറിച്ചുള്ള ഡാറ്റ സ്വീകരിച്ച് പാസ്സാക്കിയ മൂല്യം പരിശോധിക്കുന്നു, സിസ്റ്റം കോൾ നമ്പർ, സിസ്റ്റം കോൾ ടേബിളിന്റെ പരിധിക്കപ്പുറമല്ല, തുടർന്ന് അവസാനം, ഒരു ആർഗ്യുമെന്റായി eax-ലേക്ക് അയച്ച മൂല്യം ഉപയോഗിച്ച്, system_call() യഥാർത്ഥ സിസ്റ്റം ഔട്ട്‌പുട്ട് ഹാൻഡ്‌ലറിലേക്ക് ചാടുന്നു കടൽ യഥാർത്ഥ മോഡിൽ നിന്നുള്ള ഇന്ററപ്റ്റ് വെക്റ്ററുകളുടെ പഴയ നല്ല പട്ടിക ഇപ്പോൾ ഓർക്കുക. നിങ്ങളെ ഒന്നും ഓർമ്മിപ്പിക്കുന്നില്ലേ? വാസ്തവത്തിൽ, തീർച്ചയായും, എല്ലാം കുറച്ചുകൂടി സങ്കീർണ്ണമാണ്. പ്രത്യേകിച്ചും, സിസ്റ്റം കോൾ, കേർണൽ സ്റ്റാക്കിൽ നിന്ന് ഉപയോക്തൃ സ്റ്റാക്കിലേക്ക് ഫലങ്ങൾ പകർത്തുകയും ഒരു റിട്ടേൺ കോഡ് കൈമാറുകയും മറ്റ് ചില കാര്യങ്ങൾ ചെയ്യുകയും വേണം. eax-ൽ വ്യക്തമാക്കിയിട്ടുള്ള ആർഗ്യുമെന്റ് നിലവിലുള്ള ഒരു സിസ്റ്റം കോളിനെ പരാമർശിക്കാത്ത സാഹചര്യത്തിൽ (മൂല്യം പരിധിക്ക് പുറത്താണ്), syscall_badsys ലേബലിലേക്കുള്ള ഒരു മാറ്റം സംഭവിക്കുന്നു. ഇവിടെ, EAx മൂല്യം സ്ഥിതിചെയ്യേണ്ട ഓഫ്‌സെറ്റിലെ സ്റ്റാക്കിലേക്ക് -ENOSYS എന്ന മൂല്യം തള്ളുന്നു - സിസ്റ്റം കോൾ നടപ്പിലാക്കിയിട്ടില്ല. ഇത് system_call() ന്റെ നിർവ്വഹണം പൂർത്തിയാക്കുന്നു.

സിസ്റ്റം കോൾ ടേബിൾ arch/i386/kernel/syscall_table.S എന്ന ഫയലിലാണ് സ്ഥിതി ചെയ്യുന്നത് കൂടാതെ വളരെ ലളിതമായ ഒരു രൂപവുമുണ്ട്: ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - പഴയ "സെറ്റപ്പ്()" സിസ്റ്റം കോൾ, പുനരാരംഭിക്കുന്നതിന് ഉപയോഗിച്ചു */ .long sys_exit .long sys_fork .long sys_read .long sys_write .long .long sys_open sys_waitpid .long sys_creat ...മറ്റൊരു വിധത്തിൽ പറഞ്ഞാൽ, മുഴുവൻ പട്ടികയും ഈ ഫംഗ്‌ഷനുകൾ നൽകുന്ന സിസ്റ്റം കോൾ നമ്പറുകളുടെ ക്രമത്തിൽ ക്രമീകരിച്ചിരിക്കുന്ന ഫംഗ്‌ഷൻ വിലാസങ്ങളുടെ ഒരു നിരയല്ലാതെ മറ്റൊന്നുമല്ല. ഇരട്ട മെഷീൻ പദങ്ങളുടെ (അല്ലെങ്കിൽ 32-ബിറ്റ് വാക്കുകൾ - നിങ്ങൾക്ക് ഇഷ്ടമുള്ളത്) ഒരു സാധാരണ നിരയാണ് പട്ടിക. സിസ്റ്റം കോളുകൾ സർവീസ് ചെയ്യുന്ന ചില ഫംഗ്‌ഷനുകളുടെ കോഡ് പ്ലാറ്റ്‌ഫോം-ആശ്രിത ഭാഗത്താണ് - arch/i386/kernel/sys_i386.c, പ്ലാറ്റ്‌ഫോം-ഇന്ഡിപെൻഡന്റ് ഭാഗം - kernel/sys.c.

സിസ്റ്റം കോളുകളുടെയും ഗേറ്റ് 0x80യുടെയും കാര്യമാണിത്.

ലിനക്സിൽ സിസ്റ്റം കോളുകൾ കൈകാര്യം ചെയ്യുന്നതിനുള്ള പുതിയ സംവിധാനം. sysenter/sysexit.

സൂചിപ്പിച്ചതുപോലെ, ഗേറ്റ് 0x80 അടിസ്ഥാനമാക്കിയുള്ള പരമ്പരാഗത സിസ്റ്റം കോളുകൾ പ്രോസസ്സ് ചെയ്യുന്നത് ഇന്റൽ പെന്റിയം 4 പ്രോസസറുകളിലെ പ്രകടനം നഷ്‌ടപ്പെടുത്തുന്നതിന് കാരണമാകുമെന്ന് പെട്ടെന്ന് വ്യക്തമായി. പെന്റിയം II പ്രൊസസറും അതിലും ഉയർന്നതുമായ മെഷീനുകളിൽ കേർണൽ പെർഫോമൻസ് വർധിപ്പിക്കാൻ നിർദ്ദേശങ്ങളും രൂപകൽപ്പനയും (പെന്റിയം II+ ഉപയോഗിച്ചാണ് ഇന്റൽ പ്രോസസറുകൾ സൂചിപ്പിച്ച sysenter/sysexit നിർദ്ദേശങ്ങൾ പിന്തുണയ്ക്കുന്നത്). പുതിയ സംവിധാനത്തിന്റെ സാരം എന്താണ്? വിചിത്രമെന്നു പറയട്ടെ, പക്ഷേ സാരാംശം അതേപടി തുടരുന്നു. വധശിക്ഷ മാറ്റി. ഇന്റൽ ഡോക്യുമെന്റേഷൻ അനുസരിച്ച്, സിസെന്റർ നിർദ്ദേശം "ഫാസ്റ്റ് സിസ്റ്റം കോൾ" മെക്കാനിസത്തിന്റെ ഭാഗമാണ്. പ്രത്യേകിച്ചും, ഒരു പ്രത്യേകാവകാശ തലത്തിൽ നിന്ന് മറ്റൊന്നിലേക്ക് വേഗത്തിൽ മാറുന്നതിന് ഈ നിർദ്ദേശം ഒപ്റ്റിമൈസ് ചെയ്തിരിക്കുന്നു. കൂടുതൽ കൃത്യമായി പറഞ്ഞാൽ, ഇത് റിംഗ് 0 (റിംഗ് 0, CPL=0) ലേക്ക് പരിവർത്തനം വേഗത്തിലാക്കുന്നു. ഈ സാഹചര്യത്തിൽ, സിസെന്റർ നിർദ്ദേശം ഉപയോഗിക്കുന്നതിന് ഓപ്പറേറ്റിംഗ് സിസ്റ്റം പ്രോസസ്സർ തയ്യാറാക്കണം. OS കേർണൽ ലോഡുചെയ്യുമ്പോഴും സമാരംഭിക്കുമ്പോഴും ഈ ക്രമീകരണം ഒരിക്കൽ നടപ്പിലാക്കുന്നു. വിളിക്കുമ്പോൾ, OS മുമ്പ് സജ്ജമാക്കിയ മെഷീൻ-നിർദ്ദിഷ്ട രജിസ്റ്ററുകൾക്ക് അനുസൃതമായി സിസെന്റർ പ്രോസസ്സർ രജിസ്റ്ററുകൾ സജ്ജമാക്കുന്നു. പ്രത്യേകിച്ചും, സെഗ്മെന്റ് രജിസ്റ്ററും ഇൻസ്ട്രക്ഷൻ പോയിന്റർ രജിസ്റ്ററും - cs:eip, അതുപോലെ സ്റ്റാക്ക് സെഗ്മെന്റ്, സ്റ്റാക്ക് ടോപ്പ് പോയിന്റർ - ss, esp എന്നിവ ഇൻസ്റ്റാൾ ചെയ്തിട്ടുണ്ട്. ഒരു പുതിയ കോഡ് സെഗ്‌മെന്റിലേക്കുള്ള പരിവർത്തനവും ഓഫ്‌സെറ്റും റിംഗ് 3 മുതൽ 0 വരെ നടത്തുന്നു.

sysexit നിർദ്ദേശം വിപരീതമാണ് ചെയ്യുന്നത്. ഇത് പ്രിവിലേജ് ലെവൽ 0-ൽ നിന്ന് പ്രിവിലേജ് ലെവൽ 3-ലേക്ക് (CPL=3) പെട്ടെന്ന് മാറ്റം വരുത്തുന്നു. ഈ സാഹചര്യത്തിൽ, കോഡ് സെഗ്‌മെന്റ് രജിസ്റ്റർ പ്രോസസറിന്റെ മെഷീൻ-ആശ്രിത രജിസ്റ്ററിൽ സംഭരിച്ചിരിക്കുന്ന cs സെഗ്‌മെന്റ് മൂല്യം 16 + ആയി സജ്ജീകരിച്ചിരിക്കുന്നു. Eip രജിസ്റ്ററിൽ edx രജിസ്റ്ററിന്റെ ഉള്ളടക്കങ്ങൾ അടങ്ങിയിരിക്കുന്നു. സിസെന്റർ നിർദ്ദേശത്തിന്റെ പ്രവർത്തനത്തിനുള്ള സന്ദർഭം തയ്യാറാക്കുമ്പോൾ പ്രൊസസറിന്റെ മെഷീൻ-ആശ്രിത രജിസ്റ്ററിൽ OS നേരത്തെ നൽകിയ 24-ന്റെ ആകെത്തുകയും cs മൂല്യങ്ങളും ss-ൽ നൽകിയിട്ടുണ്ട്. ecx രജിസ്റ്ററിലെ ഉള്ളടക്കങ്ങൾ esp-ൽ നൽകിയിട്ടുണ്ട്. sysenter/sysexit നിർദ്ദേശങ്ങളുടെ പ്രവർത്തനത്തിന് ആവശ്യമായ മൂല്യങ്ങൾ ഇനിപ്പറയുന്ന വിലാസങ്ങളിൽ സംഭരിച്ചിരിക്കുന്നു:

  1. SYSENTER_CS_MSR 0x174 - സിസ്റ്റം കോൾ ഹാൻഡ്‌ലർ കോഡ് സ്ഥിതിചെയ്യുന്ന സെഗ്‌മെന്റിന്റെ മൂല്യം നൽകിയ കോഡ് സെഗ്‌മെന്റ്.
  2. SYSENTER_ESP_MSR 0x175 - സിസ്റ്റം കോൾ ഹാൻഡ്‌ലറിനായി സ്റ്റാക്കിന്റെ മുകളിലേക്ക് പോയിന്റർ.
  3. SYSENTER_EIP_MSR 0x176 - കോഡ് സെഗ്‌മെന്റിനുള്ളിലെ ഓഫ്‌സെറ്റിലേക്കുള്ള പോയിന്റർ. സിസ്റ്റം കോൾ ഹാൻഡ്‌ലർ കോഡിന്റെ തുടക്കത്തിലേക്ക് പോയിന്റുകൾ.
ഈ വിലാസങ്ങൾ പേരുകളില്ലാത്ത മോഡൽ-ആശ്രിത രജിസ്റ്ററുകളെ സൂചിപ്പിക്കുന്നു. മൂല്യങ്ങൾ wrmsr നിർദ്ദേശം ഉപയോഗിച്ച് മോഡൽ-ആശ്രിത രജിസ്റ്ററുകളിലേക്ക് എഴുതുന്നു, അതേസമയം edx:eax-ൽ യഥാക്രമം 64-ബിറ്റ് മെഷീൻ പദത്തിന്റെ മുൻനിര ഭാഗങ്ങളും താഴ്ന്ന ഭാഗങ്ങളും അടങ്ങിയിരിക്കണം, കൂടാതെ ecx-ൽ എൻട്രി ചെയ്യുന്ന രജിസ്റ്ററിന്റെ വിലാസം അടങ്ങിയിരിക്കണം. നിർമ്മിക്കപ്പെടുന്ന. ലിനക്സിൽ, മോഡൽ-ആശ്രിത രജിസ്റ്ററുകളുടെ വിലാസങ്ങൾ ഹെഡർ ഫയലിൽ ഇനിപ്പറയുന്ന രീതിയിൽ നിർവചിച്ചിരിക്കുന്നു ഉൾപ്പെടുന്നു/asm-i368/msr-index.h (പതിപ്പ് 2.6.22-ന് മുമ്പ്, അവ നിർവചിക്കപ്പെട്ടത് ഹെഡർ ഫയലിലെങ്കിലും ഉൾപ്പെടുന്നു/asm-i386 /msr.h, ലിനക്സ് കേർണൽ 2.6.22-ന്റെ ഉദാഹരണം ഉപയോഗിച്ചാണ് ഞങ്ങൾ സിസ്റ്റം കോൾ മെക്കാനിസം പരിഗണിക്കുന്നതെന്ന് ഞാൻ നിങ്ങളെ ഓർമ്മിപ്പിക്കട്ടെ: MSR_IA32_SYSENTER_CS 0x00000174 നിർവചിക്കുകമോഡൽ-ആശ്രിത രജിസ്റ്ററുകൾ സജ്ജീകരിക്കുന്നതിന് ഉത്തരവാദിത്തമുള്ള കേർണൽ കോഡ് arch/i386/sysenter.c എന്ന ഫയലിൽ സ്ഥിതിചെയ്യുന്നു, ഇത് ഇതുപോലെ കാണപ്പെടുന്നു: 1. void enable_sep_cpu(void) ( 2. int cpu = get_cpu(); 3. struct tss_struct *tss = &per_cpu(init_tss, cpu); 4. എങ്കിൽ (!boot_cpu_has(X86_FEATURE_SEP)_cpu.6 put. 5); തിരികെ; MSR_IA32_SYSENTER_ESP, tss->x86_tss.esp1, 0); 11. wrmsr(MSR_IA32_SYSENTER_EIP, (നീളം ഒപ്പിടാത്തത്) sysenter_entry, 0); 12. put_cpu(); )ഇവിടെ, tss വേരിയബിളിൽ, ടാസ്ക് സ്റ്റേറ്റ് സെഗ്മെന്റിനെ വിവരിക്കുന്ന ഘടനയുടെ വിലാസം നമുക്ക് ലഭിക്കും. x86 ആർക്കിടെക്ചറിനുള്ള ഹാർഡ്‌വെയർ മൾട്ടിടാസ്കിംഗ് മെക്കാനിസത്തിന്റെ ഭാഗമാണ് ടാസ്ക് സന്ദർഭം വിവരിക്കാൻ TSS (ടാസ്ക് സ്റ്റേറ്റ് സെഗ്മെന്റ്) ഉപയോഗിക്കുന്നത്. എന്നിരുന്നാലും, Linux പ്രായോഗികമായി ഹാർഡ്‌വെയർ ടാസ്‌ക് സന്ദർഭ സ്വിച്ചിംഗ് ഉപയോഗിക്കുന്നില്ല. ഇന്റൽ ഡോക്യുമെന്റേഷൻ അനുസരിച്ച്, TSS സെഗ്‌മെന്റിനെ പരാമർശിക്കുന്ന ഒരു ഇന്റർസെഗ്മെന്റ് ജമ്പ് നിർദ്ദേശം (jmp അല്ലെങ്കിൽ കോൾ) നടപ്പിലാക്കുന്നതിലൂടെയോ അല്ലെങ്കിൽ GDT-ൽ (LDT) ഒരു ടാസ്‌ക് ഗേറ്റ് ഡിസ്‌ക്രിപ്‌റ്റർ എക്‌സിക്യൂട്ട് ചെയ്‌തുകൊണ്ടോ മറ്റൊരു ടാസ്‌ക്കിലേക്ക് മാറുന്നത് സാധ്യമാണ്. പ്രോഗ്രാമർക്ക് അദൃശ്യമായ ഒരു പ്രത്യേക പ്രോസസ്സർ രജിസ്റ്റർ - TR (ടാസ്ക് രജിസ്റ്റർ) ടാസ്ക് ഡിസ്ക്രിപ്റ്റർ സെലക്ടർ ഉൾക്കൊള്ളുന്നു. ഈ രജിസ്റ്റർ ലോഡുചെയ്യുന്നത് TR-മായി ബന്ധപ്പെട്ട സോഫ്‌റ്റ്‌വെയർ-അദൃശ്യ അടിത്തറയും പരിധി രജിസ്റ്ററുകളും ലോഡുചെയ്യുന്നു.

ലിനക്സ് ഹാർഡ്‌വെയർ സന്ദർഭ സ്വിച്ചിംഗ് ഉപയോഗിക്കുന്നില്ലെങ്കിലും, സിസ്റ്റത്തിൽ ഇൻസ്റ്റാൾ ചെയ്തിട്ടുള്ള ഓരോ പ്രോസസറിനും ഒരു TSS എൻട്രി അനുവദിക്കാൻ കേർണൽ നിർബന്ധിതരാകുന്നു. കാരണം, പ്രോസസർ യൂസർ മോഡിൽ നിന്ന് കേർണൽ മോഡിലേക്ക് മാറുമ്പോൾ, അത് ടിഎസ്എസിൽ നിന്ന് കേർണൽ സ്റ്റാക്ക് വിലാസം വീണ്ടെടുക്കുന്നു. കൂടാതെ, I/O പോർട്ടുകളിലേക്കുള്ള ആക്‌സസ് നിയന്ത്രിക്കാൻ TSS ആവശ്യമാണ്. TSS-ൽ പോർട്ട് ആക്സസ് അവകാശങ്ങളുടെ ഒരു മാപ്പ് അടങ്ങിയിരിക്കുന്നു. ഈ മാപ്പിനെ അടിസ്ഥാനമാക്കി, ഇൻ/ഔട്ട് നിർദ്ദേശങ്ങൾ ഉപയോഗിച്ച് ഓരോ പ്രക്രിയയ്ക്കും പോർട്ടുകളിലേക്കുള്ള ആക്‌സസ് നിയന്ത്രിക്കുന്നത് സാധ്യമാകും. ഇവിടെ tss->x86_tss.esp1 കേർണൽ സ്റ്റാക്കിലേക്ക് പോയിന്റ് ചെയ്യുന്നു. __KERNEL_CS സ്വാഭാവികമായും കേർണൽ കോഡിന്റെ ഒരു വിഭാഗത്തിലേക്ക് ചൂണ്ടിക്കാണിക്കുന്നു. sysenter_entry() ഫംഗ്‌ഷന്റെ വിലാസമാണ് ഓഫ്‌സെറ്റ്-eip.

sysenter_entry() ഫംഗ്‌ഷൻ arch/i386/kernel/entry.S എന്ന ഫയലിൽ നിർവചിച്ചിരിക്കുന്നത് ഇതുപോലെയാണ്: /* SYSENTER_RETURN vsyscall പേജിലെ "sysenter" നിർദ്ദേശത്തിന് ശേഷം പോയിന്റ് ചെയ്യുന്നു. ചിഹ്നം നിർവചിക്കുന്ന vsyscall-sysentry.S കാണുക. */ # sysenter കോൾ ഹാൻഡ്‌ലർ സ്റ്റബ് ENTRY(sysenter_entry) CFI_STARTPROC സിമ്പിൾ CFI_SIGNAL_FRAME CFI_DEF_CFA esp, 0 CFI_REGISTER esp, ebp movl TSS_sysenter_esp0(%esp : സിസ്കാൽ * irqs അപ്രാപ്‌തമാക്കി, ഇവിടെ പ്രവേശിച്ചതിന് ശേഷം ഞങ്ങൾ അത് പ്രാപ്‌തമാക്കുന്നു: */ ENABLE_INTERRUPTS(CLBR_NONE) pushl $(__USER_DS) CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET ss, 0*/ pushl %ebp_ETFA_ADCFS 0 pushfl CFI_ADJUST_C FA_OFFSET 4 pushl $(__USER_CS) CFI_ADJUST_CFA_OFFSET 4 /*CFI_REL_OFFSET cs, 0*/ /* * നിലവിലെ_thread_info()->sysenter_return സ്റ്റാക്കിലേക്ക് പുഷ് ചെയ്യുക. * ഒരു ചെറിയ ഓഫ്‌സെറ്റ് ഫിക്സ്അപ്പ് ആവശ്യമാണ് - 4*4 എന്നാൽ മുകളിൽ പറഞ്ഞിരിക്കുന്ന 4 വാക്കുകൾ അർത്ഥമാക്കുന്നു; +8 copy_thread's esp0 ക്രമീകരണവുമായി പൊരുത്തപ്പെടുന്നു. */ pushl (TI_sysenter_return-THREAD_SIZE+8+4*4)(%esp) CFI_ADJUST_CFA_OFFSET 4 CFI_REL_OFFSET eip, 0 /* * ഉപയോക്തൃ സ്റ്റാക്കിന്റെ സാധ്യതയുള്ള ആറാമത്തെ ആർഗ്യുമെന്റ് ലോഡുചെയ്യുക. . (%bp ) /* ശ്രദ്ധിക്കുക, _TIF_SECCOMP ബിറ്റ് നമ്പർ 8 ആണ്, അതിനാൽ ഇതിന് testw ആവശ്യമാണ്, കൂടാതെ testb */ testw $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), TIF_SYSCALL_AUDIT), TIF_flags ), %eax jae syscal_ badsys കോൾ *sys_call_table(,%eax,4) movl %eax,PT_EAX(%esp) DISABLE_INTERRUPTS(CLBR_ANY) TRACE_IRQS_OFF movl TI_flags(%ebp), %ecx testw $_TIF_cMALLW അത് നിർബന്ധമായും രജിസ്റ്ററുകൾ പരിഷ്കരിക്കുന്നു കൂടാതെ sysexit അപ്രാപ്തമാക്കുക */ movl PT_EIP(%esp), %edx movl PT_OLDESP(%esp), %ecx xorl %ebp,%ebp TRACE_IRQS_ON 1: mov PT_FS(%esp), %fs ENABLE_INTERRUPTS_up. " 2: movl $0,PT_FS(%esp) jmp 1b .section __ex_table,"a" .align 4 .long 1b,2b .popsection ENDPROC(sysenter_entry) system_call() പോലെ, മിക്ക ജോലികളും ചെയ്യുന്നത് *sys_call_table(,%eax,4) എന്ന ലൈൻ കോളിലാണ്. ഇവിടെയാണ് ഒരു പ്രത്യേക സിസ്റ്റം കോൾ ഹാൻഡ്‌ലർ വിളിക്കുന്നത്. അതിനാൽ, അടിസ്ഥാനപരമായി കുറച്ച് മാറിയിട്ടുണ്ടെന്ന് വ്യക്തമാണ്. ഇന്ററപ്റ്റ് വെക്റ്റർ ഇപ്പോൾ ഹാർഡ്‌വെയറിൽ ഉൾച്ചേർത്തിരിക്കുന്നു എന്നതും പ്രോസസർ ഒരു പ്രത്യേക തലത്തിൽ നിന്ന് മറ്റൊന്നിലേക്ക് വേഗത്തിൽ നീങ്ങാൻ ഞങ്ങളെ സഹായിക്കുന്നു എന്നതും ഒരേ ഉള്ളടക്കമുള്ള എക്‌സിക്യൂഷന്റെ ചില വിശദാംശങ്ങൾ മാത്രം മാറ്റുന്നു. ശരിയാണ്, മാറ്റങ്ങൾ അവിടെ അവസാനിക്കുന്നില്ല. കഥ ആരംഭിച്ചത് എങ്ങനെയെന്ന് ഓർക്കുക. തുടക്കത്തിൽ തന്നെ വെർച്വൽ പങ്കിട്ട ഒബ്‌ജക്‌റ്റുകൾ ഞാൻ ഇതിനകം പരാമർശിച്ചു. അതിനാൽ, നേരത്തെ ഒരു സിസ്റ്റം കോൾ നടപ്പിലാക്കുന്നത്, പറയുക, libc സിസ്റ്റം ലൈബ്രറിയിൽ നിന്ന് ഒരു ഇന്ററപ്റ്റ് കോൾ പോലെയാണ് തോന്നിയതെങ്കിൽ (സന്ദർഭ സ്വിച്ചുകളുടെ എണ്ണം കുറയ്ക്കുന്നതിന് ലൈബ്രറി ചില പ്രവർത്തനങ്ങൾ ഏറ്റെടുത്തിട്ടുണ്ടെങ്കിലും), ഇപ്പോൾ VDSO സിസ്റ്റം കോളിന് നന്ദി libc ഇല്ലാതെ ഏതാണ്ട് നേരിട്ട് ഉണ്ടാക്കാം. ഇത് ഒരു തടസ്സമായി നേരിട്ട് മുമ്പ് നടപ്പിലാക്കാമായിരുന്നു. എന്നാൽ ഇപ്പോൾ ഒരു ഡൈനാമിക്കലി ലിങ്ക്ഡ് ലൈബ്രറിയിൽ (DSO) നിന്ന് എക്‌സ്‌പോർട്ടുചെയ്‌ത ഒരു റെഗുലർ ഫംഗ്‌ഷനായി കോൾ അഭ്യർത്ഥിക്കാം. ബൂട്ടിൽ, തന്നിരിക്കുന്ന പ്ലാറ്റ്‌ഫോമിന് ഏത് മെക്കാനിസം ഉപയോഗിക്കണമെന്നും ഉപയോഗിക്കാമെന്നും കേർണൽ നിർണ്ണയിക്കുന്നു. സാഹചര്യങ്ങളെ ആശ്രയിച്ച്, സിസ്റ്റം കോൾ എക്സിക്യൂട്ട് ചെയ്യുന്ന ഫംഗ്ഷനിലേക്ക് കേർണൽ എൻട്രി പോയിന്റ് സജ്ജമാക്കുന്നു. അടുത്തതായി, linux-gate.so.1 ലൈബ്രറിയായി ഫംഗ്‌ഷൻ ഉപയോക്തൃ സ്‌പെയ്‌സിലേക്ക് എക്‌സ്‌പോർട്ടുചെയ്യുന്നു. linux-gate.so.1 ലൈബ്രറി ഡിസ്കിൽ ഭൗതികമായി നിലവിലില്ല. ഇത് പറയുകയാണെങ്കിൽ, കേർണൽ അനുകരിക്കുകയും സിസ്റ്റം പ്രവർത്തിക്കുന്നിടത്തോളം കൃത്യമായി നിലനിൽക്കുകയും ചെയ്യുന്നു. നിങ്ങൾ സിസ്റ്റം നിർത്തുകയും മറ്റൊരു സിസ്റ്റത്തിൽ നിന്ന് റൂട്ട് ഫയൽ സിസ്റ്റം മൌണ്ട് ചെയ്യുകയും ചെയ്താൽ, നിർത്തിയ സിസ്റ്റത്തിന്റെ റൂട്ട് ഫയൽ സിസ്റ്റത്തിൽ ഈ ഫയൽ കാണില്ല. വാസ്തവത്തിൽ, പ്രവർത്തിക്കുന്ന ഒരു സിസ്റ്റത്തിൽ പോലും നിങ്ങൾക്കത് കണ്ടെത്താൻ കഴിയില്ല. ഭൗതികമായി അത് നിലവിലില്ല. അതുകൊണ്ടാണ് linux-gate.so.1 VDSO അല്ലാത്തത് - അതായത്. വെർച്വൽ ചലനാത്മകമായി പങ്കിട്ട ഒബ്ജക്റ്റ്. ഓരോ പ്രക്രിയയുടെയും അഡ്രസ് സ്‌പെയ്‌സിലേക്ക് ഇമുലേറ്റ് ചെയ്‌ത ഡൈനാമിക് ലൈബ്രറിയെ കേർണൽ മാപ്പ് ചെയ്യുന്നു. ഇനിപ്പറയുന്ന കമാൻഡ് പ്രവർത്തിപ്പിച്ച് നിങ്ങൾക്ക് ഇത് എളുപ്പത്തിൽ പരിശോധിക്കാൻ കഴിയും: f0x@devel0:~$ cat /proc/self/maps 08048000-0804c000 r-xp 00000000 08:01 46 /bin/cat 0804c000-0804d000 rw-p 00003001d-p 400080 06e00 0 rw-p 0804d000 00:00 0 ... b7fdf000-b7fe1000 rw-p 00019000 08:01 2066 /lib/ld-2.5.so bffd2000-bffe8000 rw-p bffd2000 00000000 00: 00 0ഇവിടെ അവസാനത്തെ വരി നമുക്ക് താൽപ്പര്യമുള്ള വസ്തുവാണ്: ffffe000-fffff000 r-xp 00000000 00:00 0മേൽപ്പറഞ്ഞ ഉദാഹരണത്തിൽ നിന്ന് ഒബ്ജക്റ്റ് മെമ്മറിയിൽ കൃത്യമായി ഒരു പേജ് ഉൾക്കൊള്ളുന്നുവെന്ന് വ്യക്തമാണ് - 4096 ബൈറ്റുകൾ, വിലാസ സ്ഥലത്തിന്റെ പിൻഭാഗത്ത്. നമുക്ക് മറ്റൊരു പരീക്ഷണം നടത്താം: f0x@devel0:~$ ldd `which cat` linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e87000) /lib/ ld-linux.so.2 (0xb7fdf000) f0x@devel0:~$ ldd `ഏത് gcc` linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/tls/i686/cmov/libc .so.6 (0xb7e3c000) /lib/ld-linux.so.2 (0xb7f94000) f0x@devel0:~$ഇവിടെ ഞങ്ങൾ രണ്ട് അപേക്ഷകൾ വെറുതെ എടുത്തു. 0xffffe000 എന്ന സ്ഥിരമായ വിലാസത്തിൽ തന്നെ, പ്രോസസ്സ് വിലാസ സ്ഥലത്തേക്ക് ലൈബ്രറി മാപ്പ് ചെയ്തിരിക്കുന്നതായി കാണാൻ കഴിയും. ഇനി ഈ മെമ്മറി പേജിൽ യഥാർത്ഥത്തിൽ എന്താണ് സംഭരിച്ചിരിക്കുന്നതെന്ന് നോക്കാം...

ഇനിപ്പറയുന്ന പ്രോഗ്രാം ഉപയോഗിച്ച് VDSO പങ്കിട്ട കോഡ് സംഭരിച്ചിരിക്കുന്ന മെമ്മറി പേജ് നിങ്ങൾക്ക് ഉപേക്ഷിക്കാം: #ഉൾപ്പെടുത്തുക #ഉൾപ്പെടുത്തുക #include int main () (char* vdso = 0xffffe000; char* ബഫർ; FILE* f; ബഫർ = malloc (4096); എങ്കിൽ (!ബഫർ) പുറത്തുകടക്കുക (1); memcpy (ബഫർ, vdso, 4096) ; എങ്കിൽ (!(f = fopen ("test.dump", "w+b"))) (സൌജന്യ (ബഫർ); പുറത്തുകടക്കുക (1); ) fwrite (ബഫർ, 4096, 1, f); fclose (f) ; സൗജന്യം (ബഫർ); തിരികെ 0; )കൃത്യമായി പറഞ്ഞാൽ, നേരത്തെ ഇത് കമാൻഡ് ഉപയോഗിച്ച് എളുപ്പത്തിൽ ചെയ്യാമായിരുന്നു dd if=/proc/self/mem of=test.dump bs=4096 skip=1048574 count=1, എന്നാൽ പതിപ്പ് 2.6.22 മുതലുള്ള കേർണലുകൾ, അല്ലെങ്കിൽ ഒരുപക്ഷേ അതിനുമുമ്പ്, /proc/`pid`/mem-ലേക്ക് പ്രോസസ്സ് മെമ്മറി മാപ്പ് ചെയ്യില്ല. ഈ ഫയൽ അനുയോജ്യതയ്ക്കായി സംരക്ഷിച്ചിട്ടുണ്ടെങ്കിലും കൂടുതൽ വിവരങ്ങളൊന്നും അടങ്ങിയിട്ടില്ല.

തന്നിരിക്കുന്ന പ്രോഗ്രാം കംപൈൽ ചെയ്ത് പ്രവർത്തിപ്പിക്കാം. തത്ഫലമായുണ്ടാകുന്ന കോഡ് ഡിസ്അസംബ്ലിംഗ് ചെയ്യാൻ ശ്രമിക്കാം: f0x@devel0:~/tmp$ objdump --disassemble ./test.dump ./test.dump: ഫയൽ ഫോർമാറ്റ് elf32-i386 വിഭാഗത്തിന്റെ ഡിസ്അസംബ്ലിംഗ് .ടെക്സ്റ്റ്: ffffe400<__kernel_vsyscall>: ffffe400: 51 push %ecx ffffe401: 52 push %edx ffffe402: 55 push %ebp ffffe403: 89 e5 mov %esp,%ebp ffffe405: 0f 34 sysenter ... ffffe40e: ffffe40e<__kernel_vsyscall+0x3>ffffe410: 5d പോപ്പ് %ebp ffffe411: 5a പോപ്പ് %edx ffffe412: 59 പോപ്പ് %ecx ffffe413: c3 ret ... f0x@devel0:~/tmp$സിസ്റ്റം കോളുകൾക്കുള്ള ഞങ്ങളുടെ ഗേറ്റ്‌വേ ഇതാ, എല്ലാം പൂർണ്ണമായി കാണാനാകും. __kernel_vsyscall ഫംഗ്‌ഷനെ വിളിക്കുന്ന ഒരു പ്രക്രിയ (അല്ലെങ്കിൽ libc സിസ്റ്റം ലൈബ്രറി) 0xffffe400 എന്ന വിലാസത്തിൽ അവസാനിക്കുന്നു (ഞങ്ങളുടെ കാര്യത്തിൽ). അടുത്തതായി, __kernel_vsyscall ഉപയോക്തൃ പ്രോസസ്സ് സ്റ്റാക്കിൽ ecx, edx, ebp രജിസ്റ്ററുകളുടെ ഉള്ളടക്കങ്ങൾ സംരക്ഷിക്കുന്നു. ഞങ്ങൾ നേരത്തെ തന്നെ ecx, edx രജിസ്റ്ററുകളുടെ ഉദ്ദേശ്യത്തെക്കുറിച്ച് സംസാരിച്ചു; ebp-ൽ ഇത് പിന്നീട് ഉപയോക്താവിന്റെ സ്റ്റാക്ക് പുനഃസ്ഥാപിക്കാൻ ഉപയോഗിക്കുന്നു. സിസെന്റർ നിർദ്ദേശം നടപ്പിലാക്കി, "ഇന്ററപ്റ്റ് ഇന്റർസെപ്ഷൻ", അതിന്റെ ഫലമായി, sysenter_entry യിലേക്കുള്ള അടുത്ത പരിവർത്തനം (മുകളിൽ കാണുക). 6 ആർഗ്യുമെന്റുകളുള്ള ഒരു സിസ്റ്റം കോൾ പുനരാരംഭിക്കുന്നതിന് 0xffffe40e-ലെ jmp നിർദ്ദേശം ചേർത്തിരിക്കുന്നു (http://lkml.org/lkml/2002/12/18/ കാണുക). പേജിൽ സ്ഥാപിച്ചിരിക്കുന്ന കോഡ് arch/i386/kernel/vsyscall-enter.S (അല്ലെങ്കിൽ ഹുക്ക് 0x80 എന്നതിനുള്ള arch/i386/kernel/vsyscall-int80.S) എന്ന ഫയലിലാണ്. __kernel_vsyscall ഫംഗ്‌ഷന്റെ വിലാസം സ്ഥിരമാണെന്ന് ഞാൻ കണ്ടെത്തിയെങ്കിലും, അങ്ങനെയല്ല എന്ന അഭിപ്രായമുണ്ട്. സാധാരണഗതിയിൽ, __kernel_vsyscall() ലെ എൻട്രി പോയിന്റിന്റെ സ്ഥാനം AT_SYSINFO പാരാമീറ്റർ ഉപയോഗിച്ച് ELF-auxv വെക്‌ടറിൽ നിന്ന് കണ്ടെത്താനാകും. ELF-auxv വെക്‌ടറിൽ സ്റ്റാർട്ടപ്പിലെ സ്റ്റാക്ക് വഴി പ്രോസസ്സിലേക്ക് കൈമാറുന്ന വിവരങ്ങൾ അടങ്ങിയിരിക്കുന്നു കൂടാതെ പ്രോഗ്രാം പ്രവർത്തിക്കുമ്പോൾ ആവശ്യമായ വിവിധ വിവരങ്ങൾ അടങ്ങിയിരിക്കുന്നു. ഈ വെക്‌ടറിൽ പ്രത്യേകമായി പ്രോസസ് എൻവയോൺമെന്റ് വേരിയബിളുകൾ, ആർഗ്യുമെന്റുകൾ മുതലായവ അടങ്ങിയിരിക്കുന്നു.

__kernel_vsyscall ഫംഗ്‌ഷനെ നിങ്ങൾക്ക് എങ്ങനെ നേരിട്ട് വിളിക്കാം എന്നതിന്റെ ഒരു ചെറിയ ഉദാഹരണം C-യിൽ ഇതാ: #ഉൾപ്പെടുന്നു int pid; int main () ( __asm ​​("movl $20, %eax \n" "call *%gs:0x10 \n" "movl %eax, pid \n"); printf ("pid: %d\n", pid) ; റിട്ടേൺ 0;)ഈ ഉദാഹരണം മനു ഗാർഗ് പേജിൽ നിന്ന് എടുത്തതാണ്, http://www.manugarg.com. അതിനാൽ, മുകളിലുള്ള ഉദാഹരണത്തിൽ, ഞങ്ങൾ getpid() സിസ്റ്റം കോൾ ചെയ്യുന്നു (നമ്പർ 20 അല്ലെങ്കിൽ __NR_getpid). AT_SYSINFO വേരിയബിളിന്റെ തിരയലിൽ പ്രോസസ്സ് സ്റ്റാക്കിൽ കയറാതിരിക്കാൻ, ലോഡ് ചെയ്യുമ്പോൾ libc.so സിസ്റ്റം ലൈബ്രറി AT_SYSINFO വേരിയബിളിന്റെ മൂല്യം ത്രെഡ് കൺട്രോൾ ബ്ലോക്കിലേക്ക് (TCB) പകർത്തുന്നു എന്ന വസ്തുത ഞങ്ങൾ പ്രയോജനപ്പെടുത്തും. ഈ വിവരങ്ങളുടെ ബ്ലോക്ക് സാധാരണയായി gs-ലെ ഒരു സെലക്ടർ റഫറൻസ് ചെയ്യുന്നു. ആവശ്യമുള്ള പരാമീറ്റർ ഓഫ്‌സെറ്റ് 0x10-ൽ ഉണ്ടെന്ന് ഞങ്ങൾ അനുമാനിക്കുകയും %gs:$0x10 എന്നതിൽ സംഭരിച്ചിരിക്കുന്ന വിലാസത്തിലേക്ക് ഒരു കോൾ ചെയ്യുകയും ചെയ്യുക.

ഫലം.

വാസ്തവത്തിൽ, പ്രായോഗികമായി, ഈ പ്ലാറ്റ്‌ഫോമിൽ FSCF (ഫാസ്റ്റ് സിസ്റ്റം കോൾ ഫെസിലിറ്റി) പിന്തുണയ്‌ക്കൊപ്പം പോലും കാര്യമായ പ്രകടന വർദ്ധനവ് നേടുന്നത് എല്ലായ്പ്പോഴും സാധ്യമല്ല. ഒരു വഴി അല്ലെങ്കിൽ മറ്റൊന്ന്, പ്രക്രിയ അപൂർവ്വമായി നേരിട്ട് കേർണലിലേക്ക് പ്രവേശിക്കുന്നു എന്നതാണ് പ്രശ്നം. കൂടാതെ ഇതിന് നല്ല കാരണങ്ങളുണ്ട്. കേർണൽ പതിപ്പ് പരിഗണിക്കാതെ തന്നെ പ്രോഗ്രാം പോർട്ടബിലിറ്റി ഉറപ്പ് നൽകാൻ libc ലൈബ്രറി ഉപയോഗിക്കുന്നത് നിങ്ങളെ അനുവദിക്കുന്നു. സ്റ്റാൻഡേർഡ് സിസ്റ്റം ലൈബ്രറിയിലൂടെയാണ് മിക്ക സിസ്റ്റം കോളുകളും പോകുന്നത്. FSCF-നെ പിന്തുണയ്‌ക്കുന്ന ഒരു പ്ലാറ്റ്‌ഫോമിനായി കംപൈൽ ചെയ്‌ത ഏറ്റവും പുതിയ കേർണൽ നിങ്ങൾ കംപൈൽ ചെയ്‌ത് ഇൻസ്‌റ്റാൾ ചെയ്‌താലും, ഇത് പ്രകടന നേട്ടത്തിന്റെ ഒരു ഗ്യാരണ്ടിയല്ല. നിങ്ങളുടെ സിസ്റ്റം ലൈബ്രറി libc.so int 0x80 ഉപയോഗിക്കുന്നത് തുടരും, glibc പുനർനിർമ്മിക്കുന്നതിലൂടെ മാത്രമേ നിങ്ങൾക്ക് ഇത് കൈകാര്യം ചെയ്യാൻ കഴിയൂ എന്നതാണ് വസ്തുത. Glibc-ൽ VDSO ഇന്റർഫേസും __kernel_vsyscall-ഉം പൊതുവെ പിന്തുണയ്‌ക്കുന്നുണ്ടോ, ഇപ്പോൾ ഉത്തരം നൽകാൻ എനിക്ക് ബുദ്ധിമുട്ടാണ്.

ലിങ്കുകൾ.

മനു ഗാർഗിന്റെ പേജ്, http://www.manugarg.com
യോഹാൻ പീറ്റേഴ്സന്റെ ചിന്തകൾ ചിതറിക്കുക/ശേഖരിക്കുക, http://www.trilithium.com/johan/2005/08/linux-gate/
ലിനക്സ് കേർണൽ നന്നായി മനസ്സിലാക്കുന്നു, അതില്ലാതെ നമ്മൾ എവിടെയായിരിക്കും :)
തീർച്ചയായും, ലിനക്സ് സോഴ്സ് കോഡുകൾ (2.6.22)