C: Interprozesskommunikation über Message Queues
In diesem Beispiel zeige ich kurz, wie Message Queues zum Austausch von Nachrichten zwischen zwei unterschiedlichen Prozessen verwendet werden können.
Hierzu einmal das Headerfile msg_queue.h:
#ifndef _PRJ_MSG_QUEUE_H_ #define _PRJ_MSG_QUEUE_H_ #include <stdlib.h> #include <stdio.h> #include <string.h> #define QUEUE_KEY 2048 #define STOP_COMMAND "exit" /* type this to stop writting to message queue */ typedef struct _userOptions { size_t write; size_t read; } userOptions; typedef struct _msgBuffer { long type; char text[2048]; } msgBuffer; void prj_usage(); userOptions prj_initUserOptions(userOptions options); int prj_parseArgs(int *argc, char **argv, userOptions *options); int prj_startQueue(key_t const key); void prj_stopQueue(int msqid); int prj_readFromQueue(int msqid); int prj_writeToQueue(int msqid); int main(int argc, char *argv[]); /* *********************************************** */ static char const *help = "\nHandles a message queue.\n" "\n" "Usage: " PROGNAME " [OPTIONS]\n" "\n" "Options:\n" " -h Print information to STDOUT and exit.\n" " -w Write data to message queue.\n" " -r Read data from message queue.\n" ; #endif /* !_PRJ_MSG_QUEUE_H_ */
Und hier der eigentliche Code in msg_queue.c:
Gute Hilfe bieten hierbei die Manpages von msgctl, msgsnd bzw. msgrcv.
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <getopt.h> /* should exist on LINUX */ #include "msg_queue.h" /* * \brief prj_usage * * Gibt die Hilfe aus und beendet das Programm. * */ void prj_usage() { printf("%s\n", help); exit(0); } /* * \brief prj_initUserOptions * * Befüllt struct userOptions mit Standardwerten. * * \param options struct userOptions * * \return struct userOptions */ userOptions prj_initUserOptions(userOptions options) { options.write = 0; options.read = 0; return (options); } /* * \brief prj_parseArgs * * Wertet die übermittelten Argumente aus. * * \param argc Anzahl der Argumente * \param argv Argumente * \param options struct userOptions * * \return restliche Anzahl an Argumenten, die nicht ausgewertet wurden */ int prj_parseArgs(int *argc, char **argv, userOptions *options) { signed char ch; *options = prj_initUserOptions(*options); while ((ch = getopt(*argc, argv, "hwr")) != -1) { switch (ch) { case 'w': options->write = 1; break; case 'r': options->read = 1; break; case 'h': default: prj_usage(); } } *argc -= optind; if ((options->write == 0) && (options->read == 0)) prj_usage(); return (optind); } /* * \brief prj_startQueue * * Startet die Message Queue. * * \param key Selbst definierte ID zur Message Queue. * * \return Message Queue Identifier */ int prj_startQueue(key_t const key) { int msqid; if ((msqid = msgget(key, 0666 | IPC_CREAT)) == -1) { printf("Message Queue %d konnte nicht erstellt werden\n\n", (int) key); } else { printf("Message Queue %d angebunden.\n\n", (int) key); } return msqid; } /* * \brief prj_stopQueue * * Stoppt die Message Queue. * * \param msqid Message Queue Identifier von prj_startQueue() * */ void prj_stopQueue(int msqid) { if (msgctl(msqid, IPC_RMID, 0) == -1) { printf("\nMessage Queue konnte nicht gestoppt werden.\nBitte mit `ipcs -a' überprüfen.\n"); } else { printf("\nMessage Queue gestoppt.\n"); } } /* * \brief prj_readFromQueue * * Liest Daten aus der Message Queue und gibt die Daten auf stdout aus. * * \param msqid Message Queue Identifier von prj_startQueue() * * \return on success 0, otherwise -1 */ int prj_readFromQueue(int msqid) { msgBuffer message; message.type = 23; printf("Ready to receive messages, Captain Wiesel...\n\n"); /* a little joke */ for (;;) { if (msgrcv(msqid, &message, sizeof(message.text), 0, 0) == -1) return -1; printf("%s\n", message.text); } return 0; } /* * \brief prj_writeToQueue * * Liest von stdin Eingaben des Users und gibt die Daten an die Message Queue. * Entspricht die Eingabe des Users dem Wert von `STOP_COMMAND', wird das Schreiben beendet. * * \param msqid Message Queue Identifier von prj_startQueue() * * \return on success 0, otherwise -1 */ int prj_writeToQueue(int msqid) { msgBuffer message; size_t len; size_t stop_cmd_len = strlen(STOP_COMMAND); message.type = 1; /* in this case not really needed */ while (fgets(message.text, sizeof(message.text), stdin) != NULL) { len = strlen(message.text); len--; if (message.text[len] == '\n') message.text[len] = '\0'; if (len == stop_cmd_len) if (memcmp(message.text, STOP_COMMAND, stop_cmd_len) == 0) return 0; if (msgsnd(msqid, &message, len + 2, 0) == -1) return -1; } return 0; } int main(int argc, char *argv[]) { userOptions options; int msqid = 0; int err = 0; argv += prj_parseArgs(&argc, argv, &options); msqid = prj_startQueue((key_t const) QUEUE_KEY); errno = 0; if (options.read == 1) err = prj_readFromQueue(msqid); else if (options.write == 1) { err = prj_writeToQueue(msqid); prj_stopQueue(msqid); } if (err == -1) printf("\nFehler %d ist aufgetreten: %s\n", errno, strerror(errno)); return errno; }
Beim Kompilieren mit zum Beispiel gcc sollte darauf geachtet werden, dass PROGNAME definiert wird:
![]() |
gcc -DPROGNAME=“\“msq_queue\““ -Wall -Wextra -Werror -o msg_queue msg_queue.c |
Daraufhin kann man mit „msq_queue -r“ die Message Queue gestartet bzw. angebunden werden und Daten aus der Queue gelesen werden.
Mit „msq_queue -w“ werden interaktiv Daten an die Message Queue geschrieben und nach der Eingabe von „exit\n“ die Message Queue geschlossen.
Im Screenshot ist links der Listener zu sehen, in der rechten Konsole der Writer.