Linux-Interprozesskommunikation

Über diesen Artikel

Einführung von IPC (Interprocess Communication) für Linux.

Was ist prozessübergreifende Kommunikation?

Inter Process Communication (IPC) bezeichnet den Datenaustausch zwischen Prozessen, die die Ausführungseinheiten eines Programms darstellen. Prozessabhängigkeiten werden vom Betriebssystem so lose wie möglich gekoppelt. Daher muss IPC über die Funktionen des Linux-Betriebssystems erfolgen. Es gibt mehrere Möglichkeiten, wie das Betriebssystem Daten für Prozesse austauschen kann. Wir bieten eine Vielzahl einzigartiger Methoden. Die folgenden fünf werden hier vorgestellt.

  1. Gemeinsamer Speicher
  2. Semafo
  3. Zugeordneter Speicher
  4. Rohr
  5. Socket-Kommunikation

(Wenn Sie weitere Fragen haben, lassen Sie es mich bitte in den Kommentaren wissen.)

Lass uns einen Blick darauf werfen.

geteilte Erinnerung

Teilen Sie den gleichen Speicher zwischen Prozessen. Der größte Vorteil von Shared Memory ist die Zugriffsgeschwindigkeit. Sobald der gemeinsam genutzte Speicher erstellt wurde, kann auf ihn zugegriffen werden, ohne die Funktionen des Kernels zu verwenden, sodass auf ihn mit der gleichen Geschwindigkeit wie auf den normalen Speicher zugegriffen werden kann. Diese Methode wird häufig für Software verwendet, die eine Verarbeitungsleistung erfordert, die den Vorteil dieser Zugriffsgeschwindigkeit erfordert. Wenn Sie dagegen von zwei Prozessen gleichzeitig schreiben, tritt ein Konflikt auf. Der gemeinsam genutzte Speicher verfügt nicht über einen integrierten Mechanismus zum gegenseitigen Ausschluss, um diesen Konflikt zu verhindern. Sie müssen es selbst vorbereiten.

Hier ist ein Beispielcode, der gemeinsam genutzten Speicher verwendet. Ordnen Sie den gemeinsam genutzten Speicher mit process_a zu und schreiben Sie die Zeichenfolge "Hello World!" In den gemeinsam genutzten Speicher. Die in den gemeinsam genutzten Speicher geschriebenen Daten werden von process_b gelesen und auf der Konsole gedruckt.

process_a.cpp


#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/ipc.h>

