So verwenden Sie Shared Memory mit Linux in C

Lesezeit: 11 Minuten

Benutzeravatar von bleepzter
Piepser

Ich habe ein kleines Problem mit einem meiner Projekte.

Ich habe versucht, ein gut dokumentiertes Beispiel für die Verwendung von Shared Memory mit zu finden fork() aber ohne erfolg.

Grundsätzlich ist das Szenario, dass wenn der Benutzer das Programm startet, ich zwei Werte im gemeinsamen Speicher speichern muss: aktueller_Pfad die ein verkohlen* und ein Dateiname was auch ist verkohlen*.

Abhängig von den Befehlsargumenten wird ein neuer Prozess gestartet fork() und dieser Prozess muss die lesen und ändern aktueller_Pfad Variable im gemeinsamen Speicher gespeichert, während die Dateiname Variable ist nur lesbar.

Gibt es ein gutes Tutorial zu Shared Memory mit Beispielcode (falls möglich), auf das Sie mich verweisen können?

  • Sie können erwägen, Threads anstelle von Prozessen zu verwenden. Dann wird der gesamte Speicher ohne weitere Tricks geteilt.

    – Elomage

    4. Februar 2014 um 12:22 Uhr

  • Die folgenden Antworten behandeln sowohl den System V IPC-Mechanismus, shmget() et al. und auch das reine mmap() Ansatz mit MAP_ANON (aka MAP_ANONYMOUS) – obwohl MAP_ANON wird nicht von POSIX definiert. Es gibt auch POSIX shm_open() und shm_close() zur Verwaltung von Shared-Memory-Objekten. […continued…]

    – Jonathan Leffler

    15. März 2020 um 3:59 Uhr


  • […continuation…] Diese haben den gleichen Vorteil wie der gemeinsam genutzte IPC-Speicher von System V – das gemeinsam genutzte Speicherobjekt kann über die Lebensdauer des Prozesses hinaus bestehen bleiben, der es erstellt (bis ein Prozess ausgeführt wird shm_unlink()), während Mechanismen mit mmap() erfordern eine Datei und MAP_SHARED um die Daten zu speichern (und MAP_ANON verhindert Persistenz). Es gibt ein vollständiges Beispiel im Begründungsabschnitt der Spezifikation von shm_open().

    – Jonathan Leffler

    15. März 2020 um 4:01 Uhr


Benutzeravatar von slezica
sležica

Es gibt zwei Ansätze: shmget und mmap. ich werde reden über mmapda es moderner und flexibler ist, aber Sie können es sich ansehen man shmget (oder diese Anleitung), wenn Sie lieber die Tools im alten Stil verwenden möchten.

Das mmap() Die Funktion kann verwendet werden, um Speicherpuffer mit hochgradig anpassbaren Parametern zuzuweisen, um Zugriff und Berechtigungen zu steuern und sie bei Bedarf mit Dateisystemspeicher zu sichern.

Die folgende Funktion erstellt einen In-Memory-Puffer, den ein Prozess mit seinen Kindern teilen kann:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

void* create_shared_memory(size_t size) {
  // Our memory buffer will be readable and writable:
  int protection = PROT_READ | PROT_WRITE;

  // The buffer will be shared (meaning other processes can access it), but
  // anonymous (meaning third-party processes cannot obtain an address for it),
  // so only this process and its children will be able to use it:
  int visibility = MAP_SHARED | MAP_ANONYMOUS;

  // The remaining parameters to `mmap()` are not important for this use case,
  // but the manpage for `mmap` explains their purpose.
  return mmap(NULL, size, protection, visibility, -1, 0);
}

Das Folgende ist ein Beispielprogramm, das die oben definierte Funktion verwendet, um einen Puffer zuzuweisen. Der übergeordnete Prozess schreibt eine Nachricht, verzweigt sich und wartet dann darauf, dass sein untergeordneter Prozess den Puffer ändert. Beide Prozesse können den gemeinsamen Speicher lesen und schreiben.

#include <string.h>
#include <unistd.h>

