informatica:sol:laboratorio16:esercitazionia:scexamples

Alcuni esempi di semplici programmi C con processi e pipe

nproc.c (chiamate: fork, wait, getpid)

Si creano nproc processi figli che eseguono un certo numero di iterazioni, si attende quindi la loro terminazione stampando la condizione di terminazione l'exit status e l'eventuale segnale che ha terminato il processo.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>

// utility macro
#define SYSCALL(r,c,e) \
    if((r=c)==-1) { perror(e);exit(errno); }

// numero di processi di default
const int NPROC = 2;

// forward declaration, procedura eseguita da un processo
void procF(int);

int main(int argc, char * argv[]) {
    int nproc;
    
    // check arguments
    if(argc>1)  nproc = atoi(argv[1]);
    else  nproc = NPROC;
    
    // per distinguere tra il processo padre ed i processi figli
    int ppid = getpid();
    
    // genero nproc processi figli
    for(int i=0;i<nproc;i++) {
	int r;
	
	SYSCALL(r,fork(),"creando un processo");
	if(r == 0) {               // processo figlio
	    procF(i+2);            // eseguo i+2 iterazioni
	    break;                 // altrimenti genero troppi processi
	} else {                   // processo padre
	    printf("Creato processo figlio con pid %d\n",r);
	                           // continua il ciclo
	}
    } 
    
    // il solo processo padre attende i figlio
    if(getpid() == ppid) {
	int status,r;
	for(int i=0;i<nproc;i++) {
	    SYSCALL(r, wait(&status), "wait");
	    if(WIFEXITED(status))
		printf("Figlio con pid %d terminato con exit, codice %d\n",
		       r, WEXITSTATUS(status));
	    else if (WIFSIGNALED(status)) {
		printf("Figlio con pid %d terminato da un segnale (sig=%d)\n",
		       r, WTERMSIG(status));
	    } else if (WIFSTOPPED(status) || WIFCONTINUED(status)) 
		printf("Figlio con pid %d interrotto da o ripartito con un segnale\n",r);
	}
    } // else sono un processo figlio e termino senza fare nient'altro
    
    return getpid() % 256; // termino con questo codice
}

// procedura eseguita dai processi figli
void procF(int niter) {
  for(int i=0;i<niter;++i) {
    printf("Sono il processo %d all'iterazione %d\n",getpid(),i);
    sleep(1);
  }
}

command.c (chiamate: fork, wait, getpid, execvp)

Programma che prende come argomento un programma eseguibile ed i suoi eventuali argomenti e lancia il programma attendendone la terminazione.

#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>        
#include <sys/types.h>       
#include <unistd.h>          
#include <errno.h>           

// utility macro
#define SYSCALL(r,c,e) \
    if((r=c)==-1) { perror(e);exit(errno); }

int main(int argc, char *argv[]) {
    if (argc == 1) {
	fprintf(stderr, "usa: %s comando [argomenti-comando]\n", argv[0]);
	return EXIT_FAILURE;
    }
    
    int pid;
    SYSCALL(pid, fork(), "fork");
  
    if(pid == 0) { // figlio 
	int r;
	execvp(argv[1],&argv[1]);
	perror("execvp");
	return errno;
    } 
    int status;
    SYSCALL(pid, wait(&status),"wait");
    printf("Processo %d terminato con ",pid);
    if(WIFEXITED(status))  printf("exit(%d)\n",WEXITSTATUS(status));
    else printf("un segnale (sig=%d)\n", WTERMSIG(status));
    return 0;
}

zombie.c (chiamate: fork, waitpid, getpid, execlp)

Il seguente programma crea un numero di processi zombie mostrando il loro stato con il comando unix 'ps'.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>

// utility macro
#define SYSCALL(r,c,e) \
    if((r=c)==-1) { perror(e);exit(errno); }