int main ()
{
    /*Segment-ID generieren*/
    const std::string file_path("./key_data.dat");
    const int id = 42;
    const auto key = ftok(file_path.c_str(), id);
    if(-1 == key) {
        std::cerr << "Failed to acquire key" << std::endl;
        return EXIT_FAILURE;
    }

    /*Segmentzuordnung*/
    const int shared_segment_size = 0x6400;
    const auto segment_id = shmget(key, shared_segment_size,
                                   IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
    if(-1==segment_id) {
        std::cerr << segment_id << " :Failed to acquire segment" << std::endl;
        return EXIT_FAILURE;
    }

    /*Schlüssel und Segment-ID anzeigen*/
    std::cout << "Schlüssel:" << key << std::endl;
    std::cout << "Segment-ID:" << segment_id << std::endl;

    /*An gemeinsam genutzten Speicher anhängen*/
    char* const shared_memory = reinterpret_cast<char*>(shmat(segment_id, 0, 0));
    printf ("shared memory attached at address %p\n", shared_memory);

    /*In den gemeinsamen Speicher schreiben*/
    sprintf (shared_memory, "Hello, world.");


    std::cout << "Hit any key when ready to close shared memory" << std::endl;
    std::cin.get();

    /*Gemeinsamen Speicher trennen*/
    shmdt(shared_memory);
    /*Freigegebenen Speicher freigeben*/
    shmctl (segment_id, IPC_RMID, 0);

    return EXIT_SUCCESS;
}

process_b.cpp


#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <sys/shm.h>
#include <sys/stat.h>

int main ()
{
    /*Segment-ID generieren*/
    const std::string file_path("./key_data.dat");
    const int id = 42;
    const auto key = ftok(file_path.c_str(), id);
    if(-1 == key) {
        std::cerr << "Failed to acquire key" << std::endl;
        return EXIT_FAILURE;
    }


    /*An gemeinsam genutzten Speicher anhängen*/
    const auto segment_id = shmget(key, 0, 0);
    const char* const shared_memory = reinterpret_cast<char*>(shmat(segment_id, 0, 0));
    printf ("shared memory attached at address %p\n", shared_memory);

    /*Gemeinsamen Speicher lesen*/
    printf ("%s\n", shared_memory);

    /*Gemeinsamen Speicher trennen*/
    shmdt(shared_memory);

    return EXIT_SUCCESS;
}

Linux-Befehle für die Kommunikation zwischen Prozessen

Einführung in die Befehle, die beim Erstellen eines Programms für die Kommunikation zwischen Prozessen erforderlich sind. Einer ist der Befehl ipcs. Zeigt den Status der Kommunikation zwischen Prozessen an.

kei@Glou-Glou:~/src/ipc/shared_memory$ ipcs

------Nachrichtenwarteschlange--------
Schlüssel msqid Eigentümerberechtigung Anzahl der verwendeten Bytes Nachricht

------Shared-Memory-Segment--------
Schlüssel shmid Besitzer Privileg Byte nattch State
0x00000000 884736     kei        600        16777216   2                       
0x00000000 1409025 kei 600 268435456 2 Ziel
0x00000000 819202 kei 600 524288 2 Ziel
       
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Kürzung~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    

------Semapho-Array--------
Schlüssel Semid Eigentümer Autorität nsems

Als nächstes werde ich den Befehl ipcrm einführen. Geben Sie die reservierten freigegebenen Ressourcen frei.

kei@Glou-Glou:~/src/ipc/shared_memory$ ipcrm shm <shmid>

Semaphor

Semafo teilt ganzzahlige Daten zwischen Prozessen, die keine Eltern-Kind-Beziehung haben. Es verfügt über einen Mechanismus zur Steuerung des gleichzeitigen Zugriffs auf mehrere Prozesse. Der Nachteil besteht jedoch darin, dass nur ganzzahlige Daten verarbeitet werden können. Die Hauptverwendung ist der gegenseitige Ausschluss des gemeinsam genutzten Speichers. Unten finden Sie den von Semafo verwendeten Beispielcode.

process_a.cpp


#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<cstdlib>
#include<iostream>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *__buf;
};

enum SEMAPHORE_OPERATION
{
    UNLOCK = -1,
    WAIT = 0,
    LOCK = 1,
};