int main() {
  char parent_message[] = "hello";  // parent process will write this message
  char child_message[] = "goodbye"; // child process will then write this one

  void* shmem = create_shared_memory(128);

  memcpy(shmem, parent_message, sizeof(parent_message));

  int pid = fork();

  if (pid == 0) {
    printf("Child read: %s\n", shmem);
    memcpy(shmem, child_message, sizeof(child_message));
    printf("Child wrote: %s\n", shmem);

  } else {
    printf("Parent read: %s\n", shmem);
    sleep(1);
    printf("After 1s, parent read: %s\n", shmem);
  }
}

  • Aus diesem Grund ist Linux für unerfahrene Entwickler so frustrierend. Die Manpage erklärt nicht, wie man es tatsächlich verwendet, und es gibt keinen Beispielcode. 🙁

    – Piepser

    13. April 2011 um 22:46 Uhr

  • Haha, ich weiß, was du meinst, aber eigentlich liegt es daran, dass wir es nicht gewohnt sind, Manpages zu lesen. Als ich lernte, sie zu lesen und mich an sie gewöhnte, wurden sie sogar noch nützlicher als lausige Tutorials mit besonderen Demonstrationen. Ich erinnere mich, dass ich in meinem Betriebssystemkurs eine 10/10 bekommen habe, wobei ich während der Prüfung nur Manpages als Referenz verwendet habe.

    – sležica

    13. April 2011 um 22:51 Uhr


  • shmget ist ein wirklich altmodischer, und manche würden sagen, veralteter Weg, Shared Memory zu machen … Besser zu verwenden mmap und shm_openeinfache Dateien oder einfach MAP_ANONYMOUS.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    13. April 2011 um 23:29 Uhr

  • @Mark @R Ihr habt Recht, ich werde in der Antwort darauf hinweisen, um später darauf zurückgreifen zu können.

    – sležica

    14. April 2011 um 21:16 Uhr

  • Nun, diese Antwort wurde aus irgendeinem Grund populär, also habe ich beschlossen, sie lesenswert zu machen. Es hat nur 4 Jahre gedauert

    – sležica

    26. Mai 2017 um 20:16 Uhr

Benutzeravatar von Mayank
Mayank

Hier ist ein Beispiel für Shared Memory:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024  /* make it a 1K shared memory segment */

int main(int argc, char *argv[])
{
    key_t key;
    int shmid;
    char *data;
    int mode;

    if (argc > 2) {
        fprintf(stderr, "usage: shmdemo [data_to_write]\n");
        exit(1);
    }

    /* make the key: */
    if ((key = ftok("hello.txt", 'R')) == -1) /*Here the file must exist */ 
{
        perror("ftok");
        exit(1);
    }

    /*  create the segment: */
    if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1) {
        perror("shmget");
        exit(1);
    }

    /* attach to the segment to get a pointer to it: */
    if ((data = shmat(shmid, NULL, 0)) == (void *)-1) {
        perror("shmat");
        exit(1);
    }

    /* read or modify the segment, based on the command line: */
    if (argc == 2) {
        printf("writing to segment: \"%s\"\n", argv[1]);
        strncpy(data, argv[1], SHM_SIZE);
    } else
        printf("segment contains: \"%s\"\n", data);

    /* detach from the segment: */
    if (shmdt(data) == -1) {
        perror("shmdt");
        exit(1);
    }

    return 0;
}

Schritte :

  1. Verwenden Sie ftok, um einen Pfadnamen und eine Projektkennung in einen System V IPC-Schlüssel zu konvertieren

  2. Verwenden Sie shmget, das ein gemeinsames Speichersegment zuweist

  3. Verwenden Sie shmat, um das von shmid identifizierte gemeinsame Speichersegment an den Adressraum des aufrufenden Prozesses anzuhängen

  4. Führen Sie die Operationen im Speicherbereich durch

  5. Trennen Sie mit shmdt

  • Warum werfen Sie 0 in eine Leere*, anstatt NULL zu verwenden?

    – Clément Péau

    28. März 2017 um 15:45 Uhr

  • Dieser Code verarbeitet jedoch nicht das Löschen des gemeinsam genutzten Speichers. Nachdem das Programm beendet wurde, muss es manuell über ipcrm -m 0 gelöscht werden.

    – Bumm

    16. September 2019 um 7:15 Uhr


Benutzeravatar von Bharat
Bharat

Diese sind für die Verwendung von Shared Memory enthalten

#include<sys/ipc.h>
#include<sys/shm.h>

int shmid;
int shmkey = 12222;//u can choose it as your choice

