User Tools

Site Tools


progsys:index

Programmation Système

Site Web : http://dept-info.labri.fr/ENSEIGNEMENT/prs

Quelques exemples et corrections des TP de Programmation Système en L3 Info…

Redirection de la sortie d'erreur

redir.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc, char *argv[]) 
{  
  int fd = open("error", O_WRONLY|O_CREAT|O_APPEND, 0644);
  dup2(fd, 2); close(fd);
  printf("hello on stdout\n");
  fprintf(stderr, "hello on stderr\n");  
  return EXIT_SUCCESS;
}

Commande ls

myls.c
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
 
int main(int argc, char **argv) 
{  
  DIR * dir = opendir(".");
  if(dir == NULL) return EXIT_FAILURE;
  struct dirent * entry;
  while((entry = readdir(dir)) != NULL) 
    printf("%s\n", entry->d_name);    
  return EXIT_SUCCESS;
}

Création de N processus fils

forkn.c
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
 
int main (int argc, char *argv[])
{  
  int n = atoi(argv[1]);
 
  for(int i = 0 ; i < n ; i++) {    
    if( (fork() == 0) {      
      printf("fils %d (pid = %d)\n",i, getpid());
      return EXIT_SUCCESS; 
    }    
  }
 
  for(int i = 0 ; i < n ; i++) wait(NULL); 
 
  return EXIT_SUCCESS;
}

Faux Pipe

On code “ls | wc -w” à la MS-DOS, en passant par un fichier temporaire, i.e. “ls > /tmp/toto ; wc -w < /tmp/toto”.

faux-pipe.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
 
int main (int argc, char *argv[])
{    
  // création d'un nouveau processus fils...
  int pid = fork();
 
  if(pid == 0) { // fils
    int fd = open("/tmp/toto", O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if(fd < 0) { perror("open"); exit(EXIT_FAILURE);}
    dup2(fd,STDOUT_FILENO); // redirection de la sortie standard dans le fichier
    close(fd);
    execlp(argv[1],argv[1],NULL); // cmd1 remplace le processus fils
    perror("exec cmd1"); exit(EXIT_FAILURE);    
  }
  else { // pére
    wait(NULL); // j'attend la fin de mon fils...
    int fd = open("/tmp/toto", O_RDONLY);
    if(fd < 0) { perror("open"); exit(EXIT_FAILURE);}
    dup2(fd,STDIN_FILENO); // redirection de l'entrée du programme
    close(fd);
    unlink("/tmp/toto"); // suppression du fichier à la fin du programme...
    execvp(argv[2],argv+2); // cmd2 remplace le processus principal  
    perror("exec cmd2"); exit(EXIT_FAILURE);    
  }
 
  return EXIT_SUCCESS;
}

Commande log

log.c
/* log file cmd args... */
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main(int argc, char* argv[])
{ 
  int p[2];
  pipe(p);
  char c;
 
  if(fork()) { /* père */
    int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if(fd < 0) exit(EXIT_FAILURE);
    close(p[1]); /* ici, sinon read() bloquera à la fin ! */
    while(read(p[0], &c, 1) > 0) {
      write(1, &c, 1);
      write(fd, &c, 1);
    }
    close(fd); 
    close(p[0]);
  }
  else { /* fils */
    close(p[0]);
    dup2(p[1], 1);
    close(p[1]);
    execvp(argv[2], argv+2);
    exit(EXIT_FAILURE);
  }
 
  return EXIT_SUCCESS;
}

Si l'on dispose de la commande tee qui duplique son entrée standard (0) sur la sortie standard (1) et dans un fichier passé un argument, on peut simplifier notre programme de la façon suivante.

log2.c
int main(int argc, char* argv[])
{  
  int p[2];
  pipe(p);
 
  if(fork()) { /* père */
    close(p[1]);
    dup2(p[0], 0);
    close(p[0]);
    execlp("tee", "tee", argv[1], NULL);
    exit(EXIT_FAILURE);
  }
  else { /* fils */
    close(p[0]);
    dup2(p[1], 1);
    close(p[1]);
    execvp(argv[2], argv+2);
    exit(EXIT_FAILURE);
  }
 
  return EXIT_SUCCESS;
}

Une chaîne de commandes

On souhaite écrire un programme ./chaine cmd0 cmd1 cmd2 … cmdN qui exécute une chaîne de commandes de la façon suivante :

./cmd1 | ./cmd2 | ./cmd3 | ... | cmdN

Dans cette première version, chaque fils crée un nouveau fils en cascade :

Père ==> Fils0 ==> Fils1 ==> ... ==> FilsN-1
(cmd0)   (cmd1)   (cmdN-1)           cmdN

Le pipe est partagé naturellement entre FilsK-1 et FilsK.

chaine1.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
  int nb = argc-1;
  int tube[2];
  int i;
 
  for(i=0; i < nb-1; i++) {
    pipe(tube);
    if(fork() != 0) { /* père */
      dup2(tube[1],1);
      close(tube[0]); 
      close(tube[1]);
      break; 
 
    } else { /* fils */
      dup2(tube[0],0);
      close(tube[0]); 
      close(tube[1]);
    }
  }  
 
  execlp(argv[i+1], argv[i+1], NULL); 
  perror("execlp"); 
 
  return EXIT_SUCCESS;
}

Pour tester ce programme, nous pouvons exécuter les commandes suivantes :

$ echo bbb > file.txt
$ echo aaa >> file.txt
$ echo aaa >> file.txt
$ echo ccc >> file.txt
$ ./chaine cat sort uniq < file.txt
aaa
bbb
ccc

Examinons maintenant une autre version… Nous allons juste inverser le code du père et du fils dans la boucle for ! Expliquer par quel miracle ce code peut marcher ?

chaine2.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
  int nb = argc-1;
  int tube[2];
  int i;
 
  for(i=0; i < nb-1; i++) {
    pipe(tube);
    if(fork() == 0) { /* fils */
      dup2(tube[1],1);
      close(tube[0]); 
      close(tube[1]);
      break; 
 
    } else { /* père */
      dup2(tube[0],0);
      close(tube[0]); 
      close(tube[1]);
    }
  }  
 
  execlp(argv[i+1], argv[i+1], NULL); 
  perror("execlp"); 
 
  return EXIT_SUCCESS;
}

