Programmazione di sistema

Documenti analoghi
Una semplice applicazione client/server 1

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Socket. Nei sistemi operativi moderni i servizi disponibili in rete si basano principalmente sul modello client/server.

Creare un'elementare backdoor in C in ambiente UNIX

La sincronizzazione è legata alla implementazione delle pipe: int pipe(int fd[2]);

Esempio 1: stampa locale di file remoto

CORSO DI SISTEMI OPERATIVI A - ESERCITAZIONE 3. 1 strace : visualizzazione delle system call invocate da un processo

Lab. di Sistemi Operativi - Esercitazione n 7- -Gestione dei processi Unix-

(VHUFLWD]LRQLGLEDVHVXOOH6RFNHWLQ&

CORSO DI SISTEMI OPERATIVI A - ESERCITAZIONE 3

Interazione con il DNS Conversioni di Nomi ed Indirizzi

unsigned long inet_addr(cp) char *cp;

In generale può essere utile che i due processi eseguano del codice diverso

Corso di Laboratorio di Sistemi Operativi

INTERNET DOMAIN SOCKETS (Cap.59)

Elementi di programmazione con interfaccia Socket

Corso di Programmazione Concorrente Processi. Valter Crescenzi

Interazione (TCP) Client-Server con le socket

CREAZIONE DI UN FILE

IPC Inter Process Communication

Gestione dei processi

Laboratorio di Sistemi Operativi Cognome Nome Mat.

Laboratorio di Reti di Calcolatori

Esercitazione di Lab. di Sistemi Operativi 1 a.a. 2011/ Comunicazione Tra Processi (IPC) Parte -

Esercitazione di Lab. di Sistemi Operativi 1 a.a. 2011/ Comunicazione Tra Processi (IPC)- - 1 Parte -

Applicazione Client-Server con Server Concorrente Specifiche

SC per Inter Process Comminication. Comunicazione fra macchine diverse: socket

Esercitazioni Socket

COMUNICAZIONE TRA PROCESSI REMOTI IN UNIX

CORSO DI SISTEMI OPERATIVI A - ESERCITAZIONE 6

Chiamate di sistema per la Inter Process Communication (IPC) in POSIX. E.Mumolo, DEEI

ESERCITAZIONE 2 RIPASSO. EX. 1 Un processo padre (parent) crea due processi figli (children) e attende la loro terminazione. Se, e solo se,...

POSIX Systems Programming. geek evening 0x0d. ambienti POSIX. By lord_dex ZEI e Salug! presentano:

Esercitazione Laboratorio di Sistemi Operativi Cognome Nome Mat.

Lab. di Sistemi Operativi - Esercitazione n 9- -Thread-

Socket TCP. prima parte

AXO. Operativo. Architetture dei Calcolatori e Sistema. programmazione di sistema

Inter-process communication: socket

Sviluppo di Applicazioni su Rete. Introduzione all API socket di Berkeley. Interazione tra Processi. Modello Client-Server

Processi UNIX. I Processi nel SO UNIX. Gerarchie di processi UNIX. Modello di processo in UNIX

Chiamate di sistema per la Gestione dei processi in POSIX. E.Mumolo, DEEI

Reti (già Reti di Calcolatori )

request reply richiesta client processo di servizio processo server principale From - Valeria Cardellini, Corso Sist. Distr. A.A.

Esercitazione [6] Client/Server con Socket

File I/O. M. R. Guarracino: File I/O 1

Scrivere alla fine di un file Vi sono due modi per scrivere alla fine di un file:

Esercitazione di Lab. di Sistemi Operativi 1 a.a. 2011/2012

Server Iterativi. Server TCP Ricorsivi. Fork. Server Ricorsivi. un server iterativo gestisce una connessione alla volta. Prof.

Programmazione di applicazioni di rete

Programmazione di Sistema 3

Nuker v1.0 by ALwarrior

Digressione: man (2)...

programmazione distribuita Introduzione Introduzione alla programmazione distribuita

CREAZIONE PROCESSI IN UNIX 20

Processi: Exit, Wait, Exec

Processi e Sincronizzazione. Laboratorio Software C. Brandolese M. Grotto

Laboratorio di Reti di Calcolatori

Progetto fine al superamento del corso di Sistemi Operativi. Http server proxy Http web monitor

Reti di Calcolatori - Laboratorio. Lezione 5. Gennaro Oliva

Digressione: man 2...

Program m azione di Sistem a 6

Basi di network programming sotto Unix/Linux (draft version) Claudio Piciarelli

Introduzione ai Device Drivers in Linux. E.Mumolo, DEEI

DATAGRAM SOCKET. Angelastro Sergio Diomede Antonio Viterbo Tommaso

Sistemi di Elaborazione. Introduzione alla Programmazione distribuita

Socket II MIDLAB. Sirio Scipioni. M I D L A B.

Program m azione di Sistem a 3

Controllo dei Processi. M. R. Guarracino - Primitive di Controllo dei Processi

Progettazione di un client TCP. Progettazione di un server TCP. Esempio: daytime TCP. Client TCP daytime

IPC System V. Code di messaggi

Esercitazione sulle Socket

Creare una applicazione Winsock di base

I/O su Socket TCP: read()

Le system call: fork(), wait(), exit()

Basic Sniffer Tutorial

Sulla libreria standard, III. Manipolare file con stdio.h

Gestione dei processi

Esercizio sulla gestione di file in Unix

Programmazione multiprocesso

Socket TCP. seconda parte

Comunicazione tra processi: pipe Le pipe sono un meccanismo UNIX di Inter Process Communication (IPC)

Esercitazione di Lab. di Sistemi Operativi a.a. 2012/ I processi Unix -

Laboratorio di Sistemi Operativi

Progettazione di Applicazioni Robuste. Applicazione Echo. Schema Generale di un Server TCP Ricorsivo 1. Applicazione echo

Domain Name Service. Mapping nomi/indirizzi con Socket API in C

Esercitazione [5] Input/Output su Socket

ESERCIZI RISOLTI IN C LANGUAGE (programmazione avanzata)

ESERCIZI SULLA PROGRAMMAZIONE DI SISTEMA GNU/LINUX

Acknowledgment: Prof Vincenzo Auletta, Università di Salerno. Approfondimento alla programmazione distribuita

Sistemi Operativi. Esercitazione 2 Compilazione, Makefile e Processi

Sistemi Operativi. Des crizione e controllo dei proces s i

Puntatori. Un puntatore contiene un numero che indica la locazione di memoria dove è presente la variabile puntata

Socket per TCP: Fondamenti

Cosa e un processo? Stato del processo

Il sistema operativo LINUX Indice

INGEGNERIA DEL WEB. VinX

T.A.R.I. Socket (ICT, AL)

Processi in Linux. Stru/ura file eseguibili

rsystem Maximiliano Marchesi

Precedenza e associatività. Complementi sul C - 2. Esempi. Esempi

Transcript:

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