int main()
{
  //now your main starting
  shmid = shmget(shmkey,1024,IPC_CREAT);
  // 1024 = your preferred size for share memory
  // IPC_CREAT  its a flag to create shared memory

  //now attach a memory to this share memory
  char *shmpointer = shmat(shmid,NULL);

  //do your work with the shared memory 
  //read -write will be done with the *shmppointer
  //after your work is done deattach the pointer

  shmdt(&shmpointer, NULL);

Probieren Sie dieses Codebeispiel aus, ich habe es getestet, Quelle: http://www.makelinux.net/alp/035

#include <stdio.h> 
#include <sys/shm.h> 
#include <sys/stat.h> 

int main () 
{
  int segment_id; 
  char* shared_memory; 
  struct shmid_ds shmbuffer; 
  int segment_size; 
  const int shared_segment_size = 0x6400; 

  /* Allocate a shared memory segment.  */ 
  segment_id = shmget (IPC_PRIVATE, shared_segment_size, 
                 IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); 
  /* Attach the shared memory segment.  */ 
  shared_memory = (char*) shmat (segment_id, 0, 0); 
  printf ("shared memory attached at address %p\n", shared_memory); 
  /* Determine the segment's size. */ 
  shmctl (segment_id, IPC_STAT, &shmbuffer); 
  segment_size  =               shmbuffer.shm_segsz; 
  printf ("segment size: %d\n", segment_size); 
  /* Write a string to the shared memory segment.  */ 
  sprintf (shared_memory, "Hello, world."); 
  /* Detach the shared memory segment.  */ 
  shmdt (shared_memory); 

  /* Reattach the shared memory segment, at a different address.  */ 
  shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0); 
  printf ("shared memory reattached at address %p\n", shared_memory); 
  /* Print out the string from shared memory.  */ 
  printf ("%s\n", shared_memory); 
  /* Detach the shared memory segment.  */ 
  shmdt (shared_memory); 

  /* Deallocate the shared memory segment.  */ 
  shmctl (segment_id, IPC_RMID, 0); 

  return 0; 
} 

Hier ist ein mmap-Beispiel:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/*
 * pvtmMmapAlloc - creates a memory mapped file area.  
 * The return value is a page-aligned memory value, or NULL if there is a failure.
 * Here's the list of arguments:
 * @mmapFileName - the name of the memory mapped file
 * @size - the size of the memory mapped file (should be a multiple of the system page for best performance)
 * @create - determines whether or not the area should be created.
 */
