2016-05-12 9 views
7

szukałem w granicach jądra Linux na pamięci współdzielonejJak limity na wspólnej pracy pamięci na Linux

/proc/sys/kernel/shmall 

określa maksymalną liczbę stron, które mogą zostać przydzielone. Biorąc pod uwagę tę liczbę jako x i rozmiar strony jako p. Zakładam, że "x * p" bajtów jest limitem wspólnej pamięci systemowej.

Teraz Napisałem mały program do tworzenia segmentu pamięci współdzielonej i dołączony do tego segmentu pamięci wspólnej dwukrotnie poniżej

shm_id = shmget(IPC_PRIVATE, 4*sizeof(int), IPC_CREAT | 0666); 

if (shm_id < 0) { 
    printf("shmget error\n"); 
    exit(1); 
} 
printf("\n The shared memory created is %d",shm_id); 

ptr = shmat(shm_id,NULL,0); 
ptr_info = shmat(shm_id,NULL,0); 

W powyższym programie ptr i ptr_info były różne. Tak więc pamięć dzielona jest mapowana na 2 wirtualne adresy w mojej przestrzeni adresowej procesu.

Kiedy zrobić ipcs wygląda to

... 
0x00000000 1638416 sun  666  16000000 2 
... 

Teraz zbliża się do granicy shmallx * p wspomniano powyżej w moim pytaniu. Czy to ograniczenie ma zastosowanie do sumy wszystkich pamięci wirtualnych przydzielonych dla każdego segmentu pamięci współużytkowanej? lub czy ten limit obowiązuje w pamięci fizycznej?

Pamięć fizyczna jest tylko jedna tutaj (pamięć współdzielona) i z powyższego programu, gdy mam 2 shmat, jest dwa razy więcej pamięci przydzielonej w mojej przestrzeni adresowej procesu. Więc ten limit zostanie wkrótce osiągnięty, jeśli będzie ciągły shmat w jednym segmencie pamięci wspólnej?

Odpowiedz

1

Ograniczenie dotyczy tylko pamięci fizycznej, czyli rzeczywistej pamięci współdzielonej przydzielonej dla wszystkich segmentów, ponieważ shmat() po prostu odwzorowuje przydzielony segment na przestrzeń adresową procesu.

Można go śledzić w jądrze, jest tylko jedno miejsce, gdzie granica ta jest sprawdzana — w newseg() function który przydziela nowe segmenty (ns->shm_ctlall porównanie). shmat() implementation jest zajęty wieloma rzeczami, ale nie przejmuje się limitem shmall, więc możesz mapować jeden segment tyle razy ile chcesz (cóż, przestrzeń adresowa jest również ograniczona, ale w praktyce rzadko troszczysz się o to limit).

Można również spróbować test z przestrzeni użytkownika za pomocą prostego programu jak ten:

#define _GNU_SOURCE 
#include <errno.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <unistd.h> 

unsigned long int get_shmall() { 
     FILE *f = NULL; 
     char buf[512]; 
     unsigned long int value = 0; 

     if ((f = fopen("/proc/sys/kernel/shmall", "r")) != NULL) { 
       if (fgets(buf, sizeof(buf), f) != NULL) 
         value = strtoul(buf, NULL, 10); // no proper checks                                        
       fclose(f); // no return value check                                              
     } 
     return value; 
} 

int set_shmall(unsigned long int value) { 
     FILE *f = NULL; 
     char buf[512]; 
     int retval = 0; 

     if ((f = fopen("/proc/sys/kernel/shmall", "w")) != NULL) { 
       if (snprintf(buf, sizeof(buf), "%lu\n", value) >= sizeof(buf) || 
        fwrite(buf, 1, strlen(buf), f) != strlen(buf)) 
         retval = -1; 
       fclose(f); // fingers crossed                                               
     } else 
       retval = -1; 
     return retval; 
} 

