Lab. di Sistemi Operativi

Transcript

Lab. di Sistemi Operativi
Lab. di Sistemi Operativi
Esercitazioni proposte per la lezione del 15 aprile 2011
Utilizzando il compilatore gcc in Linux e disponendosi in gruppi di due persone per ogni PC del laboratorio.
1. Utilizzare l’editor vim per realizzare il sorgente in linguaggio ”c” di un programma il cui obiettivo
sia quello di emettere su stdout il messaggio ”Ciao, mondo!”. Assegnare a tale file sorgente il nome
”es71.c”.
2. Utilizzare il gcc per produrre il file eseguibile corrispondente al comportamento descritto dal sorgente
es71.c
3. Quale effetto ha il comando gcc invocato con argomento es71.c? Cosa viene prodotto?
4. Che cosa rappresenta il file a.out?
5. Mettete in esecuzione il file a.out prodotto dal compilatore. Visualizzate l’exit value del processo che
ha eseguito a.out e, nel caso in cui non abbiate terminato la funzione main con il return di un valore
specifico, modificate il sorgente di conseguenza
6. Perfezioniamo l’invocazione del compilatore: il gcc accetta un numero enorme di opzioni che modificano
il proprio comportamento. Possiamo, ad esempio, utilizzare l’opzione -o seguita da un argomento per
indurre il compilatore ad assegnare al file prodotto il nome espresso dall’argomento. Ivocare il gcc sul
sorgente es71.c in modo che venga generato un file eseguibile di nome es71
7. Mettiamo in esecuzione il nuovo file prodotto dal compilatore. Verifichiamo che i caratteri emessi dalla
funzione printf sono realmente diretti su stdout provando a ridirigere alternativamente stdout e stderr
su /dev/null.
8. Introduciamo qualche imperfezione nel file sorgente, ad esempio rimuovendo la direttiva include necessaria per il prototipo della printf. Come reagisce il compilatore? Cambia qualcosa se aggiungiamo
l’opzione -Wall?
9. Come utilizzare le funzioni di output formattato: stampare il valore di una variabile intera con printf
(es72.c).
10. Stampare un singolo carattere (es73.c).
11. Stampare un array di caratteri (es74.c).
12. Array e puntatori: Modificare es74.c in modo da stampare anche il valore dell’array come puntatore.
13. Di che tipo è la variabile array?
14. Possiamo assegnare un valore alla variabile array? Proviamo a farlo modificando opportunamente
es74.c.
15. Possiamo avere lvalue dello stesso tipo di array? Creiamo la variabile a2 di tipo char *.
16. a2 e’ una copia di array? Se modifico il contenuto di array cosa avviene a a2?
17. Accesso alla linea di comando: Creare un programma che visualizzi il numero di argomenti presenti
nella propria command line (es75.c)
18. Modificare es75.c in modo che venga visualizzato il valore di argv.
1
19. (30 minuti) es76.c Possibile implementazione del comando head di Unix. Il comando head riporta su
stdout le prime linee di un file. In assenza di opzioni sulla command line, le linee stampate sono 10 e
il file di input è stdin. Il comando accetta due opzioni, -n per impostare il numero di linee diverso da
10 e -f per forzare l’apertura di un file diverso da stdin.
20. (45 minuti) es77.c Il filtro deve accettare N parametri che rappresentano nomi di file F1, . . . , FN e
riportare su stdout un carattere di ogni file indicato, il primo carattere del primo file F1, l’ultimo carattere dell’ultimo file FN, il secondo carattere del secondo file F2, il penultimo carattere del penultimo
file FN-1 e cosı̀ di seguito. Nel caso in cui un file non abbia numero di caratteri sufficienti a consentire
l’estrazione, il filtro deve terminare immediatamente con exit value pari a 1, mentre deve restituire 0
in caso contrario.
21. (45 minuti) es78.c Prova di Fondamenti di informatica del 6 settembre 2000: Si progetti il
filtro filtro utilizzando il linguaggio C e le funzioni primitive che consentono di operare sui file. Il
programma deve prevedere un singolo parametro (R). Il filtro filtro deve riportare sullo standard
output una selezione dei caratteri dello standard input: in particolare, si deve riportare in uscita un
carattere se e solo se non compare nella stringa R oppure compare un numero di volte maggiore di 1.
22. (25 minuti) es79.c Modificare es76. in modo da usare getopt. Usare man getopt.
2
Soluzione
Esercitazioni proposte per la lezione del 15 aprile 2011
Utilizzando il compilatore gcc in Linux e disponendosi in gruppi di due persone per ogni PC del laboratorio.
1. Utilizzare l’editor vim per realizzare il sorgente in linguaggio ”c” di un programma il cui obiettivo
sia quello di emettere su stdout il messaggio ”Ciao, mondo!”. Assegnare a tale file sorgente il nome
”es71.c”.
Soluzione:
#include <s t d i o . h>
int main ( int argc , char ∗∗ argv ) {
p r i n t f ( ” Ciao , mondo ! \ n” ) ;
return ( 0 ) ;
}
2. Utilizzare il gcc per produrre il file eseguibile corrispondente al comportamento descritto dal sorgente
es71.c
Soluzione:
$ gcc es71.c
3. Quale effetto ha il comando gcc invocato con argomento es71.c? Cosa viene prodotto?
Soluzione:
$ ls -l
...
-rw-r--r--rwxr-xr-x
...
1 valealex valealex
1 valealex valealex
94 2006-02-12 21:54 es71.c
7053 2006-02-12 21:54 a.out
4. Che cosa rappresenta il file a.out?
Soluzione:
# Il file a.out contiene la sequenza di istruzioni binarie che il
# microprocessore deve eseguire per produrre il comportamento descritto
# dal file sorgente secondo le regole sintattiche del linguaggio c
5. Mettete in esecuzione il file a.out prodotto dal compilatore. Visualizzate l’exit value del processo che
ha eseguito a.out e, nel caso in cui non abbiate terminato la funzione main con il return di un valore
specifico, modificate il sorgente di conseguenza
Soluzione:
$ ./a.out
$ echo $?
0
6. Perfezioniamo l’invocazione del compilatore: il gcc accetta un numero enorme di opzioni che modificano
il proprio comportamento. Possiamo, ad esempio, utilizzare l’opzione -o seguita da un argomento per
indurre il compilatore ad assegnare al file prodotto il nome espresso dall’argomento. Ivocare il gcc sul
sorgente es71.c in modo che venga generato un file eseguibile di nome es71
1
Soluzione:
$ gcc es71.c -o es71
$ ls -l
7. Mettiamo in esecuzione il nuovo file prodotto dal compilatore. Verifichiamo che i caratteri emessi dalla
funzione printf sono realmente diretti su stdout provando a ridirigere alternativamente stdout e stderr
su /dev/null.
Soluzione:
$ ./es71 > /dev/null
$ ./es71 2> /dev/null
Ciao, mondo!
8. Introduciamo qualche imperfezione nel file sorgente, ad esempio rimuovendo la direttiva include necessaria per il prototipo della printf. Come reagisce il compilatore? Cambia qualcosa se aggiungiamo
l’opzione -Wall?
Soluzione:
$
$
#
#
#
#
#
gcc es71.c -o es71
gcc es71.c -o es71 -Wall
Nella seconda invocazione, si richiede al gcc l’emissione dei
messaggi di warning (avvertimento) legati al controllo sintattico
del sorgente. Omettendo l’include, il gcc segnala l’assenza del
prototipo della funzione printf, effettivamente incluso nel file
stdio.h
9. Come utilizzare le funzioni di output formattato: stampare il valore di una variabile intera con printf
(es72.c).
Soluzione:
/∗ f i l e : e s 7 2 . c ∗/
/∗ j o b : stampa i l v a l o r e d i i , i n t e r o ∗/
/∗ c o m p i l a r e con : g c c −Wall e s 7 2 . c −o e s 7 2 ∗/
#include <s t d i o . h>
int i ; /∗ v a r i a b i l e g l o b a l e , d i c h i a r a z i o n e ∗/
int main ( int argc , char ∗∗ argv ) {
i =0; /∗ assegnamento ∗/
p r i n t f ( ” i v a l e : %d\n” , i ) ;
return ( 0 ) ;
}
10. Stampare un singolo carattere (es73.c).
Soluzione:
/∗ f i l e : e s 7 3 . c ∗/
/∗ j o b : stampa i l v a l o r e d i c , c a r a t t e r e ∗/
/∗ c o m p i l a r e con : g c c −Wall e s 7 3 . c −o e s 7 3 ∗/
#include <s t d i o . h>
char c ; /∗ v a r . g l o b a l e , d i c h i a r a z i o n e ∗/
int main ( int argc , char ∗∗ argv ) {
c= ’A ’ ; /∗ assegnamento ∗/
p r i n t f ( ” c v a l e : %c \n” , c ) ;
return ( 0 ) ;
}
2
11. Stampare un array di caratteri (es74.c).
Soluzione:
/∗ f i l e : e s 7 4 . c ∗/
/∗ j o b : stampa un a r r a y d i cha r ∗/
#include <s t d i o . h>
char a r r a y [ 5 0 ] ; /∗ d i c h i a r a z i o n e , r i s e r v a
l o s p a z i o n e c e s s a r i o i n memoria ∗/
int main ( int argc , char ∗∗ argv ) {
a r r a y [ 0 ] = ’A ’ ; /∗ assegnamento a l
primo e l e m e n t o ∗/
a r r a y [ 1 ] = ’ \0 ’ ; /∗ secondo elemento ,
c a r a t t e r e n u l l o che r a p p r e s e n t a
i l f i n e s t r i n g a ∗/
p r i n t f ( ” a r r a y v a l e %s \n” , a r r a y ) ;
return ( 0 ) ;
}
12. Array e puntatori: Modificare es74.c in modo da stampare anche il valore dell’array come puntatore.
Soluzione:
/∗ f i l e : e s 7 4 . c ∗/
/∗ j o b : stampa un a r r a y d i cha r ∗/
#include <s t d i o . h>
char a r r a y [ 5 0 ] ; /∗ d i c h i a r a z i o n e , r i s e r v a
l o s p a z i o n e c e s s a r i o i n memoria ∗/
int main ( int argc , char ∗∗ argv ) {
a r r a y [ 0 ] = ’A ’ ; /∗ assegnamento a l
primo e l e m e n t o ∗/
a r r a y [ 1 ] = ’ \0 ’ ; /∗ secondo elemento ,
c a r a t t e r e n u l l o che r a p p r e s e n t a
i l f i n e s t r i n g a ∗/
p r i n t f ( ” a r r a y v a l e %s \n” , a r r a y ) ;
p r i n t f ( ” a r r a y v a l e %p\n” , a r r a y ) ;
return ( 0 ) ;
}
13. Di che tipo è la variabile array?
Soluzione:
#
#
#
#
#
#
#
Come ogni vettore di elementi, la variabile
array e’ di tipo "puntatore" quindi rappresenta
un indirizzo in memoria. Con maggiore attenzione,
possiamo affermare che array e’ un puntatore a
char (il tipo degli elementi contenuti nel
vettore) che punta alla locazione occuapata dal
primo elemento.
14. Possiamo assegnare un valore alla variabile array? Proviamo a farlo modificando opportunamente
es74.c.
Soluzione:
3
/∗ f i l e : e s 7 4 . c ∗/
/∗ j o b : stampa un a r r a y d i cha r ∗/
#include <s t d i o . h>
char a r r a y [ 5 0 ] ; /∗ d i c h i a r a z i o n e , r i s e r v a
l o s p a z i o n e c e s s a r i o i n memoria ∗/
int main ( int argc , char ∗∗ argv ) {
a r r a y [ 0 ] = ’A ’ ; /∗ assegnamento a l
primo e l e m e n t o ∗/
a r r a y [ 1 ] = ’ \0 ’ ; /∗ secondo elemento ,
c a r a t t e r e n u l l o che r a p p r e s e n t a
i l f i n e s t r i n g a ∗/
p r i n t f ( ” a r r a y v a l e %s \n” , a r r a y ) ;
p r i n t f ( ” a r r a y v a l e %p\n” , a r r a y ) ;
a r r a y =(char ∗ ) 0 ; /∗ e r r o r e : a r r a y non l v a l u e ∗/
/∗ un l v a l u e e ’ q u a l c o s a che puo ’ t r o v a r s i a l l a s i n i s t r a
d i un assegnamento ( l v a l u e= l e f t v a l u e ) . a r r a y e ’ un
p u n t a t o r e ma d i v a l o r e c o s t a n t e i n quanto punta e p u n t e r a ’ sempre
a l l a prima l o c a z i o n e d e l l ’ are a d i memoria r i s e r v a t a a l l ’ a t t o
d e l l a sua d i c h i a r a z i o n e . Per i l c o m p i l a t o r e a r r a y e ’ a s s i m i l a b i l e
ad un s i m b o l o , q u i n d i ad una c o s t a n t e . ∗/
p r i n t f ( ” a r r a y v a l e %p\n” , a r r a y ) ;
return ( 0 ) ;
}
15. Possiamo avere lvalue dello stesso tipo di array? Creiamo la variabile a2 di tipo char *.
Soluzione:
/∗ f i l e : e s 7 4 . c ∗/
/∗ j o b : stampa un a r r a y d i cha r ∗/
#include <s t d i o . h>
char a r r a y [ 5 0 ] ; /∗ d i c h i a r a z i o n e , r i s e r v a
l o s p a z i o n e c e s s a r i o i n memoria ∗/
char ∗ a2 ; /∗ d i c h i a r a z i o n e , r i s e r v a l o
s p a z i o p e r un p u n t a t o r e ∗/
int main ( int argc , char ∗∗ argv ) {
a r r a y [ 0 ] = ’A ’ ;
a r r a y [ 1 ] = ’ \0 ’ ;
a2=a r r a y ; /∗ c o r r e t t o : a2 , v a r i a b i l e d e l l o s t e s s o
t i p o d i array , v i e n e r e s a u g u a l e ad a r r a y ∗/
p r i n t f ( ” a2 v a l e %s \n” , a2 ) ;
p r i n t f ( ” a2 v a l e %p\n” , a2 ) ;
a2=(char ∗ ) 0 ; /∗ c o r r e t t o : a2 e ’ l v a l u e ∗/
p r i n t f ( ” a2 v a l e %p\n” , a2 ) ;
return ( 0 ) ;
}
16. a2 e’ una copia di array? Se modifico il contenuto di array cosa avviene a a2?
Soluzione:
#
#
#
#
a2 e’ un secondo riferimento alla memoria puntata da array, quindi
sia a2 che array fanno riferimento alla stessa porzione di memoria.
a2, quindi, non e’ una copia dell’intero vettore ma solo un nuovo
riferimento. Se modifico array[2], anche a2[2] sara’ modificato.
4
17. Accesso alla linea di comando: Creare un programma che visualizzi il numero di argomenti presenti
nella propria command line (es75.c)
Soluzione:
/∗ f i l e : e s 7 5 . c ∗/
/∗ j o b : a c c e s s o a l l a command l i n e ∗/
#include <s t d i o . h>
int main ( int argc , char ∗∗ argv ) {
p r i n t f ( ” I l v a l o r e d i a r g c e ’ : %d\n” , a r g c ) ;
return ( 0 ) ;
}
/∗ L ’ i n t e r f a c c i a d e l l a f u n z i o n e main ( ) r i s p e c c h i a
∗ q u e l l a dei p r o c e s s i unix : in i n g r e s s o troviamo
∗ l e i n f o r m a z i o n i ( argc , a r g v ) n e c e s s a r i e p e r a c c e d e r e
∗ a l l a command l i n e ed i n u s c i t a possiamo r i t o r n a r e
∗ un numero i n t e r o ( e x i t v a l u e ) ∗/
18. Modificare es75.c in modo che venga visualizzato il valore di argv.
Soluzione:
/∗ f i l e : e s 7 5 . c ∗/
/∗ j o b : a c c e s s o a l l a command l i n e ∗/
#include <s t d i o . h>
int main ( int argc , char ∗∗ argv ) {
p r i n t f ( ” I l v a l o r e d i a r g c e ’ : %d\n” , a r g c ) ;
p r i n t f ( ” I l v a l o r e d i argv e ’ : %p\n” , argv ) ;
/∗ a r g v e ’ una v a r i a b i l e d i t i p o p u n t a t o r e ,
∗ i l t i p o d e g l i e l e m e n t i c u i punta e ’ ” ch ar ∗” ,
∗ q u i n d i a r g v e ’ un p u n t a t o r e a un p u n t a t o r e
∗ a c a r a t t e r e . a r g v [ 0 ] e ’ i l primo e l e m e n t o
∗ ( p u n t a t o r e a c a r a t t e r e ) d e l v e t t o r e a r g v ∗/
p r i n t f ( ” I l v a l o r e d i argv [ 0 ] e ’ : %p\n” , argv [ 0 ] ) ;
/∗ a r g v [ 0 ] e ’ un p u n t a t o r e a c a r a t t e r e , q u i n d i
∗ p o t r e b b e e s s e r e una s t r i n g a c ∗/
p r i n t f ( ” I l v a l o r e d i argv [ 0 ] , come s t r i n g a , e ’ : %s \n” , argv [ 0 ] ) ;
/∗ a r g v [ 0 ] e ’ l a prima s t r i n g a d e l l a command l i n e ;
∗ i n g e n e r a l e a r g v [ i ] e ’ l a i −esima s t r i n g a d e l l a
∗ command l i n e a p a t t o che i s i a minore d i a r g c ∗/
return ( 0 ) ;
}
19. (30 minuti) es76.c Possibile implementazione del comando head di Unix. Il comando head riporta su
stdout le prime linee di un file. In assenza di opzioni sulla command line, le linee stampate sono 10 e
il file di input è stdin. Il comando accetta due opzioni, -n per impostare il numero di linee diverso da
10 e -f per forzare l’apertura di un file diverso da stdin.
Soluzione:
/∗ f i l e : e s 7 6 . c ∗/
/∗ comando head ∗/
#include <s t d i o . h>
#include < f c n t l . h>
#include <u n i s t d . h>
#include <s t r i n g . h>
5
#include < s t d l i b . h>
int main ( int argc , char ∗∗ argv ) {
char c , ∗ f i l e n a m e ;
int l i n e s = 0 , f d i n = 0 , f d o u t = 1 , i = 0 , n = 1 0 ;
f o r ( i = 1 ; i < a r g c − 1 ; i ++) {
/∗ c o n t r o l l o −n X ∗/
i f ( strcmp ( argv [ i ] , ”−n” ) == 0 ) {
n = a t o i ( argv [ i + 1 ] ) ;
i ++; /∗ mangia l ’ argomento ∗/
continue ; /∗ p r o s s i m o ∗/
}
i f ( strcmp ( argv [ i ] , ”−f ” ) == 0 ) {
f i l e n a m e = ( argv [ i + 1 ] ) ;
f d i n = open ( f i l e n a m e , O RDONLY) ;
i ++; /∗ mangia l ’ argomento ∗/
i f ( f d i n < 0) {
p r i n t f ( ” e r r o r : %s non found . \ n” , f i l e n a m e ) ;
return ( 1 ) ;
}
continue ; /∗ p r o s s i m o ∗/
}
p r i n t f ( ” e r r o r i n v a l i d o p t i o n : %s \n” , argv [ i ] ) ;
return ( 2 ) ;
}
while ( r e a d ( f d i n , &c , 1 ) > 0 ) {
i f ( l i n e s < n) {
w r i t e ( fdout , &c , 1 ) ;
}
i f ( c == ’ \n ’ ) {
l i n e s ++;
}
}
return ( 0 ) ;
}
20. (45 minuti) es77.c Il filtro deve accettare N parametri che rappresentano nomi di file F1, . . . , FN e
riportare su stdout un carattere di ogni file indicato, il primo carattere del primo file F1, l’ultimo carattere dell’ultimo file FN, il secondo carattere del secondo file F2, il penultimo carattere del penultimo
file FN-1 e cosı̀ di seguito. Nel caso in cui un file non abbia numero di caratteri sufficienti a consentire
l’estrazione, il filtro deve terminare immediatamente con exit value pari a 1, mentre deve restituire 0
in caso contrario.
Soluzione:
#include
#include
#include
#include
int main
<s t d i o . h>
< f c n t l . h>
<s y s / t y p e s . h>
<u n i s t d . h>
( int argc , char ∗∗ argv ) {
int i ;
int f d ;
char c ;
6
char nr ;
i f ( a r g c <= 1 ) {
p r i n t f ( ” E r r o r e n e l numero d i p a r a m e t r i \n” ) ;
return ( −1);
}
i f ( ( ( a r g c − 1 ) % 2 ) != 0 )
{
p r i n t f ( ” E r r o r e numero non p a r i d i p a r a m e t r i \n” ) ;
return ( −2);
}
f o r ( i = 0 ; i < ( a r g c − 1 ) / 2 ; i ++) {
i f ( ( f d = open ( argv [ i + 1 ] , O RDONLY) ) < 0 ) {
puts ( ” Errore apertura f i l e ” ) ;
return ( −3);
}
l s e e k ( fd , ( long ) i , SEEK SET ) ;
/∗ l s e e k sempre ok , anche s e s u p e r a l a f i n e ∗/
nr = r e a d ( fd , &c , 1 ) ;
/∗ c o n t r o l l o rea d ∗/
i f ( nr !=1) return ( 1 ) ;
w r i t e ( 1 , &c , nr ) ;
i f ( ( f d = open ( argv [ a r g c − i − 1 ] , O RDONLY) ) < 0 ) {
puts ( ” Errore apertura f i l e ” ) ;
return ( −3);
}
/∗ c o n t r o l l o EINVAL ∗/
i f ( l s e e k ( fd , −(long ) ( i + 1 ) , SEEK END) <0) {
return ( 1 ) ;
}
nr = r e a d ( fd , &c , 1 ) ;
/∗ ho sempre un c a r a t t e r e s e sono q u i ∗/
w r i t e ( 1 , &c , nr ) ;
}
return ( 0 ) ;
}
21. (45 minuti) es78.c Prova di Fondamenti di informatica del 6 settembre 2000: Si progetti il
filtro filtro utilizzando il linguaggio C e le funzioni primitive che consentono di operare sui file. Il
programma deve prevedere un singolo parametro (R). Il filtro filtro deve riportare sullo standard
output una selezione dei caratteri dello standard input: in particolare, si deve riportare in uscita un
carattere se e solo se non compare nella stringa R oppure compare un numero di volte maggiore di 1.
Soluzione:
/∗ e s 7 8 . c ∗/
#include <u n i s t d . h>
char ∗R;
int c o n t e g g i o [ 2 5 6 ] ;
int main ( int argc , char ∗∗ argv ) {
int i ;
char ch ;
i f ( a r g c !=2) return ( 1 ) ;
R=argv [ 1 ] ;
f o r ( i =0; R[ i ] ; i ++) {
c o n t e g g i o [R[ i ] ] + + ;
7
}
for ( ; ; ) {
i f ( r e a d (0 ,& ch , 1 ) ! = 1 ) break ;
i f ( c o n t e g g i o [ ch ] ! = 1 ) w r i t e (1 ,& ch , 1 ) ;
}
return ( 0 ) ;
}
22. (25 minuti) es79.c Modificare es76. in modo da usare getopt. Usare man getopt.
Soluzione:
/∗ f i l e : e s 7 9 . c ∗/
/∗ comando head con g e t o p t ∗/
#include <s t d i o . h>
#include < f c n t l . h>
#include <u n i s t d . h>
#include <s t r i n g . h>
#include < s t d l i b . h>
int main ( int argc , char ∗∗ argv ) {
char c ;
int l i n e s = 0 , f d i n = 0 , f d o u t = 1 , n = 1 0 ;
int opt ;
for ( ; ; ) {
opt=g e t o p t ( argc , argv , ”n : f : ” ) ;
i f ( opt==−1) break ;
switch ( opt ) {
case ’ n ’ :
n = a t o i ( optarg ) ;
break ;
case ’ f ’ :
f d i n = open ( optarg , O RDONLY) ;
break ;
}
}
while ( r e a d ( f d i n , &c , 1 ) > 0 ) {
i f ( l i n e s < n) {
w r i t e ( fdout , &c , 1 ) ;
}
i f ( c == ’ \n ’ ) {
l i n e s ++;
}
}
return ( 0 ) ;
}
8