Unitat 1 · Sistemes Operatius (SO)
El desenvolupament de programari de sistema el realitzarem amb els llenguatge C i basa en els compiladors GNU gcc.
Fitxer: scripts/check.sh
hola.o
.Fitxer: sources/ex1.c
gcc -o ex1 ex1.c
gcc -S -o ex1.s ex1.c
gcc -c -o ex1.o ex1.c
gcc -E -o ex1.i ex1.c
objdump -d ex1.o
o objdump -d -M intel ex1
Notes
-M intel
indica l’ús de la sintaxi Intel per a una millor llegibilitat.char *
.Fitxer: sources/arrel.c
La funció main() té dos arguments que tradicionalment s’anomenen argc (Longitud del vector d’argument) i argv (Matriu de punters de caràcters).
check_args.c
chsh -s /bin/sh jordi
.Fitxer: sources/check_args.c
En el llenguatge de programació C, la directiva #include diu al preprocessador que insereixi el contingut d’un altre fitxer al codi font en el punt on es trobi la directiva #include.
#include <header_file>
: El preprocessador cercarà una ruta de directori predeterminada per localitzar el fitxer de capçalera. Normalment, els fitxers són: /usr/include.
#include "header_file"
: El preprocessador buscarà el fitxer de capçalera al mateix directori que el fitxer font
Header | Funcionalitat |
---|---|
stdio | Subministra entrada i sortida: funcions FILE, stdin, stdout, stderr i fprint(). |
stdlib | Subministra funcions d’utilitat: malloc(), calloc() i realloc(). |
unistd | Subministraments EXIT_FAILURE, EXIT_SUCCESS. |
errno | Defineix la variable errno externa i tots els valors que pot prendre. |
assert | Subministra funcions de diagnòstic. |
time | Subministraments Funcions de data i hora. |
math | Proporciona funcions de suport matemàtiques. |
string | Proporciona funcions memcpy(), memset() i strlen(). |
getopt | Proporciona optarg, opterr i getopt() externs. |
La directiva #define permet la definició de macros dins del codi font. Aquestes definicions de macro permeten declarar valors constants per utilitzar-los en tot el codi. Les definicions de macro no són variables i el programa no les pot canviar. Utilitzeu aquesta sintaxi quan creeu constants que representen nombres, cadenes o expressions.
Una declaració externa porta aquest nom a l’espai de noms de la unitat de compilació actual (també conegut com fitxer) i permet al programa accedir a aquesta variable. La paraula clau extern amplia la visibilitat de la funció a tot el programa, la funció es pot utilitzar (anomenar) a qualsevol lloc de qualsevol dels fitxers de tot el programa, sempre que aquests fitxers continguin una declaració de la funció.
La variable global errno és defineix a la biblioteca errno.h.
$ man errno
perror
de la biblioteca <stdio.h>
. Funció: void perror(const char*);
Aquesta funció primer mostra el missatge i després l’error. #include <stdio.h>
.Fitxer: sources/errno_example.c
echo
amb cFitxer: sources/echo.c
cd
amb cFitxer: sources/cd.c
Un fitxer Makefile és un fitxer de text que conté un conjunt de regles utilitzades per construir un programa. Aquestes regles indiquen al sistema com compilar i enllaçar el programa.
Uns dels objectius del SO és proporcionar una màquina virtual que uniformitzi la complexitat dels dispositius d’E/S, necessitem independitzar les operacions i els dispositius.
Dispositius reals: Existeix en el món real. Combinació de diferents elements harwdare i software.
Dispositius físics: Formats pel perifèric i pel seu hardware de control i el programari que el gestiona (drivers).
Dispositius lògics: El resultat d’un programari del sistema que crea aquest dispositiu.
Un dispositiu virtual és un dispositiu que a priori no està associat a cap dispositiu real. En temps d’execució el sistema operatiu associarà el dispositiu virtual amb el dispositiu real.
El processos utilitzen els descriptors de fitxers per accedir als dispositius un cop ja han estat oberts per llegir i escriure informació.
Valor | Significat | Defecte |
---|---|---|
0 | stdin (teclat) | SI |
1 | stdout (pantalla) | SI |
2 | stderr (pantalla) | SI |
\(3...N\) | disponibles pels usuaris | NO |
Per crear o obrir un fitxer fem anar les crides a sistema open() i creat(). Aquestes retornen la seva descripció, o retornen -1 si hi ha hagut algun error.
Per tancar un fitxer utilitzarem close(). Aquesta crida a sistema desassocia el fitxer del procés. Retorna 0 si tot funciona correctament, en ca d’error -1.
int main() {
int fd1, fd2, fd3;
FILE *f;
printf("STDIN_FILENO: %d\n", STDIN_FILENO);
printf("stdout: %d\n", fileno(stdout));
printf("STDERR_FILENO: %d\n", STDERR_FILENO);
printf("\nOpening /dev/zero...\n");
if ((fd1 = open("/dev/zero", O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open /dev/zero: %s\n", strerror(errno));
} else {
printf("fd1: %d\n", fd1);
}
Fitxer: sources/descriptors.c
printf("\nOpening /dev/zero a second time...\n");
if ((fd2 = open("/dev/zero", O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open /dev/zero: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("fd2: %d\n", fd2);
printf("\nNow closing fd1, but keeping fd2 open..\n");
(void)close(fd1);
printf("\nOpening /dev/zero a third time...\n");
if ((fd3 = open("/dev/zero", O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open /dev/zero: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("fd3: %d\n", fd3);
printf("\nNow closing fd2 and fd3.\n");
(void)close(fd2);
(void)close(fd3);
printf("Now opening /dev/zero as a stream.\n");
if ((f = fopen("/dev/zero", "r")) == NULL) {
fprintf(stderr, "Unable to open /dev/zero: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("f: %d\n", fileno(f));
(void)fclose(f);
return EXIT_SUCCESS;
}
read()
: Intenta llegir/escriure fins a nbytes bytes del fitxer especificat per fd i emmagatzemar-los a l’àrea de memòria que comença a buf. Paràmetres:
int main(int argc, char* argv[]) {
char string[11]; int b_read;
int file = open ("my_file", O_RDONLY);
if(file == -1) {
perror("Error while opening file");
exit(1);
}
b_read = read(file, string, 10);
close(file);
string[10] = 0;
printf("%d B have been read. The obtained string is: %s\n",
b_read, string);
return 0;
}
Fitxer: sources/llegir_fitxer.c
Fitxer: sources/escriure_fitxer.c
Fitxer: sources/llegir_fitxer2.c
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main() {
int fd;
if((fd = creat("new_file2", 0644)) < 0) {
perror("new_file2"); exit(-1);
}
if(write(fd, buf1, 10) != 10) perror("buf1"); // offset == 10
if(lseek(fd, 4, SEEK_SET) == -1) perror("lseek"); // offset == 4
if(write(fd, buf2, 10) != 10) perror("buf2"); // offset == 14
return 0;
}
Fitxer: sources/escriure_fitxer2.c
&x
ens dóna l’adreça de la variable x
.El valor que retorna l’operador & depèn de la posició del seu operand i, per tant, no està sota el control del programador.
Fitxer: sources/punters_diapos.c
#include <stdio.h>
int sumar_per_valor(int a, int b) {
a = a + b;
return a;
}
int main() {
int x = 5;int y = 3;
printf("Abans de la crida per valor: x = %d, y = %d\n", x, y);
int resultat = sumar_per_valor(x, y);
printf("Després de la crida per valor: x = %d, y = %d\n", x, y);
printf("Resultat de la suma: %d\n", resultat);
return 0;
}
Fitxer: sources/pas_valor.c
Fitxer: sources/pas_referencia.c
És una estructura LIFO (Last-In,First-Out). La pila és una regió especial de memòria i la gestiona automàticament la CPU, de manera que no cal assignar ni desassignar memòria. La memòria de pila es divideix en trames successives on cada vegada que es crida una funció, s’assigna una nova trama de pila.
La heap és una àrea de memòria on s’assigna memòria de manera dinàmica durant el temps d’execució. Aquesta memòria es gestiona a través de funcions específiques com malloc()
, calloc()
i free()
, i és responsabilitat del programador garantir que la memòria s’alliberi quan ja no sigui necessària.
Fitxer: sources/zones_mem.c
Un stack overflow es produeix quan la pila del programa supera la seva capacitat màxima. Això pot passar quan es criden funcions recursives de manera infinita o quan s’assignen grans quantitats de memòria a la pila.
Fitxer: sources/pas_referencia.c
capta_dades(int n, int* nums) {
while (scanf("%d", &num) != EOF) {
if (n >= max_elements) {
max_elements *= 2;
int* temp = (int*)realloc(nums, max_elements * sizeof(int));
if (temp == NULL) {
printf("Error en l'assignació de memòria.\n");
free(nums);
return 1;
}
nums = temp;
}
nums[n] = num;
n++;
}
}
Una estructura és un tipus de dades derivats format per membres que són tipus de dades fonamentals o derivats. Una única estructura emmagatzemaria les dades d’un objecte. Una matriu d’estructures emmagatzemaria les dades de diversos objectes.
Typedef s’utilitza per crear sinònims per a noms de tipus de dades definits prèviament.
typedef struct {
float x; float y;
} point ;
float dist( point A, point B) {
return(sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y)));
}
int main(){
float d; point A, B;
printf("The coordinates of the point A are: ");
scanf("%f %f",&A.x,&A.y);
printf("\nThe coordinates of the point B are: ");
scanf("%f %f",&B.x,&B.y);
printf("\nThe distance between A and B is %f\n", dist(A,B));
exit (0);
}
Tot el codi i exemples addicionals els trobareu a la carpeta 01-sources
del repositori GitHub del curs.
El kernel de Linux s’ha escrit en C, per tant, és important conèixer aquest llenguatge de programació si volem entendre com funciona el sistema operatiu.
Unitat 1 · Sistemes Operatius (SO) 🏠