Scarica Fondamenti di informatica e più Appunti in PDF di Fondamenti di informatica solo su Docsity! Fondamenti di informatica • Alfabeto = simboli utilizzabili • Codice = sequenze di simboli o regole per definire le combinazioni ammissibili • Dati = insieme degli elementi da rappresentare Configurazioni = tutte di uguale dimensione Dimensione= dipende dall'alfabeto dei simboli e dalla quantità di elementi da rappresentare Alfabeto dei simboli: S= {…} Cardinalità dei simboli: |S| (quanti simboli ho) Elementi = n Dimensione configurazioni = k 𝑘𝑘 = �log|𝑆𝑆| 𝑛𝑛� Sistema binario = alfabeto {0,1} – cifra della codifica = binary digit = bit (cifre) - Byte = 8 bit - Kilobyte = 210 bit - Megabyte = 220 bit - Gigabyte = 230 bit - Terabyte = 240 bit Elementi da considerare: • Elementi da rappresentare • Codifica che semplifichi le operazioni • Codifica che “conservi” le proprietà dell’insieme degli elementi da rappresentare Rappresentiamo dei valori Bisogna trovare un corrispondente per qualsiasi valore in base 10 nel sistema binario Es.: 315 ≠ 135 notazione posizionale – valore ∑ 𝑐𝑐𝑖𝑖𝑏𝑏𝑖𝑖𝑛𝑛 𝑜𝑜 Least significant digit = quella più a destra, cambia valore a ogni configurazione alternandosi Most significant digit = quella più a sinistra La cifra successiva cambia valore ogni due volte La terza cifra cambia ogni quattro configurazioni 000 001 010 011 100 101 110 111 Definisco un codice che associa al valore numerico una configurazione Se voglio rappresentare il 17, devo rappresentare tutti i valori tra 0 e 17 (18 elementi) n = valore da rappresentare 𝑘𝑘 = ⌈log2(𝑛𝑛 + 1)⌉ Valore = ∑ 𝑐𝑐𝑖𝑖2𝑖𝑖𝑚𝑚 0 1010 = 0 ∗ 20 + 1 ∗ 21 + 0 ∗ 22 + 1 ∗ 23 = 10 • Come faccio a rappresentare il 17 nel sistema binario? I numeri naturali si rappresentano in questo modo: Ogni volta che la codifica binaria finisce con un 1 sto esprimendo un numero dispari, con lo 0 un numero pari In base 10 si rappresentano i valori con la notazione modulo e segno, cosa che nel sistema binario non c’è = convenzionalmente il primo bit indica il + con lo 0 o il – con l’1 e il resto delle cifre indica la configurazione effettiva -2310MS = ?2MS +0 +1 +2 = 0 0 0 0 0 0 0 0 1 0 1 0 = +0 +1 +2 −7 −6 −5 = 1 0 0 1 0 1 1 0 1 1 0 1 = −1 −2 −3 +3 +4 +5 = 0 0 1 0 1 0 0 1 0 1 0 1 = +3 +4 +5 −4 −3 −2 = 1 1 0 1 1 0 1 1 1 0 1 0 = −4 −5 −6 +6 +7 −8 = 0 1 1 0 1 1 1 0 0 0 1 0 = +6 +7 −0 −1 = 1 1 1 1 = −7 Valori numerici razionali Se ho poche cifre dopo la virgola a disposizione la precisione è limitata Più cifre ho a disposizione minore è l’errore di approssimazione che commetto Errore assoluto 𝜀𝜀𝐴𝐴 costante Errore relativo 𝜀𝜀𝑅𝑅 = 𝜀𝜀𝐴𝐴 𝑣𝑣𝑣𝑣𝑣𝑣𝑜𝑜𝑣𝑣𝑣𝑣 Notazione in virgola mobile (floating point) = la densità dei punti tra un interno e l’altro cambia lungo l’asse Fa in modo che l’errore relativo rimanga costante, e l’errore assoluto invece aumenti lungo l’asse Quando il numero è piccolo in valore assoluto, si dedica più spazio ai decimali per avere maggior precisione Per i numeri razionali la notazione è in modulo-segno 13.7510𝑀𝑀𝑆𝑆 = 101.1102𝑀𝑀𝑆𝑆 7.3210𝑀𝑀𝑆𝑆 = 111.010100 …2𝑀𝑀𝑆𝑆 Notazione scientifica: 13.7510𝑀𝑀𝑆𝑆 = 1.375 ∗ 101 11012𝑀𝑀𝑆𝑆 = 1.101 ∗ 23 Notazione → 1. ____ ∗ 2𝑣𝑣𝑒𝑒𝑒𝑒 Con due bit dopo la virgola ogni due numeri interi ho tre valori Considero solo la parte dopo la virgola e trascrivo quello che c’è prima sulla destra 𝑣𝑣𝑣𝑣𝑣𝑣𝑣𝑣𝑣𝑣𝑣𝑣 = (−1)𝑆𝑆 ∙ (1 ∙ 𝑀𝑀) × 2𝑣𝑣𝑒𝑒𝑒𝑒 Normalizzata = 1.M Denormalizzata = 0.M (campo esponente con tutti zeri e mantissa con un numero) ∞ lo scrivo con tutti 1 all’esponente e zeri alla mantissa 𝑁𝑁𝑣𝑣𝑁𝑁 → indica qualcosa cche non è più un numero (tutti 1 all’esponente e mantissa con numero) 0 1 1 0 1 1 0 1 1 0 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Configurazioni di due bit in base 3 00 +0 01 +1 02 +2 10 +3 11 +4 12 -4 20 -3 21 -2 22 -1 Rappresentazione dell’informazione non numerica Codifica dei caratteri - codice ASCII (American Standard Code for Information Interchange) base: 7 bit - codice ASCII esteso: 8 bit (1 byte) • codice ASCII base per le prime 128 configurazioni • caratteri nazionali à è é • simboli caratteri semigrafici • cornici Codice ASCII • caratteri numerici prima dei caratteri alfabetici • maiuscole prima delle minuscole • la distanza tra due caratteri numerici è pari alla differenza tra il valore dei corrispondenti codici ASCII Inserisci un valore intero e calcola e visualizza il suo valore assoluto Iterazione L’utente dà un importo in euro e stabilisco quante monete da 5, 2 e 1 devo usare per calcolare quell’importo Il resto della divisione lo facciamo quando gli operandi sono interi Algoritmo che acquisiti tre valori interi visualizzi 1 nel momento in cui corrispondano a una terna pitagorica, 0 se non lo sono bisogna avere l’operazione da fare nel caso la condizione sia vera Calcolo il massimo tra 10 valori Acquisisce un valore intero strettamente positivo e visualizziamo 1 se è dispari, 0 se è pari Algoritmo che acquisisce un valore intero strettamente positivo e calcola e visualizza 1 quando il valore acquisito fa riferimento a un anno bisestile, altrimenti visualizzo 0 Algoritmo che acquisisce un numero intero positivo e calcola di quante cifre è composto Operazioni di cast per calcolare la media gcc- Wall-std=c89-pedantic -o NOMEFILEESEGUIBILE NOMEFILESORGENTE - Wall → segnala tutti I warning - std=c89 → controlla standard ANSI c89 - pedantic → le violazioni degli standard vengono segnalate errori = non si produce l’eseguibile (problemi di sintassi) warning=viene creato un eseguibile (possibili problemi di semantica) num = num +1; → ++ num++; ++num; num = num -1; → -- num--; --num; var op ; → var op = ; ris=ris/val; → ris/=val; tot=tot+cont; → tot+=cont; Il costrutto if if(espressione){ istruzione_V; } if(espressione) istr_V1; istr_V2; istr_V3; Scrivo un programma che, acquisito un valore, calcola e visualizza il suo valore assoluto Acquisisce un valore intero, visualizza + se positivo, - se negativo e spazio se nullo if(a==0) ↔ if(!a) if(val) if(val!=0) if (espressione) istr_V; ⌈else istr_F; ⌋ while (espressione) istruzione V; Programma che acquisisce 20 numeri interi e calcola e visualizza il valore massimo while (espressione) istr; do{ istruzione V; }while (espressione); do{ istr1; istr2; } while (espressione); Scrivo programma che chiede di inserire un valore strettamente positivo e finché non è tale lo richiede Iterazioni a conteggio Bisogna ripetere un’attività per un numero di volte prefissato Il costrutto for organizza for ( ; ; ) 1 → tutte le istruzioni da eseguire prima di entrare nel ciclo → i=0, tot=0 2 → condizione che esegue il ciclo → i<NELEM 3 → istruzioni che sono di servizio a ripetere l’esercizio → i++ for (i=0;i<NELEM;1++) if(num[i]>avg) printf(“%d\n”, num[i]); Scrivo un programma in C che acquisito un valore naturale che dev’essere compreso tra 1 e 1023 (estremi inclusi), e finché non è tale viene richiesto, calcola e visualizza la sua rappresentazione del sistema binario Legge di De Morgan C1 || C2 → !(!C1 && !C2) C1 && C2 → !(!C1 || !C2) Scrivo un programma in C che acquisito un valore naturale che dev’essere compreso tra 1 e 1023 (estremi inclusi), e finché non è tale viene richiesto, calcola e visualizza la sua rappresentazione del sistema binario, utilizzando il numero massimo di bit previsti in relazione all’ampiezza dell’intervallo. Acquisisci due valori tra 1 e 1023 e calcola e visualizza la loro rappresentazione in binario usando lo stesso numero di cifre per entrambi, che deve essere il minimo indispensabile. #include <stdio.h> #define MAX “1023” #define MIN “1” #define BASE “2” #define NBIT “10” int main (int argc, char*agrv[]){ int a, b; int bin[NBIT], i; do{ scanf(“%d”, &a); }while (a<1||a>1023); i=NBIT-1 Array bidimensionali int mat[NRIGHE][NCOLONNE] per passare attraverso tutta la tabella scrivo un ciclo for dentro l’altro for (i=0 ; i<NRIGHE ; i++) for(j=0 ; j<NCOLONNE ; j++) Scrivo programma in C che acquisisce i dati interi di un array bidimensionale di dimensione 5x5 e calcola e visualizza 1 se si tratta di una matrice identità, 0 altrimenti Linearizzazione della memoria I dati strutturati struct datas { int giorno; int mese, anno; } struct studentis { char cognome [MAXLEN+1]; char nome [MAXLEN+1]; int voti esami [NELEM]; int erasmus; int laureato; float media; - 1 = acquisire un valore - 2 = stabilire se sia un numero primo - 3 = calcolare il fattoriale - 4 = stabilire se il valore sia palindromo - 0 = termina • Il ruolo dei sottoprogrammi è quello di spostare una parte di operazione in un punto diverso che realizza la computazione che mi serve producendo il risultato richiesto • Il sottoprogramma è un programma che lavora su una ridotta attività rispetto a una visione più grande • Un sottoprogramma può non ricevere nessuna informazione in ingresso o ricevere una o più istruzioni NB: nei temi d’esame è più probabile trovare ‘scrivi un sottoprogramma’ che ‘scrivi un programma’ ________ menu ( ____ ) { /*codice*/ return ____; } void = nessun tipo in particolare return = interrompe la funzione ed eventualmente porta un valore restituendolo al chiamante Scrivo un sottoprogramma che visualizza il menu, acquisisce la scelta dell’utente e finché non è compatibile la richiede, e restituisce la scelta fatta al chiamante void menu ( ) { printf(“-----\n”); printf(“1. inserisci valore\n”); … printf(“0. termina \n”) return ; scanf, gets, printf → sono tre sottoprogrammi che sono già stati creati per noi e ci danno un’idea su come si chiamano i sottoprogrammi Scrivere un sottoprogramma che ricevuto un valore intero in ingresso, calcola e restituisce 1 se primo, 0 altrimenti if (cond) if (cond) return 0; return 0; else return 1; return 1; #include #define typedef /*prototipi*/ ↓ void menu ( ); int sceltamenu ( ); int primo (int); int main (int argc, char argv[]) { … } Il passaggio parametri Scrivere un sottoprogramma che ricevuta in ingresso una stringa calcola e restituisce il numero di caratteri utilizzati int lunghezzastringa (char s[]) { int dim; for(dim=0;s[dim]!=’\0’;dim++) ; return dim; } Scrivo un sottoprogramma che riceve in ingresso un array di valori interi, e qualsiasi altro parametro ritenuto strettamente necessario, e calcola e restituisce al chiamante l’indice dell’elemento con valore massimo; nel caso ce ne siamo diversi restituisco quello con indice più basso int maxarrayint (int v[], int dim) { int i, imax; imax=0; for (i=1;i<dim;i++) if(v[i]>v[imax]) imax=i; return imax; } Scrivo un sottoprogramma che ricevuto in ingresso un array bidimensionale di valori interi, e qualsiasi programma ritenuto strettamente necessario, ne visualizza il contenuto Si consideri che il chiamante ha dichiarato NCOL #define NCOL void visualizzamat (int m[][NCOL], int nr, int nc) { int i, j; for (i=0;i<nr;i++) Passaggio di dati strutturati Il main è un sottoprogramma come gli altri, con l’unico vincolo che ha un nome fisso e comanda su tutti gli altri - passaggio per valore → se passo due date sto passando 6 interi - passaggio per riferimento → se passo due date sono 2 indirizzi typedef struct datas { int giorno; int mese; int anno; } data_t; Sottoprogramma che, ricevute in ingresso due dati da e a, conta e restituisce il numero di giorni che intercorrono tra le due date Se faccio passaggio per riferimento e uso l’asterisco, poi me lo devo portare appresso in tutto il sottoprogramma Ricorsione Sottoprogramma che direttamente o indirettamente richiama se stesso _f ( ) { f( ); } Sottoprogramma del fattoriale in versione ricorsiva _f( ) { h( ); } ↓ _h( ) { f( ); } Argomenti da riga di comando Tipicamente il programma controlla che i valori siano compatibili e non procede nell'elaborazione se così non è, visualizzando il modo di uso del programma Il sottoprogramma main riceve in ingresso - una array di indirizzi di caratteri → char*argv[] - il numero di elementi presenti nell’array → int argc Tutti i caratteri presenti sulla riga di comando vengono memorizzati in un unico array di caratteri, gestito a stringa (terminatore in fondo) e mettendo un terminatore al posto di ogni spazio → argc: 4 (incluso il nome del programma stesso, che c’è sempre, per cui argc>=1 sempre, e mettendo un terminatore al posto degli spazi) → argv[]: 4 indirizzi (un array di 4 riferimenti a caratteri, gestiti a stringa, per cui con ‘\0’ alla fine) Tutti gli argomenti acquisiti sulla riga di comando sono stringhe, nel caso si tratti di valori numerici è necessario calcolare il corrispondente valore numerico Sottoprogrammi di libreria (libreria: stdlib.h) - int atoi(char[]); riceve in ingresso una stringa (che si presuppone contenere caratteri numerici) e restituisce l’intero corrispondente - float atof (char[]); riceve in ingresso una stringa (che si presuppone contenere caratteri numerici) e restituisce il valore reale corrispondente Tutti gli argomenti acquisiti sulla riga di comando sono stringhe, nel caso si tratti di singoli caratteri è necessario riferirsi al carattere - è il primo carattere di una stringa a cui fa riferimento l’argomento → argv[i][0] - è il contenuto all’indirizzo cui fa riferimento l’argomento → *(argv[i]) int main (int argc, char*argv[]){ if (argc==_numero_atteso_){ /*utilizzo degli argomenti e flusso principale*/ }else{ printf(“parametri non corretti\n”); return 0; } La memoria allocata dinamicamente in un sottoprogramma rimane lì finché non mi serve più la memoria e faccio una free Scrivo un programma che chiede all’utente quanti dati interi vorrà utilizzare e li acquisisce, li riordina in senso crescente e li visualizza Scrivo un sottoprogramma che ricevuta in ingresso una stringa crea e restituisce una nuova stringa che contiene esclusivamente le consonanti della stringa di partenza. Scrivo un programma chiamante che acquisita una stringa avvalendosi del sottoprogramma precedentemente specificato, visualizza le consonanti in essa presenti. Liste concatenate semplici Chiediamo all’utente un valore intero (valore sentinella che ci dirà quando finiscono i dati e non fa parte di essi) e tutti i valori che vuole darci fino al valore sentinella e visualizziamo tutti i valori superiori al valor medio di quelli acquisiti. - Ho bisogno di memorizzare una serie i dati per visualizzare poi quelli superiori al valor medio - Non so quanti dati mi vengono dati e non posso stimarlo (array) o calcolarlo (malloc) - Lista concatenata di elementi in cui mi serve il parametro dell’informazione successiva - Mi serve una nuova struttura flessibile che mi permetta di dire dove sta l’informazione successiva, aggiungere e togliere informazioni - Una lista concatenata semplice è una struttura che mi permette di allocare memoria, di tenere traccia di quella allocata e liberarla quando non serve più - Le liste si appoggiano sul meccanismo dell’allocazione dinamica Mi serve una parametro che mi dia un riferimento al primo parametro che arriva (indirizzo) L’unica variabile che posso e devo dichiarare è il riferimento al primo elemento della lista (variabile di tipo indirizzo che guarderà agli elementi successivi) Diremo che la lista all’inizio vale NULL (perché non sta ancora guardando da nessuna parte) L’ultimo elemento della lista ha il riferimento che guarda a NULL Utilizzo il sottoprogramma append per mettere nuovi elementi in coda alla lista ilist_t*append (ilist_t*, int); ilist_t*insertinorder (ilist_t*, int) → sottoprogramma che inserisce i valori in ordine ilist_t*push (ilist_t*, int) → sottoprogramma che aggiunge in testa anziché in coda sottoprogramma find → ricevuto in ingresso un intero, controlla se è presente nella lista e restituisce il suo indirizzo • il valor medio lo calcolo mentre acquisisco i dati • come dichiarare un tipo di dato opportuno per la dichiarazione delle liste? while (→l.next) /*finché non arrivi sull’ultimo elemento della lista*/ el=el→next; el=el→next; el→next=n } else printf (“append: errore allocazione memoria\n”); return head; } insertinorder ilist_t*insertinorder (ilist_t*h, int num) { ilist_t*n; /*elemento nuovo*/ ilist_t*pre; /*elemento prima di dove inserire*/ n=(ilist_t*)malloc(sizeof(ilist_t) if (n){ n→val=num; if(!h||(num<=h→val){ n→next=h; h=n; } else { /* pre guarda al primo che è senz’altro più piccolo*/ pre=h; /*fino a che c’è un altro valore e il valore è più piccolo*/ while (pre→next && num>pre→next→val) pre=pre→next; /*se sono sull’ultimo elemento o prima di uno più grande*/ /*inserisco – non fa differenza*/ n→next=pre→next; pre→ next=n; } else printf (“insertinorder: errore allocazione memoria\n”); return h; } Scrivo un sottoprogramma che trova la lunghezza della lista int listlength (ilist_t*h) { ilist_t*el; int l; el=h; l=0; while (el){ l++; el=el→next; } return l; } Provo a realizzare il sottoprogramma find che riceve in ingresso la testa della lista e un numero e restituisce l’indirizzo se lo trova, altrimenti NULL delete Cancellare un elemento della lista 1. eliminare il primo elemento della lista (eccezione) - devo dire alla testa di guardare all’elemento che segue quello che voglio eliminare - libero la memoria 2. eliminare un elemento in mezzo alla lista - devo avere le informazioni anche dell’elemento precedente a quello che voglio eliminare int listlength (ilist_t*h) { int l; l=0; while (h){ l++; h=h→next } return l; } int listlengthr (ilist_t*h) { if (!h) return 0; return 1+listlengthr (h→next); } ilist_t*delete (ilist_t*h, int num) { ilist_t*pre, *del; del=h; while (h && (h→val==num)){ del=h; h=h→next /*oppure h=del→next*/ free (del); } if (h){ pre=h; while (pre && pre→next) if (pre→next→val==num){ del=pre→next; pre→next=del→next; /*pre→next=pre→next→next*/ free (del); } else pre=pre→next; } return h; } L’accesso ai file Prima di leggere un file bisogna aprirlo e è necessario che l’apertura vada a buon fine - il file c’è - il file non c’è - il file c’è ma non ho i diritti d’accesso - il file c’è ma è vuoto Spesso non sappiamo la quantità di dati all’interno di un file La lettura non fallisce e continua finché non c’è nulla di nuovo da leggere EOF → End Of File Trasferimento dati dal file alla memoria Scrivo un sottoprogramma che riceve in ingresso il nome di un file binario e un array di interi, legge dal file i dati, li mette nell’array e dice quanti ne ha messi. Scrivo un programma che acquisisce una sequenza di valori di lunghezza a priori ignota, terminata usando l’utente inserisce un valore inferiore a 1. Il programma salva i numeri primi nel file primi.csv scrivendoli uno per riga In questo esercizio non devo memorizzare i dati 3 modi per lavorare sul dato letto while (fscaf(fin, “%c”, &c)==1){ /* laavoro sul dato */ … } ris=fscanf (fin, “%c”, &c); while (ris==1){ /* lavoro sul dato */ … ris=fscanf (fin, “%c”, &c); } fscanf (fin, “%c”, &c); while (!feof (fin)){ /* lavoro sul dato */ … fscanf (fin, “%c”, &c); } Le variabili globali #include #define typedef prototipi int nelem; list_t*head=NULL; int main (int argc, char*argv[]) { … } Il costrutto switch switch = ci dà la possibilità di scegliere come si deve comportare una variabile if (var==1) istr1 else if (var==2) istr2 else if (var==3) istr3 il costrutto switch può essere utilizzato quando abbiamo un intero o un singolo carattere che sottende un intero switch (var){ case1: istr1 break; /*interrompe l’esecuzione delle istruzioni*/ case2: istr2 break; default: /*opzionale, istruzione che vale in tutti gli altri casi non elencati*/ } switch (var){ case ‘a’: case ‘e’: case ‘i’: case ‘o’: case ‘u’: ncov++; break; default: ncons++; } - solleva il segnale di read rispetto alla memoria e aspetto che ci si il segnale di ackowledged dalla memoria, cosicché ciò che sta transitando sul bus dati è ciò che sta cercando - carica l’istruzione nel memory data register e la copia nell’instruction register - quindi incrementa di uno il contenuto del program counter - interpreta l’istruzione che è arrivata e la decodifica (attiva tutte quelle micro operazioni che compiono l’istruzione) La CPU continua a ripetere questo ciclo → prelievo, decodifico, eseguo FETCH, DECODE, EXECUTE Dispositivi periferici Aggiungono la semplicità di interazione con il sistema di calcolo • Memoria di massa - memoria per contenere e memorizzare in modo permanente dati e programmi che vogliamo poter elaborare ed eseguire - ha capacità superiori rispetto alla memoria di lavoro - si tengono dati e programmi non in esecuzione - se la memoria di lavoro non è sufficiente, una parte della memoria di massa viene utilizzata • Periferiche d’ingresso • Periferiche d’uscita • Periferiche d’ingresso e uscita Istruzioni - Istruzioni aritmetico-logiche - Istruzioni di leggi/scrivi - Istruzioni che fanno riferimento al flusso di controllo (invece di eseguire l’istruzione successiva, viene eseguita un’istruzione che sta in un punto diverso del mio programma) → salto, jump (si aumenta comunque il program counter, si ottimizza il caso più frequente) - Se c’è stato un salto, lo capiamo dal contenuto del PSW - Ogni istruzione ha due parti: codice operativo e operandi - I bit del codice operativo vengono posti in ingresso ad un circuito di decodifica che attiva gli appositi segnali - Il linguaggio che il nostro processore è in grado di interpretare è Assembly Piramide della gerarchia di memoria - Più si è in alto, più si è vicino alla CPU - Gli elementi in alto hanno tempi d’accesso ridotti rispetto a quelli in basso - La memoria cache è quella ad accesso più rapido, che costa di più - Gli elementi in basso hanno più capacità I software Sopra lo stato della macchina fisica (hardware) viene costruita una serie di strati software che costituiscono il sistema operativo L’obiettivo del sistema operativo è utilizzare le risorse del sistema di calcolo rendendole disponibili e facilmnete utilizzabili agli strati superiori; nel fare ciò rende disponibile le risorse con una visione semplificata ed estesa - semplificata: riduce e maschera la complessità con cui rendo disponibili le risorse; ad esempio se voglio accedere alla memoria di massa verrà resa disponibile una primitiva d’accesso senza specificare la tipologia di hard disk/ssd - estesa: le risorse sono limitate, ma dal punto dell’esecutore sopra sono illimitate e non si preoccupano dell’occupazione della memoria; potenziata rispetto alle reali risorse che la macchina mi dà a disposizione Risorse: Kernel = si occupa della gestione del processore e delle divisione del tempo di elaborazione Il kernel semplifica i dettagli di cosa vuol dire mandare in esecuzione un programma e offe una visione per cui c’è a disposizione un processore per ogni processo che voglia essere eseguito - Quando lanciamo un programma, questa diventa attivo e viene chiamato processo (= programma in esecuzione) - Un programma può dar vita a più processi - Sistema mono programmato = un processo alla volta - Il kernel fa si che si possa comunque mandare in esecuzione un numero di programmi maggiore a uno - Viene condiviso e suddiviso il tempo di elaborazione - Quando creo un nuovo processo, questo è pronto per essere eseguito, ma non ha la CPU →rimane in uno stato di "ready" - Quando al processo viene data la CPU, questo passa in fase di esecuzione, “run” - Un processo in esecuzione potrebbe essere in attesa di eventi esterni e non aver bisogno della CPU → finisce nello stato di “wait” - Strategia → il processo dopo essere stato nello stato “run” ogni quanto di tempo viene sospeso e rimesso in coda tra i processi “ready”, anche se non ha bisogno di nulla dall’esterno - Se primo che il quanto scada hanno bisogno di eventi esterni, finiscono nello stato di “wait” e non sfruttano appieno il proprio tempo - Si vuole consentire a tutti i processi un quanto di tempo per cui a rotazione vengono eseguiti e messi in coda - Ogni cambio di processo avviene un salvataggio del contesto e ripristino del contesto del processo che aspettava - Il kernel fa si che l’utente non si debba preoccupare di cosa significa eseguire un processo e del fatto che ci sono più processi in esecuzione - Scheduling → politiche di organizzazione dell’utilizzo del processore e del tempo - Politica del round robin (esistono anche altre politiche, come first come first serving) Gestore della memoria lavoro - Il gestore fornisce un meccanismo tramite il quale il processo accede alla memoria di lavoro tramite i concetto dell’indirizzo - Ogni processo ha a disposizione una propria area di memoria, che deve essere logicamente accessibile e protetta da processi estranei - Mette a disposizione una memoria che non ha i limiti fisici di quella che è la memoria effettiva e non fa percepire l’idea che ci siano altre aree di memoria dedicate a altri processi - Memory Management Unit = aiuta a rendere disponibile a un processo una parte di memoria di lavoro disaccoppiando il fatto che il processo non sappia dove si trova in memoria - Frammentazione della memoria (interna o esterna) - Il gestore dei processi si occupa di trovare dello spazio in memoria assegnando una pagina al processo, e nel caso fosse tutta occupata scarica la memoria di un processo che non è running e carica la pagine del processo da eseguire - Una parte della memoria di massa viene usata per ospitare le informazioni dei processi in esecuzione che non entrano nella memoria di lavoro (swapping) Gestore delle periferiche Strato che si preoccupa di darci una visione semplificata ed estesa Il driver è una serie di istruzioni associate in modo specifico a una certa periferica, realizza le operazioni fondamentali e mette e disposizione primitive generiche d’accesso semplificate Sono previste modalità diverse di scambio di informazioni e di comunicazione: 1. Polling (il master del sistema, la CPU, periodicamente va a vedere se i dispositivi periferici hanno informazioni da scambiare) 2. Interrupt (si dà ad ogni dispositivo la possibilità di segnalare la richiesta di interazione) Esiste un ulteriore co-processore (DMA = Direct Memory Access) che si occupa dell’accesso diretto alla memoria