Indice : Dans ce cas, un même père crée tous les fils de la façon suivante… mais cela n'explique pas tout !

Père             (cmdN)
     ==> Fils0   (cmd0)
     ==> Fils1   (cmd1) 
     ==> 
     ... 
     ==> FilsN-1 (cmdN-1)

Calculer en Pipeline

Générer/afficher un fichier de k double

on utilise les entrées et sorties standard pour faire simple…

generer.c
/* $ ./generer k > fichier */
int main(int argc, char* argv[]) {
  int k = atoi(argv[1]);
  double x;
  for(int i = 0 ; i < k ; i++) {
    x = (double)i;
    write(1, &x, sizeof(double));
  }
  return EXIT_SUCCESS;
}
afficher.c
/* $ ./afficher k < fichier */
int main(int argc, char* argv[]) {
  int k = atoi(argv[1]);
  double x;
  for(int i = 0 ; i < k ; i++) {
    read(0, &x, sizeof(double));
    printf("%f ", x); 
  }
  printf("\n");
  return EXIT_SUCCESS;
}
Calculer Pipeline (correction partielle à compléter)
calculer.c
/* calculer < in > out */
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/wait.h>
 
#define N 4
typedef double (*funtab_t)(double);
double ajouter_un_dizieme(double d){return d+0.1;}
double ajouter_un_centieme(double d){return d+0.01;}
double ajouter_un_millieme(double d){return d+0.001;}
 
funtab_t funtab[]={ajouter_un_millieme,ajouter_un_centieme,ajouter_un_dizieme,ajouter_un_millieme};
 
int nombre_suivant(double *d)
{
  if(read(0,d,sizeof(double)) == sizeof(double))
    return 0;
  return -1;
}
 
void executer(int i) 
{
  double x = 0.0d;
  while(nombre_suivant(&x) != -1) {
    double y = funtab[i](x);
    write(1, &y, sizeof(double));
  }
}
 
void fermer_tubes(int* tubes)
{
  for(int i = 0 ; i < N-1 ; i++) {close(tubes[i*2+0]); close(tubes[i*2+1]); }  
}
 