int main()
{
    /*Semapho sichern*/
    const key_t key = 112;
    int sem_flags = 0666;
    int sem_id = semget(key, 1, sem_flags | IPC_CREAT);
    if(-1 == sem_id)
    {
        std::cerr << "Failed to acquire semapore" << std::endl;
        return EXIT_FAILURE;
    }

    /*Initialisierung des Semaphos*/
    union semun argument;
    unsigned short values[1];
    values[0] = 1;
    argument.array = values;
    semctl(sem_id, 0, SETALL, argument);

    /*Warten Sie, bis Prozess B ausgeführt wird*/
    std::cout << "Waiting for post operation..." << std::endl;
    sembuf operations[1];
    operations[0].sem_num = 0;
    operations[0].sem_op = WAIT;
    operations[0].sem_flg = SEM_UNDO;
    semop(sem_id, operations, 1);

    /*Veröffentlichung von Semapho*/
    auto result = semctl(sem_id, 1, IPC_RMID, NULL);
    if(-1 == result)
    {
        std::cerr << "Failed to close semaphore" << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

process_b.cpp


#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<cstdlib>
#include<iostream>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *__buf;
};

enum SEMAPHORE_OPERATION
{
    UNLOCK = -1,
    WAIT = 0,
    LOCK = 1,
};

int main()
{
    /*Semapho sichern*/
    const key_t key = 112;
    int sem_flags = 0666;
    int sem_id = semget(key, 1, sem_flags);
    if(-1 == sem_id)
    {
        std::cerr << "Failed to acquire semapore" << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << "Unlock semaphore" << std::endl;

    /*Post an Semafo*/
    sembuf operations[1];
    operations[0].sem_num = 0;
    operations[0].sem_op = UNLOCK;
    operations[0].sem_flg = SEM_UNDO;
    semop(sem_id, operations, 1);

    return EXIT_SUCCESS;
}

Zugeordneter Speicher

Über die Datei kommunizieren mehrere Prozesse. Die Speicherzuordnung wird beim Zugriff auf eine Datei durchgeführt, um den Prozess zu beschleunigen. Die Speicherzuordnung bezieht sich auf die Zuordnung von Dateien, Geräten usw. zu einem virtuellen Adressraum, sodass auf sie zugegriffen werden kann, als wären sie Speicher. Auf diese Weise können Sie Daten ohne Serialisierung in eine Datei einfügen.

Anschließend wird der Beispielcode für die Kommunikation zwischen Prozessen unter Verwendung der Speicherzuordnung unten gezeigt.

process_a.cpp


#include<cstdlib>
#include<cstdio>
#include<string>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include<unistd.h>

const unsigned int FILE_LENGTH = 0x100;
const std::string FILE_NAME("./data.dat");

int main()
{
    /* Prepare a file large enough to hold an unsigned integer. */
    auto fd = open(FILE_NAME.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    lseek(fd, FILE_LENGTH+1, SEEK_SET);
    write(fd, "", 1);
    lseek(fd, 0, SEEK_SET);
    /* Create the memory mapping. */
    char* const file_memory = reinterpret_cast<char*>(mmap(0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0));
    close(fd);
    /* Write a random integer to memory-mapped area. */
    sprintf(file_memory, "%s", "Hello World!");
    /* Release the memory (unnecessary because the program exits). */
    munmap (file_memory, FILE_LENGTH);

    return EXIT_SUCCESS;
}

process_b.cpp


#include<cstdlib>
#include<cstdio>
#include<string>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#include<unistd.h>

const unsigned int FILE_LENGTH = 0x100;
const std::string FILE_NAME("./data.dat");

int main()
{
    /* Open the file. */
    auto fd = open(FILE_NAME.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
    /* Create the memory mapping. */
    char* const file_memory = reinterpret_cast<char*>(mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
    close(fd);
    /* Read the integer, print it out, and double it. */
    printf("%s\n", file_memory);

    /* Release the memory (unnecessary because the program exits). */
    munmap(file_memory, FILE_LENGTH);

    return EXIT_SUCCESS;
}

Rohr

Pipes bieten eine einseitige Kommunikation zwischen Eltern-Kind-Prozessen. Sie werden in der Befehlszeile damit vertraut sein.

$ ps aux | grep apache

Das Ergebnis der Ausführung von ps aux wird mit dem Befehl grep durch die Pipe | geleitet. Der schwierige Teil dieser Pipe ist jedoch, dass eine Eltern-Kind-Beziehung zwischen den Prozessen erforderlich ist. Das Programm process_a hat das Programm process_b gestartet und versucht, ein Beispielprogramm zu erstellen, das Daten über eine Pipe austauscht, aber es hat nicht funktioniert. Wenn jemand es kann, lass es uns bitte in den Kommentaren wissen.

FIFO Auch als Named Piped bekannt. Eine Pipe mit einem Namen im Dateisystem. Alle Prozesse können FIFOs ohne Eltern-Kind-Beziehung erstellen, darauf zugreifen und löschen. Dieses FIFO kann auch mit dem Befehl mkfifo von der Konsole aus erstellt werden.

$ mkfifo ./fifo.tmp

Auf das erstellte FIFO kann wie auf eine normale Datei zugegriffen werden.

Schreiben Sie an FIFO


$ cat > fifo.tmp

FIFO lesen


$ cat fifo.tmp

Unten finden Sie einen Beispielcode für den Datenaustausch mit FIFO.

process_a.cpp


#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

const size_t BUFFER_SIZE(80);

int main()
{
    //Dateideskriptor
    int fd;

    //FIFO erstellen
    // mkfifo(<pathname>, <permission>)
    mkfifo("/tmp/myfifo", 0666);

    std::string message("Hello World!");
    //Öffnen Sie FIFO nur zum Schreiben
    fd = open("/tmp/myfifo", O_WRONLY);

    //Eine Nachricht schreiben
    write(fd, message.c_str(), message.size() + 1);
    close(fd);

    return EXIT_SUCCESS;
}

process_b.cpp


#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

const size_t BUFFER_SIZE(80);

int main()
{
    //Dateideskriptor
    int fd;

    //FIFO erstellen
    // mkfifo(<pathname>, <permission>)
    mkfifo("/tmp/myfifo", 0666);

    char str[BUFFER_SIZE];
    //Öffnen Sie FIFO schreibgeschützt
    fd = open("/tmp/myfifo", O_RDONLY);
    read(fd, str, BUFFER_SIZE);
    const std::string message(str);

    //Anzeige der gelesenen Inhalte
    std::cout << message << std::endl;
    close(fd);

    return EXIT_SUCCESS;
}

Das FIFO kann von mehreren Prozessen gelesen und geschrieben werden. Mehrere gleichzeitige Zugriffe werden automatisch ausschließlich verarbeitet.

Socket-Kommunikation

Sockets ermöglichen die Kommunikation zwischen unabhängigen Prozessen. Darüber hinaus gibt es Vorteile, die andere Methoden nicht haben. Es kann mit Prozessen auf anderen Maschinen kommunizieren. Unten finden Sie einen Beispielcode mit einem Socket.

process_a.cpp


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cassert>
#include<sys/socket.h>
#include<sys/un.h>
#include<unistd.h>

int server (const int& client_socket)
{
    while (1) {
        const size_t MAX_SIZE = 128;
        char buffer[MAX_SIZE];
        read(client_socket, buffer, MAX_SIZE);
        const std::string message(buffer);
        if(message.size() == 0) {
            return 0;
        } else {
            std::cout << message << std::endl;
            return 1;
        }
    }

    assert(!"This line must not be executed.");
}

int main()
{
    const std::string socket_name("my_socket");
    int socket_fd;
    sockaddr_un name;
    int client_sent_quit_message;
    /*Erstellen Sie einen Socket*/
    socket_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
    /*Als Server festlegen*/
    name.sun_family = AF_LOCAL;
    strcpy(name.sun_path, socket_name.c_str());
    bind(socket_fd, reinterpret_cast<sockaddr*>(&name), SUN_LEN (&name));
    /*Steckdose öffnen*/
    listen(socket_fd, 5);
    /*Warten Sie auf eine Nachricht, wenn Sie verbunden sind*/
    do {
        sockaddr_un client_name;
        socklen_t client_name_len;
        int client_socket_fd;
        /*Warten Sie, bis eine Verbindung besteht*/
        client_socket_fd = accept(socket_fd, reinterpret_cast<sockaddr*>(&client_name), &client_name_len);
        /*Eine Nachricht erhalten*/
        client_sent_quit_message = server(client_socket_fd);
        /*Trennen*/
        close(client_socket_fd);
    } while (!client_sent_quit_message);
    /*Steckdose schließen*/
    close(socket_fd);
    unlink(socket_name.c_str());

    return EXIT_SUCCESS;
}

process_b.cpp


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<sys/socket.h>
#include<sys/un.h>
#include<unistd.h>

/* Write TEXT to the socket given by file descriptor SOCKET_FD. */
int write_text(const int& socket_fd, const std::string& message)
{
    /* Write the string. */
    write(socket_fd, message.c_str(), message.size() + 1);
    return 0;
}

int main ()
{
    const std::string socket_name("my_socket");
    const std::string message = "Hello World!!";
    int socket_fd;
    sockaddr_un name;
    /*Erstellen Sie einen Socket*/
    socket_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
    /*Stellen Sie den Socket-Namen ein*/
    name.sun_family = AF_LOCAL;
    strcpy(name.sun_path, socket_name.c_str());
    /*Steckdose anschließen*/
    connect(socket_fd, reinterpret_cast<sockaddr*>(&name), SUN_LEN (&name));
    /*Nachricht senden*/
    write_text(socket_fd, message);
    close(socket_fd);
    return EXIT_SUCCESS;
}

Fazit

Ich habe kurz die von Linux bereitgestellte IPC-Methode vorgestellt. Wenn es so viele Methoden gibt, fragen Sie sich möglicherweise, welche Sie verwenden sollen. Hier werden die Fähigkeiten des Programmierers gezeigt. Jede Methode hat Vor- und Nachteile. Überlegen Sie sich die beste Methode und wählen Sie sie unter den Bedingungen der Software aus.

Verweise

Recommended Posts

Linux-Interprozesskommunikation
Arten der Kommunikation zwischen Prozessen
Kommunikation zwischen Prozessen ~ gemeinsamer Speicher ~
Interprozesskommunikation für Embedded DL
Linux
Linux Memorandum
Linux-Befehl Nr. 4
Linux-Befehl
Linux-Befehl Nr. 3
Linux Übersicht
Linux-Grundlagen
Linux-Organisation
Linux-Praxis
Ubuntu Linux 20.04
Linux Zusammenfassung
Linux-Prozess
Linux-Berechtigungen
Tools zur Überprüfung der Linux-Netzwerkkommunikation
Linux-Befehl Nr. 5
Über Linux
Linux-Grundlagen
Vergiss Linux
Über Linux
Linux-Befehl
Über Linux
Über Linux
Linux-Weiterleitung