Cum se utilizează memoria partajată cu Linux în C (Programare, C, Linux, Fork, Memorie Partajată)

bleepzter a intrebat.

Am o mică problemă cu unul dintre proiectele mele.

Am încercat să găsesc un exemplu bine documentat de utilizare a memoriei partajate cu fork() dar fără succes.

Practic, scenariul este că atunci când utilizatorul pornește programul, trebuie să stochez două valori în memoria partajată: current_path care este un fișier char* și o valoare nume_fișier care este de asemenea char*.

În funcție de argumentele comenzii, un nou proces este inițiat cu fork() și acest proces trebuie să citească și să modifice fișierul current_path stocată în memoria partajată, în timp ce nume_fișier este doar de citire.

Există un tutorial bun despre memoria partajată cu cod de exemplu (dacă este posibil) la care mă puteți îndruma?

Comentarii

  • Ați putea lua în considerare utilizarea firelor de execuție în loc de procese. Atunci întreaga memorie este partajată fără alte trucuri. –  > Por elomage.
  • Răspunsurile de mai jos discută atât mecanismul IPC al System V, shmget() et al. și, de asemenea, mecanismul pur mmap() abordare cu MAP_ANON (alias MAP_ANONYMOUS) – deși MAP_ANON nu este definită de POSIX. Există, de asemenea, POSIX shm_open() și shm_close() pentru gestionarea obiectelor de memorie partajată. […continuare…] –  > Por Jonathan Leffler.
  • […continuare…] Acestea au același avantaj pe care îl are memoria partajată System V IPC – obiectul de memorie partajată poate persista dincolo de durata de viață a procesului care l-a creat (până când un alt proces execută shm_unlink()), în timp ce mecanismele care utilizează mmap() necesită un fișier și MAP_SHARED pentru a persista datele (și MAP_ANON împiedică persistența). Există un exemplu complet în secțiunea „Rationale” din specificația de shm_open(). –  > Por Jonathan Leffler.
5 răspunsuri
slezica

Există două abordări: shmget și mmap. Voi vorbi despre mmap, deoarece este mai modernă și mai flexibilă, dar puteți arunca o privire la man shmget (sau acest tutorial) dacă preferați să folosiți instrumentele de tip vechi.

Site-ul mmap() poate fi utilizată pentru a aloca tampoane de memorie cu parametri foarte personalizabili pentru a controla accesul și permisiunile și pentru a le susține cu stocare în sistemul de fișiere, dacă este necesar.

Următoarea funcție creează un buffer în memorie pe care un proces îl poate partaja cu copiii săi:

#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);
}

Următorul este un exemplu de program care utilizează funcția definită mai sus pentru a aloca un buffer. Procesul părinte va scrie un mesaj, va crea o bifurcație și apoi va aștepta ca procesul copil să modifice memoria tampon. Ambele procese pot citi și scrie în memoria partajată.

#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
", shmem);
    memcpy(shmem, child_message, sizeof(child_message));
    printf("Child wrote: %s
", shmem);

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

Comentarii

    59

  • Acesta este motivul pentru care Linux este atât de frustrant pentru dezvoltatorii neexperimentați. Pagina de manual nu explică cum să îl folosești efectiv și nu există niciun cod de exemplu. 🙁 –  > Por bleepzter.
  • 53

  • Haha Știu ce vrei să spui, dar de fapt este din cauză că nu suntem obișnuiți să citim paginile de manual. Când am învățat să le citesc și m-am obișnuit cu ele, au devenit chiar mai utile decât niște tutoriale amărâte cu demonstrații particulare. Îmi amintesc că am luat 10/10 la cursul de Sisteme de operare folosind doar manpages ca referință în timpul examenului. –  > Por slezica.
  • 20

  • shmget este un mod cu adevărat demodat, iar unii ar spune că e demodat, de a face memorie partajată… Este mai bine să folosiți mmap și shm_open, fișiere simple, sau pur și simplu MAP_ANONYMOUS. –  > Por R.. GitHub NU MAI AJUTAȚI ICE.
  • @Mark @R Aveți dreptate, voi sublinia asta în răspuns pentru referințe viitoare. –  > Por slezica.
  • Ei bine, acest răspuns a devenit popular dintr-un motiv oarecare, așa că am decis să îl fac să merite să fie citit. A durat doar 4 ani –  > Por slezica.
Mayank

Iată un exemplu pentru memoria partajată :

#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]
");
        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: */
    data = shmat(shmid, NULL, 0);
    if (data == (char *)(-1)) {
        perror("shmat");
        exit(1);
    }

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

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

    return 0;
}