int main()
{
  /* création de N-1 tubes */
  int tubes[N-1][2];
  for(int i = 0 ; i < N-1 ; i++) { 
    /* TODO: création tube */
  }
 
  /* création de N fils */
  for(int i = 0 ; i < N ; i++) { 
    /* fils i qui utilise tubes[i] et tubes[i+1] */
    if(fork() == 0) { 
      /* TODO: redirection tube */
      fermer_tubes((int*)tubes);
      executer(i);
      return 0;
    }
  }
 
  fermer_tubes((int*)tubes);
 
  /* attente de tous les fils */
  for(int i = 0 ; i < N ; i++) wait(NULL);
 
  return 0;
}

DS 2014-2015 : Correction Exo 2

consprod.c
/* Exo 2.2 et 2.3 */
/* compilation: gcc -std=c99 consprod.c -o consprod */
/* execution: ./consprod */
 
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
 
void producteur(FILE* output)
{
  for(int i = 0 ; i < 4 ; i++)
    fprintf(output, "coucou\n"); 
}
 
void consommateur(FILE* input)
{
  char c;
  while(fread(&c, 1, 1, input) > 0) 
    printf("%c",c);
}
 
 
int main()
{
  int fd[2];
  FILE* fp[2];
  pipe(fd);
  fp[0] = fdopen(fd[0], "r");
  fp[1] = fdopen(fd[1], "w");
 
 
  /* Exo 2.3: En principe, il est mieux que le processus qui
     interagit avec le terminal rende la main en dernier et soit
     donc le père. */
 
  /* FILS */
  if(fork() == 0) { 
    fclose(fp[0]);     
    producteur(fp[1]);
    fclose(fp[1]);
    return EXIT_SUCCESS;
  }
 
  /* PAPA */
  fclose(fp[1]);
  consommateur(fp[0]);
  fclose(fp[0]); 
  wait(NULL);
 
  return EXIT_SUCCESS;
}
consprodN.c
/* Exo 2.5 */
/* compilation: gcc -std=c99 consprodN.c -o consprodN */
/* execution: ./consprodN 4 */
 
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
void producteur(FILE* output)
{
  for(int i = 0 ; i < 4 ; i++)
    fprintf(output, "coucou\n"); 
}
 
void consommateur(FILE* input)
{
  char c;
  while(fread(&c, 1, 1, input) > 0) 
    printf("%c",c);
}
 
 
int main(int argc, char* argv[])
{
  int N = atoi(argv[1]);
 
  int fd[2];
  FILE* fp[2];
  pipe(fd);
  fp[0] = fdopen(fd[0], "r");
  fp[1] = fdopen(fd[1], "w");
 
  for(int i = 0 ; i < N ; i++) {
    if(fork() == 0) { /* ième FILS */
      fclose(fp[0]);     
      producteur(fp[1]);
      fclose(fp[1]);
      return EXIT_SUCCESS;
    }
  }
 
  /* PAPA */
  fclose(fp[1]);
  consommateur(fp[0]);
  fclose(fp[0]); 
  for(int i = 0 ; i < N ; i++) wait(NULL); /* elimination zombies */
  return EXIT_SUCCESS;
}

Ecrire un boucle for avec setjmp()/longjmp()

loop.c
#include <setjmp.h>
#include <stdio.h>
 
int main() {
  jmp_buf env;
 
  int i = 0;
  setjmp(env);
  printf("%d\n",i);
  i++;
  if(i < 10) longjmp(env, 1);
 
  return 0;
}

Groupe de processus en avant-plan dans un terminal

Voici un code montrant comment il est possible de faire en sorte que les signaux (ctrl-c ou SIGINT dans cet exemple) soient envoyés au fils (exécutant ici la commande cat) et non au groupe père+fils (comportement par défaut). Pour ce faire, il est nécessaire que le fils dispose de sont propre groupe (1) et que ce groupe soit mis en avant-plan (2). A la fin de l'excution du fils, le père doit repasser en avant-plan (3). Si vous commentez les lignes (1), (2) et (3), alors le père et fils recevront tous les deux le signal SIGINT.

foreground.c
#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED
 
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
 
void myhandler(int s) {
  printf("[pid %d] signal %d received\n", getpid(), s);
}
 
