2016-05-12 21 views
7

stavo cercando nei limiti del kernel di Linux sulla memoria condivisaCome funziona limiti al lavoro di memoria condivisa su Linux

/proc/sys/kernel/shmall 

specifica la quantità massima di pagine che possono essere assegnati. Considerando questo numero come x e le dimensioni della pagina come p. Suppongo che "x * p" byte sia il limite della memoria condivisa del sistema.

Ora ho scritto un piccolo programma per creare un segmento di memoria condivisa e ho attaccato a quel segmento di memoria condivisa due volte più in basso

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

Nel programma precedente ptr e ptr_info erano diverse. Quindi la memoria condivisa è mappata a 2 indirizzi virtuali nel mio spazio di indirizzamento del processo.

Quando faccio un ipcs Sembra che questa

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

Ora venendo al limite shmallx * p osservato in precedenza nella mia interrogazione. Questo limite è applicabile alla somma di tutta la memoria virtuale allocata per ogni segmento di memoria condivisa? o questo limite si applica alla memoria fisica?

La memoria fisica è solo una qui (memoria condivisa) e dal programma di cui sopra quando faccio 2 shmat c'è il doppio della quantità di memoria allocata nel mio spazio di indirizzamento del processo. Quindi questo limite colpirà presto se continui su shmat su un singolo segmento di memoria condivisa?

risposta

1

Il limite si applica solo alla memoria fisica, ovvero la memoria condivisa reale allocata per tutti i segmenti, perché lo shmat() esegue solo il mapping del segmento assegnato nello spazio di indirizzamento del processo.

È possibile rintracciarlo nel kernel, c'è solo un posto in cui viene controllato questo limite — nello newseg() function che alloca nuovi segmenti (confronto ns->shm_ctlall). shmat() implementation è occupato con un sacco di cose, ma non si preoccupa affatto del limite shmall, quindi puoi mappare un segmento quante volte vuoi (beh, lo spazio degli indirizzi è anche limitato, ma in pratica raramente ti interessa questo limite).

Si può anche provare alcuni test dal userspace con un semplice programma come questo:

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

Ciò che fa è che imposta il shmall limite di una sola pagina sopra quello che è attualmente utilizzato dal sistema, poi cerca per ottenere la pagina di dimensioni nuovo segmento e mappare due volte (tutte con successo), poi cerca di ottenere un altro pagina di dimensioni del segmento e non riesce a fare questo (eseguire il programma come superutente perché scrive /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

Questa è una buona spiegazione. Ho un paio di commenti. Questo programma deve essere eseguito come sudo per poter scrivere in "/ proc/sys/kernel/shmall". Un'altra osservazione è dopo l'exe del programma, mi aspetto che il valore di "/ proc/sys/kernel/shmall" rimanga il valore impostato nel programma fino al prossimo riavvio. ma quando faccio un gatto del valore in/proc/sys/kernel/shmall, mostra ancora il vecchio valore 18446744073692774399. Puoi spiegare perché? – Nuetrino

+0

@Nuetrino: è perché sto ripristinando il valore shmall originale, è solo un test, quindi è meglio avere un valore predefinito di sistema corretto dopo, credo. –

+0

Non esattamente nel codice finché posso vedere che non lo si ripristina. – Nuetrino

0

non ho trovato alcuna allocazione di memoria fisica in funzione do_shmat (linux/IPC/shm.c)

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

così shmat consuma solo VM (il vostro spazio di indirizzi del processo), la funzione principale di shmat è mmap

+0

Ma la mia domanda è diversa. Riguarda i limiti specificati da "/ proc/sys/kernel/shmall".Questo ammonta alla somma di tutto lo spazio di indirizzi virtuali assegnato per tutti i segmenti di memoria condivisa o no ?. È già chiaro per me shmat alloca la memoria nel vm dello spazio degli indirizzi del processo. – Nuetrino

Problemi correlati