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
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
@Nuetrino: to dlatego, że przywracam oryginalną wartość shmall, to tylko test, więc lepiej mieć po nich prawidłową domyślną wartość systemową. –
Niezupełnie w kodzie, o ile widzę, że go nie przywracasz. – Nuetrino