int main() {
  printf("papa = %d\n", getpid());
 
  struct sigaction act;
  act.sa_handler = myhandler;
  act.sa_flags = 0; 
  sigemptyset(&act.sa_mask);
  sigaction(SIGINT, &act, NULL); // mise en place d'un handler pour ctrl-c (hérité par le fils)
 
  signal(SIGTTOU, SIG_IGN); // ignorer ce signal qui survient quand papa repasse en foreground...
 
  int fils = fork();
  if(fils == 0) {   /* FILS */
    printf("fils = %d\n", getpid());
    char c; 
    while(read(0, &c, 1) > 0) { write(1, &c, 1); } // commande "cat" 
    return EXIT_SUCCESS;
  }
  else { /* PAPA */
    setpgid(fils, 0); // (1) le père crée un nouveau groupe pour son fils
    tcsetpgrp(0, fils); // (2) le groupe du fils devient le "terminal foreground process group"
    wait(NULL);
    tcsetpgrp(0, getpgid(0)); // (3) le pére redevient le "terminal foreground process group" et reçoit un SIGTTOU
    return EXIT_SUCCESS;
  }
 
  return EXIT_FAILURE;
}

Appel système interruptible (ou non)

Les appels systèmes sont par défaut non interruptibles, sauf les appels systèmes “arbitrairement long” comme par exemple une lecture sur l'entrée standard ou sur un pipe ! Mais qu'en est-il par exemple de l'écriture de plusieurs Go dans un fichier, un appel système qui peut durer longtemps… ?

write2g.c
#define _BSD_SOURCE
 
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
 
void handler(int s) { fprintf(stderr,"signal %d received!\n",s); }
 
int main(int argc, char* argv[])
{
  printf("pid = %d\n", getpid());
  signal(SIGINT, handler);
  int fd = open("/tmp/toto", O_WRONLY | O_TRUNC | O_CREAT | O_SYNC, 0644);
  size_t size = 2*1000*1000*1000; // ~2GB
  char * buffer = malloc(size);
  perror("malloc");
  if(!buffer) { return 0; }
  printf("writing ~2G in /tmp/toto... try to interrupt me with ctrl-c?\n");
  ssize_t w = write(fd, buffer, size);
  perror("write"); 
  printf("write %zd / %zd\n", w, size);
  close(fd);
}

Les n reines (version multi-threads)

Attention, ce code n'est pas fini, car il faut protéger l'accès concurrent à la variable cpt !!!

nreines-threads.c
/* gcc -std=c99 -pthread nreines-threads.c  */
 
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
 
#define MAX 16
 
typedef bool echiquier[MAX][MAX];
 
// variables globales
int cpt = 0;
int n;
 
static bool ok (int n, int ligne, int colonne, echiquier e);
 
void nreines (int n, int ligne, echiquier e, int *cpt)
{
  for (int col = 0; col < n; col++)
    if (ok (n, ligne, col, e))
    {
      if (ligne == n - 1) (*cpt)++; // 1 solution trouvée en plus !      
      else
      {
	e[ligne][col] = true;
	nreines (n, ligne + 1, e, cpt);
	e[ligne][col] = false;
      }
    }
}
 
 
static bool ok (int n, int ligne, int colonne, echiquier e)
{
  int l, c;
  for (l = 0; l < ligne; l++)
    if (e[l][colonne])
      return false;
 
  for (l = ligne - 1, c = colonne - 1; l >= 0 && c >= 0; l--, c--)
    if (e[l][c])
      return false;
 
  for (l = ligne - 1, c = colonne + 1; l >= 0 && c <= n; l--, c++)
    if (e[l][c])
      return false;
  return true;
}
 
 
void usage (char *s)
{
  fprintf (stderr, "%s entier", s);
  exit (EXIT_FAILURE);
}
 
 
void * start(void * arg) 
{
  size_t i = (size_t)arg;
  echiquier e; // ne doit pas être globale !
  memset (e, 0, sizeof (e));
  e[0][i] = true; // on place la première reine sur la ligne 0, ième colonne
  nreines (n, 1, e, &cpt); // on explore à partir de la ligne 1
}
 
int main (int argc, char *argv[])
{
  if (argc < 2) usage (argv[0]);
  n = atoi(argv[1]);
  pthread_t tids[n];
  assert(n > 0);
 
  assert(sizeof(size_t) == sizeof(void*));
 
  for(size_t i = 0; i <n ; i++)
    pthread_create(tids + i, NULL, start, (void*)i);
 
  for(int i = 0; i <n ; i++)
    pthread_join(tids[i],NULL);  
 
 printf ("%d\n", cpt);
 
  return EXIT_SUCCESS;
}

Essayer

essayer.c
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
 
static sigjmp_buf buf;
 
static void handler(int sig)
{
  siglongjmp(buf, 1);
}
 
