Использование fallocate() после shm_open() приводит к тому, что память не освобождается после shm_unlink()
У меня есть приложение, которое использует общую память с файлами сопоставленной памяти. Целевой операционной системой является Ubuntu 14.04 (64-разрядная версия). Ядро Linux в этом дистрибутиве имеет версию 4.4.0. gcc находится на версии 4.8.4.
До недавнего времени я использовал следующие вызовы функций (в указанном порядке) для выделения и освобождения общей памяти.
shm_open
ftruncate
mmap
/* use shared memory */
munmap
shm_unlink
Этот подход имеет проблему, заключающуюся в том, что он не обнаруживает, достаточно ли памяти для общей памяти. Приложение вылетит на более позднем этапе с SIGBUS
сигнал при доступе к общей памяти.
Я обнаружил, что у людей была такая же проблема, и они решили ее с помощью fallocate()
вместо ftruncate()
, fallocate()
вернет ошибку, если недостаточно памяти для запрошенного размера.
Я реализовал то же самое в моем приложении и fallocate()
может правильно определить ситуацию, когда недостаточно памяти. Однако сейчас я сталкиваюсь с другой проблемой.
Проблема в том, что память зарезервирована fallocate()
не освобождается после звонка shm_unlink()
, Это не было проблемой при использовании ftruncate()
,
Рассмотрим следующий минимальный пример (fallocate.c
) которые демонстрируют это поведение.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <fcntl.h>
static const char* name = "/test";
static const size_t size = (size_t)4*1024*1024*1024;
int main ()
{
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO);
if (fd == -1) {
printf("shm_open failed (%s)\n", strerror(errno));
return 1;
}
if (fallocate(fd, 0, 0, size)) {
printf("fallocate failed (%s)\n", strerror(errno));
return 1;
}
if (shm_unlink(name)) {
printf("shm_unlink failed (%s)\n", strerror(errno));
return 1;
}
printf("break here to check if memory still used\n");
return 0;
}
Я использовал следующее CMakeLists.txt
для компиляции
add_executable(fallocate fallocate.c)
target_compile_definitions(fallocate PRIVATE _GNU_SOURCE)
target_link_libraries(fallocate PRIVATE rt)
Запустите этот пример в gdb
и сломаться на последнем printf
заявление. Вы увидите следующее поведение.
-
test
файл больше не присутствует в/dev/shm
- Память все еще находится в категории "использованных", если смотреть на
top
выход; он перейдет в категорию "бесплатно" только после завершения процесса
Это ожидаемое поведение или я использую API неправильно?
Изменить: по запросу адресное пространство процесса после shm_unlink()
(с помощью gets()
после shm_unlink()
провести процесс)
Выход из cat /proc/<PID>/status
Name: fallocate
State: S (sleeping)
Tgid: 12445
Ngid: 0
Pid: 12445
PPid: 26349
TracerPid: 0
Uid: 1001 1001 1001 1001
Gid: 1001 1001 1001 1001
FDSize: 256
Groups: 4 27 108 124 999 1001 1002
NStgid: 12445
NSpid: 12445
NSpgid: 12445
NSsid: 26349
VmPeak: 8628 kB
VmSize: 8460 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 840 kB
VmRSS: 840 kB
VmData: 80 kB
VmStk: 132 kB
VmExe: 4 kB
VmLib: 2052 kB
VmPTE: 36 kB
VmPMD: 12 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
Threads: 1
SigQ: 0/61795
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Seccomp: 0
Speculation_Store_Bypass: thread vulnerable
Cpus_allowed: ff
Cpus_allowed_list: 0-7
Mems_allowed: 00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 1
nonvoluntary_ctxt_switches: 2
Выход из pmap <PID>
0000000000400000 4K r-x-- fallocate
0000000000600000 4K r---- fallocate
0000000000601000 4K rw--- fallocate
00007f1e92093000 100K r-x-- libpthread-2.19.so
00007f1e920ac000 2044K ----- libpthread-2.19.so
00007f1e922ab000 4K r---- libpthread-2.19.so
00007f1e922ac000 4K rw--- libpthread-2.19.so
00007f1e922ad000 16K rw--- [ anon ]
00007f1e922b1000 1784K r-x-- libc-2.19.so
00007f1e9246f000 2048K ----- libc-2.19.so
00007f1e9266f000 16K r---- libc-2.19.so
00007f1e92673000 8K rw--- libc-2.19.so
00007f1e92675000 20K rw--- [ anon ]
00007f1e9267a000 28K r-x-- librt-2.19.so
00007f1e92681000 2044K ----- librt-2.19.so
00007f1e92880000 4K r---- librt-2.19.so
00007f1e92881000 4K rw--- librt-2.19.so
00007f1e92882000 140K r-x-- ld-2.19.so
00007f1e92a75000 16K rw--- [ anon ]
00007f1e92aa3000 4K rw--- [ anon ]
00007f1e92aa4000 4K r---- ld-2.19.so
00007f1e92aa5000 4K rw--- ld-2.19.so
00007f1e92aa6000 4K rw--- [ anon ]
00007ffe6f72b000 132K rw--- [ stack ]
00007ffe6f7ee000 12K r---- [ anon ]
00007ffe6f7f1000 8K r-x-- [ anon ]
ffffffffff600000 4K r-x-- [ anon ]
total 8464K
2 ответа
Вы не закрываете дескриптор открытого файла, и "файл" общей памяти, скорее всего, находится в tmpfs
файловая система на основе памяти (при условии Linux).
Этот код создает файл:
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO);
Этот код делает его большим (4 ГБ):
if (fallocate(fd, 0, 0, size)) {
Этот код просто отменяет связь с файловой системой:
if (shm_unlink(name)) {
На этом этапе дескриптор открытого файла означает, что резервный файл все еще существует, даже если он был удален из каталога с его именем. (Это буквально означает "unlink".) Такой файл не будет фактически удален из файловой системы, пока не будет закрыта последняя ссылка на файл - и эта последняя ссылка является дескриптором открытого файла вашего процесса.
добавлять
close( fd );
и проверьте использование системной памяти до и после close()
вызов.
shm_unlink
удаляет только имя, связанное с объектом памяти. Это не удалит объект, если есть другие вещи, ссылающиеся на него. У вас есть открытый файловый дескриптор, ссылающийся на объект памяти. После того, как вы закроете его, refcount должен достигнуть нуля, и память должна быть освобождена.