Pași :

  1. Utilizați ftok pentru a converti un nume de cale și un identificator de proiect într-o cheie IPC System V

  2. Utilizați shmget care alocă un segment de memorie partajată

  3. Utilizați shmat pentru a atașa segmentul de memorie partajată identificat de shmid la spațiul de adrese al procesului apelant.

  4. Efectuați operațiile asupra zonei de memorie

  5. Se detaș ează folosind shmdt

Comentarii

  • De ce introduceți 0 într-un void* în loc să folosiți NULL? –  > Por Clément Péau.
  • Totuși, acest cod nu se ocupă de ștergerea memoriei partajate. După ieșirea programului, trebuie să o ștergeți manual prin ipcrm -m 0. –  > Por bumfo.
Bharat

Acestea sunt incluziuni pentru utilizarea memoriei partajate

#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);

shakram02

încercați această mostră de cod, am testat-o, sursă: 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
", shared_memory); 
  /* Determine the segment's size. */ 
  shmctl (segment_id, IPC_STAT, &shmbuffer); 
  segment_size  =               shmbuffer.shm_segsz; 
  printf ("segment size: %d
", 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
", shared_memory); 
  /* Print out the string from shared memory.  */ 
  printf ("%s
", shared_memory); 
  /* Detach the shared memory segment.  */ 
  shmdt (shared_memory); 

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

  return 0; 
} 

Comentarii

  • Acesta este un cod bun, doar că nu cred că arată cum să acceseze segmentul de memorie partajată de către un client (folosind shmget și shmat dintr-un alt proces), ceea ce este un fel de scop al memoriei partajate… =( –  > Por étale-cohomology.
Leo

Iată un exemplu de mmap:

#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 '' 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;                                                                                        
}                                                                                                     

Comentarii

  • open Adaugă un fișier I/O. Utilizați shm_open în loc. –  > Por osvein.
  • @Spookbuster, în unele implementări ale shm_open, open() este apelat sub acoperire, așa că nu sunt de acord cu evaluarea ta; iată un exemplu: code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html –  > Por Leo.
  • în timp ce unele implementări shm_open() utilizează open() sub capotă, POSIX are cerințe mai mici pentru descriptorii de fișiere produși de shm_open(). De exemplu, implementările nu sunt obligate să susțină funcții de intrare/ieșire precum read() și write() pentru descriptorii de fișiere shm_open(), ceea ce permite anumitor implementări să facă optimizări pentru shm_open() care nu pot fi făcute pentru open(). Dacă tot ce veți face cu el este mmap(), ar trebui să utilizați shm_open(). –  > Por osvein.
  • Cele mai multe configurații Linux-glibc realizează o astfel de optimizare prin utilizarea tmpfs pentru a susține shm_open(). În timp ce același tmpfs poate fi accesat de obicei prin open(), nu există o modalitate portabilă de a cunoaște calea acestuia. shm_open() vă permite să utilizați această optimizare într-o manieră portabilă. POSIX îi conferă lui shm_open() potențialul de a avea performanțe mai bune decât open(). Nu toate implementările vor utiliza acest potențial, dar nu va avea performanțe mai slabe decât open(). Dar sunt de acord că afirmația mea că open() adaugă întotdeauna costuri suplimentare este prea largă. –  > Por osvein.