int essayer(void (*f)(), int sig)
{
  int r = 0;
 
  struct sigaction sa, old;
  sa.sa_handler = handler;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);
  sigaction(sig, &sa, &old);
 
  if(sigsetjmp(buf, 1) == 0)
    f();
  else r = 1;
 
  sigaction(sig, &old, NULL);
 
  return r;
}
 
void toto()
{
  *(int *)0L = 12; // SEGV
}
 
int main(int argc, char *argv[])
{
  printf("essai de toto : %s\n", essayer(toto, SIGSEGV) == 0 ? "ok" : "echec");
  return 0;
}

Envoyer & Recevoir Signaux

Le fils bombarde son père de signaux. Voici une version incomplète à modifier, car on perd des signaux !!! Il faut mettre en place un système d'acquitement des signaux du père vers le fils (signal SIGUSR1).

signaux-pere-fils-v0.c
// compilation: gcc -std=c99 signaux-pere-fils.c
// test: ./a.out 100 1 2 3 4 5 6
 
#define _GNU_SOURCE
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
 
#define NSIGNORT 32
int tab[NSIGNORT];
 
void myhandler(int sig) {
  printf("on vient de recevoir le signal %d (%s) %d fois\n",
	 sig, strsignal(sig), ++tab[sig]);
}
 
int main(int argc, char * argv[]) {
 
  printf("pid: %d\n", getpid());
 
  int fpid = fork();
 
  if(fpid == 0) { /* FILS: emettre signaux */
 
    int ppid = getppid(); // mon père
    int k = atoi(argv[1]);
 
    for(int s = 2 ; s < argc ; s++)
      for(int i = 0 ; i < k ; i++) {
	kill(ppid, atoi(argv[s])); // envoi signal
      }	
 
    // end it
    printf("I will kill daddy in 3 sec...\n");
    sleep(3);
    kill(ppid, 9); 
  }
 
  else { /* PÈRE : recevoir signaux */
 
    // installation du handler pour tous les signaux non RT
    struct sigaction act;
    act.sa_handler = myhandler; 
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
 
    for(int sig = 1 ; sig < NSIGNORT ; sig++) {
      sigaction(sig, &act, NULL);
      tab[sig] = 0;
    }
 
    while(1) {
      pause(); // attente de signaux...      
    }
 
    wait(NULL);
 
  }
 
  return 0;
}

Voici une proposition de correction qui met en place l'acquittement des signaux et utilise sigprocmask()/sigsuspend() plutôt que pause() pour ne pas perdre de signaux !

signaux-pere-fils.c
// compilation: gcc -std=c99 signaux-pere-fils.c
// test: ./a.out 100 1 2 3 4 5 6
 
#define _GNU_SOURCE
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
 
#define NSIGNORT 32
int tab[NSIGNORT];
 
void myack(int sig) { }
 
void myhandler(int sig) {
  printf("on vient de recevoir le signal %d (%s) %d fois\n",
	 sig, strsignal(sig), ++tab[sig]);
}
 
int main(int argc, char * argv[]) {
 
  printf("pid: %d\n", getpid());
 
  // par défaut, on bloque tous les signaux (hérité par le fils)
  sigset_t fullmask;
  sigfillset(&fullmask);
  sigprocmask(SIG_SETMASK, &fullmask, NULL); 
 
  int fpid = fork();
 
  if(fpid == 0) { /* FILS: emettre signaux */
 
    struct sigaction act;
    act.sa_handler = myack; 
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGUSR1, &act, NULL);
 
    int ppid = getppid(); // mon père
    int k = atoi(argv[1]);
 
    // on débloque SIGUSR1 uniquement
    sigset_t ackmask;
    sigfillset(&ackmask);
    sigdelset(&ackmask, SIGUSR1);
 
    for(int s = 2 ; s < argc ; s++)
      for(int i = 0 ; i < k ; i++) {
	kill(ppid, atoi(argv[s]));    // envoi signal
	sigsuspend(&ackmask);         // attente ack
      }	
 
    // I will kill daddy in 3 sec...
    sleep(3);
    kill(ppid, 9); 
  }
 
  else { /* PÈRE : recevoir signaux */
 
    // sleep(1); // debug    
 
    // installation du handler pour tous les signaux non RT
    struct sigaction act;
    act.sa_handler = myhandler; 
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
 
    for(int sig = 1 ; sig < NSIGNORT ; sig++) {
      sigaction(sig, &act, NULL);
      tab[sig] = 0;
    }
 
    sigset_t emptymask;
    sigemptyset(&emptymask);
 
    while(1) {
      sigsuspend(&emptymask);      // attente de signaux (en les débloquant tous)...
      kill(fpid, SIGUSR1);         // envoie ack
    }
 
    wait(NULL);
 
  }
 
  return 0;
}

