Este important să rețineți că programarea nucleului diferă semnificativ de programarea spațiului utilizator. Nucleul este o entitate de sine stătătoare, care nu poate folosi biblioteci de spațiu utilizator, nici măcar libc pe Linux sau kernel32.dll pe Windows. Ca urmare, funcțiile obișnuite utilizate în spațiul utilizatorului (printf, malloc, free, open, read, write, memcpy, strcpy etc.) nu mai pot fi utilizate.
În concluzie, programarea nucleului se bazează pe un API complet nou și independent, care nu are legătură cu API-ul din spațiul utilizatorului, indiferent dacă ne referim la POSIX , Win32 sau ANSI C. O diferență importantă în programarea kernelului este modul în care îl accesați. și alocarea memoriei. Datorită faptului că programarea nucleului se face la un nivel foarte apropiat de mașina fizică, există reguli importante privind gestionarea memoriei.
În primul rând, funcționează cu mai multe tipuri de memorie:
– memorie fizică
– memorie virtuală în spațiul de adrese kernel
– memorie virtuală din spațiul de adrese al unui proces
– memorie rezidentă – știm sigur că paginile accesate sunt prezente în memoria fizică
Memoria virtuală din spațiul de adresă al unui proces nu poate fi considerată rezidentă din cauza mecanismelor de memorie virtuală implementate de sistemul de operare: paginile pot fi în schimb sau pot să nu fie prezente în memoria fizică ca urmare a mecanismului de paginare a cererii.
Memoria din spațiul de adrese kernel poate fi rezidentă sau nu. Atât segmentele de date, cât și de cod ale unui modul, cât și stiva de nucleu a unui proces sunt rezidente.
Memoria dinamică poate fi rezidentă sau nu, în funcție de modul în care este alocată. Când lucrați cu memoria rezidentă, lucrurile sunt simple: memoria poate fi accesată în orice moment. Cu toate acestea, dacă se lucrează cu memorie nerezidentă, atunci aceasta poate fi accesată numai din anumite contexte.
Memoria nerezidentă poate fi accesată numai din contextul procesului. Accesarea memoriei nerezidente din contextul de întrerupere are rezultate imprevizibile și, prin urmare, atunci când sistemul de operare detectează un astfel de acces, va lua măsuri drastice: blocarea sau resetarea sistemului, pentru a preveni corupția gravă.
Memoria virtuală a unui proces nu poate fi accesată direct din nucleu. În general, este total descurajat să accesezi spațiul de adrese al unui proces, dar există situații în care un driver de dispozitiv trebuie să o facă. Cazul tipic este în care driverul de dispozitiv trebuie să acceseze un buffer din spațiul utilizator. În acest caz, driverul de dispozitiv trebuie să folosească funcții speciale și să nu acceseze direct tamponul. Acest lucru este necesar pentru a preveni accesul la zonele de memorie nevalide.
O altă diferență față de programarea în spațiul utilizator, în raport cu lucrul cu memorie, se datorează stivei, stiva a cărei dimensiune este fixă și limitată. În kernel-ul Linux, se folosește în mod implicit o stivă 4K, iar în Windows, se folosește o stivă de 12K. Din acest motiv, trebuie evitată alocarea de structuri mari pe stivă sau utilizarea apelurilor recursive.
În ceea ce privește modul de execuție în nucleu, distingem două contexte: contextul procesului și contextul întreruperii. Ne aflăm în contextul procesului când rulăm cod în urma unui apel de sistem sau când rulăm în contextul unui fir de nucleu. Când rulăm în rutina de a face față unei pauze sau a unei acțiuni amânate, rulăm într-un context de pauză.
Una dintre cele mai importante caracteristici ale programării nucleului este paralelismul. Atât Linux, cât și Windows acceptă sisteme SMP cu procesoare multiple, dar și nucleul acceptă preventiv mai multe procesoare. Acest lucru face programarea nucleului mai dificilă, deoarece accesul la variabilele globale trebuie sincronizat fie cu spinlock, fie cu primitive de blocare.
Atât Linux cât și Windows folosesc nuclee preventive. Noțiunea de multitasking preventiv nu trebuie confundată cu noțiunea de nucleu preventiv. Noțiunea de multitasking preventiv se referă la faptul că sistemul de operare întrerupe rularea unui proces în mod forțat, atunci când acesta a expirat timpul și rulează în spațiul utilizatorului, pentru a rula un alt proces.
Pentru programare în Nucleul Linux, convenția folosită pentru a apela funcții pentru a indica succesul este identică cu cea din programarea UNIX: 0 pentru succes sau o altă valoare decât 0 pentru eșec.