580 lines
12 KiB
C
580 lines
12 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/msg.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
|
|
#define DEFAULT_TIMER 60 /* Durée par défaut du test (en secondes) */
|
|
#define DEFAULT_WINSIZE 100 /* Taille par défaut de la fenêtre de messages */
|
|
#define DEFAULT_MSGSIZE 100 /* Taille par défaut de chaque message */
|
|
|
|
#define FATHER_KEY 64 /* Clé du port de messages du père */
|
|
#define CHILD_KEY 65 /* Clé du port de messages du fils */
|
|
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
typedef struct {
|
|
long mtype;
|
|
char * mtext;
|
|
} T_Msg;
|
|
|
|
int My_Port, Its_Port;
|
|
int Child_Pid, Father_Pid, Its_Pid, My_Pid;
|
|
int Verbose;
|
|
unsigned int Timer;
|
|
unsigned int Win_Size;
|
|
unsigned int Msg_Size;
|
|
int Cpt_Msg;
|
|
T_Msg ** Msg_Window;
|
|
int End_Test, End_Sigum;
|
|
|
|
char * error_code (void);
|
|
void stop_test (int);
|
|
void stop_father (void);
|
|
void stop_child (void);
|
|
void clean (int);
|
|
void trace (char *);
|
|
void print_result (void);
|
|
void parse_arg (int, char **);
|
|
extern char * strdup (const char *);
|
|
|
|
/*----------------------------------------------------------------*/
|
|
int main (int argc, char ** argv)
|
|
{
|
|
unsigned int i, size;
|
|
char str [256];
|
|
T_Msg * Msg;
|
|
|
|
/* Récupération des arguments de la ligne de commande */
|
|
|
|
parse_arg (argc, argv);
|
|
|
|
/* Récupération du no de process courant */
|
|
|
|
Father_Pid = getpid ();
|
|
|
|
/* Création d'un process fils avec qui dialoguer */
|
|
|
|
if ((Child_Pid = fork ()) != 0)
|
|
{
|
|
/*--------- Code pour le process père -----------*/
|
|
|
|
Its_Pid = Child_Pid;
|
|
My_Pid = Father_Pid;
|
|
|
|
My_Port = 0;
|
|
|
|
signal (SIGINT, stop_test);
|
|
signal (SIGUSR1, stop_test);
|
|
|
|
/* Création d'un port de messages */
|
|
|
|
if ((My_Port = msgget (FATHER_KEY, 0777|IPC_CREAT|IPC_EXCL)) == -1)
|
|
{
|
|
fprintf (stderr, "Unable to create message queue for the father process : error %d (%s)\n", errno, error_code ());
|
|
clean (TRUE);
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
sprintf (str, "Message port %d created", My_Port);
|
|
trace (str);
|
|
}
|
|
|
|
/* On attend que le fils ait créé son port de messages */
|
|
|
|
sleep (2);
|
|
|
|
/* Initialisation de la fenêtre de messages */
|
|
|
|
Msg_Window = (T_Msg **)malloc (Win_Size);
|
|
|
|
for (i = 0; i < Win_Size; i++)
|
|
{
|
|
Msg_Window[i] = (T_Msg *)malloc (sizeof (T_Msg));
|
|
Msg_Window[i]->mtype = 1;
|
|
Msg_Window[i]->mtext = (char *)malloc (Msg_Size);
|
|
Msg_Window[i]->mtext[0] = (char)i;
|
|
}
|
|
|
|
/* Récupération de la port de messages du fils */
|
|
|
|
if ((Its_Port = msgget (CHILD_KEY, 0)) == -1)
|
|
{
|
|
fprintf (stderr, "Unable to retrieve the message queue of the child process : error %d (%s)\n", errno, error_code ());
|
|
clean (TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
Msg = malloc (sizeof (T_Msg));
|
|
Msg->mtext = (char *)malloc (Msg_Size);
|
|
|
|
/* Initialisation du Timer */
|
|
|
|
signal (SIGALRM, stop_test);
|
|
|
|
alarm (Timer);
|
|
|
|
/* Début du test : traitement des messages */
|
|
|
|
fprintf (stdout, "Testing IPC message queue for %d second (s) with %d message (s) of %d byte (s)...\n", Timer, Win_Size, Msg_Size);
|
|
|
|
/* Envoi de tous les messages de la fenêtre au fils */
|
|
|
|
for (i = 0; i < Win_Size; i++)
|
|
{
|
|
errno = 0;
|
|
|
|
if (msgsnd (Its_Port, Msg_Window[i], Msg_Size, IPC_NOWAIT) == 0)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Message %d sent to port %d", i, Its_Port);
|
|
trace (str);
|
|
}
|
|
}
|
|
|
|
if (errno != 0 && errno != EAGAIN)
|
|
fprintf (stderr, "Unable to send message %d to port %d : error %d (%s)\n", i, Its_Port, errno, error_code ());
|
|
}
|
|
|
|
Cpt_Msg = 0;
|
|
|
|
while (End_Test == FALSE)
|
|
{
|
|
/* Réception de messages */
|
|
|
|
errno = 0;
|
|
|
|
if ((size = msgrcv (My_Port, Msg, Msg_Size, 0, MSG_NOERROR)) > 0)
|
|
{
|
|
i = (int)(Msg->mtext[0]);
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Message %d received from port %d", i, My_Port);
|
|
trace (str);
|
|
}
|
|
|
|
Cpt_Msg++;
|
|
|
|
/* Réenvoi du message au fils */
|
|
|
|
errno = 0;
|
|
|
|
if (msgsnd (Its_Port, Msg_Window[i], Msg_Size, IPC_NOWAIT) == 0)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Message %d replied", i);
|
|
trace (str);
|
|
}
|
|
}
|
|
|
|
if (errno != 0 && errno != EAGAIN)
|
|
fprintf (stderr, "Unable to reply message %d to port %d (%s)\n", i, Its_Port, error_code ());
|
|
}
|
|
|
|
if (errno != 0)
|
|
fprintf (stderr, "Unable to receive a message from port %d (%s)\n", My_Port, error_code ());
|
|
}
|
|
|
|
stop_father ();
|
|
}
|
|
else
|
|
{
|
|
/*---------- Code pour le process fils ----------*/
|
|
|
|
My_Pid = getpid ();
|
|
Its_Pid = Father_Pid;
|
|
My_Port = 0;
|
|
|
|
/* Trap du signal SIGUSR1 envoyé par le process père à la fin du test */
|
|
|
|
signal (SIGUSR1, stop_test);
|
|
signal (SIGINT, stop_test);
|
|
|
|
/* Création d'un port de messages */
|
|
|
|
if ((My_Port = msgget (CHILD_KEY, 0777|IPC_CREAT|IPC_EXCL)) == -1)
|
|
{
|
|
fprintf (stderr, "Unable to create message queue for the child process (%s)\n", error_code ());
|
|
clean (TRUE);
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
sprintf (str, "Message port %d created", My_Port);
|
|
trace (str);
|
|
}
|
|
|
|
/* On attend que le père ait créé son port de messages */
|
|
|
|
sleep (2);
|
|
|
|
Msg_Window = (T_Msg **)malloc (Win_Size);
|
|
|
|
for (i = 0; i < Win_Size; i++)
|
|
{
|
|
Msg_Window[i] = (T_Msg *)malloc (sizeof (T_Msg));
|
|
Msg_Window[i]->mtype = 1;
|
|
Msg_Window[i]->mtext = (char *)malloc (Msg_Size * sizeof (char));
|
|
Msg_Window[i]->mtext[0] = (char)i;
|
|
}
|
|
|
|
/* Récupération du port de messages du père */
|
|
|
|
if ((Its_Port = msgget (FATHER_KEY, 0)) == -1)
|
|
{
|
|
fprintf (stderr, "Unable to retrieve the message queue of the father process (%s)\n", error_code ());
|
|
clean (TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Traitement des messages */
|
|
|
|
Msg = malloc (sizeof (T_Msg));
|
|
Msg->mtext = (char *)malloc (Msg_Size * sizeof (char));
|
|
|
|
while (End_Test == FALSE)
|
|
{
|
|
/* Réception de messages */
|
|
|
|
errno = 0;
|
|
|
|
if ((size = msgrcv (My_Port, Msg, Msg_Size, 0, MSG_NOERROR)) > 0)
|
|
{
|
|
i = (int)(Msg->mtext[0]);
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Message %d received from port %d", i, My_Port);
|
|
trace (str);
|
|
}
|
|
|
|
Cpt_Msg++;
|
|
|
|
/* Réenvoi du message au fils */
|
|
|
|
errno = 0;
|
|
|
|
if (msgsnd (Its_Port, Msg_Window[i], Msg_Size, IPC_NOWAIT) == 0)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Message %d replied", i);
|
|
trace (str);
|
|
}
|
|
}
|
|
|
|
if (errno != 0 && errno != EAGAIN)
|
|
fprintf (stderr, "Unable to reply message %d to port %d (%s)\n", i, Its_Port, error_code ());
|
|
}
|
|
|
|
if (errno != 0)
|
|
fprintf (stderr, "Unable to receive a message from port %d (%s)\n", My_Port, error_code ());
|
|
}
|
|
|
|
stop_child ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
void stop_test (int signum)
|
|
{
|
|
End_Sigum = signum;
|
|
End_Test = TRUE;
|
|
}
|
|
/*----------------------------------------------------------------*/
|
|
void stop_father (void)
|
|
{
|
|
switch (End_Sigum)
|
|
{
|
|
case SIGINT:
|
|
|
|
/* Fin initiée par l'utilisateur : ménage simple */
|
|
|
|
case SIGUSR1:
|
|
|
|
/* Fin anormale en provenance du fils : ménage simple */
|
|
|
|
clean (FALSE);
|
|
|
|
break;
|
|
|
|
case SIGALRM:
|
|
|
|
/* Fin normale : ménage + stop du fils + affichage du réultat */
|
|
|
|
clean (TRUE);
|
|
print_result ();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Fin anormale sur le process père : ménage + avertit le process fils */
|
|
|
|
clean (TRUE);
|
|
|
|
break;
|
|
}
|
|
|
|
exit (1);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
void print_result (void)
|
|
{
|
|
/* Affichage du résultat du test */
|
|
|
|
fprintf (stdout, "%d message (s) exchanged (%d Msg/sec)\n", Cpt_Msg, (int)(Cpt_Msg / Timer));
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
void stop_child (void)
|
|
{
|
|
switch (End_Sigum)
|
|
{
|
|
case SIGINT:
|
|
|
|
/* Fin initiée par l'utilisateur : ménage simple */
|
|
|
|
case SIGUSR1:
|
|
|
|
/* Fin initiée par le process père : ménage simple */
|
|
|
|
clean (FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Fin anormale sur le process fils : ménage + avertit le process père */
|
|
|
|
clean (TRUE);
|
|
|
|
break;
|
|
}
|
|
|
|
exit (0);
|
|
}
|
|
/*----------------------------------------------------------------*/
|
|
void clean (int warn)
|
|
{
|
|
unsigned int i;
|
|
char str [256];
|
|
|
|
/* On avertit l'autre process */
|
|
|
|
if (warn == TRUE) kill (Its_Pid, SIGUSR1);
|
|
|
|
if (Msg_Window != NULL)
|
|
{
|
|
for (i = 0; i < Win_Size; i++)
|
|
{
|
|
if (Msg_Window[i] != NULL)
|
|
{
|
|
if (Msg_Window[i]->mtext != NULL) free (Msg_Window[i]->mtext);
|
|
free (Msg_Window[i]);
|
|
}
|
|
}
|
|
free (Msg_Window);
|
|
}
|
|
|
|
/* Destruction de la port de messages */
|
|
|
|
if (My_Port > 0)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
sprintf (str, "Removing message queue %d...\n", My_Port);
|
|
trace (str);
|
|
}
|
|
|
|
msgctl (My_Port, IPC_RMID, NULL);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
char * error_code (void)
|
|
{
|
|
static char * str;
|
|
|
|
if (str) free (str);
|
|
|
|
switch (errno)
|
|
{
|
|
case EACCES:
|
|
str = strdup ("EACCES");
|
|
break;
|
|
|
|
case EEXIST:
|
|
str = strdup ("EEXIST");
|
|
break;
|
|
|
|
case EIDRM:
|
|
str = strdup ("EIDRM");
|
|
break;
|
|
|
|
case ENOENT:
|
|
str = strdup ("ENOENT");
|
|
break;
|
|
|
|
case ENOMEM:
|
|
str = strdup ("ENOMEM");
|
|
break;
|
|
|
|
case ENOSPC:
|
|
str = strdup ("ENOSPC");
|
|
break;
|
|
|
|
case EFAULT:
|
|
str = strdup ("EFAULT");
|
|
break;
|
|
|
|
case EINTR:
|
|
str = strdup ("EINTR");
|
|
break;
|
|
|
|
case EINVAL:
|
|
str = strdup ("EINVAL");
|
|
break;
|
|
|
|
case E2BIG:
|
|
str = strdup ("E2BIG");
|
|
break;
|
|
|
|
case ENOMSG:
|
|
str = strdup ("ENOMSG");
|
|
break;
|
|
|
|
case EAGAIN:
|
|
str = strdup ("EAGAIN");
|
|
break;
|
|
|
|
default:
|
|
str = strdup ("unknown");
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
void trace (char *str)
|
|
{
|
|
time_t dts;
|
|
struct tm *dt;
|
|
char current_date [30];
|
|
char str_trace [256];
|
|
|
|
/* Get current date */
|
|
|
|
time (&dts);
|
|
dt = localtime (&dts);
|
|
sprintf (current_date, "%04d/%02d/%02d %02d:%02d:%02d", dt->tm_year + 1900, dt->tm_mon + 1, dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec);
|
|
|
|
sprintf (str_trace, "Process %d - %s : %s\n", My_Pid, current_date, str);
|
|
fprintf (stderr, str_trace);
|
|
fflush (stderr);
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
void parse_arg (int argc, char ** argv)
|
|
{
|
|
int i;
|
|
|
|
Verbose = FALSE;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (!strcmp (argv[i], "--help") || !strcmp (argv[i], "-h"))
|
|
{
|
|
fprintf (stdout, "Usage : %s [options]\n", argv[0]);
|
|
fprintf (stdout,"options :\n");
|
|
fprintf (stdout, "\t --help -h\n");
|
|
fprintf (stdout, "\t --clean\n");
|
|
fprintf (stdout, "\t --verbose\n");
|
|
fprintf (stdout, "\t --duration <time in seconds> (default is %d)\n", DEFAULT_TIMER);
|
|
fprintf (stdout, "\t --window <message number> (default is %d)\n", DEFAULT_WINSIZE);
|
|
fprintf (stdout, "\t --message <message size> (default is %d bytes)\n", DEFAULT_MSGSIZE);
|
|
exit (1);
|
|
}
|
|
|
|
if (!strcmp (argv[i], "--clean"))
|
|
{
|
|
if ((My_Port = msgget (FATHER_KEY, 0)) != -1) msgctl (My_Port, IPC_RMID, NULL);
|
|
if ((Its_Port = msgget (CHILD_KEY, 0)) != -1) msgctl (Its_Port, IPC_RMID, NULL);
|
|
exit (1);
|
|
}
|
|
|
|
if (!strcmp (argv[i], "--verbose"))
|
|
{
|
|
Verbose = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp (argv[i], "--duration"))
|
|
{
|
|
i++;
|
|
if (i == argc)
|
|
{
|
|
fprintf (stderr, "Missing argument after %s\n", argv[i - 1]);
|
|
exit (0);
|
|
}
|
|
Timer = atoi (argv[i]);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp (argv[i], "--window"))
|
|
{
|
|
i++;
|
|
if (i == argc)
|
|
{
|
|
fprintf (stderr, "Missing argument after %s\n", argv[i - 1]);
|
|
exit (0);
|
|
}
|
|
Win_Size = atoi (argv[i]);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp (argv[i], "--message"))
|
|
{
|
|
i++;
|
|
if (i == argc)
|
|
{
|
|
fprintf (stderr, "Missing argument after %s\n", argv[i - 1]);
|
|
exit (0);
|
|
}
|
|
Msg_Size = atoi (argv[i]);
|
|
continue;
|
|
}
|
|
|
|
fprintf (stdout, "Unknown option '%s'\n", argv[i]);
|
|
exit (0);
|
|
}
|
|
|
|
if (Timer <= 0) Timer = DEFAULT_TIMER;
|
|
if (Win_Size <= 0) Win_Size = DEFAULT_WINSIZE;
|
|
if (Msg_Size <= 0) Msg_Size = DEFAULT_MSGSIZE;
|
|
}
|
|
|