Sisteme de operare: introducere
Notă: mare parte din acest material a fost preluat de la dl. Marius Minea din introducerea pentru laboratorul de "Programarea Calculatoarelor".
Mediul de lucru
Laboratoarele la aceasta disciplina se desfăşoară pe calculatoare cu sistem de operare Linux, mai precis distributia KUbuntu.
Comenzi Unix
UNIX are un sistem de fişiere arborescent. Spre deosebire de sistemul de
fisiere DOS/Windows, separatorul între componentele numelui de fişier
(directoare şi numele propriu-zis) este caracterul / (slash) şi nu
\ (backslash). De asemenea, numele de fişiere nu conţin un
identificator al discului fizic (A:, C:, etc.), ci întreaga ierarhie de
fişiere porneşte de la o rădăcină unică, notată /.
În continuare, numele comenzilor sunt scrise cu caractere tip maşină
de scris, argumentele lor sunt scrise cursiv, iar textul
între [ ] denotă un argument opţional. Cîteva comenzi de bază sunt:
- mkdir nume_director
Creează un director (directory) cu numele dat.
- cd [nume_director]
Schimbă directorul curent, trecînd in cel specificat (implicit: în
directorul personal (home directory) al utilizatorului).
La fel ca şi sub DOS/Windows, . denotă directorul curent, iar
.. directorul părinte. Notaţia ~ se referă la directorul
personal al utilizatorului curent.
- ls [nume_catalog]
Listează (afişează numele pentru) fişierele din directorul dat (implicit:
directorul curent).
- cat fişier
Afişează conţinutul fişierului
- cp [opţiuni] fişier1 fişier2
Face o copie a lui fişier1, cu numele fişier2
cp [opţiuni] fişier1 fişier2 fişier_n dir
Copiază fisierele indicate în directorul dir.
Opţiuni: -i (interactiv): întreabă înainte de a suprascrie;
-r (recursiv, folosit): copiază toate fişierele şi
subdirectoarele din directorul sursă indicat.
- mv [optiuni] fişier1 fişier2
Redenumeşte fişier1, cu numele fişier2
mv [opţiuni] fişier1 fişier2 fişier_n dir
Mută fişierele indicate în directorul dir.
Opţiuni: -i (interactiv): întreabă înainte de a suprascrie.
(Un catalog e mutat implicit împreună cu întreg conţinutul, deci opţiunea
-r nu mai apare, spre deosebire de copiere)
- rm [opţiuni] nume_fisier
şterge fişierul specificat.
Opţiunea -i (interactiv) solicită confirmare, -f forţează
(fără confirmare) iar -r şterge recursiv tot conţinutul directorului
indicat.
- rmdir nume_director
şterge directorul indicat (acesta trebuie să fie gol).
- man nume_comandă/apel sistem/funcţie de bibliotecă
Afişează pagina de manual pentru comanda indicată (cu toate opţiunile).
Pentru o navigare şi manipulare mai facilă a fişierelor se poate folosi
programul Midnight Commander (mc), similar
lui Norton Commander/Total Commander din mediul DOS/Windows.
Compilatorul gcc
Compilatorul gcc (GNU Compiler
Collection) este unul din cele mai performante compilatoare pentru
limbajul C, şi e realizat în acelaşi model de distribuţie gratuită a
sursei ca şi celelalte componente ale sistemului GNU/Linux.
Fişierele sursă au în mod convenţional extensia .c. Obişnuit,
pentru a compila programul nume.c, se execută comanda
gcc -o nume nume.c
Argumentul nume al opţiunii -o indică numele dat
executabilului (de obicei, acelaşi nume de bază ca şi pentru sursă dar nu neapărat).
Dacă nu se foloseşte opţiunea -o nume, executabilul e
denumit implicit a.out.
[În UNIX, fişierele executabile nu sunt identificate prin extensie (cum ar
fi .exe), ci printr-un atribut (permisiune de execuţie) care în
acest caz e setat automat de compilator].
Pentru a compila programe cu funcţii matematice standard e nevoie de
biblioteca libm. În acest caz, se foloseşte opţiunea -l
urmată, fără spaţiu, de numele bibliotecii (fără prefixul lib
folosit convenţional pentru toate bibliotecile). De exemplu:
gcc -lm -o prog prog.c
La fel atunci cînd folosim fire de excuţie pthreads avem nevoie de biblioteca
libpthread, deci vom adăuga opţiunea -lpthread.
La compilare, e utila generarea cat mai multor avertismente.
Aceasta se specifica cu optiunea -Wall (warnings: all).
Opţiunile la compilare pot fi introduse în orice ordine. E important
de înţeles că -o nume e o singură opţiune,
chiar dacă e formată din două cuvinte separate prin spaţii. E incorectă
intercalarea altor opţiuni între -o şi nume. De exemplu,
în comanda
gcc -o -Wall nume nume.c
se interpretează -Wall ca nume dorit al fişierului de ieşire.
Alte opţiuni pentru gcc vor fi discutate ulterior.
Erori de compilare
Pentru început, scrieţi şi compilaţi următorul program simplu, care
afişează faloarea lui pi calculată cu ajutorul funcţiei arcsinus,
prezentă cu numele asin în biblioteca standard de funcţii matematice.
#include <math.h>
#include <stdio.h>
int main(void)
{
printf("Valoarea lui pi este: ");
printf("%f", 2 * asin(1));
return 0;
}
E important să urmărim cu atenţie şi să înţelegem eventualele mesaje
de apărute la compilare, pentru a putea corecta erorile semnalate.
Cîteva exemple de erori care pot apărea la transcrierea greşită
a acestui program:
error: studio.h: No such file or directory
Compilatorul nu a găsit fişierul indicat, deoarece a fost scris cu
nume eronat: nu este studio.h, ci stdio.h, de la
Standard Input/Output.
error: syntax error before 'return'
Acest mesaj va apare dacă uităm să terminăm linia anterioară cu punct-virgulă.
E important de reţinut că adesea cauza reală a erorii poate să se afle de
fapt puţin înainte de locul în care o detectează compilatorul.
Acesta indică locul presupusei erori prin numele de fişier urmat de
numărul liniei sursă, de exemplu: /home/student/pi.c:8:error: ...
error: syntax error at end of input
Această eroare va fi semnalată dacă se uită acoloada închisă }
şi deci fişierul se termină în mod neaşteptat.
Rularea programelor și linia de comandă
Fie următorul program, să-l numim myecho.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
for (i=1; i<argc; i++)
printf("Argumentul %d este: %s\n", i, argv[i]);
exit(0);
}
Să presupunem că am compilat în diretorul curent programul myecho.c, folosind comanda:
gcc -o myecho myecho.c
Cum lansăm această comandă în execuție? Vom utiliza următoarea linie de comandă:
./myecho arg1 arg2 arg3 gata
Să analizăm această linie (notăm cu simbolul ◊ un caracter spațiu):
./myecho◊arg1◊arg2◊arg3◊gata
- ./
Reprezintă calea către fișierul myecho. Punctul reprezintă directorul curent iar bara oblică (slash) reprezintă despărțitorul de cale. Pentru ce avem nevoie de această construcție ciudată? Explicația o găsiți la secțiunea Variabila de mediu PATH.
-
myecho
Este numele fișierului executabil. Împreună cu calea, reprezintă primul element din linia de comandă și anume, în cazul de față, comanda ce trebuie executată.
-
◊
Spațiul este, pentru interpretorul de comenzi, un despărțitor. El separă linia de comandă în elemente. Primul element poate fi o comandă (o cale către un fișier executabil sau un nume, vezi mai jos) sau o declarație de variabilă de mediu.
Fiți cu băgare de seamă: un spațiu prost plasat va sparge linia de comandă în elemente acolo unde nu doriți. Pentru ca un element să conțină spațiu (și alte caractere speciale), trebuie utilizat un mecanism de citare.
-
arg1◊arg2◊arg3◊gata
În cazul în care primul element e o comandă, urmatoarele elemente reprezintă argumente ce vor fi transmise comenzii executate. În C, aceste argumente pot fi accesate prin vectorul argv
.
Variabila de mediu PATH
Atunci cînd primul element din linie este o comandă, shell-ul se comportă diferit dacă aceasta este o cale spre un fișier ce conține semnul / sau dacă este doar un șir de caractere. În primul caz, va lansa în execuție exact fișierul specificat. În al doilea, dacă nu e vorba de o comandă internă, alias sau altceva interpretat direct de către shell, fișierul executabil este căutat într-o listă de directoare.
Această listă se află în variabila de mediu PATH
și este sub forma unor căi de director despărțite prin semnul : (două puncte).
Pentru a vedea conținutul acestei variabile, folositi comanda:
echo $PATH
Astfel, dacă dorim să executăm un program aflat în directorul curent, iar acest director nu se află în PATH
, trebuie să specificăm explicit o cale către el. De aceea, folosim o linie de genul:
./program
Punctul reprezintă directorul curent, așa cum .. reprezintă directorul părinte. Slash-ul este despărțitorul de cale. Astfel, interpretorul de comenzi nu va mai căuta în lista de directoare din PATH, deoarece are o cale exactă către fișierul pe care dorim să îl executăm.
Mecanisme de citare
Deseori avem nevoie ca anumite caractere cu semnificație specială pentru shell să nu fie interpretate special, ci ca și caractere normale. Spre exemplu, să zicem că dorim să ștergem un fișier care are în numele său un spațiu, My Pictures
. Comanda:
rm My Pictures
va afișa o eroare, deoarece nu găsește fișierele My
și Pictures
. Interpretorul de comenzi sparge linia în trei elemente: numele comenzii - rm - și două argumente: My și Pictures.
Pentru a evita această situație, trebuie să facem cumva ca tot șirul My Pictures
să fie transmis ca un singur argument comenzii rm.
Pentru astfel de situații avem la dispoziție mecanismele de citare:
Mecanism | Descriere |
\ |
Backslash. Caracterul imediat unui backslash este interpretat ca atare, pierzîndu-și eventuala semnificație specială |
"..." |
Șirurile de caractere incluse între ghilimele nu sînt interpretate special, cu câteva excepții, dintre care cea mai importantă este că valoarea variabilelor de mediu ($NUME_VARIABILĂ) este înlocuită la fel ca și în cazul în care nu se utilizează ghilimele.
|
'...' |
Caracterele dintre apostroafe nu sînt interpretate special, ele fiind folosite ca atare. Nici un alt caracter, în afara apostrofului de încheiere, nu este interpretat de către shell în vreun mod special. |
Folosind aceste mecanisme, putem acum șterge fișierul de mai sus în unul din modurile următoare:
rm My\ Pictures
rm "My Pictures"
rm 'My Pictures'
Pentru a observa diferența dintre ultimele două mecanisme, rulați următoarele două linii:
echo "Path is: $PATH"
echo 'Path is: $PATH'
Încheierea programelor, oprirea temporară a execuției și marcarea sfârșitului de fișier
Terminalul accepta mai multe combinații de taste cu semnificație specială. Dintre ele, amintim următoarele:
Combinație taste | Semnificație |
CTRL-C | Incheie execuția programului lansat în foreground (legat de terminal) |
CTRL-Z | Suspendă temporar execuția programului din foreground. Execuția poate fi reluată cu comenzile fg sau bg (îin foreground sau în background) |
CTRL-D | Instruiește terminalul să închidă fișierul intrare standard pentru comanda ce se execută în foreground |
Un program rulează în foregroun dacă se așteaptă terminarea lui pentru a se citi și interpreta următoarea comandă. Un program executat în background va rula în paralel cu comenzilee care le dăm în continuare, shell-ul citește aceste comenzi și le interpretează și în timp ce comanda rulează.
Depanare
In general este bine ca atunci cind cream fisierul executabil sa utilizam optiunea -g a compilatorului:
gcc -Wall -g -o foo foo.c
Asa, in executabilul foo vor fi incluse si simboluri utile pentru depanare.
Strategii simple de depanare:
- printf()-uri prin cod: tipariti mesaje pe consola pentru a urmari executia programului, eventual cu valori ale variabilelor "interesante". Atentie: este bine sa puneti "\n" la sfirsitul acestor linii, in caz contrar ele nu vor fi imediat tiparite (buffer-ele bibliotecii standard C, atunci cind se lucreaza cu terminale/console, nu sint golite decit explicit cu fflush() sau la intilnirea caracterului newline).
- gdb: comanda gdb este un debugger, utilizarea lui nu este foarte simpla; exista si interfete grafice pentru el, de exemplu kgdb sau ddd
- valgrind:
- strace: