uniroma1
.*_main.c
.cognome.nome
. Sulle postazioni del laboratorio sarà /home/studente/Desktop/cognome.nome/
.cognome.nome.zip
(zip -r cognome.nome.zip cognome.nome/
).cognome.nome.zip
.rm -rf cognome.nome
).Per maggiori informazioni fate riferimento al regolamento delle esercitazioni.
stdin
in C)Questo è un esercizio di mera conoscenza del linguaggio C. Il canale stdin
(di tipo FILE*
) modella in C la sorgente di caratteri ASCII che proviene come input, salvo diversamente specificato, dal terminale. Si chiede di scrivere nel file E1-get-cmd-line/e1.c
una funzione void get_cmd_line(char* argv[]);
che legge la prossima linea di testo da stdin
, estrae ciascun token (sequenza consecutiva di caratteri, esclusi spazi, tab \t
e ritorni a capo \n
) e produce un array di al più n<=MAX_TOKENS=64
stringhe come segue:
malloc
;NULL
, fungendo da “terminatore”.Assumere che la linea di testo letta da stdin
contenga al più 1024 caratteri compreso il ritorno a capo \n
.
Esempio: se la linea letta da stdin
è rm -f .DS_store
, la chiamata get_cmd_line(argv)
restituisce in argv
l’array di stringhe {"rm", "-f". ".DS_Store", NULL}
.
Suggerimenti. Basandosi sul comando man
(oppure la documentazione online cercando funzione
opengroup
) usare:
fgets
per leggere una linea da stdio
di al più MAX_LINE=1024
caratteri terminata dal ritorno a capo \n
;strtok
per tokenizzare la linea una volta letta da stdio.L’array di stringhe prodotto deve essere deallocabile con la semplice funzione:
void free_args(char* argv[]) {
while (*argv) free(argv++);
}
Usare il main di prova nella directory di lavoro E1-get-cmd-line
compilando con `gcc e1_main.c e1.c -o e1´.
Una shell
è un programma che chiede all’utente di eseguire altri programmmi sotto forma di nuovi processi, passandogli eventuali argomenti specificati dall’utente. Una shell fornisce normalmente un prompt
, vale a dire un breve testo (es. $, >, ecc.) che segnala all’utente che la shell è in attesa di ricevere comandi.
Si chiede di scrivere nel file E2-shell/e2.c
una semplice shell sotto forma di una funzione int do_shell(const char* prompt);
che prende come parametro la stringa di prompt e si comporta come segue;
stdin
un comando seguito dai suoi eventuali argomenti (es: ls -l
, dove ls
è il comando e -l
è il suo unico argomento). Per ottenere comando e argomenti da stdin
usare il risultato dell’esercizio 1;quit
terminare con successo la shell;fork
un nuovo processo che esegua il comando con gli argomenti dati usando execvp
;unknown command
seguito dal nome del comando e tornare al punto 1;wait
la terminazione del processo e tornare al punto 1.Per valutare il corretto funzionamento della shell, effettuare i seguenti quattro test:
ls -l
e verificare che listi la directory correnteecho hello
e verificare che venga stampato hellosergente hartman
e verificare che venga stampato un messaggio di errorequit
e verificare la terminazione della shellIl risultato di ogni system call deve essere controllato e in caso di errore segnalato con perror
e terminazione EXIT_FAILURE.
In questo esercizio vogliamo implementare una funzione unsigned int counting_threads(unsigned int th, unsigned int it, unsigned int val)
che calcola il prodotto dei tre valori th
, it
, val
comportandosi come segue:
th
threads indipendentiit
e val
NOTA: per evitare che i thread indpendenti sovrascrivano a vicenda una stessa variabile, si suggerisce di usare un array globale di th
valori interi, uno per ciascun thread. In questo modo sarà possibile per ciascun thread scrivere in una variabile a lui riservata.
NOTA: per poter utilizzare le primitive per gestire i thread, è necessario aggiungere alla linea di comando di compilazione il flag -lpthread
.
Usare il main di prova nella directory di lavoro E3-counting-threads
compilando con `gcc e3_main.c e3.c -o e3´.
Rispondi alle seguenti domande, tenendo conto che una risposta corretta vale 1 punti, mentre una risposta errata vale 0 punti.
Domanda 1 Relativamente al seguente codice C (supponendo che la fork
non generi un errore), quale tra le seguenti affermazioni è VERA?
Padre: 1
” e poi “Figlio: 2
”Figlio: 2
” e poi “Padre: 1
”Figlio: 1
” e poi “Padre: 1
”Figlio: 1
” e poi “Padre: 2
”Figlio: 2
” e poi “Padre: 2
”Domanda 2 Relativamente al seguente codice C (supponendo che la fork
non generi un errore), qual è l’output atteso?
Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=40
”Figlio 1: x=10
”, “Figlio 2: x=10
”, “Figlio 3: x=10
”, “Padre: x=30
”Figlio 1: x=10
”, “Figlio 2: x=10
”, “Figlio 3: x=10
”, “Padre: x=40
”Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=30
”Figlio 1: x=10
”, “Figlio 2: x=30
”, “Figlio 3: x=60
”, “Padre: x=100
”Domanda 3 Relativamente al seguente codice C (supponendo che la execv
non generi un errore), qual è l’output atteso?
--- started ---
”, “Hello
”, “NULL
, “--- finished ---
”--- started ---
”, “Hello
”, “--- finished ---
”--- started ---
”, “Hello
”--- started ---
”, “--- finished ---
”stdin
in C)#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "e1.h"
char* dup_string(const char* in) {
size_t n = strlen(in);
char* out = malloc(n + 1);
strcpy(out, in);
return out;
}
void get_cmd_line(char* argv[MAX_TOKENS]) {
int argc = 0;
char line[MAX_LINE];
fgets(line, MAX_LINE, stdin);
char* token = strtok(line, " \t\n");
argc = 0;
while (argc < MAX_TOKENS && token != NULL) {
argv[argc++] = dup_string(token);
token = strtok(NULL, " \t\n");
}
argv[argc] = NULL;
}
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "e2.h"
#define MAX_LINE 1024
#define MAX_TOKENS 64
void do_cmd(char* argv[MAX_TOKENS]) {
int res;
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) {
res = execvp(argv[0], argv);
if (res == -1) {
printf("unkwnown command %s\n", argv[0]);
_exit(EXIT_FAILURE);
}
}
res = wait(NULL);
if (pid == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
}
void deallocate_cmd(char* argv[MAX_TOKENS]) {
while (*argv != NULL)
free(*argv++);
}
char* dup_string(const char* in) {
size_t n = strlen(in);
char* out = malloc(n + 1);
strcpy(out, in);
return out;
}
void get_cmd_line(char* argv[MAX_TOKENS]) {
int argc = 0;
char line[MAX_LINE];
fgets(line, MAX_LINE, stdin);
char* token = strtok(line, " \t\n");
argc = 0;
while (argc < MAX_TOKENS && token != NULL) {
argv[argc++] = dup_string(token);
token = strtok(NULL, " \t\n");
}
argv[argc] = NULL;
}
int do_shell(const char* prompt){
for (;;) {
printf("%s", prompt);
char* argv[MAX_TOKENS];
get_cmd_line(argv);
if (argv[0] == NULL) continue;
if (strcmp(argv[0], "quit") == 0) break;
do_cmd(argv);
deallocate_cmd(argv);
}
return EXIT_SUCCESS;
}
#include <pthread.h>
#include <stdlib.h>
#include "ex.h"
unsigned int *shared_array;
// Inserire qui la soluzione
void* thread_work(void* arg)
{
thread_data_t* data = (thread_data_t*)arg;
int i;
for (i = 0; i < data->it; i++) {
shared_array[data->tid] += data->val;
}
return NULL;
}
unsigned int counting_threads(unsigned int th, unsigned int it, unsigned int val)
{
shared_array = (unsigned int*)calloc(th, sizeof(unsigned int));
pthread_t* threads = (pthread_t*)malloc(th * sizeof(pthread_t));
thread_data_t* threads_data = (thread_data_t*)malloc(th * sizeof(thread_data_t));
int i;
for (i = 0; i < th; i++) {
threads_data[i].it = it;
threads_data[i].val = val;
threads_data[i].tid = i;
if (pthread_create(&threads[i], NULL, thread_work, &threads_data[i]) != 0) {
exit(EXIT_FAILURE);
}
}
unsigned int sum = 0;
for (i = 0; i < th; i++) {
if (pthread_join(threads[i], NULL) != 0) {
exit(EXIT_FAILURE);
}
sum += shared_array[i];
}
free(shared_array);
free(threads);
free(threads_data);
return sum;
}
Figlio: 1
” e poi “Padre: 1
”Figlio 1: x=10
”, “Figlio 2: x=20
”, “Figlio 3: x=30
”, “Padre: x=40
”--- started ---
”, “Hello
”