. .

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&uuml;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 &uuml;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.


Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>