1. SOA/esempi utili catalogati/c_ps/2006-06-21.c
/*TESTO 2006-06-21
Si realizzi in ambiente Unix/C il SERVER della seguente interazione tra processi:
- il sistema consiste di due processi: un processo server Ps e un processo cliente Pc; per la comunicazione tra Ps e i processi
cilenti Pcivengono utilizzate socket Stream; scegliere la porta su cui il sevrer offre il servizio: 789 o 7890 (motivare). Il
server crea un figlio e poi si pone in attesa dell'arrivo di dati dal client;
- il client invia una stringa testuale;
- il processo server la legge, la invia al figlio, che a sua volta la visualizza a video;
- se la stringa e' STOP il processo server fa terminate il processo figlio mediante l'invio di un segnale, e poi termina anch'esso;
altrimenti il server si rimette in attesa di dati dal client.
Come generico client Ps si suggerisce l'utilizzo del programma telnet, invocato come "telnet localhost numeroportaserver"
(es. "telnet localhost 7890"). Attenzione che telnet attacca ad ogni stringa inviata la sequenza di caratteri "\r\n".
Si siggerisce l'utilizzo delle funzionie C strcmp per verificare la presenza della parola chiave STOP.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 7890
//Funzione di gestione segnali.
void handler()
{
printf("\nProcesso %d esce",getpid());
exit(0);
}
int main()
{
char buffer[256]="";
int pidFiglio;
int p[2]; //la pipe
struct sigaction sig;
struct sockaddr_in server,client;
int sock,msgsock;
int lenght;
if(pipe(p)<0) //creo la pipe, sara' il canale di comunicazione tra padre e figlio
{
perror("pipe");
exit(-1);
}
if((pidFiglio=fork())<0) //creo il figlio che stampera' le stringhe
{
perror("fork figlio");
exit(-1);
}
else
if(pidFiglio==0) //sono il figlio?
{
//FIGLIO
printf("Sono il figlio con pid %d\n",getpid());
close(p[1]); //chiudo il descrittore di scrittura della pipe, non mi serve
//installo il gestore affidabile del segnale di uscita per tutti i processi
//non e' strettamente necessario, il testo non lo chiedeva ed era sufficiente (e necessaria) una gestione non affidabile
//lo metto giusto come ulteriore esempio (minimo) di gestione affidabile dei segnali
sig.sa_handler = handler;
sigemptyset(&sig.sa_mask);
sig.sa_flags=SA_RESTART;
sigaction(SIGINT,&sig,NULL);
do //ciclo infinito di lettura dalla pipe
{
if(read(p[0],buffer,sizeof(buffer))<0) //leggo la stringa che mi invia il sevrer
perror("read pipe");
printf("\nFiglio: ho ricevuto la stringa %s",buffer);
}
while(1);
}
else //altrimenti sono il padre (server)
{
//PADRE
printf("Sono il padre con pid %d\n",getpid());
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
kill(pidFiglio,SIGKILL);
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
kill(pidFiglio,SIGINT);
exit(-1);
}
lenght=sizeof(server);
if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
{
perror("getting socket name");
kill(pidFiglio,SIGINT);
exit(-1);
}
printf("Socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
do //questo e' il ciclo principale del server: legge i messaggi che gli manda il client e li gira al figlio
{
if(read(msgsock,buffer,sizeof(buffer))<0) //leggo il messaggio
perror("reading message");
if(!strncmp(buffer,"STOP\r\n",6))//se ricevo STOP uccido il figlio ed esco
{
kill(pidFiglio,SIGINT); //uccido il figlio
close(sock);
close(msgsock);
exit(0);
}
//mando la stringha ricevuta al figlio
if(write(p[1],buffer,sizeof(buffer))<0)
perror("writing message");
}
while(1);
}
return 0;
}
2 SOA/esempi utili catalogati/c_ps/2006-06-21-ver2.c/* TESTO 2006-06-21-ver2
Si realizzi in ambiente Unix/C il server della seguente interazione tra processi :
-il sistema consiste di due tipi di processi: un processo server Ps e i processi clienti Pci;
-per la comunicazione tra Ps e i processi clienti Pci vengono utilizzate socket Stream;
-il server Ps offre un servizio concorrente (un figlio per ogni connessione) alla porta 987
oppure 9876 (motivare la scelta della porta utilizzata);
-il client invia una stringa del tipo cmd1 | cmd2 (digitata dall'utente) affinch`e un
opportuna coppia di processi sul server realizzi il piping dei comandi cmd1 e cmd2 ;
-lo standard output del processo che realizza cmd2 deve essere ridiretto sulla socket
connessa affinch`e sia ricevuto e visualizzato dal cliente ;
-a partire dal momento in cui riceve un segnale SIGUSR1, il server Ps deve memorizzare
in un file di testo (denominato server-log.txt) gli indirizzi IP dei clienti che richiedono
il servizio.
Come generico client Pci, si suggerisce l'utilizzo del programma telnet, invocato come
telnet localhost numeroportaserver. Una volta connessi al server, scrivere ad esempio
ls | wc nel terminale del telnet per verificare il funzionamento del server.
Deve essere utilizzata la gestione affidabile dei segnali.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
//le porte sotto il 1024 sono riservate
#define PORT 9876
//descrittore del file server-log.txt
int f=0;
//Funzione di gestione segnali.
void handler()
{
//FILE /////////////////////////
//
if(!f)
if((f=open("server-log.txt",O_CREAT|O_WRONLY|O_TRUNC))<0)
perror("open file");
///////////////////////////////
}
int main()
{
char * env[]={NULL,(char *)0};
char buffer[256]="",app[256]="";
char * buffer2;
char * nome_cli;
struct sigaction sig;
struct sockaddr_in server,client;
int sock,msgsock;
int lenght;
int pid,p[2];
printf("Sono il server con pid %d\n",getpid());
//SOCKET //////////////////////
//
//preparo la struttura sockaddr_in per settare i parametri della socket
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
//leggo la porta che mi e' stata effettivamente assegnata
lenght=sizeof(server);
if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
{
perror("getting socket name");
exit(-1);
}
printf("Socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
////////////////////////////////
//SEGNALI //////////////////////
//
sig.sa_handler = handler; //gestore del segnale
sigemptyset(&sig.sa_mask);
sig.sa_flags=SA_RESTART;
sigaction(SIGUSR1,&sig,NULL);
////////////////////////////////
//ciclo infinito del server in attesa di connessioni
do
{
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
//se e' stato ricevuto un segnale il descrittore f e' diverso da zero
if(f)
{
//leggo il nome del client
nome_cli=inet_ntoa(client.sin_addr);
//lo scrivo bel file
write(f,nome_cli,strlen(nome_cli));
fflush(NULL);
}
if((pid=fork())<0) //creo il figlio che gestira' la connessione
{
perror("fork figlio gestore connessione");
exit(-1);
}
if(pid==0) //sono il figlio?
{
/////////////////////////////
//CODICE FIGLIO GESTORE
printf("Sono il gestore della connessione con pid %d\n",getpid());
//ciclo del gestore della connessione: continua a leggere le stringhe del client e ad eseguire i comandi richiesti
do
{
//resetto i buffer
memset(app,0,sizeof(app));
memset(buffer,0,sizeof(buffer));
//leggo la stringa che mi manda il client con i comandi da eseguire
if(read(msgsock,app,sizeof(app))<0)
{
perror("reading message");
exit(-1);
}
//tolgo gli spazi e i caratteri aggiunti da telnet... sicuramente c'era un modo migliore!!
int i=0,j=0;
do
{
if(app[i]!=' ' && app[i]!='\r' && app[i]!='\n')
buffer[j++]=app[i];
}
while(++i<strlen(app));
printf("Eseguo il comando %s\n",buffer);
//se c'e' il carettere | allora creo un altro figlio che eseguira' il secondo comando
//metto in buffer2 la posizione del simbolo | piu' uno, che rappresenta la posizione del secondo comando
if((buffer2=strchr(buffer,'|')+1))
{
//pipe di comunicazione tra i processi
if(pipe(p))
perror("pipe");
if((pid=fork())<0) //creo il nipote che eseguira' il secondo comando
{
perror("fork figlio secondo comando");
exit(-1);
}
if(pid==0) //sono il nipote?
{
/////////////////////////////////
//CODICE NIPOTE ESECUTORE di cmd2
//tronco la stringa buffer al carattere |
//in questo modo mi resta solo il primo comando
buffer[buffer2-buffer-1]=0x0;
//chiudo stdount
close(1);
//il nuovo stdout e' lo stdin del figlio cmd2
if(dup(p[1])<0)
perror("dup1");
close(p[0]);
close(p[1]);
env[0]=buffer;
if(execvp(buffer,env)<0)
perror("execv primo");
exit(0);
////////////////////////////
}
if((pid=fork())<0) //creo il nipote che eseguira' il primo comando
{
perror("fork figlio primo comando");
exit(-1);
}
if(pid==0) //sono il nipote?
{
/////////////////////////////////
//CODICE NIPOTE ESECUTORE di cmd1
//chiudo stdin e stdout
close(0);
close(1);
//nuovo stdin
if(dup(p[0])<0)
perror("dup2");
//nuovo stdout
if(dup(msgsock)<0)
perror("dup3");
close(p[0]);
close(p[1]);
env[0]=buffer2;
if(execvp(buffer2,env)<0)
perror("execv secondo");
exit(0);
////////////////////////////////
}
}
else
{
printf("La stringa deve essere nel formato cmd1 | cmd2\n");
exit(0);
}
close(p[0]);
close(p[1]);
}
while(1);
}
close(msgsock);
}
while(1);
return 0;
}
2.
3-SOA/esempi utili catalogati/c_ps/2006-07-19.c/*TESTO 2006-07-19
Si realizzi in ambiente Unix/C il SERVER della seguente interazione tra processi:
- il sistema consiste di 3 tipi di processi: server Ps, un processo Pstore e i clienti Pci;
- per la comunicazione tra Ps e i processi clienti Pci vengono utilizzate socket Stream;
- il server Ps offre un servizio concorrente (un figlio per ogni connesione) alla porta 876 oppure alla porta 8765 (giustificare)
- inizialmente il server Ps attiva un processo figlio persistente Pstore che visualizza il proprio PID e gestisce un vettore
si 10 elementi interi aggiornato sulla base delle richieste dei clienti
- i client inviano stringhe del tipo
"STORE indirizzo valore_intero" con 0<=indirizzo<=9. Ad esempio "STORE 0 11". La richiesta sovrascrive il valore
eventualmente gia' memorizzato all'indirizzo specificato.
- alla ricezione del segnale SIGUSR1 il processo Pstore deve visualizzare il contento del vettore;
- alla ricezione del segnale SIGUSR2 il processo Pstore deve azzerare tutti gli elementi del vettore;
Deve essere usata la gestione affidabile dei segnali.
Come generico client Ps si suggerisce l'utilizzo del programma telnet, invocato come "telnet localhost numeroportaserver"
(es. "telnet localhost 8765"). Una volta connessi al sevrer scrivere, ad esempio, STORE 8 22 [INVIO] nel terminale del telnet
e inviare un SIGUSR1 al processo Pstore per verficare il funzionamento del server.
Si siggerisce l'utilizzo delle funzioni C sscanf per estrarre dal messaggio l'indirizzo del messaggio e il valore da assegnare, e strcmp per
verificare la presenza della parola chiave STORE
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 8765
int storeVet[10];
int sock,msgsock;
//Funzione di gestione segnali. Se ne potevano fare anche 3 diverse
void handler(int signo)
{
static int i=0;
switch(signo) //che segnale ho riceuvuto?
{
case SIGUSR1:
{
printf("\nStampo i valori del vettore: ");
for(i=0;i<10;i++)
printf("%d ",storeVet[i]);
printf("\n");
break;
}
case SIGUSR2:
{
printf("\nAzzero i valori del vettore\n");
memset(storeVet,0,sizeof(storeVet));
break;
}
case SIGINT:
{
printf("\nProcesso %d esce\n",getpid());
close(sock);
close(msgsock);
exit(0);
}
}
}
int main()
{
struct //ho pensato di usare una struttura per leggere la posizione e il numero da inserire
{
int pos;
int num;
}storeRec;
char buffer[256]="";
char comando[256]="";
int pidStore;
int p[2]; //la pipe
struct sigaction sig,sig1,sig2;
struct sockaddr_in server,client;
int lenght,rval;
if(pipe(p)<0) //creo la pipe, sara' comune per tutti i processi
{
perror("pipe");
exit(-1);
}
sig2.sa_handler = handler; //installo il gestore del segnale di uscita per tutti i processi. Non obbligatorio
sigemptyset(&sig2.sa_mask);
sig2.sa_flags=SA_RESTART;
sigaction(SIGINT,&sig2,NULL);
if((pidStore=fork())<0) //creo il figlio Pstore, che sara' permenente fino alla fine del programma
{
perror("fork Pstore");
exit(-1);
}
else
if(pidStore==0) //sono il figlio(Pstore)?
{
//PSTORE
printf("Sono il Pstore con pid %d\n",getpid());
close(p[1]); //chiudo il descrittore discrittura della pipe, non mi serve
sig.sa_handler = handler; //installo il gestore del segnale SIGUSR1
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSER2 durante la gestione di SIGUSR1
sig.sa_flags=SA_RESTART;
sig1.sa_handler = handler; //installo il gestore del segnale SIGUSR2
sigemptyset(&sig1.sa_mask);
sigaddset(&sig1.sa_mask,SIGUSR1); //blocco SIGUSER2 durante la gestione di SIGUSR2
sig1.sa_flags=SA_RESTART;
sigaction(SIGUSR1,&sig,NULL);
sigaction(SIGUSR2,&sig1,NULL);
do //ciclo infinito di lettura dalla pipe
{
if(read(p[0],&storeRec,sizeof(storeRec))<0) //leggo il dato che contiene posizione e nuovo numero
perror("read pipe");
printf("Pstore: ricevuto %d %d\n",storeRec.pos,storeRec.num);
storeVet[storeRec.pos]=storeRec.num; //setto in nuovo numero
}
while(1);
}
else //altrimenti sono il padre (server)
{
//PADRE
printf("Sono il padre con pid %d\n",getpid());
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
kill(pidStore,SIGKILL);
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
kill(pidStore,SIGINT);
exit(-1);
}
lenght=sizeof(server);
if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
{
perror("getting socket name");
kill(pidStore,SIGINT);
exit(-1);
}
printf("Socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
do //questo e' il ciclo principale del server: attende connesioni, e crea un figlio per ogni una
{
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
if(fork()==0) //genero il figlio che gestira' la connesione attuale
{
//FIGLIO GESTORE DELLA CONNESIONE
close(sock);
close(p[0]);
do
{
if((rval=read(msgsock,buffer,256))<0) //leggo il messaggio
perror("reading message");
sscanf(buffer,"%s %d %d",comando,&(storeRec.pos),&(storeRec.num)); //estraggo comando, posizione nel vettore e nuovo valore
printf("Figlio socket: ricevuto %s %d %d\n",comando,storeRec.pos,storeRec.num);
if(!strncmp(comando,"STORE",5))//se il comando era effettivamente STORE, agisco di conseguenza
{
if(write(p[1],&storeRec,sizeof(storeRec))<0)
perror("write pipe");
}
}
while(1);
}
else
close(msgsock);
}
while(1);
}
return 0;
}
4-SOA/esempi utili catalogati/c_ps/2006-09-08.c/******************************************************************************************************************
*
* Questo esercizio poteva essere risolto in piu' modi, e con diverse interpretazioni al testo tutte piu' o meno
* corrette. In questa particolare soluzione:
*
* -alla ricezione del SIGUSR1 si passa dalla porta 8001 alla 8080. Alla ricezione del SIGUSR2 si attiva
* anche la porta 8080 in aggiunta alla 8001. Dopo la ricezione di uno qualunque dei due segnali ENTRAMBI verranno
* ignorati;
*
* -i processi figli che ricevono il messaggio ed estraggono l'ultima parola non escono subito
* dopo la prima ricevione, ma restano sempre attivi fino a quando non vengono uccisi;
*
* -ho deciso di non aprire subito entrambe le socket, ma di aprirne una, chiuderla se necessario, aprirne una seconda
* se necessario. Si poteva anche aprirne subito due e decidere quale usare con una var globale
*
*
* ATTENZIONE: per la gestione della doppia porta in contemporaneo la cosa piu' ortodossa e' usare la
* funzione select. Questa pero' non e' stata vista dagli studenti del corso di Broggi, quindi qui
* di seguito e' mostrata una soluzione alternativa che non ne fa uso.
*
********************************************************************************************************************/
/*TESTO 2006-09-08
Si realizzi in ambienteUnix/C il SERVER della seguente interazione tra processi:
-il sistema consistye di due tipi di processi: un server Ps e i client Pci;
-per la comunicazione tra Ps e i processi Pci vengono utilizzate socket STREAM;
-il server Ps offre un servizion concorrente (un figlio per ogni connessione) alle potre 8001
e/o 8080;
-il sevrer, che opera inizialmente sulla porta 8001, dopo aver ricevuto un segnale SIGUSR1 attende
connessioni sulla porta 8080 (OPZIONALE: la ricezione di un segnale SIGUSR2 fa si che il sevrer attenda
su entrambe le porta); N.B. dopo aver ricevuto un segnale SIGUSR1/2, ulteriori segnali SIGUSR1/2 devono
essere ignorati;
-il clinet invia una stringa contenente parole separate da spazi, ed ottiene in risposta l'ultima parola
inviata
Deve essere utilizzata la gestione affidabile dei segnali. Si noti che una accept interrota dalla ricezione di un
sengale ritorna -1 con errno che vale EINTR.
Come generico clinet Pci si suggerisce di utilizzare i programma telnet invocato come "telnet localhost porta". Una
volta connessi scrivere, ad esempio, "pippi tololino pluto" per verificare il funzionamento del programma. Telnet invia
assieme alla stringa digitata i caratteri di fine linea \r\n.
Si rammenta la presenza della funzione char * strrchr(const char *s,int c). Essa ritorna un puntatore all'ultima occorenza
del carattere c nella stringa s.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#define PORT1 8001
#define PORT2 8080
int sock,msgsock,porta;
struct sockaddr_in server;
//funzione per aprire la porta 8080
void apri8080()
{
//ne creo una nuova
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("creazione stream server");
exit(-1);
}
server.sin_port=htons(PORT2); //imposto la nuova porta, gli altri campi vanno gia' bene
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
printf("Server %d on port # %d\n",getpid(),ntohs(server.sin_port));
listen(sock,5); //listen sulla PORT2, massimo di 5 connesioni in attesa
}
//Funzione di gestione segnali
void handler(int signo)
{
int ps;
struct sigaction sig;
//ignoro i successivi SIGUSR1 e SIGUSR2
//sigprocmask non funziona dentro l'handler del segnale stesso
//si poteva usare una var globale che controllasse se e' la prima volta che ricevo questo segnale
sig.sa_handler = SIG_IGN; //per ignorare il segnale (vedi man sigaction)
sigemptyset(&sig.sa_mask);
sig.sa_flags=0;
sigaction(SIGUSR1,&sig,NULL);
sigaction(SIGUSR2,&sig,NULL);
switch(signo) //che segnale ho riceuvuto?
{
case SIGUSR1:
{
//chiudo la vecchia socket
close(sock);
//apro una socket sulla porta 8080
apri8080();
break;
}
case SIGUSR2: //(opzionale)
{
//creo un nuovo figlio, che fungera' da SERVER sull'altra porta
if((ps=fork())<0)
{
perror("fork nuovo server");
exit(-1);
}
if(ps==0)//CODICE SERVER FIGLIO
{
//chiudo la vecchia socket che ho ereditato dal server padre
close(sock);
//apro una socket sulla porta 8080
apri8080();
}//FINE SERVER FIGLIO
break;
}
case SIGINT: //non richiesto
{
printf("\nProcesso %d esce\n",getpid());
close(sock);
close(msgsock);
exit(0);
}
}
}
int main()
{
char buffer[256];
char * ultimo;
struct sigaction sig;
struct sockaddr_in client;
int lenght,pid;
//creo la SOCKET
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("creazione stream socket");
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket con PORT1
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT1); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
printf("Server %d on port # %d\n",getpid(),ntohs(server.sin_port));
listen(sock,5);
//SEGNALI
sig.sa_handler = handler; //installo il gestore del segnale SIGUSR1
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante la gestione di SIGUSR1
sig.sa_flags=0;
sigaction(SIGUSR1,&sig,NULL);
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante la gestione di SIGUSR2
sigaction(SIGUSR2,&sig,NULL);
sigemptyset(&sig.sa_mask);
sigaction(SIGINT,&sig,NULL);
//CICLO PRINCIPALE
do //attende connesioni, e crea un figlio per ogni una
{
lenght=sizeof(client);
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght); //attendo connesioni...
if(msgsock == -1 && errno!=EINTR) //controllo se c'e' un errore e se questo e' dovuto ad un segnale, nel qual caso lo ignoro
{
perror("accept"); //se invece l'errore non dipende da un segnale c'e' qualcosa che non va ed esco
exit(-1);
}
if(errno!=EINTR) //se sono uscito dalla accept per un segnale allora non devo generare nessun figlio gestore...
{
if((pid=fork())<0) //genero il figlio che gestira' la connesione attuale
{
perror("fork gestore");
exit(-1);
}
else
if(pid==0)
{
//CODICE FIGLIO GESTORE DELLA CONNESIONE
close(sock);
printf("Figlio gestore della connesione su porta %d, utilzza porta %d per comunicazione, pid %d\n",ntohs(server.sin_port),ntohs(client.sin_port),getpid());
do
{
memset(buffer,0,256);//azzero il buffer di lettura
if(read(msgsock,buffer,256)<0) //leggo il messaggio
perror("reading message");
else
{
printf("Figlio %d ha ricevuto %s\n",getpid(),buffer);
//estraggo il puntatore all'ultimo spazio
ultimo=strrchr(buffer,' ');
if(ultimo)
ultimo++; //in questo modo punto al primo carattere dopo lo spazio
else
ultimo=buffer; //se buffer non contiene spazi, allora e' costituito da una sola parola, e rimando tutto buffer
if(write(msgsock,ultimo,strlen(ultimo))<0) //speidsco l'ultima parola
perror("writing message");
}
}
while(1);
//FINE CODICE FIGLIO GESTORE
}
}
//CODICE PADRE
close(msgsock);
}
while(1);
return 0;
}
5-SOA/esempi utili catalogati/c_ps/2006-09-25.c/*TESTO 2006-09-25
Si realizzi in ambienteUnix/C la seguente interazione tra processi:
-il processo genera due processi figli;
-ogni processo figliovisualizza il proprio pid e un numero casuale tra 0 e N compresi; N e' un numero intero che l'utente
specifica come unico argomento di invocazione del programma;
-il processo che ha generato il numero casuale minore invia un SIGUSR1 all'altro processo figlio, visualizza un messaggio
con il proprio pid e si pone in attesa di un segnale;
-il processo che riceve il segnale SIGUSR1 visualizza un messaggio con il proprio pid e si termina;
-il padre, dopo che un figlio e' terminato, invia un segnale all'altro figlio, e termina con un messaggio.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
//Funzione di gestione segnali
void handler()
{
printf("Processo figlio %d ha ricevuto un SIGUSR1 ed esce\n",getpid());
exit(3);
}
int main(int argc, char *argv[])
{
struct sigaction sig;
sigset_t newmask;
int exit_pid,pid[2],status,pp[2][2],numero,mynumero,N,i;
if(argc<2 || atoi(argv[1])<=0 || atoi(argv[1])>=RAND_MAX)
{
printf("ERRORE negli argomenti\n");
printf("Uso: programma <N>\n");
printf("N deve essere maggiore di 0 e minore di RAND_MAX\n");
exit(-1);
}
//setto N
N=atoi(argv[1]);
//creo le due pipe che i figli useranno per scambiarsi i pid e il numero casuale
//una pp[0] verra usata in scrittura dal primo figlio, pp[2] dal secondo
//
//pp e' un vettore di pipe
if(pipe(pp[0])<0)
{
perror("pipe 0");
exit(-1);
}
if(pipe(pp[1])<0)
{
perror("pipe 1");
exit(-1);
}
//GENERO I FIGLI
for(i=0;i<2;i++)
{
if((pid[i]=fork())<0) //genero il primo figlio
{
perror("fork primo figlio");
exit(-1);
}
if(pid[i]==0)
{
//CODICE FIGLI
printf("Sono il figlio %d\n",getpid());
//metto nel *mio* vettore il mio pid
pid[i]=getpid();
//blocco il segnale SIGUSR1
sigemptyset(&newmask);
sigaddset(&newmask,SIGUSR1);
sigprocmask(SIG_BLOCK,&newmask,NULL);
//installo il gestore del sengale SIGUSR1
sig.sa_handler = handler;
sigemptyset(&sig.sa_mask);
sig.sa_flags=0; //non ho bisogno di flag particolari
sigaction(SIGUSR1,&sig,NULL);
//chiudo la mia pipe di scrittura dal lato di lettura
close(pp[i][0]);
//chiudo la mia pipe di lettura dal alto di scrittura
close(pp[(i+1)%2][1]);
//inizializzo il generatore di numero casuali e ne genero uno compreso tra 0 e N;
srand(getpid());
mynumero=rand()%N;
printf("Figlio %d ha generato il numero %d\n",getpid(),mynumero);
//scrivo il mio pid e il numero casuale
if(write(pp[i][1],&pid[i],sizeof(int))<0)
{
perror("write pid");
exit(-1);
}
if(write(pp[i][1],&mynumero,sizeof(int))<0)
{
perror("write numero");
exit(-1);
}
//leggo pid e numero casuale dall'altro figlio
if(read(pp[(i+1)%2][0],&pid[(i+1)%2],sizeof(int))<0)
{
perror("read pid");
exit(-1);
}
if(read(pp[(i+1)%2][0],&numero,sizeof(int))<0)
{
perror("read numero");
exit(-1);
}
//notare come la pipe sia il punto di sincronismo tra i due figli: per fare una
//lettura/scrittura entrambi sevono essere arrivati a questo punto, e quindi hanno
//anche il gestore dei segnali gia' pronto
//se il mio numero e' minore di quello che ho ricevuto, o se sono
//uguali ma il mio pid e' minore, mando un SIGUSR all'altro figlio
if(mynumero<numero || (mynumero==numero && getpid()<pid[(i+1)%2]))
{
printf("Figlio %d manda un SIGUSR1 a %d\n",getpid(),pid[(i+1)%2]);
kill(pid[(i+1)%2],SIGUSR1);
}
//mi metto in attesa di un segnale qualunque
//questo va bene sia per entrambi i figli
//sig.sa_mask e' empty
printf("Figlio %d si sospende...\n",getpid());
sigsuspend(&(sig.sa_mask));
}
}
//CODICE PADRE
printf("Padre: ho pid %d, i figli sono %d %d\n",getpid(),pid[0],pid[1]);
//chiudo entrambe le pipe, non mi servono
close(pp[0][0]);
close(pp[0][1]);
close(pp[1][0]);
close(pp[1][1]);
//aspetto che il primo figlio esca (sara' quello che ha ricevuto SIGUSR1 dall'altro)
exit_pid=wait(&status);
if(exit_pid<0)
{
perror("wait");
exit(-1);
}
printf("Padre: figlio %d uscito\n",exit_pid);
//mando un sigusr1 al processo che e' ancora vivo
kill( ((pid[0]==exit_pid) ? pid[1] : pid[0]) , SIGUSR1);
printf("Padre: ho mandato una SIGUSR1 al figlio %d\n",((pid[0]==exit_pid) ? pid[1] : pid[0]));
return 0;
}
/*
---IF aritmentico---
variabile = (condizione) ? valore1 : valore2;
Questo e' equivalente a:
if(condizione)
variabile=valore1:
else
variabile=valore2;
---Resto---
L'operatore % da il resto di una divisione:
resto = dividendo%divisore;
ES.
0%2 = 0
1%2 = 1
2%2 = 0
3%2 = 1
4%2 = 0
5%2 = 1
ecc.
*/
6-SOA/esempi utili catalogati/c_ps/2006-11-29.c/*TESTO 2006-11-29
Si realizzi in ambiente Unix/C la seguente interazione tra processi:
-un processo determina il proprio pid P e procede a generare processi figli fintanto che il pid di un figlio Pf non assume valore
maggiore od uguale del pid del padre P maggiorato di 5 (cioe' Pf>=P+5). Discutere in quali condizioni il pid di un figlio e' maggiore
del pid del padre;
-ogni processo figlio cosi' generato visualizza il proprio pid;
-il processo figlio con pid minore invia un segnale a tutti gli altri processi e poi termina visualizzando un messaggio di terminazione;
-gli altri figli non appena ricevono il segnale visualizzano un messaggio e terminano;
-il padre termina dopo che sono terminati tutti i figli
--SOLUZIONE--
Per quanto riguarda la domanda: il pid del figlio NON e' sempre maggiore di quello del padre. Infatti, dato che il pid e' rappresentato con 16 bit, puo'
accadere che si arrivi al pid 65536, dopo di che si ricomincia da 0 con il primo pid disponibile. Quindi, se per esempio il padre ha pid 65530 potrebbe generare
due figli, uno 65533, uno 65536, e poi un terzo con pid, per esempio, 100 quindi minore di quello del padre.
PERO'... questa era la risposta alla domanda. Per lo svolgimento degli altri punti del compito era possibile fare un'ipotesi semplificativa (scrivendolo nel compito):
si suppone di non raggiungere mai il limite di 65536, e quindi si assumere che i pid dei figli siamo sempre maggiori di quelli del padre e sempre crescenti.
Questa ipotesi NON viene penalizzata nel giudizio, era lecita.
**La soluzione riportata qui sotto si basa su questa ipotesi.**
In fondo c'e' una piccola nota con un abbozzo di soluzione nel caso piu' generale di pid non crescenti.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
//Funzione di gestione segnali
void handler(int signo)
{
if(signo==SIGINT)
{
printf("Processo figlio %d ha ricevuto un SIGINT ed esce\n",getpid());
exit(0);
}
}
int main()
{
struct sigaction sig;
sigset_t newmask;
int p[2],pid,i,j,status;
//questo vettore conterra' i pid dei figli. Visto che li assumiamo crescenti, essi non saranno mai piu di 5...
int pidV[5];
//creo la pipe che usera' il padre per mandare i pid di tutti i figli al figlio con pid minore
if(pipe(p)<0)
{
perror("pipe");
exit(-1);
}
//stampo il pid del padre
printf("Sono il padre con pid %d\n",getpid());
//Genero il primo figlio, quello che avra pid MINORE
if((pidV[0]=fork())<0) //genero il primo figlio
{
perror("fork primo figlio");
exit(-1);
}
if(pidV[0]==0)
{//CODICE DEL PRIMO FIGLIO
//chiudo la pipe in scrittura
close(p[1]);
//stampo il pid
printf("Sono il figlio %d\n",getpid());
do{
if(read(p[0],&pid,sizeof(pid))<0)
{
perror("read pid");
exit(-1);
}
//se ho ricevuto il mio pid vuole dire che ho termianto e posso uscire
//in pratica faro' in modo che il padre mi mandi i pid di tutti i figli in sequenza, ma con
//quello del figlio minore per ultimo. Il figlio minore li leggere uno per uno con
//la read, e manda un SIGINT fin tanto che non legge il suo pid, che e' il segnale che
//i pid sono finiti e puo' uscire
if(pid==getpid())
break;
//mando un SIGINT al figlio pid
kill(pid,SIGINT);
}
while(1);
printf("Figlio %d con pid minore esce\n",getpid());
exit(0);
}
//CODICE DEL PADRE
//chiudo la pipe in lettura
close(p[0]);
//Genero gli altri figli
i=1;
do
{
if((pidV[i]=fork())<0) //genero il figlio
{
perror("fork primo figlio");
exit(-1);
}
if(pidV[i]==0)
{
//CODICE FIGLI
//non mi servono le pipe;
close(p[0]);
close(p[1]);
//blocco il segnale SIGINT
sigemptyset(&newmask);
sigaddset(&newmask,SIGINT);
sigprocmask(SIG_BLOCK,&newmask,NULL);
//installo il gestore del segnale SIGINT
sig.sa_handler = handler;
sigemptyset(&sig.sa_mask);
sig.sa_flags=0;
sigaction(SIGINT,&sig,NULL);
//stampo il pid
printf("Sono il figlio %d\n",getpid());
//mi sospendo in attesa di qualunqe segnale
sigsuspend(&(sig.sa_mask));
//fine codice figli
}
//aspettiamo un po', nel frattempo potreste lanciare qualche processo (tipo un browser), giusto per non avere sempre i pid in perfetta sequenza...
sleep(2);
}
while(pidV[i++]<(getpid()+5) && i<5);
//CODICE PADRE
//mando al figlio minore i pid di tutti i figli, lui compreso. Il suo pid pero' sara' l'ultimo... occhio agli indici!
for(j=i-1;j>=0;j--)
write(p[1],&(pidV[j]),sizeof(pidV[j]));
//aspetto i figli che ho generato...
for(j=0;j<i;j++)
wait(&status);
return 0;
}
/*
SOLUZIONE CASO GENERALE
Si poteva procedere in questo modo: tutti i figli vengono generati nello stesso modo in un ciclo do-while come quello a riga 121. Il padre, una volta raggiunta
la condizione di uscita, guarda quale dei figli ha il pid piu' piccolo e gli manda un SIGUSR1, e poi fa una write sulla pipe uguale a quella alla riga 157. Avro'
precedentemente installato il gestore SIGUSR1 per tutti i figli, nel quale e' contenuto qualcosa di questo tipo:
if(signo==SIGUSR1)
{
do{
if(read(p[0],&pid,sizeof(pid))<0)
{
perror("read pid");
exit(-1);
}
if(pid==getpid())
break;
//mando un SIGINT al figlio pid
kill(pid,SIGINT);
}
while(1);
printf("Figlio %d con pid minore esce\n",getpid());
exit(0);
}
Cioe' esattamente quello che fa il primo figlio nella soluzione precedente. Ovviamente si dovranno dichiarere tutte le variabili necessaria globabli, ecc. ecc.
Il ciclo di generazione figli cambia in questo modo: si elimina tutta la parte dalla riga 76 alla 111 (generazione del primo figlio), si parte con i=0 e si
toglie la condizione i<5. Naturalmente e' necessario usare un vettore pidV molto piu' grande, direi che, per quello detto all'inizio, di 65536 dovrebbe bastare.
Se a qualcuno interssa mi faccia sapere, che possiamo guardare questa soluzione in dettaglio.
*/
7-SOA/esempi utili catalogati/c_ps/2007-01-23.c/* TESTO 2007-01-23
Prova UNIX:
Si realizzi in ambiente Unix/C il server della seguente interazione tra processi:
-il sistema consiste di un processo server Ps e dei processi clienti Pci; per la
comunicazione tra Ps e i processi clienti Pci vengono utilizzate socket di tipo Stream;
la porta su cui il server Ps offre il servizio va scelta tra la 207 e la 2007, inserendo in
un commento la motivazione.
-il client invia al server una stringa che rappresenta un numero N con 1 N 4.
-il processo server Ps fornisce un servizio concorrente : il figlio creato per servire una
connessione deve creare N processi Pni che visualizzano il proprio PID e terminano;
-il client deve ricevere un messaggio di tipo stringa contenente i PID degli N processi
Pni;
Si consideri anche l'estensione di far generare a ciascuno degli N processi Pni un
numero casuale (rappresentato come stringa) che deve essere trasmesso al cliente insieme al
PID del processo. Come client si utilizzi il comando "telnet nomehost porta".
*/
/* NOTA
Il codice commentato e indicato con ESTENSIONE realizza la funzionalita' richiesta nell'estensione
del testo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 2007 //le porte sotto il 1024 sono riservate al sistema operativo
int main()
{
char buffer[256];
int pidV[4]; //un vettore per i pid dei processi Pni, che saranno al massimo 4
struct sockaddr_in server,client;
int sock,msgsock;
int lenght,pid,N,i;
/*ESTENSIONE
int p[2],nrand;
*/
//stampo il pid del padre
printf("Sono il padre con pid %d\n",getpid());
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
printf("Server socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
//ciclo infinito del server concorrente
do
{
printf("Accept nel server...\n");
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght); //attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
if((pid=fork())<0) //creo il figlio gestore della connessione
{
perror("fork figlio");
exit(-1);
}
else
if(pid==0) //sono il figlio?
{
//CODICE FIGLIO GESTORE
printf("Figlio gestore con pid %d\n",getpid());
//non mi serve piu' questo descrittore
close(sock);
/*ESTENSIONE
if(pipe(p)<0)
{
perror("pipe");
exit(-1);
}
*/
//ciclo del gestore: deve continuamente leggere dalla socket i messaggi che il Pclient gli manda
do
{
if(read(msgsock,buffer,256)<0) //leggo il numero di processi Pni da creare
perror("reading message");
N=(int)(buffer[0]-'0'); //lo metto in un numero
if(N<1 || N>4) //e' un numero valido?
{
printf("Figlio %d : il cliente mi ha mandato il numero %d non valido, esco.\n",getpid(),N);
exit(-1);
}
printf("Figlio %d : il cliente mi ha mandato %d\n",getpid(),N);
sprintf(buffer,"Processi Pni ");
//ciclo di creazione dei Pni
for(i=0;i<N;i++)
{
if((pidV[i]=fork())<0)
{
perror("fork figlio del gestore");
exit(-1);
}
else
if(pidV[i]==0) //sono il figlio?
{
//CODICE figlio Pni
close(msgsock); //non mi serve piu'
printf("Figlio del gestore %d con pid %d\n",getppid(),getpid());
/*ESTENSIONE
close(p[0]);
srand(getpid()); //inizializzo il generatore di numeri casuali
nrand=rand()%10; //numero casuale da 0 a 9
printf("Numero casuale di %d = %d\n",getpid(),nrand);
write(p[1],&nrand,sizeof(nrand));
*/
exit(-1);
}
/* ESTENSIONE
read(p[0],&nrand,sizeof(nrand)); //leggo dal figlio Pni il suo numero casuale
sprintf(buffer,"%s Pid=%d con numero casuale %d ",buffer,pidV[i],nrand);
*/
sprintf(buffer,"%s Pid=%d ",buffer,pidV[i]); //da commentare nella versione con ESTENSIONE!!!
}
printf("Figlio %d manda al clinent la stringa: %s\n",getpid(),buffer);
if(write(msgsock,&buffer,strlen(buffer)+1)<0) //spedisco al client la risposta
perror("writing message");
}
while(1);//ciclo del figlio gestore della connessione
}
//CODICE PADRE
close(msgsock);
}
while(1);//ciclo del server padre
return 0;
}
8-SOA/esempi utili catalogati/c_ps/2007-02-20.c/*TESTO 2007-02-20
Scrv un prog che:
- Se invocato senza parametri sulla riga di comando
entra in un ciclo infinito in cui visualizza un carattere punto ogni
2 secondi.
- Se invocato con un solo parametro, invia un segnale SIGUSR1 al processo
il cui pid è specificato come parametro, affinche' esso riduca di 200 millisecondi il periodo di
visualizzazione del carattere punto.
- Se invocato con due parametri, invia un segnale SIGUSR2 al processo
il cui pid è specificato come primo parametro, attende un
numero di secondi pari al valore del secondo parametro, per poi inviare un
segnale SIGUSR1 al processo con il pid specificato ; il processo che
ha ricevuto un segnale SIGUSR2 deve porsi esplicitamente in attesa di
un nuovo segnale SIGUSR1 prima di poter proseguire con la
visualizzazione del carattere punto.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
unsigned long attendiMicro=2000000; //2 secondi in microsecondi
unsigned long decrementaMicro=200000; //200ms in microsecondi (provare a metterlo anche a 500ms, o 1 secondo, per rendere piu' visibile l'effetto...)
//Funzione di gestione segnali
void handler(int signo)
{
sigset_t s;
switch(signo)
{
case SIGUSR1 :
printf("\nProcesso %d ha ricevuto un SIGUSR1\n",getpid());
fflush(NULL);
//decremento il numero di microsecondi che devo attendere
attendiMicro=(attendiMicro-decrementaMicro)>0 ? (attendiMicro-decrementaMicro) : attendiMicro;
break;
case SIGUSR2:
printf("\nProcesso %d ha ricevuto un SIGUSR2... attendo SIGUSR1\n",getpid());
fflush(NULL);
//blocco tutti i segnali, eccetto SIGUSR1 e i non bloccabili (SIGKILL e SIGSTOP)
//
//non e' strettamente necessario, andava bene anche con tutti i segnali abilitati, tramite una semplice
//sigemptyset(&s). Diciamo che in questo modo impedisco ad altri segnali non gestiti di interrompere il processo,
//ma soprattutto ad un eventuale SIGUSR2 di interferire
sigfillset(&s);
sigdelset(&s,SIGUSR1);
//attendo esplicitamente il segnale SIGUSR1 prima di proseguire...
sigsuspend(&s);
break;
}
}
int main(int argc, char *argv[])
{
struct sigaction sig;
int N1,N2;
//se argc==1 vuole dire che non avevamo nessun argomento da linea di comando
if(argc==1)
{
//installo il gestore per SIGUSR1 e SIGUSR2
sig.sa_handler = handler;
sigemptyset(&sig.sa_mask);
//blocco il SIGUSR2 durante l'esecuzione dell'handler SIGUSR1, per sicurezza. Non era richiesto.
sigaddset(&sig.sa_mask,SIGUSR2);
//come sopra, blocco il SIGUSR1 durante l'esecuzione dell'handler SIGUSR2, anche questo non richiesto.
//Domanda: perche' funziona lo stesso?
sigaddset(&sig.sa_mask,SIGUSR1);
sig.sa_flags=0;
sigaction(SIGUSR1,&sig,NULL);
sigaction(SIGUSR2,&sig,NULL);
printf("Processo %d attende indefinitamente",getpid());
fflush(NULL); //altrimenti non vedete nulla...
while(1)
{
printf(".");
fflush(NULL);//altrimenti non vedete nulla...
//uso una usleep per poter apprezare i millisecondi, e poter effettuale il decremento richiesto quando arriva il SIGUSR1
//la sleep non poteva andare bene, in quando accetta solo secondi interi
usleep(attendiMicro);
}
}
//se ho almeno un argomento da linea di comando...
if(argc>=2)
{
//leggo il pid a cui dovro' mandare i segnali
N1=atoi(argv[1]);
if(N1<=0)
{
printf("ERRORE negli argomenti\nUso: programma <N1> <N2>\nN1 e N2 parametri facoltativi. N1 deve essere un pid valido, N2 un numero intero maggiore di zero.\n");
exit(-1);
}
//ho 2 (o piu') argomenti?
if(argc>=3)
{
//leggo i secondi che dovro' attendere
N2=atoi(argv[2]);
if(N2<=0)
{
printf("ERRORE negli argomenti\nUso: programma <N1> <N2>\nN1 e N2 parametri facoltativi. N1 deve essere un pid valido, N2 un numero intero maggiore di zero.\n");
exit(-1);
}
printf("Processo %d manda un SIGUSR2 a %d...",getpid(),N1);
kill(N1,SIGUSR2);
printf("e attende %d secondi...\n",N2);
fflush(NULL);//altrimenti non vedete nulla...
sleep(N2);
}
printf("Processo %d manda un SIGUSR1 a %d\n",getpid(),N1);
kill(N1,SIGUSR1);
}
return 0;
}
/*
---IF aritmentico---
variabile = (condizione) ? valore1 : valore2;
Questo e' equivalente a:
if(condizione)
variabile=valore1:
else
variabile=valore2;
*/
9-SOA/esempi utili catalogati/c_ps/2007-07-23.c/* TESTO 2007-07-23
Si realizzi un SERVER concorrente su socket STREAM che abbia il seguente comportamento:
- il processo server ricevce dal client un singolo messaggio con un numero variabile n (2<= n <= 10) di linee di testo,
in cui la prima contiene il nume di un comando UNIX, e terminate da una linea contenente unicamente la parola chiave
"STOP"; Esempio: (n=5)
sort
1 2 3
4 5 6
7 8 9
STOP
- se n>2 il server provvede a memorizzare in un file temporaneo il contenuto delle righe intermedie (linee 2... n-1) del
messaggio ricevuto per renderlo disponibilie come argomento al momento dell'esecuzione del comando indicato dal client;
- l'output (stdout e stderr) generato dal comendo deve essere inviato al client
- al termine dell'esecuzione del comando il file temporaneo deve essere rimosso
- dopo aver ricevuto un segnale SIGUSR1 il server deve iniziare a visualizzare sul suo stdout l'indirizzo di ogni cliente
che si connette, cessando tale comportamento alla notifica di un segnale SIGUSR2
Si siggerisce l'utilizzo di telnet come client.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <string.h>
//una porta a caso
#define PORT 9001
//descrittore del file server-log.txt
int scriviIndirizzo=0;
//Funzione di gestione segnali SIGUSR1 e SIGUSR2
void handler(int signo)
{
if(signo == SIGUSR1)
scriviIndirizzo=1;
else
scriviIndirizzo=0;
}
int main()
{
char * env[]={NULL,NULL,(char *)0};
char buffer[256]="",nomeFile[256]="",comando[256]="";
struct sigaction sig;
struct sockaddr_in server,client;
int sock,msgsock;
int lenght;
int pid,f,n=0,j=0;
printf("Sono il server con pid %d\n",getpid());
//SOCKET //////////////////////
//
//preparo la struttura sockaddr_in per settare i parametri della socket
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
//leggo la porta che mi e' stata effettivamente assegnata
lenght=sizeof(server);
if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
{
perror("getting socket name");
exit(-1);
}
printf("Socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
////////////////////////////////
//SEGNALI //////////////////////
//
sig.sa_handler = handler; //gestore del segnale SIGUSR1
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante l'esecuzione dell'handler
sig.sa_flags=SA_RESTART;
sigaction(SIGUSR1,&sig,NULL);
sig.sa_handler = handler; //gestore del segnale SIGUSR2
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante l'esecuzione dell'handler
sigaction(SIGUSR2,&sig,NULL);
////////////////////////////////
//ciclo infinito del server in attesa di connessioni
do
{
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
//se e' stato ricevuto il segnale SIGUSR1 allora stampo le info del client
if(scriviIndirizzo)
printf("Connection from %s, port %d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
if((pid=fork())<0) //creo il figlio che gestira' la connessione
{
perror("fork figlio gestore connessione");
exit(-1);
}
if(pid==0) //sono il figlio?
{
/////////////////////////////
//CODICE FIGLIO GESTORE
printf("Sono il gestore della connessione, ho pid %d\n",getpid());
//preparo il nome del file temporaneo.
//e' meglio che il nome del file sia sempre diverso, con un nome specifico per ogni gestore.
//In questo modo evitiamo che diversi figli/gestori scrivano e leggano dallo stesso file.
//Usiamo il pid
memset(nomeFile,0,sizeof(nomeFile));
sprintf(nomeFile,"%d.txt",getpid());
f=open(nomeFile,O_CREAT | O_RDWR | O_TRUNC,0666);
//continua a leggere le stringhe del client, fino a quando non riceve "STOP" o non ne ha lette 10
do
{
//resetto i buffer
memset(buffer,0,sizeof(buffer));
j=0;
//faccio un ciclo per leggere la riga che mi manda il client, carattere per carattere
do
{
//leggo la stringa che mi manda il client caratte per carattere
if(read(msgsock,&buffer[j],1)<0)
{
perror("reading");
exit(-1);
}
}
while(buffer[j++]!='\n');
//se ho ricevuto la stringa STOP e almeno un'altra stringa (n>=1), esco
if(!strcmp(buffer,"STOP\r\n") && n)
break;
//se e' la prima riga che leggo allora la salvo in comando
if(!n)
{
//elimino l'eventuale \r e \n, che non piaciono molto a execvp
if(strchr(buffer,'\r')) strchr(buffer,'\r')[0]=0x0;
if(strchr(buffer,'\n')) strchr(buffer,'\n')[0]=0x0;
strcpy(comando,buffer);
}
else //altrimenti la salvo nel file
if(write(f,buffer,strlen(buffer))<0)
{
perror("writing argomenti");
exit(-1);
}
}
while(++n<9); //controllo quante linee ho ricevuto, alla decima esco in ogni caso
//chiudo il file in modo che salvi le modifiche
close(f);
if((pid=fork())<0) //creo il NIPOTE eseguira' il comando
{
perror("fork figlio gestore connessione");
exit(-1);
}
if(pid==0) //sono il figlio del gestore (nipote del padre)?
{
/////////////////////////////
//CODICE NIPOTE
printf("Eseguo il comando %s\n",comando);
fflush(NULL);
//chiudo stdout e stderr
close(1);
close(2);
//il nuovo stdout e' la socket
if(dup(msgsock)<0)
perror("dup1");
//il nuovo stderr e' la socket
if(dup(msgsock)<0)
perror("dup1");
close(msgsock);
env[0]=comando;
env[1]=nomeFile;
if(execvp(comando,env)<0)
perror("execvp");
/////////////////
}
//il gestore attende che il nipote abbia finito di eseguire il comando
wait(NULL);
//elimino il file temporaneo
if(unlink(nomeFile)<0)
perror("unlink");
close(msgsock);
//faccio uscire il figlio
exit(0);
////////////////////////////
}
close(msgsock);
}
while(1);
return 0;
}
10-SOA/esempi utili catalogati/c_ps/2007-07-23-easy.c/* 2007-07-23 easy **** ATTENZIONE ******
* Questa e' una versione semplificata della soluzione, che funziona solo se il client e' telent!
*
* Il problema sta nella read alla riga 187: con un generico client potrei inviare tutti i dati in unica stringa
* (es.sort\n1 2 3\n4 5 6\n7 8 9\nSTOP), il che renderebbe non funzionante il ciclo indicato sotto.
* Per fortuna telnet invia subito la riga quando premete invio editandola, quindi ci semplifica abbastanza le cose,
* ma non e' la soluzione piu' ortodossa.
*
*/
/* TESTO
Si realizzi un SERVER concorrente su socket STREAM che abbia il seguente comportamento:
- il processo server ricevce dal client un singolo messaggio con un numero variabile n (2<= n <= 10) di linee di testo,
in cui la prima contiene il nume di un comando UNIX, e terminate da una linea contenente unicamente la parola chiave
"STOP"; Esempio: (n=5)
sort
1 2 3
4 5 6
7 8 9
STOP
- se n>2 il server provvede a memorizzare in un file temporaneo il contenuto delle righe intermedie (linee 2... n-1) del
messaggio ricevuto per renderlo disponibilie come argomento al momento dell'esecuzione del comando indicato dal client;
- l'output (stdout e stderr) generato dal comendo deve essere inviato al client
- al termine dell'esecuzione del comando il file temporaneo deve essere rimosso
- dopo aver ricevuto un segnale SIGUSR1 il server deve iniziare a visualizzare sul suo stdout l'indirizzo di ogni cliente
che si connette, cessando tale comportamento alla notifica di un segnale SIGUSR2
Si siggerisce l'utilizzo di telnet come client.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
//una porta a caso
#define PORT 9001
//variabile che indica se dobbiamo o meno stampare l'indirizzo del client
int scriviIndirizzo=0;
//Funzione di gestione segnali SIGUSR1 e SIGUSR2
void handler(int signo)
{
if(signo == SIGUSR1)
scriviIndirizzo=1;
else
scriviIndirizzo=0;
}
int main()
{
char buffer[256]="",nomeFile[256]="",comando[256]="";
struct sigaction sig;
struct sockaddr_in server,client;
int sock,msgsock;
int lenght;
int pid,f,i=0;
printf("Sono il server con pid %d\n",getpid());
//SOCKET //////////////////////
//
//preparo la struttura sockaddr_in per settare i parametri della socket
sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
if(sock<0)
{
perror("creazione stream socket");
exit(-1);
}
//preparo la struttura sockaddr_in per settare i parametri della socket
server.sin_family=AF_INET; //internet protocol
server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
server.sin_port=htons(PORT); //e dalla porta specificata
if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
{
perror("binding stream socket");
exit(-1);
}
//leggo la porta che mi e' stata effettivamente assegnata
lenght=sizeof(server);
if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
{
perror("getting socket name");
exit(-1);
}
printf("Socket port # %d\n",ntohs(server.sin_port));
listen(sock,5); //massimo di 5 connesioni in attesa
////////////////////////////////
//SEGNALI //////////////////////
//
sig.sa_handler = handler; //gestore del segnale SIGUSR1
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante l'esecuzione dell'handler
sig.sa_flags=SA_RESTART;
sigaction(SIGUSR1,&sig,NULL);
sig.sa_handler = handler; //gestore del segnale SIGUSR2
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante l'esecuzione dell'handler
sigaction(SIGUSR2,&sig,NULL);
////////////////////////////////
//ciclo infinito del server in attesa di connessioni
do
{
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
if(msgsock == -1)
{
perror("accept");
exit(-1);
}
//se e' stato ricevuto il segnale SIGUSR1 allora stampo le info del client
if(scriviIndirizzo)
printf("Connection from %s, port %d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));
if((pid=fork())<0) //creo il figlio che gestira' la connessione
{
perror("fork figlio gestore connessione");
exit(-1);
}
if(pid==0) //sono il figlio?
{
/////////////////////////////
//CODICE FIGLIO GESTORE
printf("Sono il gestore della connessione, ho pid %d\n",getpid());
//preparo il nome del file temporaneo.
//e' meglio che il nome del file sia sempre diverso, con un nome specifico per ogni gestore.
//In questo modo evitiamo che diversi figli/gestori scrivano e leggano dallo stesso file.
//Usiamo il pid
memset(nomeFile,0,sizeof(nomeFile));
sprintf(nomeFile,"%d.txt",getpid());
f=open(nomeFile,O_CREAT | O_RDWR | O_TRUNC,0666);
//leggo la stringa che mi manda il client contenente il comando da eseguire
if(read(msgsock,comando,sizeof(comando))<0)
{
perror("reading comando");
exit(-1);
}
//continua a leggere le stringhe del client salvandole in un file, fino a quando non riceve "STOP"
do
{
//resetto i buffer
memset(buffer,0,sizeof(buffer));
//leggo la stringa che mi manda il client con il comando da eseguire
//FUNZIONE SOLO SE IL CLIENT E' TELNET!!
if(read(msgsock,buffer,sizeof(buffer))<0)
{
perror("reading argomenti");
exit(-1);
}
//se ho ricevuto la stringa STOP esco
if(!strcmp(buffer,"STOP\r\n"))
break;
//scrivo l'argomento nel file
if(write(f,buffer,strlen(buffer))<0)
{
perror("writing message");
exit(-1);
}
}
while(++i<9); //controllo quante linee ho ricevuto, alla decima esco
//chiudo il file in modo che salvi le modifiche
close(f);
//elimino i caratteri aggiungi da telnet (\r\n), che non piaciono a execv
comando[strlen(comando)-2]=0x0;
//preparo la stringa con il comando piu' argomento
sprintf(comando,"%s %s",comando,nomeFile);
printf("Eseguo il comando %s\n",comando);
fflush(NULL);
//chiudo stdout e stderr
close(1);
close(2);
//il nuovo stdout e' la socket
if(dup(msgsock)<0)
perror("dup1");
//il nuovo stderr e' la socket
if(dup(msgsock)<0)
perror("dup1");
close(msgsock);
//in questo modo torna nel programma e posso cancellare il file tmp
if(system(comando)<0)
perror("sistem");
//elimino il file temporaneo
if(unlink(nomeFile)<0)
perror("unlink");
//faccio uscire il figlio
//questo fara' uscire anche telnet, come richiesto nel testo
exit(0);
////////////////////////////
}
close(msgsock);
}
while(1);
return 0;
}
11-SOA/esempi utili catalogati/c_ps/2007-09-11.c// 2007-09-11 ESEMPIO DI SOLUZIONE DI UNO STUDENTE
//
//
//file
#include <sys/stat.h>
//socket
#include <sys/socket.h>
//printf
#include <string.h>
#include <stdio.h>
//exit
#include <stdlib.h>
#include <netinet/in.h>
//file
#include <netdb.h>
//file
#include <fcntl.h>
#include <arpa/inet.h>
//segnali affidabili,kill
#include <signal.h>
//fork,segnali,pipe,file,sleep,alarm,pause
#include <unistd.h>
//nanosleep
#include <time.h>
//fork,getpid(),waitpid(),socket,file
#include <sys/types.h>
//wait,waitpid()
#include <sys/wait.h>
void error(char *msg)
{
perror(msg);
exit(0);
}
int main(){
srand(time(NULL));
//P1
int P1,P2;
int piped[2];
int c,j;
if(pipe(piped)<0)
{
perror("pipe");
exit(-1);
}
P1=getpid();
if ((P2=fork()) < 0)
{
perror("fork error");
exit(1);
}
else
if (P2 == 0) //P2
{
sleep(5); // attende 5 sec
//Generazione di 5 figli
int pid;
for(j=0;j<5;j++){
if ((pid=fork()) < 0)
{
perror("fork error");
exit(1);
}
else
if (pid == 0) //figlio
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char send_buffer[256];
char rec_buffer[256]="";
portno = 10000+j;
printf("Figlio di P2 con pid=%d :provo a comunicare con P1 tramite la porta %d\n",getpid(), portno);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERRORE in apertura");
server = gethostbyname("localhost");
if (server == NULL) {
fprintf(stderr,"ERRORE, l’host non esiste\n");
exit(0);
}
serv_addr.sin_family = AF_INET;
memcpy((char *)&serv_addr.sin_addr,(char *)server->h_addr,server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
sleep(-1);//se non c'e' una connesione sospende il processo
int PID;
PID=getpid();
n = write(sockfd,&PID,sizeof(int));
if (n < 0)
error("ERRORE in scrittura sulla socket");
printf("Figlio di P2 con pid=%d: sono riuscito a connettermi con P1 tramite la porta %d e termino\n",getpid(), portno);
close(sockfd);
exit(0);
}
//P2
write(piped[1], &pid, sizeof (int));
}
//P2
printf("P2:ho generato 5 figli. Adesso aspetto che terminino\n");
while ((waitpid(-1, NULL, 0)) > 0);
printf("P2:Sono terminati tutti i miei figli; ora termino anche io\n");
exit(0);
}
else //P1
{
int sockfd, newsockfd, portno, clilen, PID;
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERRORE DI APERTURA DELLA SOCKET");
portno = 10000+rand() %5; // portno e' un numero casuale fra 10000 e 10004
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERRORE DI BINDING");
listen(sockfd,5);
clilen = sizeof(cli_addr);
printf("P1:sto in ascolto sulla porta %d\n",portno);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERRORE DI ACCEPT");
int n;
n = read(newsockfd,&PID,sizeof(int));
if (n < 0) perror("ERRORE in lettura dalla socket");
printf("P1:Ho recevuto un client di PID %d\n",PID);
int PidX;
for(j=0;j<5;j++){
n = read(piped[0],&PidX, sizeof (int));
if (n < 0) perror("ERRORE in lettura dalla socket");
if (PidX!=PID) {
printf("P1 :Mando un seganle SIGKILL al figlio con Pid %d per terminarlo\n",PidX);
kill(PidX,SIGKILL);
}
}
close(sockfd);
close(newsockfd);
printf("P1:Aspetto che P2 termini\n");
while ((wait(NULL)) > 0);
printf("P1: P2 e' terminato; termino anche io\n");
}
}
3.
12-SOA/esempi utili catalogati/c_ps/2007-11-23.c/* TESTO
Si realizzi un programma in C che abbia il seguente comportamento:
- il processo genera due processi figli;
- il primo processo figlio entra in un ciclo infinito durante il quale visualizza a video un carattere asterisco (*) al secondo;
- il secondo processo figlio entra in un ciclo infinito durante il quale visualizza a video un carattere punto (.) al secondo;
- il processo padre attende indefinitamente: all’arrivo di un segnale SIGUSR1, termina i figli e termina anch’esso.
- a fronte della ricezione di un segnale SIGUSR1 da parte di uno qualunque dei due figli, ENTRAMBE le visualizzazioni dei
caratteri da parte dei processi devono arrestarsi. La ricezione da parte di uno qualunque dei due figli di un segnale SIGUSR2
deve riavviare ENTRAMBE le visualizzazioni.
Devono essere utilizzate le primitive per la gestione affidabile dei segnali.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
//flag che abilita la scrittura
int abilitaScrittura=1;
//vettore dei pid
int pid[2];
//pid dell'altro processo
int altroPid=0;
//Funzione di gestione segnali SIGUSR1 e SIGUSR2
//
//ATTENZIONE
//
//L'idea e' questa: quando un processo (es. P1) riceve un segnale, modofica il PROPRIO flag di abilitazione
//scrittua e inoltra lo stesso segnale all'altro figlio (es. P2), in modo che possa a sua volta modificare
//il proprio flag. Bisogna fare attenzione che, quando l'altro figlio (es. P2)riceve il segnale inoltrato, oltre che
//modificare il proprio flag, vorra a volta inoltrare il segnale, in quanto non sa se gli e' arrivato
//dall'esterno (utente) o dall'altro figlio (es. P1). Ci sono due modi per fare questo:
//1) usare il flag SA_SIGINFO. In questo modo handler viene chiamato con 3 parametri, di cui uno e' uno struct che
// contiene il pid del processo che ha mandato il segnale (vedi "man sigaction" e flag SA_SIGINFO
//2) controllando il proprio flag, meno elegante e sicuro, ma piu' semplice
//
//In questa soluzione usiamo il metodo 2
//
void handler(int signo)
{
printf("Ricevuto segnale\n");
if(!pid[0] || !pid[1]) //se non sono il padre
{
if(signo == SIGUSR1)
{
if(abilitaScrittura)
{
abilitaScrittura=0;
kill(altroPid,SIGUSR1);
}
}
else
{
if(!abilitaScrittura)
{
abilitaScrittura=1;
kill(altroPid,SIGUSR2);
}
}
}
else //sono il padre
{
//uccido i figli
kill(pid[0],SIGKILL);
kill(pid[1],SIGKILL);
//attendo i figli
wait();
}
}
int main()
{
struct sigaction sig;
sigset_t mask;
int i=0,p[2];
////////////////////////////////////////////
//SEGNALI
sig.sa_handler=handler; //gestore del segnale
sig.sa_flags=SA_RESTART;
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante l'esecuzione dell'handler
sigaction(SIGUSR1,&sig,NULL);
sigemptyset(&sig.sa_mask);
sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante l'esecuzione dell'handler
sigaction(SIGUSR2,&sig,NULL);
//////////////////////////////////////////
//creo la pipe
if(pipe(p)<0)
perror("pipe");
printf("Padre con pid %d\n",getpid());
for(i=0;i<2;i++)
{
if((pid[i]=fork())<0) //creo il figlio che gestira' la connessione
{
perror("fork figlio");
exit(-1);
}
if(pid[i]==0) //sono il figlio?
{
/////////////////////////////
//CODICE FIGLIO i-esimo
printf("Figlio %d con pid %d\n",i,getpid());
//leggo l'altro pid
if(read(p[0],&altroPid,sizeof(altroPid))<0)
perror("read altro pid");
//ciclo di scrittura
while(1)
{
//sono abilitato?
if(abilitaScrittura)
printf("%c",i==0 ? '*' : '.');
fflush(NULL);
//attendo un secondo
sleep(1);
}
////////////////////////////
}
}
//scrivo al primo figlio il pid del secondo
if(write(p[1],&pid[1],sizeof(pid[1]))<0)
perror("write secondo pid");
//scrivo al secondo figlio il pid del primo
if(write(p[1],&pid[0],sizeof(pid[0]))<0)
perror("write primo pid");
//attendo solo SIGUSR1
sigfillset(&mask);
sigdelset(&mask,SIGUSR1);
sigsuspend(&mask);
printf("Padre esce.\n");
return 0;
}
13- SOA/esempi utili catalogati/c_ps/2008-01-23.c/*TESTO 2008-01-23
Si realizzi un server concorrente su socket STREAM che abbia il seguente comportamento:
- il processo server Ps riceve dal client un primo messaggio ?SETUP? e crea un processo figlio Pf che si mette inizialmente in attesa di un segnale;
- dopo aver ricevuto dal client un secondo messaggio ?START? il processo server invia un segnale SIGUSR1 a Pf;
- dopo aver rispettivamente inviato e ricevuto il segnale SIGUSR1, Ps e Pf devono attendere un intervallo di durata casuale tra 1 e 10 secondi,
prima di inviare un segnale SIGKILL all?altro processo e terminare;
- al processo client dovra' essere trasmesso un messaggio ASCII contenente, sia per Ps che per Pf , il relativo PID e un?indicazione del fatto che abbia inviato o meno
il segnale SIGKILL.
Si richiede l?utilizzo della gestione affidabile dei segnali.
Si suggerisce l?utilizzo del comando telnet come client per la verifica del funzionamento del server
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#define PORT1 8080
int sock,msgsock,porta;
struct sockaddr_in server;
int pidPs, pidPf;
char buffer[256];
struct sockaddr_in client;
int lenght;
struct sigaction sig;
void handler (int signo)
{
if(signo == SIGUSR1)
printf("Processo figlio: sono il processo con PID %d e ho ricevuto il segnale SIGUSR1\n", getpid());
else
printf("Segnale non gestito\n");
}
/*
NOTA: Per come e' formulato il testo resta sempre la possibilita' di una corsa critica, nel caso in cui entrambi i SIGKILL vengano inviati assieme;
la conseguenza e' che nessuna delle due write viene eseguita, e al client non arriva nessun messaggio dopo l'invio del comando START
*/
int main()
{
/*DEFINIZIONE DELLA GESTIONE "AFFIDABILE" DEI SEGNALI*/
sig.sa_handler = handler; //installo il gestore del segnale SIGUSR1
sigemptyset(&sig.sa_mask);
sig.sa_flags=0;
sigaction(SIGUSR1,&sig,NULL);
pidPs=getpid();
/*DEFINIZIONE DELLA COMUNICAZIONE ATTRAVERSO SOCKET STREAM*/
/*Creo la SOCKET*/
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0) {
perror("creazione stream socket");
exit(-1);
}
/*Preparo la struttura sockaddr_in per settare i parametri della socket con PORT1*/
server.sin_family=AF_INET; /*internet protocol*/
server.sin_addr.s_addr=INADDR_ANY; /*ascolto da tutte le interfacce*/
server.sin_port=htons(PORT1); /*e dalla porta specificata*/
if(bind(sock,(struct sockaddr *)&server,sizeof(server)) < 0) {
perror("binding stream socket");
exit(-1);
}
/*Stampo il pid del processo server e il nome della porta di comunicazione*/
printf("Server %d attivo sulla porta %d\n",getpid(),ntohs(server.sin_port));
/*Dichiaro il numero massimo di connessioni*/
listen(sock, 5);
lenght=sizeof(client);
msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght); /*Attendo connesione*/
/*Leggo il primo messaggio ricevuto dal clienr*/
if(read(msgsock,buffer,256)<0)
perror("Errore: lettura del messaggio\n");
/*Controllo il messaggio ricevuto dal client*/
if(!strncmp(buffer, "SETUP", 5)) {
pidPf=fork();
/*Creo il processo figlio Pf*/
if(pidPf<0) {
perror("Errore: creazione processo figlio\n");
exit(-1);
}
if(pidPf==0) { /*Processo figlio Pf*/
pidPf=getpid();
srand(getpid());/*Inizializzo il generatore di numeri casuali*/
sigsuspend(&(sig.sa_mask)); /*Rimango in attesa di un segnale valido*/
int sec = rand()%10+1;
printf("Figlio (%d): dormo %d s\n", getpid(), sec);
sleep(sec);
kill(pidPs, SIGKILL); /*Spedisco il segnale SIGKILL al processo server Ps*/
sprintf(buffer, "Padre (%d): segnale non inviato\nFiglio (%d): segnale inviato\n", pidPs, pidPf);
if(write(msgsock, buffer, strlen(buffer)) < 0) /*Spedisco il messaggio al client*/
perror("Errore: scrittura del messaggio\n");
exit(0);
}
} else {
printf("Errore: comando non corretto %s\n");
exit(-1);
}
/*Leggo il secondo messaggio ricevuto dal clienr*/
if(read(msgsock,buffer,256)<0)
perror("Errore: lettura del messaggio\n");
/*Controllo il messaggio ricevuto dal client*/
if(!strncmp(buffer, "START", 5)) {
srand(getpid());/*Inizializzo il generatore di numeri casuali*/
kill(pidPf, SIGUSR1); /*Spedisco il segnale SIGUSR1 al processo figlio Pf*/
int sec = rand()%10+1;
printf("Padre (%d): dormo %d s\n", getpid(), sec);
sleep(sec);
kill(pidPf, SIGKILL); /*Spedisco il segnale SIGKILL al processo figlio Pf*/
sprintf(buffer, "Padre (%d): segnale inviato\nFiglio (%d): segnale non inviato\n", pidPs, pidPf);
if(write(msgsock, buffer, strlen(buffer)) < 0) /*Spedisco il messaggio al client*/
perror("Errore: scrittura del messaggio\n");
exit(0);
} else {
printf("Errore: comando non corretto\n");
exit(-1);
}
close(sock);
close(msgsock);
return 0;
}
4.
14-SOA/esempi utili catalogati/c_ps/2008-02-25.c/*TESTO: 2008-02-25
Si realizzi un programma in C che abbia il seguente comportamento:
- il processo genera un figlio
- il figlio trasmette al padre un numero casuale compreso tra 0 e 50
- il padre genera un altro numero casuale (sempre tra 0 e 50), lo somma a quello ricevuto, e lo
rispedisce al figlio visualizzando
<proprio PID>: <numero casuale estratto> <somma inviata>
- il figlio fa la stessa cosa: genera un altro numero casuale (sempre tra 0 e 50), lo somma a quello
ricevuto, e lo rispedisce al padre visualizzando
<proprio PID>: <numero casuale estratto> <somma inviata>
- lo scambio di messaggi deve procedere finchè il numero da inviare risulta maggiore di 1000. Non
appena un processo ottiene una somma superiore a 1000, deve inviare un segnale SIGUSR1
all’altro processo, il quale alla ricezione termina visualizzando il numero di messaggi ricevuti.
- Dopo aver inviato il segnale SIGUSR1 il processo termina visualizzando il valore finale della
somma (che sar superiore a 1000).
Si utilizzi la gestione affidabile dei segnali.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
int cont = 0;
void handler(int signo)
{
if (signo==SIGUSR1) {
printf("Sono il processo %d e termino in seguito alla ricezione di un segnale,e ho ricevuto %d messaggi\n",getpid(),cont);
exit(0);
} else
printf("Segnale non gestito");
}
int main()
{
int pidp,pidf;
int p1[2];
int p2[2];
int numerocasuale,somma,numeropadre;
struct sigaction sig;
printf("Sono il processo padre con pid %d\n",getpid());
pidp=getpid();
//creazione pipe
if (pipe(p1)<0) {
perror("Pipe error");
exit(-1);
}
if (pipe(p2)<0) {
perror("Pipe error");
exit(-1);
}
//gestione affidabile dei segnali
sig.sa_handler=handler;
sigemptyset(&sig.sa_mask);
sig.sa_flags=0;
sigaction(SIGUSR1,&sig,NULL);
//il padre crea il processo figlio
if ((pidf=fork())<0) {
perror("Errore di fork");
exit(-1);
}
if (pidf==0) { //codice figlio
close(p1[0]);
close(p2[1]);
srand(getpid()); //inizializzo il generatore di numeri casuali
somma=rand()%50;
if(write(p1[1],&somma,sizeof(somma))<0) {
perror("Errore di scrittura");
exit(-1);
}
do {
if(read(p2[0],&somma,sizeof(somma))<0) {
perror("Errore in lettura");
exit(-1);
}
cont++;
numerocasuale=rand()%50;
somma+=numerocasuale;
printf("Pid %d: numero casuale %d somma inviata %d\n",getpid(),numerocasuale,somma);
if(write(p1[1],&somma,sizeof(somma))<0) {
perror("Errore in scrittura");
exit(-1);
}
} while(somma<=1000);
kill(getppid(),SIGUSR1);
printf("Sono il processo figlio %d e il valore finale di somma è %d\n",getpid(),somma);
exit(0);
} else { //processo padre
close(p1[1]);
close(p2[0]);
do {
if(read(p1[0],&somma,sizeof(somma))<0) {
perror("Errore in lettura");
exit(-1);
}
cont++;
numeropadre=rand()%50;
somma+=numeropadre;
printf("Pid %d: numero casuale %d somma inviata %d\n",getpid(),numeropadre,somma);
if(write(p2[1],&somma,sizeof(somma))<0) {
perror("Errore in scrittura");
exit(-1);
}
} while(somma<=1000);
kill(pidf,SIGUSR1);
printf("Sono il processo padre %d e il valore finale di somma è %d\n",getpid(),somma);
exit(0);
}
return 0;
}
15-SOA/esempi utili catalogati/c_ps/2008-06-19.c#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define MAX_CHILD 10
int n;
int child_pids[MAX_CHILD];
void handler(int signo) { // funzione da eseguire all'arrivo del segnale
int i;
for(i = 0; i < n; ++i) {
printf("killing child process %d\n", child_pids[i]);
kill(child_pids[i], SIGTERM);
}
exit(0);
}
int main(int argc,char *argv[])
{
int pid,i,s;
int sum;
int pdchf[2];
int numero;
int arrivo;
struct sigaction act;
sigset_t sigmask, zeromask;
if(argc !=2) {
fprintf(stderr,"Use: %s N\n",argv[0]);
exit(-1);
}
n = atoi(argv[1]);
if(n < 1)
n = 1;
else if(n > MAX_CHILD)
n = MAX_CHILD;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags= 0;
sigaction(SIGUSR1, &act, NULL);
// blocco il segnale SIGUSR1
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
// apro la pipe per la comunicazione da padre a figlio
if (pipe(pdchf) < 0) {
perror("pipe() error");
exit(1);
}
/* creo i figli */
for(i = 0; i < n ; i++) {
if((pid=fork())<0) { //controllo che non ci siano errori nel processo di creazione dei figli
perror("fork() error");
exit(-2);
} else {
if(pid == 0) { /* Processi figli */
sum = 0;
close(pdchf[1]);
printf("CHILD %d created\n", getpid());
do {
read(pdchf[0], &arrivo, sizeof(int));
printf("CHILD %d: read %d -> sum = %d\n", getpid(), arrivo, sum + arrivo);
sum += arrivo;
} while(sum <= 500);
// ho un totale di numeri superiori a 500 quindi invio il segnale al padre
kill(getppid(), SIGUSR1);
printf("CHILD %d: sum = %d, sending SIGUSR1...\n",getpid(), sum);
exit(0);
} else {
child_pids[i] = pid;
}
}
}
printf("Child process creation complete, starting to write...\n");
close(pdchf[0]); //il padre sulla pipe deve solo scrivere cosa mandare al figlio
srand(time(0));
for(s = 0; s < 5; s++) { //lo faccio entrare in un for dove manda 5 numeri e intanto è insensibile a SIGUSR1
sleep(2);
numero = rand() % 90 + 10;
printf("sending %d\n", numero);
write(pdchf[1], &numero, sizeof(int));
}
printf("Five iterations completed, unmasking SIGUSR1...\n");
sigprocmask(SIG_UNBLOCK, &sigmask, NULL); //ora che ho inviato come minimo 5 segnali il padre diventa sensibile a SIGUSR1
for(;;) {
sleep(2);
numero = rand() % 90 + 10;