C'de Linux ile paylaşılan bellek nasıl kullanılır


117

Projelerimden biriyle ilgili biraz sorunum var.

Paylaşılan hafızayı kullanmanın iyi belgelenmiş bir örneğini bulmaya çalışıyorum fork()ama başarılı olamadım.

Temel olarak senaryo, kullanıcı programı başlattığında, paylaşılan bellekte iki değer saklamam gerektiğidir: char * olan current_path ve ayrıca char * olan bir dosya_adı .

Komut argümanlarına bağlı olarak, yeni bir işlem başlatılır fork()ve bu işlemin , dosya_adı değişkeni salt okunurken paylaşılan bellekte depolanan current_path değişkenini okuması ve değiştirmesi gerekir .

Örnek kod içeren (mümkünse) beni yönlendirebileceğiniz paylaşılan hafıza hakkında iyi bir öğretici var mı?


1
İşlemler yerine iş parçacıkları kullanmayı düşünebilirsiniz. Sonra tüm hafıza başka numara olmadan paylaşılır.
elomage

Aşağıdaki cevaplar hem System V IPC mekanizmasını hem de shmget()diğerlerini tartışmaktadır . ve ayrıca (aka ) mmap()ile saf yaklaşım - ancak POSIX tarafından tanımlanmamaktadır. Ayrıca POSIX ve paylaşılan bellek nesnelerini yönetmek için de vardır . […MAP_ANONMAP_ANONYMOUSMAP_ANONshm_open()shm_close()
Devamı

[… Continuation…] Bunlar, System V IPC paylaşılan belleğinin sahip olduğu aynı avantaja sahiptir - paylaşılan bellek nesnesi, onu oluşturan sürecin ömrünün ötesinde (bazı işlemler yürütülene kadar shm_unlink()) devam edebilir, oysa kullanan mekanizmalar mmap()bir dosya gerektirir ve MAP_SHAREDkalıcıdır veriler (ve MAP_ANONkalıcılığı engeller). Şartnamesinin Gerekçe bölümünde tam bir örnek var shm_open().
Jonathan Leffler

Yanıtlar:


164

İki yaklaşım vardır: shmgetve mmap. mmapDaha modern ve esnek olduğu için bunun hakkında konuşacağım , ancak eski tarz araçları kullanmayı tercih ediyorsanız man shmget( veya bu öğreticiye ) bir göz atabilirsiniz .

mmap()Fonksiyon kontrolü erişim ve izinleri büyük ölçüde özelleştirilebilir parametreler ile bellek tamponları tahsis etmek için kullanılabilir ve gerekirse dosya sistemi depolama ile bunları geri.

Aşağıdaki işlev, bir işlemin alt öğeleriyle paylaşabileceği bir bellek içi tampon oluşturur:

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

Aşağıda, bir arabellek ayırmak için yukarıda tanımlanan işlevi kullanan örnek bir program verilmiştir. Üst süreç bir mesaj yazacak, çatallanacak ve ardından alt işleminin arabelleği değiştirmesini bekleyecektir. Her iki işlem de paylaşılan belleği okuyabilir ve yazabilir.

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

52
Linux deneyimsiz geliştiriciler için bu kadar sinir bozucu olmasının nedeni budur. Man sayfası aslında nasıl kullanılacağını açıklamıyor ve örnek bir kod yok. :(
bleepzter

47
Haha Ne demek istediğini anlıyorum, ama aslında yönetme sayfalarını okumaya alışkın olmadığımız için. Onları okumayı öğrendiğimde ve onlara alıştığımda, belirli gösterileri olan berbat derslerden bile daha yararlı hale geldiler. İşletim Sistemleri kursumda sınav sırasında referans olarak başka hiçbir şey kullanmadan bir 10/10 aldığımı hatırlıyorum.
slezica

18
shmgetPaylaşılan bellek yapmak için bir, gerçekten moda eski ve bazı kaldırılmış derdim yoludur ... Daha kullanımı mmapve shm_opendüz dosyalar, ya da sadece MAP_ANONYMOUS.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

4
@Mark @R Haklısınız, bunu ileride başvurmak için yanıtta belirteceğim.
slezica

4
Bu cevap bir sebepten dolayı popüler oldu, ben de okumaya değer yapmaya karar verdim. Sadece 4 yıl sürdü
slezica

26

İşte paylaşılan hafıza için bir örnek:

#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: */
    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\"\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;
}

Adımlar:

  1. Bir yol adını ve bir proje tanımlayıcısını System V IPC anahtarına dönüştürmek için ftok kullanın

  2. Paylaşılan bir bellek segmenti ayıran shmget kullanın

  3. Shmid ile tanımlanan paylaşılan bellek segmentini çağıran işlemin adres alanına eklemek için shmat kullanın

  4. Hafıza alanındaki işlemleri yapın

  5. Shmdt kullanarak ayırın


6
NULL kullanmak yerine neden bir boşluğa * 0 atıyorsunuz?
Clément Péau

Ancak bu kod, paylaşılan belleğin silinmesini işlemez. Program çıktıktan sonra, ipcrm -m 0 ile manuel olarak silmek gerekir.
bumfo

12

Bunlar paylaşılan hafızayı kullanmak içindir

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

8

bu kod örneğini deneyin, test ettim, kaynak: 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; 
} 

2
Bu iyi bir kod, ancak bir istemci tarafından paylaşılan bellek segmentine nasıl erişileceğini ( farklı bir işlem kullanarak shmgetve kullanarak) gösterdiğini sanmıyorum shmat, bu da paylaşılan belleğin tüm noktası ... = (
étale-cohomology

7

İşte bir mmap örneği:

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

opendosya G / Ç ek yükü ekler. shm_openBunun yerine kullanın .
osvein

1
@Spookbuster, shm_open'ın bazı uygulamalarında, kapakların altında open () çağrılır, bu yüzden değerlendirmenize katılmıyorum; işte bir örnek: code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html
Leo

bazı shm_open () uygulamaları başlık altında open () kullanırken, POSIX shm_open () tarafından üretilen dosya tanımlayıcıları için daha düşük gereksinimlere sahiptir. Örneğin, shm_open () dosya tanımlayıcıları için read () ve write () gibi I / O işlevlerini desteklemek için uygulamalar gerekli değildir, bu da bazı uygulamaların shm_open () için open () için yapılamayan optimizasyonlar yapmasına izin verir. Bununla yapacağınız tek şey mmap () ise, shm_open () kullanmalısınız.
osvein

Çoğu Linux-glibc kurulumu, shm_open () 'i desteklemek için tmpfs kullanarak böyle bir optimizasyon yapar. Aynı tmpf'lere genellikle open () aracılığıyla erişilebilirken, yolunu bilmenin taşınabilir bir yolu yoktur. shm_open () bu optimizasyonu taşınabilir bir şekilde kullanalım. POSIX shm_open () 'in open ()' dan daha iyi performans gösterme potansiyeli verir. Tüm uygulamalar bu potansiyeli kullanmayacak, ancak open () 'den daha kötü performans göstermeyecek. Ancak open () her zaman ek yük getirir iddiamın çok geniş olduğunu kabul ediyorum.
osvein
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.