int main() 
{ 
     int shm_id1 = -1, shm_id2 = -1; 
     unsigned long int shmall = 0, shmused, newshmall; 
     void *ptr1, *ptr2; 
     struct shm_info shminf; 

     if ((shmall = get_shmall()) == 0) { 
       printf("can't get shmall\n"); 
       goto out; 
     } 
     printf("original shmall: %lu pages\n", shmall); 
     if (shmctl(0, SHM_INFO, (struct shmid_ds *)&shminf) < 0) { 
       printf("can't get SHM_INFO\n"); 
       goto out; 
     } 
     shmused = shminf.shm_tot * getpagesize(); 
     printf("shmused: %lu pages (%lu bytes)\n", shminf.shm_tot, shmused); 
     newshmall = shminf.shm_tot + 1; 
     if (set_shmall(newshmall) != 0) { 
       printf("can't set shmall\n"); 
       goto out; 
     } 
     if (get_shmall() != newshmall) { 
       printf("something went wrong with shmall setting\n"); 
       goto out; 
     } 
     printf("new shmall: %lu pages (%lu bytes)\n", newshmall, newshmall * getpagesize()); 
     printf("shmget() for %u bytes: ", (unsigned int) getpagesize()); 
     shm_id1 = shmget(IPC_PRIVATE, (size_t)getpagesize(), IPC_CREAT | 0666); 
     if (shm_id1 < 0) { 
       printf("failed: %s\n", strerror(errno)); 
       goto out; 
     } 
     printf("ok\nshmat 1: "); 
     ptr1 = shmat(shm_id1, NULL, 0); 
     if (ptr1 == 0) { 
       printf("failed\n"); 
       goto out; 
     } 
     printf("ok\nshmat 2: "); 
     ptr2 = shmat(shm_id1, NULL, 0); 
     if (ptr2 == 0) { 
       printf("failed\n"); 
       goto out; 
     } 
     printf("ok\n"); 
     if (ptr1 == ptr2) { 
       printf("ptr1 and ptr2 are the same with shm_id1\n"); 
       goto out; 
     } 
     printf("shmget() for %u bytes: ", (unsigned int) getpagesize()); 
     shm_id2 = shmget(IPC_PRIVATE, (size_t)getpagesize(), IPC_CREAT | 0666); 
     if (shm_id2 < 0) 
       printf("failed: %s\n", strerror(errno)); 
     else 
       printf("ok, although it's wrong\n"); 
out: 
     if (shmall != 0 && set_shmall(shmall) != 0) 
       printf("failed to restrore shmall\n"); 

     if (shm_id1 >= 0 && shmctl(shm_id1, IPC_RMID, NULL) < 0) 
       printf("failed to remove shm_id1\n"); 

     if (shm_id2 >= 0 && shmctl(shm_id2, IPC_RMID, NULL) < 0) 
       printf("failed to remove shm_id2\n"); 

     return 0; 
} 

Co robi to ustawia shmall graniczną tylko jedną stronę ponad to, co jest aktualnie używany przez system, następnie próbuje aby strona wielkości nowy segment i mapować go dwukrotnie (wszystko z powodzeniem), a następnie próbuje dostać jeszcze jeden segment strony wielkości i nie robić (uruchomić program jako administrator, ponieważ pisze do /proc/sys/kernel/shmall):

$ sudo ./a.out 
original shmall: 18446744073708503040 pages 
shmused: 21053 pages (86233088 bytes) 
new shmall: 21054 pages (86237184 bytes) 
shmget() for 4096 bytes: ok 
shmat 1: ok 
shmat 2: ok 
shmget() for 4096 bytes: failed: No space left on device 
+0

To jest dobre wytłumaczenie. Mam kilka uwag. Ten program musi być uruchomiony jako sudo, aby móc pisać do "/ proc/sys/kernel/shmall". Kolejna uwaga jest po wyjęciu programu Oczekuję, że wartość "/ proc/sys/kernel/shmall" pozostanie wartością ustawioną w programie do następnego ponownego uruchomienia. ale kiedy robię kota wartości w/proc/sys/kernel/shmall, to nadal pokazuje starą wartość 18446744073692774399. Czy mógłbyś wyjaśnić dlaczego? – Nuetrino

+0

@Nuetrino: to dlatego, że przywracam oryginalną wartość shmall, to tylko test, więc lepiej mieć po nich prawidłową domyślną wartość systemową. –

+0

Niezupełnie w kodzie, o ile widzę, że go nie przywracasz. – Nuetrino

0

Nie mogę znaleźć żadnej fizycznej alokacji pamięci w funkcji do_shmat (linux/IPC/shm.c)

https://github.com/torvalds/linux/blob/5469dc270cd44c451590d40c031e6a71c1f637e8/ipc/shm.c

tak shmat zużywa tylko maszyną (swoją przestrzeń adresową procesu), główną funkcją shmat to mmap

+0

Ale moje pytanie jest inne. Chodzi o limity określone przez "/ proc/sys/kernel/shmall".Czy jest to suma całej wirtualnej przestrzeni adresowej przydzielonej dla wszystkich segmentów pamięci współużytkowanej, czy nie ?. Jest już dla mnie jasne, że shmat przydziela pamięć w wirtualnej przestrzeni procesowej przestrzeni adresowej. – Nuetrino

Powiązane problemy