Open Pipe Command (exo 3, DS 2017-2018)

On souhaite disposer d’une fonction open_pipe_command dont le profil est :

int open_pipe_command (int *fd, char *cmd, char **argv)

Cette fonction crée un processus chargé d’exécuter la commande cmd avec les arguments argv, et renvoie deux descripteurs (dans le tableau fd) permettant respectivement d’écrire vers l’entrée standard de la commande (descripteur fd[1]) et de lire depuis la sortie standard de la commande (descripteur fd[0]). On utilisera pour cela deux tubes permettant d’établir une connexion bidirectionnelle avec le processus exécutant la commande. La fonction retournera le pid du processus fils exécutant la commande.

open_pipe_command.c
/* gcc -Wall -std=c99 open_pipe_command.c && ./a.out */
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
 
#define HELLO "hello world!" // 12 
 
int open_pipe_command(int * fd, char * cmd, char **argv)
{
  int pr[2]; pipe(pr); 
  int pw[2]; pipe(pw); 
  /* user reads in fd[0]=pr[0] & writes in fd[1]=pw[1] */
  fd[0] = pr[0]; fd[1] = pw[1];   
  int pid = fork();
  if(pid == 0) { /* child */        
    close(pw[1]); close(pr[0]); /* useless */
    /* cmd writes in pr[1]=1 & reads in pw[0]=0 */
    dup2(pw[0],0); dup2(pr[1],1); 
    close(pw[0]); close(pr[1]); 
    execvp(cmd, argv); 
    perror("Error execvp");
    exit(EXIT_FAILURE);
  }
  close(pw[0]); close(pr[1]); /* useless */
  return pid; /* success > 0*/
}
 
int main(int argc, char * argv[])
{
  int fd[2];
  char* cmd[] = {"tr", "a-z", "A-Z", NULL};
  int pid = open_pipe_command(fd, *cmd, cmd);
  if(pid <= 0) return EXIT_FAILURE;
  write(fd[1], HELLO, strlen(HELLO)+1);
  close(fd[1]);
  char msg[128];
  int r = read(fd[0], msg, 128);  
  printf("msg[%d]: %s -> %s\n", r, HELLO, msg);
  close(fd[0]);
  waitpid(pid, NULL, 0);
  return EXIT_SUCCESS;
}

Ctrl-C

Un processus fils est dans le même PGID (process group ID) que son père par defaut (pgid père = pgid fils = pid père). Un kill sur -pid du père envoie le signal à tous le groupe, donc au fils également…

sigint.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
 
void handler(int sig)
{
    printf("paf\n");
}
 
int main(void)
{
    signal(SIGINT, handler); // TODO: utiliser sigaction() plutôt que signal() !
 
    if (fork() == 0)
    {
        printf("child: %d %d\n", getpid(), getpgid(0));
        pause();
        printf("child: bye bye!\n");
        exit(EXIT_SUCCESS);
    }
 
    printf("father: %d %d\n", getpid(), getpgid(0));
    pause();
    printf("father: bye bye!\n");
    return EXIT_SUCCESS;
}

Timeout

executer-avant-delai.c
#define _GNU_SOURCE
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <setjmp.h>
 
static struct sigaction sa, old;
static sigjmp_buf env;
 
static void myalarm(int sig)
{
  printf("alarm!\n");
  siglongjmp(env,1);
}
 
int executer_avant_delai( void (*fun)(void *), void *parametre, int delai_en_seconde)
{
  int ret = 1;
  sa.sa_handler = myalarm;
  sa.sa_flags = 0; // SA_RESETHAND;
  sigemptyset(&sa.sa_mask);
  sigaction(SIGALRM, &sa, &old);
 
  alarm(delai_en_seconde);
  if(sigsetjmp(env,1) == 0)
    fun(parametre);
  else
    ret = 0; // alarm
  alarm(0);
  sigaction(SIGALRM, &old, NULL);
  return ret;
}
progsys/index.txt · Last modified: 2024/03/18 15:06 by 127.0.0.1