Programmazione di sistema Prof. Gerardo Pelosi & Ing. Michele Scandale Il materiale illustrato a lezione e in queste note contiene anche esempi di Fabrizio Castro, Luca Fossati, Vittorio Zaccaria. Esercizio 1 Scrivere un programma con le seguenti caratteristiche: il processo principale apre un file F in scrittura e genera N processi figli e attende la loro attesa, con F, N parametri ricavati da linea di comando ogni processo figlio, aggiorna il file aperto scrivendo un numero casuale r [0, 255] di caratteri e comunica al processo padre tale numero il processo principale confronta somma i valori restituiti dai figli con la lunghezza attuale del file Soluzione #include <wait.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdarg.h> static void print_usage(const char *name) { printf("usage: %s CHILD_NUM FILE\n", name); exit( EXIT_SUCCESS); static void notifyerror(const char *format,...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); int main(int argc, char ** argv) { if (argc < 3) print_usage(argv[0]); int N = atoi(argv[1]); if (N < 1) notifyerror("!\n"); printf("(%d) Creating %d children.\n", getpid(), N); char buffer [256] = {0; 1
int fd = open(argv[2], O_CREAT O_TRUNC O_WRONLY O_APPEND, 0644); if (fd < 0) notifyerror("unable to open file %s.\n", argv[2]); pid_t pid; for (int i = 0; i < N; ++i) { pid = fork(); if (pid == -1) notifyerror("fork error!.\n"); if (pid == 0) { // Why this? break; pid_t this_pid = getpid(); if (pid) { printf("(%d) Has %d children.\n", this_pid, N); long sum = 0; for (int i = 0; i!= N; ++i) { int status; pid_t child_pid = wait(& status); printf("(%d) Child %d: terminated with exit value %d.\n", this_pid, child_pid, WEXITSTATUS(status)); if ( WIFEXITED(status)) sum += WEXITSTATUS(status); printf("(%d) Bytes written: %ld\n", this_pid, sum); long size = lseek(fd, 0L, SEEK_END); printf("(%d) File size: %ld bytes\n", this_pid, size); else { pid_t parent_pid = getppid(); printf("(%d) Hello world! My parent is %d\n", this_pid, parent_pid); srand(this_pid); ssize_t written = write(fd, buffer, rand() % 256); printf("(%d) Written %ld bytes in file.\n", this_pid, written); exit(written); 2
Esercizio 2 Scrivere un programma per simulare il comportamento di una serie di processi produttori-consumatori con le seguenti caratteristiche: il processo principale predispone un buffer infinito rappresentato da un file il processo principale prosegue creando N processi produttori ciascuno dei quali esegue, tramite exec, un altro eseguibile che svolge il ruolo di produttore il produttore legge da standard input un carattere e lo scrive nel file indicato dal file-descriptor passatogli come argomento il processo principale prosegue poi creando N processi consumatori, il cui codice provvederà a leggere un singolo carattere dal buffer condiviso (se disponibile) e a riportarlo sullo standard output Soluzione Programma principale: #include <fcntl.h> #include <sys/wait.h> void usleep(int); int main(int argc, char ** argv) { if (argc < 2) { printf("usage: %s FILENAME NUM_CHILD\n\n", argv[0]); int N = atoi(argv[2]); if (N < 1) { fprintf(stderr, "Error opening file %s.\n", argv[1]); int fd = open(argv[1], O_CREAT O_TRUNC O_APPEND O_WRONLY); if (fd < 0) { fprintf(stderr, "Error opening file %s.\n", argv[1]); for (int id = 0; id!= N; ++id) { pid_t pid = fork(); if (pid) continue; char fdbuf [128]; char idbuf [128]; snprintf(fdbuf, 128, "%d", fd); snprintf(idbuf, 128, "%d", id); execlp("./es2 - producer", "es2 - producer", fdbuf, idbuf, NULL); // Is this executed? fd = open(argv[1], O_RDONLY); pid_t pid; 3
for (int id = 0; id!= N; ++id) { pid = fork(); if (!pid) { char cur; do { usleep (10000); while (read(fd, &cur, 1)!= 1); printf("(%d) Consumer %d: char %c read from file.\n", getpid(), id, cur); exit( EXIT_SUCCESS); // Is it needed a check for pid!= 0? for (int i = 0; i!= 2 * N; ++i) wait(null); Programma produttore: #define FD_STDIN 0 int main(int argc, char ** argv) { if (argc < 3) { fprintf(stderr, "Too few parameters!!!\n"); int fd = atoi(argv[1]); int id = atoi(argv[2]); char cur; ssize_t n_read = 0; while (n_read!= 1 cur == \n cur == \r ) n_read = read(fd_stdin, &cur, 1); printf("(%d) Producer %d: char %c read from stdin buffer\n", getpid(), id, cur); write(fd, &cur, 1); 4
Esercizio 3 Scrivere un server di rete TCP/IP che svolga la seguente funzione: rimanga in attesa di connessione dai client TELNET (ogni client è servito in parallelo) per ogni client rimanga in attesa di comandi. Un comando è composto da due stringhe: nome completo del programma da eseguire parametro da passare al programma Per ogni comando ricevuto, il server esegue il programma specificato mediante execv e poi si mette in attesa per comandi successivi la connessione con il client viene chiusa quando il server riceve la stringa q come nome del programma. Si assuma per semplicità che tutte le stringhe siano di lunghezza inferiore a 40 caratteri. Soluzione pid_t wait(int *stat_loc) restituisce nella locazione stat_loc l informazione relativa allo stato di uscita del processo figlio su cui è stata effettuata la wait stessa. Le macro WIFEXITED e WEXITSTATUS consentono rispettivamente di sapere se il processo è terminato mediante una exit e con che valore questo ha terminato. Vi sono altre macro per avere altre informazioni: man 3 wait. #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <wait.h> #define SERVER_PORT 4000 #define MAX_CONN 10 #define MAX_CMD_SIZE 40 #define MAX_PARAM_SIZE 40 static int setup_server_socket( uint16_t port) { struct sockaddr_in addr; addr. sin_family = AF_INET; addr. sin_port = htons(port); addr. sin_addr.s_addr = htonl( INADDR_ANY); int sock = socket(af_inet, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "Error on creating socket!\n"); if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "Error on binding socket!\n"); return sock; static void sanitize_eol(char *str) { size_t len = strlen(str); if (len > 0 && str[len - 1] == \n ) 5
str[len - 1] = \0 ; if (len > 1 && str[len - 2] == \r ) str[len - 2] = \0 ; static ssize_t safe_recv(int sd, char *buffer, size_t len, int flags, char *term) { ssize_t l = 0; ssize_t i = 0; size_t t_len = term? strlen(term) : 0; for (; i < len; i += l) { l = recv(sd, buffer + i, len - i, flags); if (l == -1) return -1; if (term && memcmp(buffer + l - t_len, term, t_len) == 0) return l; return i; static void server_cmd_exec(int client_sock, char *cmd) { char param[ MAX_PARAM_SIZE]; ssize_t l = safe_recv(client_sock, param, MAX_PARAM_SIZE, 0, "\r\n"); close( client_sock); if (l > 0) { pid_t this_pid = getpid(); param[l] = \0 ; sanitize_eol(param); printf("(%d) Received param: %s\n", this_pid, param); char *argv[3]; argv[0] = cmd; argv[1] = param; argv[2] = NULL; printf("(%d) Requested command line: %s %s\n", this_pid, cmd, param); if (execvp(cmd, argv) == -1) printf("(%d) Exec error\n", this_pid); int main(int argc, const char ** argv) { int server_sock = setup_server_socket( SERVER_PORT); if (listen(server_sock, MAX_CONN)) { fprintf(stderr, "Error on listening!\n"); while (1) { struct sockaddr_in client_addr; socklen_t client_addr_size = sizeof( client_addr_size); int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, & client_addr_size); pid_t pid = fork(); if (pid) { close( client_sock); continue; char cmd[ MAX_CMD_SIZE]; ssize_t l; pid_t this_pid = getpid(); 6
while (1) { l = safe_recv(client_sock, cmd, MAX_CMD_SIZE, 0, "\r\n"); if (l <= 0) cmd[l] = \0 ; sanitize_eol(cmd); if (!strcmp("q", cmd)) { printf("(%d) Received a QUIT from client\n", this_pid); break; printf("(%d) Received from client: %s\n", this_pid, cmd); pid_t pid2 = fork(); if (!pid2) { server_cmd_exec(client_sock, cmd); else { int status; wait(&status); if (! WIFEXITED(status) WEXITSTATUS(status)) printf("(%d) Unknown command %s.\n", this_pid, cmd); close( client_sock); exit( EXIT_SUCCESS); 7
Esercizio 4 Scrivere due programmi di rete (client e server) con le seguenti caratteristiche: il processo principale lancia 1000 processi server ogniuno in ascolto su una porta nel range [4000, 4999] i server scrivono tutti su uno stesso file un messaggio che contiene l indirizzo e la porta del client, il proprio pid e la propria porta i server ricevono poi dal client un singolo carattere: a questo punto il server invoca execlp per eseguire un programma esterno che riceve i dati necessari per poter rispondere al client un messaggio che contiene le occorrenze all interno del file del carattere ricevuto dal client stesso Soluzione Programma server: #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <wait.h> #include <fcntl.h> #define SERVER_PORT 4000 #define MAX_CONN 1000 #define N_SERVER 1000 static int setup_server_socket(uint16_t port) { struct sockaddr_in addr; addr. sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl( INADDR_ANY); int sock = socket(af_inet, SOCK_STREAM, 0); if (sock == -1) { fprintf(stderr, "Error on creating socket!\n"); if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "Error on binding socket!\n"); return sock; static ssize_t safe_recv(int sd, char *buffer, size_t len, int flags, char *term) { ssize_t l = 0; ssize_t i = 0; size_t t_len = term? strlen(term) : 0; for (; i < len; i += l) { l = recv(sd, buffer + i, len - i, flags); if (l == -1) return -1; if (term && memcmp(buffer + l - t_len, term, t_len) == 0) return l; 8
return i; static void run_server(unsigned id, int fd) { int server_sock = setup_server_socket( SERVER_PORT + id); if (listen(server_sock, MAX_CONN)) { fprintf(stderr, "Error on listening!\n"); while (1) { struct sockaddr_in client_addr; socklen_t client_addr_size = sizeof( client_addr_size); int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, & client_addr_size); pid_t pid = fork(); if (pid) { close( client_sock); continue; if ( client_sock < 0) char buffer [2048]; snprintf(buffer, 2048, "client: %s:%d - pid: %d - port: %d\n", inet_ntoa( client_addr.sin_addr), ntohs( client_addr.sin_port), getpid(), SERVER_PORT + id); write(fd, buffer, strlen(buffer)); if ( safe_recv(client_sock, buffer, 1, 0, NULL) == -1) { printf("(%d) Unable to receive a char from client.\n", getpid()); buffer[1] = \0 ; snprintf(buffer + 2, 20, "%d", client_sock); execlp("./es4 -counter", "es4 -counter", buffer, buffer + 2, NULL); // es4 -counter will close the client -socket, but in case of failure... printf("(%d) Error execlp\n", getpid()); close( client_sock); exit( EXIT_SUCCESS); int main(int argc, const char ** argv) { int fd = open("es4 -server.log", O_CREAT O_TRUNC O_WRONLY O_APPEND, 0644); for (unsigned i = 0; i!= N_SERVER; ++i) { pid_t pid = fork(); if (! pid) run_server(i, fd); for (unsigned i = 0; i!= N_SERVER; ++i) wait(null); 9
Programma invocato dal server: #include <string.h> #include <sys/socket.h> static ssize_t safe_send(int sd, const char *buffer, size_t len, int flags) { ssize_t l = 0; ssize_t i = 0; for (; i < len; i += l) { l = send(sd, buffer + i, len - i, flags); if (l == -1) return -1; return i; int main(int argc, char ** argv) { FILE *f = fopen("es4 -server.log", "r"); if (!f) { fprintf(stderr, "Unable to open es4 -server.log \n"); unsigned counter = 0; while (!feof(f)) counter += ( fgetc(f) == argv[1][0] ); fclose(f); int sd = atoi(argv[2]); char buffer [2048]; snprintf(buffer, 2048, "Number of occurences: %u\n", counter); if ( safe_send(sd, buffer, strlen(buffer), 0) == -1) { printf("error while sending...\n"); close(sd); 10
Programma client: #define _POSIX_SOURCE #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> int setup_connection(const char *host, const char *port) { struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); hints. ai_family = AF_INET; hints. ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints. ai_protocol = 0; struct addrinfo *result; int err = getaddrinfo(host, port, &hints, &result); if (err) { fprintf(stderr, " getaddrinfo: %s\n", gai_strerror(err)); int sfd = -1; struct addrinfo *rp; for (rp = result; rp!= NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp->ai_addr, rp->ai_addrlen)!= -1) break; close(sfd); freeaddrinfo(result); if (rp == NULL) sfd = -1; return sfd; static ssize_t safe_recv(int sd, char *buffer, size_t len, int flags, char *term) { ssize_t l = 0; ssize_t i = 0; size_t t_len = term? strlen(term) : 0; for (; i < len; i += l) { l = recv(sd, buffer + i, len - i, flags); if (l == -1) return -1; if (term && memcmp(buffer + l - t_len, term, t_len) == 0) return l; return i; static ssize_t safe_send(int sd, const char *buffer, size_t len, int flags) { ssize_t l = 0; ssize_t i = 0; for (; i < len; i += l) { l = send(sd, buffer + i, len - i, flags); if (l == -1) return -1; return i; 11
static void sanitize_eol(char *str) { size_t len = strlen(str); if (len > 0 && str[len - 1] == \n ) str[len - 1] = \0 ; if (len > 1 && str[len - 2] == \r ) str[len - 2] = \0 ; int main(int argc, char ** argv) { if (argc < 3) { printf("usage: %s HOST PORT", argv[0]); int sd = setup_connection(argv[1], argv[2]); if (sd == -1) { fprintf(stderr, "Unable to connect to server.\n"); printf("insert a character: "); char c; scanf("%c%*c", &c); ssize_t l; l = safe_send(sd, &c, 1, 0); if (l == -1) { fprintf(stderr, "Unable to receive message from server.\n"); char buffer [2048]; l = safe_recv(sd, buffer, 2048, 0, "\n"); if (l == -1) { fprintf(stderr, "Unable to receive message from server.\n"); buffer[l] = \0 ; sanitize_eol(buffer); printf("received: %s\n", buffer); 12