int main(int argc, char * argv[]) {
    const int SEC=2;

    if (argc != 2) {
	fprintf(stderr, "usa: %s num-proc\n", argv[0]);
	return EXIT_FAILURE;
    }
    
    int nproc = atoi(argv[1]);
    int pid;    
    for(int i=0;i<nproc;++i) {
	SYSCALL(pid,fork(),"fork");
	if(pid==0) { // figlio 
	    sleep(SEC);
	    exit(0); // esco con successo
	} else 
	    printf("%d: creato processo con pid = %d\n",  getpid(), pid);    
    }
    
    // solo il processo padre arriva qui

    // aspettiamo un po' in modo da essere "sicuri" 
    // che i processi figli siano terminati
    sleep(2*SEC); 

    printf("Stato dei processi prima delle wait:\n");
    SYSCALL(pid,fork(),"fork");
    if(pid==0) {
	execlp("ps","ps",NULL);
	perror("eseguendo ps");
	exit(errno);
    } else {
	int stato;
	printf("Lanciato il processo con pid = %d per eseguire un ps\n",pid);
	SYSCALL(pid,waitpid(pid,&stato,0),"waitpid");
	printf("Processo %d terminato\n",pid);
    }

    // adesso attendiamo la terminazione dei processi
    for(int i=0;i<nproc;++i) {
	int stato;
	SYSCALL(pid,wait(&stato),"wait");
	if(WIFEXITED(stato)) 
	    printf("Processo con pid %d terminato con una exit(%d)\n",
		   pid,WEXITSTATUS(stato));
    }
    printf("Stato dei processi dopo la terminazione:\n");
    SYSCALL(pid,fork(),"creando il processo ps");
    if(pid==0) {
	execlp("ps","ps",NULL);
	perror("eseguendo il ps");
	exit(0);
    } else {
	int stato;
	SYSCALL(pid,waitpid(pid,&stato,0),
		"attendendo la terminazione del processo ps");
    }
    return 0;
}

pipe2proc.c (chiamate: fork, wait, getpid, write, pipe, dup, execve)

Il programma crea un processo figlio che esegue il comando sort sull'input ricevuto in una pipe che connette lo standard output del padre con lo standard input del figlio ( padre – pipe –> figlio).
Il programma produce in output l'equivalente prodotto dal comando shell:

echo "Ciao mondo ! Passo e chiudo ..." | tr ' ' '\n' | LC_ALL=C sort 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

// utility macro
#define SYSCALL(r,c,e) \
    if((r=c)==-1) { perror(e);exit(errno); }


int main(int argc, char *argv[]) {    
    int canale[2];

    int r;
    SYSCALL(r, pipe(canale), "pipe");

    if (fork() != 0) {  // padre
	close(1);
	SYSCALL(r,dup(canale[1]),"dup");

	// chiudo tutti i descrittori che non uso
	close(canale[1]);	
	close(canale[0]);
    } else {            // figlio
	close(0);
	SYSCALL(r, dup(canale[0]), "dup");

	// chiudo tutti i descrittori che non uso prima di chiamare la exec
	close(canale[1]);	
	close(canale[0]);

	// utilizzo la chiamata di sistema execve specificando le
	// variabili di ambente del processo sort
	char *path = getenv("PATH");
	char envpath[strlen("PATH=")+strlen(path)+1];
	char envlcall[] = "LC_ALL=C";
	snprintf(envpath, sizeof(envpath), "PATH=%s",path); 
	char *envp[] = { envpath, envlcall, NULL};
	char *cmd[]  = { "/usr/bin/sort", NULL };

	execve(cmd[0], cmd, envp);
	perror("execve");
	exit(errno);
    }

    SYSCALL(r, write(1, "mondo\n", 7), "write1");
    SYSCALL(r, write(1, "Ciao\n", 6), "write2");
    SYSCALL(r, write(1, "!\n", 3), "write3");
    SYSCALL(r, write(1, "Passo\n", 7), "write3");
    SYSCALL(r, write(1, "e\n", 3), "write3");
    SYSCALL(r, write(1, "chiudo\n", 8), "write3");
    SYSCALL(r, write(1, "...", 4), "write3");
	    
    // chiudo l'output prima di attendere la terminazione
    close(1);

    // attendo la terminazione del processo figlio
    int status;
    SYSCALL(r, wait(&status), "wait");
    return 0;
}
informatica/sol/laboratorio16/esercitazionia/scexamples.txt · Ultima modifica: 13/04/2016 alle 13:22 (8 anni fa) da Massimo Torquati