void* pvtmMmapAlloc (char * mmapFileName, size_t size, char create)  
{      
  void * retv = NULL;                                                                                              
  if (create)                                                                                         
  {                                                                                                   
    mode_t origMask = umask(0);                                                                       
    int mmapFd = open(mmapFileName, O_CREAT|O_RDWR, 00666);                                           
    umask(origMask);                                                                                  
    if (mmapFd < 0)                                                                                   
    {                                                                                                 
      perror("open mmapFd failed");                                                                   
      return NULL;                                                                                    
    }                                                                                                 
    if ((ftruncate(mmapFd, size) == 0))               
    {                                                                                                 
      int result = lseek(mmapFd, size - 1, SEEK_SET);               
      if (result == -1)                                                                               
      {                                                                                               
        perror("lseek mmapFd failed");                                                                
        close(mmapFd);                                                                                
        return NULL;                                                                                  
      }                                                                                               

      /* Something needs to be written at the end of the file to                                      
       * have the file actually have the new size.                                                    
       * Just writing an empty string at the current file position will do.                           
       * Note:                                                                                        
       *  - The current position in the file is at the end of the stretched                           
       *    file due to the call to lseek().  
              *  - The current position in the file is at the end of the stretched                    
       *    file due to the call to lseek().                                                          
       *  - An empty string is actually a single '\0' character, so a zero-byte                       
       *    will be written at the last byte of the file.                                             
       */                                                                                             
      result = write(mmapFd, "", 1);                                                                  
      if (result != 1)                                                                                
      {                                                                                               
        perror("write mmapFd failed");                                                                
        close(mmapFd);                                                                                
        return NULL;                                                                                  
      }                                                                                               
      retv  =  mmap(NULL, size,   
                  PROT_READ | PROT_WRITE, MAP_SHARED, mmapFd, 0);                                     

      if (retv == MAP_FAILED || retv == NULL)                                                         
      {                                                                                               
        perror("mmap");                                                                               
        close(mmapFd);                                                                                
        return NULL;                                                                                  
      }                                                                                               
    }                                                                                                 
  }                                                                                                   
  else                                                                                                
  {                                                                                                   
    int mmapFd = open(mmapFileName, O_RDWR, 00666);                                                   
    if (mmapFd < 0)                                                                                   
    {                                                                                                 
      return NULL;                                                                                    
    }                                                                                                 
    int result = lseek(mmapFd, 0, SEEK_END);                                                          
    if (result == -1)                                                                                 
    {                                                                                                 
      perror("lseek mmapFd failed");                  
      close(mmapFd);                                                                                  
      return NULL;                                                                                    
    }                                                                                                 
    if (result == 0)                                                                                  
    {                                                                                                 
      perror("The file has 0 bytes");                           
      close(mmapFd);                                                                                  
      return NULL;                                                                                    
    }                                                                                              
    retv  =  mmap(NULL, size,     
                PROT_READ | PROT_WRITE, MAP_SHARED, mmapFd, 0);                                       

    if (retv == MAP_FAILED || retv == NULL)                                                           
    {                                                                                                 
      perror("mmap");                                                                                 
      close(mmapFd);                                                                                  
      return NULL;                                                                                    
    }                                                                                                 

    close(mmapFd);                                                                                    

  }                                                                                                   
  return retv;                                                                                        
}                                                                                                     

  • open fügt Datei-I/O-Overhead hinzu. Verwenden shm_open stattdessen.

    – osven

    7. April 2018 um 21:02 Uhr

  • @Spookbuster, in einigen Implementierungen von shm_open wird open() unter der Decke aufgerufen, daher muss ich Ihrer Einschätzung widersprechen. hier ist ein beispiel: code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html

    – Löwe

    9. April 2018 um 19:29 Uhr

  • während einige shm_open()-Implementierungen open() unter der Haube verwenden, stellt POSIX geringere Anforderungen an die von shm_open() erzeugten Dateideskriptoren. Zum Beispiel sind Implementierungen nicht erforderlich, um E/A-Funktionen wie read() und write() für shm_open()-Dateideskriptoren zu unterstützen, wodurch bestimmte Implementierungen Optimierungen für shm_open() vornehmen können, die für open() nicht möglich sind. Wenn Sie damit nur mmap() machen, sollten Sie shm_open() verwenden.

    – osven

    9. April 2018 um 20:46 Uhr


  • Die meisten Linux-glibc-Setups machen eine solche Optimierung, indem sie tmpfs verwenden, um shm_open() zu unterstützen. Während auf dieselben tmpfs normalerweise über open() zugegriffen werden kann, gibt es keine portable Möglichkeit, ihren Pfad zu erfahren. Mit shm_open() können Sie diese Optimierung portabel verwenden. POSIX gibt shm_open() das Potenzial, besser als open() zu funktionieren. Nicht alle Implementierungen werden dieses Potenzial nutzen, aber es wird nicht schlechter abschneiden als open(). Aber ich stimme zu, dass meine Behauptung, dass open() immer Overhead hinzufügt, zu weit gefasst ist.

    – osven

    9. April 2018 um 20:50 Uhr


  • open fügt Datei-I/O-Overhead hinzu. Verwenden shm_open stattdessen.

    – osven

    7. April 2018 um 21:02 Uhr

  • @Spookbuster, in einigen Implementierungen von shm_open wird open() unter der Decke aufgerufen, daher muss ich Ihrer Einschätzung widersprechen. hier ist ein beispiel: code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html

    – Löwe

    9. April 2018 um 19:29 Uhr

  • während einige shm_open()-Implementierungen open() unter der Haube verwenden, stellt POSIX geringere Anforderungen an die von shm_open() erzeugten Dateideskriptoren. Zum Beispiel sind Implementierungen nicht erforderlich, um E/A-Funktionen wie read() und write() für shm_open()-Dateideskriptoren zu unterstützen, wodurch bestimmte Implementierungen Optimierungen für shm_open() vornehmen können, die für open() nicht möglich sind. Wenn Sie damit nur mmap() machen, sollten Sie shm_open() verwenden.

    – osven

    9. April 2018 um 20:46 Uhr


  • Die meisten Linux-glibc-Setups machen eine solche Optimierung, indem sie tmpfs verwenden, um shm_open() zu unterstützen. Während auf dieselben tmpfs normalerweise über open() zugegriffen werden kann, gibt es keine portable Möglichkeit, ihren Pfad zu erfahren. Mit shm_open() können Sie diese Optimierung portabel nutzen. POSIX gibt shm_open() das Potenzial, besser als open() zu funktionieren. Nicht alle Implementierungen werden dieses Potenzial nutzen, aber es wird nicht schlechter abschneiden als open(). Aber ich stimme zu, dass meine Behauptung, dass open() immer Overhead hinzufügt, zu weit gefasst ist.

    – osven

    9. April 2018 um 20:50 Uhr


1424210cookie-checkSo verwenden Sie Shared Memory mit Linux in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy