Differences

This shows you the differences between two versions of the page.

Link to this comparison view

si:laboratoare:09 [2022/12/11 16:39]
cristian.vijelie [diff și patch]
— (current)
Line 1: Line 1:
-====== Laboratorul 09. Kernel ====== 
- 
-Kernel-ul reprezintă o parte a sistemului de operare responsabilă cu accesul la hardware și managementul dispozitivelor dintr-un sistem de calcul (ex: procesoul, memoria, dispozitivele de I/O). De asemenea, el are rolul de a simplifica accesul la diferitele dispozitive hardware, oferind o interfață generică pentru aplicații prin intermediul system-call-urilor. În spatele interfeței generice se află porțiuni din kernel, numite drivere, care implementeză comunicația cu dispozitivele hardware. Un alt rol al kernel-ului este de a izola aplicațiile între ele, atât pentru stabilitatea sistemului, cât și din considerente de securitate. 
- 
-| {{ :​si:​lab:​2015:​kernel:​architecture.png?​300 |Architectura unui sistem de operare [By Bobbo (Own work) [CC-BY-SA-3.0 (http://​creativecommons.org/​licenses/​by-sa/​3.0) or GFDL (http://​www.gnu.org/​copyleft/​fdl.html)],​ via Wikimedia Commons]}} | 
-^ Architectura unui sistem de operare ^ 
- 
-Pentru a îndeplini toate aceste sarcini, codul kernel-ului rulează într-un mod special de lucru al procesorului,​ fapt care îi permite să execute o serie de instrucțiuni privilegiate. Acest mod privilegiat de lucru nu este accesibil aplicațiilor obișnuite. Spunem că aplicațiile rulează în //​user-space//​ (modul neprivilegiat),​ iar kernel-ul rulează în //​kernel-space//​ (modul privilegiat). 
- 
-===== Linux ===== 
- 
-Linux este numele unui kernel creat de către Linus Torvalds, care stă la baza tuturor distribuțiilor GNU/Linux. Inițial, el a fost scris pentru procesorul Intel 80386 însă, datorită licenței permisibile,​ a cunoscut o dezvoltare extraordinară,​ în ziua de astăzi el rulând pe o gamă largă de dispozitive,​ de la ceasuri de mână până la super-calculatoare. Această versatilitate,​ cât și numărul mare de arhitecturi și de periferice suportate, îl face ideal ca bază pentru un sistem embedded. 
- 
-<​note>​ 
-Arhitecturile suportate de kernel-ul Linux se pot afla listând conținutul directorului ''​arch''​ din cadrul surselor. 
-</​note>​ 
- 
-Linux este un kernel cu o arhitectură monolitică,​ acest lucru însemnând că toate serviciile oferite de kernel rulează în același spațiu de adresă și cu aceleași privilegii. Linux permite însă și încărcarea dinamică de cod (în timpul execuției) în kernel prin intermediul modulelor. Astfel, putem avea disponibile o multime de drivere, însă cele care nu sunt folosite des nu vor fi încarcate și nu vor rula. Spre deosebire de aplicații însă, care rulează în modul neprivilegiat (//​user-space//​) și nu pot afecta funcționarea kernel-ului,​ un modul are acces la toată memoria kernel-ului și se execută în //​kernel-space//​ (poate executa orice instrucțiune privilegiată). Un bug într-un modul sau un modul malițios poate compromite întregul sistem. 
- 
-Dezvoltarea kernel-ului Linux se face în mod distribuit, folosind sistemul de versionare Git. Versiunea oficială a kernel-ului,​ denumită //​mainline//​ sau //vanilla// este disponibilă în repository-ul lui Linus Torvalds, la adresa [[https://​git.kernel.org/​pub/​scm/​linux/​kernel/​git/​torvalds/​linux.git]]. 
- 
-Versiunea oficială este însă rar folosită într-un sistem embedded nemodificată. Este foarte comun ca fiecare sistem să folosească o versiune proprie a kernelului (numită un //tree//) bazată mai mult sau mai puțin pe versiunea oficială. Datorită licenței GPLv2 a kernelului, însă, orice producător care folosește o versiune modificată a kernelului este obligat să pună la dispoziție modificările aduse. 
- 
-Aceste modificări sunt puse la dispoziție sub formă de //​patch//​-uri care trebuie aplicate unei anumite versiuni de kernel. O altă modalitate, care este folosită și de către fundația RaspberryPi,​ este de a publica un repository de Git cu versiunea modificată (un //tree// alternativ). Datorită modelului distribuit de dezvoltare suportat de Git, această a doua metodă are avantajul că permite dezvoltarea ușoară în paralel a celor două versiuni. Modificările făcute într-una pot fi portate și în cealaltă, iar Git va ține minte ce diferențe există în fiecare versiune. Cele două versiuni sunt de fapt două //​branch//​-uri de dezvoltare, care se întâmplă să fie găzduite pe servere diferite. 
- 
-<​note>​ 
-Kernel-ul folosit pe RaspberryPi,​ care include suportul pentru SoC-ul Broadcom BCM2385 folosit de acesta, se găsește la adresa [[https://​github.com/​raspberrypi/​linux.git]]. 
-</​note>​ 
- 
-===== Linux kernel build system ===== 
- 
-Pentru compilare și generarea tuturor componentelor kernel-ului (ex: imaginea principală - //​vmlinux//,​ module, firmware) Linux folosește un sistem de build dezvoltat o dată cu kernel-ul, bazat pe utilitarul //make//. Acest sistem de build însă nu seamănă cu clasicul //​config/​make/​make install//, deși ambele sunt bazate pe utilitarul //make//. 
- 
-Toți pașii de compilare sunt implementați ca target-uri pentru //make//. De exemplu, pentru configurare se poate folosi target-ul ''​config''​. Acestă metodă de configurare însă nu este recomandată deoarece oferă o interfață foarte greoaie de configurare a kernel-ului. 
- 
-<note tip> 
-Target-ul ''​help''​ oferă informații despre aproape toate operațiile suportate de către sistemul de build. 
- 
-<code shell> 
-$ make help 
-</​code>​ 
-</​note>​ 
- 
-Operațiile oferite sunt grupate în diferite categorii: 
-  * //​cleaning//​ - conține target-uri pentru ștergerea fișierelor generate la compilare 
-    * spre deosebire de ''​clean'',​ ''​mrproper''​ șterge în plus toate fișierele generate, plus configurarea și diferite fișiere de backup 
-  * //​configuration//​ - conține diferite target-uri pentru generarea unei configurări a kernelului; există target-uri care permit: 
-    * generarea unui fișier de configurare nou; ex: ''​defconfig'',​ ''​allmodconfig'',​ ... 
-    * actualizarea unui fișier de configurare existent; ex: ''​olddefconfig'',​ ''​localyesconfig'',​ ... 
-    * editarea unui fișier de configurare existent; ex: ''​menuconfig'',​ ''​nconfig'',​ ... 
-  * //generic// - conține target-uri generice; există target-uri pentru: 
-    * compilare; ex: ''​all''​ - target-ul implicit care este executat la o invocare simplă a lui ''​make'',​ iar ''​vmlinux''​ și ''​modules''​ compilează imaginea kernel-ului și, respectiv, modulele selectate 
-    * instalare; ex: ''​headers_install'',​ ''​module_install'',​ ... 
-    * informații;​ ex: ''​kernelrelease'',​ ''​image_name'',​ ... 
-  * //​architecture dependent// - conține target-uri dependente de architectură,​ care diferă în funcție de arhitectura selectată; există target-uri pentru; 
-    * generarea de imagini în diferite formate: compresate (''​zImage''​ și ''​bzImage''​),​ pentru U-Boot (''​uImage''​),​ ... 
-    * generarea de configurări implicite pentru sisteme bazate pe arhitectura selectată; ex: ''​*_defconfig''​ 
- 
-<​note>​ 
-Arhitectura care va fi compilată este selectată de variabila de mediu ''​ARCH''​. 
- 
-<code shell> 
-$ ARCH=x86 make [<​targets>​] 
-sau 
-$ make ARCH=x86 [<​targets>​] 
-</​code>​ 
-</​note>​ 
- 
-<note important>​ 
-Dacă arhitectura nu este setată explicit, se folosește implicit arhitectura sistemului pe care se face compilarea. 
-</​note>​ 
- 
-În cele mai multe situații se dorește compilarea unui kernel pentru un sistem deja existent, fie pentru a adăuga sau elimina funcționalități sau pentru a actualiza versiunea de kernel folosită. În aceste cazuri folosirea target-urilor de generare a unei configurații noi, chiar și a celor care generează o configurație implicită pentru arhitectura noastră nu sunt neapărat utile. Este posibil ca kernel-ul existent să aibă deja o configurație personalizată care se dorește doar a fi actualizată/​modificată folosind target-urile de editare. ​ 
- 
-Un kernel care rulează poate conține fișierul de configurare (de obicei în format comprimat //gzip//) din care a fost compilat, dacă această funcționalitatea a fost selectată la build. Acest fișier se regăsește în ''/​proc/​config.gz''​. Tot ce rămâne este să extragem acest fișier și să-l modificăm conform dorințelor. 
- 
-===== Device Tree Structure ===== 
- 
-Device Tree-ul este o structura de date, la nivelul kernel-ului,​ care descrie componentele hardware prezente pe sistem, care nu pot fi descoperite automat de kernel. El este prezent pe sistemele cu arhitecturi ARM, dar si pe alte sisteme (nu pe cele care se bazeaza pe arhitectura x86). Fiecare intrare a device tree-ului descrie o componenta individuala. 
- 
-Un exemplu de intrare, prezenta in Device Tree-ul specific placii VersatilePB,​ este urmatorul: 
- 
- uart@9000 { 
- compatible = "​arm,​pl011",​ "​arm,​primecell";​ 
- reg = <0x9000 0x1000>; 
- interrupt-parent = <&​sic>;​ 
- interrupts = <6>; 
- clocks = <&​xtal24mhz>,​ <&​pclk>;​ 
- clock-names = "​uartclk",​ "​apb_pclk";​ 
- }; 
- 
-Intrarea este specifica unui modul de UART, care se afla la adresa 0x9000. Aceasta indica adresa registrelor,​ numarul de intreruperi disponibile,​ ceasurile existente si componenta hardware care gestioneaza intreruperile. 
- 
-Device Tree-ul este stocat in 2 fisiere: ''​.dtb''​ (device tree blob), in format binar, si ''​.dts''​ (device tree source), in format text. Ambele tipuri de fisiere se gasesc in ''​arch/<​arhitectura>/​boot/​dts'',​ fiind generate de target-ul ''​dtbs''​ al comenzii ''​make''​. 
- 
-===== Testare ===== 
- 
-Pentru a testa un nou kernel acesta trebuie instalat pe //target//. Această procedură diferă de la sistem la sistem, iar pe RaspberryPi constă în copierea acestuia pe card-ul SD în partiția de //boot// sub numele de ''​kernel.img''​. În momentul dezvoltării și testării unui nou kernel, instalarea fiecărei versiuni a acestuia pe //target// reprezintă un bottleneck major. 
- 
-O alternativă la instalarea kernel-ului pe //target// o reprezintă încărcarea acestuia prin rețea direct pe de //host//-ul folosit la dezvoltare, dacă există suport din partea bootloader-ului. Din păcate, bootloader-ul implicit de pe RaspberryPi nu are suport pentru a încărca o imagine de kernel de pe rețea. Un bootloader care oferă însă acestă facilitate este //U-Boot// [[#​referinte| [2]]], el folosind protocolul TFTP pentru a boota o imagine de kernel prin rețea. 
- 
-===== Instalare pachete/​programe din surse ===== 
- 
-De multe ori ne lovim de problema instalării unui pachet sau a unui program pe care îl găsim doar pe un repository public, de cele mai multe ori bazat pe Git. Astfel, pentru a ne putea folosi de acel pachet/​program,​ trebuie să cunoaștem următoarele utilitare: 
- 
-==== Git ==== 
- 
-Opțiuni și comenzi git: 
- 
-   - ''​git clone //<​repo>//''​ - va aduce toate fișierele conținute de repository-ul //repo// pe mașina locală. 
-   - ''​git pull''​ - actualizează un repository deja clonat local la ultima versiune remote. 
-   - ''​git checkout //<​branch>//''​ sau ''​git checkout //<​tag>//''​ - actualizează fișierele locale la versiunea indicată de //branch// sau de //tag//. Un repository poate avea mai multe branch-uri, care pot fi văzute ca niște versiuni diferite ale repository-ului. Un exemplu uzual de folosire a branch-urilor este pentru organizarea diferitelor versiuni ale aceluiași program. 
-   - ''​git apply //<patch file>//''​ - aplică pe fișierele locale modificările conținute de fișierul //patch//. 
- 
- 
- 
-====== Exerciții ====== 
- 
-  * Descarcati ultima varianta de kernel Linux pentru Raspberry Pi, de [[https://​github.com/​raspberrypi/​linux.git|aici]]. 
-  * Verificati ce tip de procesor este pe Raspberry PI. ''​armv7''​ este pe 32 de biti, ''​armv8''​ este pe 64 de biti. 
-  * Instalati pachetele ''​bc'',​ ''​bison'',​ ''​flex'',​ ''​libssl-dev'',​ ''​libc6-dev'',​ ''​libncurses5-dev'',​ ''​crossbuild-essential-armhf''​ sau ''​crossbuild-essential-arm64'',​ in functie de tipul procesorului. 
-  * Generati configurarea implicita a kernel-ului pentru arhitectura procesorului de pe RPI (tip: defconfig). 
-  * Eliminat suportul pentru ''​virtio drivers''​ (tip: menuconfig, tasta / pentru cautarea unei optiuni) 
-  * Compilati nucleul, device tree blob-urile si modulele de nucleu (tip: (z)Image, modules, dtbs). 
-  * Verificati ca noul nucleu este bun, folosind QEMU (tip: revedeti laboratorul 3) 
-  * Adaugati noul nucleu pe Raspberry PI si faceti configurarile necesare pentru a fi folosit dupa restart. 
- 
-<note tip> 
-  * puteti urmari ghidul oficial al celor de la Raspberry PI: [[https://​www.raspberrypi.com/​documentation/​computers/​linux_kernel.html]] 
-  * la compilare dati cat mai multe nuclee de procesor make-ului, pentru a reduce timpul de compilare, cu optiunea ''​-j''​. 
-  * nu stergeti vechiul nucleu de pe Raspberry PI; adaugati noul nucleu cu alt nume, si modificati kernel-ul folosit in fisierul ''/​boot/​config.txt''​ 
-  * imaginea de nucleu compilata este in ''​arch/​arm/​boot''​ 
-  * device tree blob-urile sunt in ''​arch/​arm/​boot/​dts''​ 
-</​note>​ 
- 
-<note tip> 
-Pentru a reduce timpul de download, puteti descarca doar o anumita versiune de kernel 
-  * mkdir linux 
-  * cd linux 
-  * git init 
-  * git remote add origin <adresa repo> 
-  * git fetch --depth 1 origin <​versiune> ​ 
-    * eg: git fetch --depth 1 origin rpi-5.15.y 
-  * git checkout rpi-5.15.y 
- 
-Pentru a putea vedea alta versiune, trebuie facut fetch pentru ea, in mod similar 
-</​note>​ 
- 
-<note important>​ 
-Nu utiati sa folositi variabliele ARCH si CROSS_COMPILE cand rulati ''​make''​ 
-</​note>​ 
- 
-<​ifauth>​ 
-<​hidden>​ 
-3. Compilați și rulați pe Qemu o versiune a kernel-ului,​ urmărind instrucțiunile de mai jos: 
- 
-<​note>​ 
-Pentru simplificarea execițiului vom folosi versiunea 3.12 a kernelului. Spre deosebire de ultimele versiuni, aceasta oferă o configurație minimală pentru platforma RaspberryPi,​ ceea ce duce la un timp de compilare rezonabil pe PC-urile disponibile în laborator. În plus, față de ultimele versiuni, compilarea acestei configurații implicite se face fără erori. 
-</​note>​ 
- 
-  * Schimbați repository-ul pe //​branch-ul//​ ''​rpi-3.12.y''​ pentru a obține sursele versiunii 3.12 a kernel-ului pentru RaspberryPi. 
-    * Efectuați un //clean// pentru a curăța fișierele binare rămase de la compilarea unei alte versiuni. 
-  * Generați fișierul configurării implicite //quick// pentru RaspberryPi. 
-  * Compilaţi kernel-ul. 
-  * Vom ignora instalarea modulelor pentru moment. 
-  * Afișați versiunea kernel-ului care rulează. 
- 
-<note tip> 
-  * Nu uitați să curățați fișierele binare generate de compilarea unei alte versiuni a kernel-ului. 
-  * Compilarea durează aproximativ 8min. 
-</​note>​ 
- 
-4. Modificați configurația anterioară astfel încât să integreze toate driverele folosite de kernel-ul original direct în imagine. Compilați și rulați noua configurație,​ afișând totodată versiunea kernel-ului. 
- 
-<​note>​ 
-Driverele integrare în imagine vor fi încărcate o dată cu imaginea kernel-ului,​ de fiecare dată, încă de la bootare. Astfel nu se va pierde timp cu încărcarea dinamică a acestora în timpul rulării. Pentru o configurație hardware stabilă și o aplicație fixată (precum un sistem embedded) acest lucru poate constitui un avantaj. 
-</​note>​ 
- 
-  * Preluați lista de module folosite de kernel-ul original folosind comanda ''​lsmod''​ pe //target// în timp ce acest kernel rulează. Copiați lista de module într-un fișier pe //host//. 
-  * Folosiți target-ul de make ''​localyesconfig'',​ împreună cu variabila de mediu ''​LSMOD''​ setată la calea către fișierul care conține lista de module, pentru a transforma configurația curentă într-o configurație care integrează driverele necesare. 
-  * Editați manual configurația modificată anterior pentru a repara eventualele warning-uri apărute. Recomandăm rularea target-ului de make ''​menuconfig''​ pentru efectuarea de modificări manuale. 
-    * Căutați locația configurărilor care cauzează warning-uri și activați-le. 
-    * Verificați eliminarea warning-urilor prin rerularea target-ului de make ''​localyesconfig''​. 
- 
-<note tip> 
-  *  
-  * ''​menuconfig''​ suportă tasta ''/''​ pentru căutare. 
-  * Folosiți tasta ''​y/​Y''​ pentru a selecta integrarea unei opțiuni în kernel. 
-  * Începeți căutarea cu configurările pentru modulele //​snd_soc_core//,​ //​snd_seq_device//​ și //​leds_gpio//​. 
-  * Activați suportul pentru //I2C// și //SPI// din meniul //Device Drivers//. 
-  * Puteți particulariza versiunea afișată a kernel-ului din meniul ''​General setup -> Local version''​. 
-</​note>​ 
- 
-Bonus. Comparați configurația kernel-ului original cu fișierul de configurare creat de voi la exercițiul 5. Ce diferență observați la opțiunile selectate de voi la pasul anterior? 
- 
-<note tip> 
-  * Revedeți [[#​linux_kernel_build_system| finalul]] secțiunii despre sistemul de build. 
-  * Dezarhivați configurația originală cu ''​gunzip //<​fisier>//''​. 
-  * Folosiți ''​kdiff3 //<​path1>//​ //<​path2>//''​ pentru a compara două fișiere sau două directoare. 
-</​note>​ 
-Bonus. **(bragging rights)** Compilați și rulați ultima versiune a kernel-ului disponibil pe Raspbian Wheezy. 
- 
-<​note>​ 
-Configurațiile pentru RaspberryPi existente în //​branch//​-ul ''​rpi-3.18.y''​ sunt voluminoase,​ ceea ce duce la un timp de compilare foarte mare pe PC-urile din laborator. Din acest motiv vom transplanta configurația //​bcmrpi_quick//​ de pe un alt branch. Ultimul //commit// care conțiune acea configurație a fost găsit folosind ''​git bisect''​ între un branch (''​rpi-3.17.y''​) care conține configurația și branch-ul nostru. Pentru mai multe informații despre utilizarea //git bisect// studiați această [[https://​www.kernel.org/​pub/​software/​scm/​git/​docs/​git-bisect-lk2009.html| pagină]]. 
-</​note>​ 
- 
-  * Folosiți ''​git cherry-pick eff92148ee1b5a1ff07e5817179fefb4a0562b17''​ pentru a transplanta commit-ul ''​eff92148ee1b5a1ff07e5817179fefb4a0562b17''​ care este cel care conține configurația dorită. 
-  * Generați configurarea. 
-    * Actualizați fișierul de configurație conform versiunii 3.18 folosind target-ul de make ''​silentoldconfig''​. Opțiunile noi adăugate în kernel de la commit-ul ''​eff92148''​ încoace vor trebui configurate manual. Folosiți valoarea ''​0''​ pentru opțiunea ''​PHYS_OFFSET''​. 
-  * Compilați noul kernel. Vom ignora din nou instalarea modulelor. 
-    * Apariția unor erori de simboluri negăsite (//​unresolved symbol//) cu o configurație transplantată astfel nu este un lucru neobișnuit. 
-    * Notați funcția sau variabila care nu poate fi găsită. 
-    * Căutați această funcție/​variabilă în surse folosind ''​grep -nHR //<​string>//''​ și notați fișierul în care apare. 
-    * Porniți configurarea manuală și căutați printre opțiunile oferite pe cea/cele care conțin bucăți din calea fișierului notat anterior. Activați-le cu tasta ''​y/​Y''​. 
-    * Reporniți compilarea. Fișierele deja compilate vor fi refolosite pe cât posibil de //make//, astfel încât procesul de compilare se reia în mare parte de unde a rămas. 
-    * Dacă mai apar erori de compilare reluați procesul (nu ar trebui să mai apară). 
-  * Porniți RaspberyPi-ul folosind noul kernel și afișati versiunea kernelul folosit. 
-</​hidden> ​ 
-</​ifauth>​ 
- 
- 
-<​hidden>​ 
-  * {{:​si:​laboratoare:​lab8_2022.txt| Soluție laborator}} 
-</​hidden>​ 
-====== Referințe ====== 
- 
-  - [[https://​www.raspberrypi.com/​documentation/​computers/​linux_kernel.html| Ghid compilare kernel RaspberryPi]] 
-<​hidden> ​ - [[http://​elinux.org/​RPi_U-Boot| Ghid compilare U-Boot pentru RaspberryPi]]</​hidden>​ 
- 
  
si/laboratoare/09.1670769544.txt.gz · Last modified: 2022/12/11 16:39 by cristian.vijelie
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0