Ответ 1
-
Используйте
page = sysconf(SC_PAGE_SIZE)
, чтобы узнать размер страницы, затем сканируйте каждый размер размера страницы, который вы хотите проверить, используяmsync(addr, page, 0)
(с(unsigned long)addr % page == 0
, т.е.addr
, совпадающим со страницами). Если он возвращает-1
с помощьюerrno == ENOMEM
, эта страница не отображается.Отредактировано: Как отмечалось ниже,
mincore(addr,page,&dummy)
превосходитmsync()
. (Реализация syscall находится вmm/mincore.c
в источниках ядра Linux, а библиотеки C обычно предоставляют обертку, которая обновляетerrno
. Поскольку syscall проверяет соответствие сразу после того, какaddr
выравнивается по странице, это (ENOMEM
). Он выполняет некоторую работу, если страница уже сопоставлена, поэтому, если производительность имеет первостепенное значение, старайтесь избегать проверки страниц, которые, как вы знаете, сопоставлены.Вы должны делать это отдельно, отдельно для каждой страницы, потому что для регионов, больших одной страницы,
ENOMEM
означает, что регион не был полностью отображен; он может быть частично отображен. Отображение всегда зависит от размера страницы. -
Насколько я могу судить, невозможно сообщить
mmap()
об ошибке, если регион уже отображен или содержит уже отображенные страницы. (То же самое относится кmremap()
, поэтому вы не можете создать сопоставление, а затем переместите его в нужную область.)Это означает, что вы рискуете расы. Лучше всего было бы выполнять фактические syscalls самостоятельно, вместо обложек библиотеки C, на всякий случай, если они распределяют память или меняют отображение памяти внутри:
#define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> static size_t page = 0; static inline size_t page_size(void) { if (!page) page = (size_t)sysconf(_SC_PAGESIZE); return page; } static inline int raw_msync(void *addr, size_t length, int flags) { return syscall(SYS_msync, addr, length, flags); } static inline void *raw_mmap(void *addr, size_t length, int prot, int flags) { return (void *)syscall(SYS_mmap, addr, length, prot, flags, -1, (off_t)0); }
Тем не менее, я подозреваю, что все, что вы пытаетесь сделать, в конечном итоге вам нужно разобрать /proc/self/maps
.
-
Я рекомендую вообще избегать стандартного ввода-вывода
stdio.h
(поскольку различные операции будут распределять память динамически и, таким образом, изменять сопоставления), а вместо этого использовать интерфейсы нижнего уровняunistd.h
, которые намного меньше вероятно, повлияет на сопоставления. Вот набор простых, грубых функций, которые вы можете использовать для поиска каждой отображаемой области и защищенных объектов в этом регионе (и отбрасывания другой информации). На практике он использует около килобайта кода и меньше, чем в стеке, поэтому он очень полезен даже на ограниченных архитектурах (скажем, встраиваемых устройствах).#include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #ifndef INPUT_BUFFER #define INPUT_BUFFER 512 #endif /* INPUT_BUFFER */ #ifndef INPUT_EOF #define INPUT_EOF -256 #endif /* INPUT_EOF */ #define PERM_PRIVATE 16 #define PERM_SHARED 8 #define PERM_READ 4 #define PERM_WRITE 2 #define PERM_EXEC 1 typedef struct { int descriptor; int status; unsigned char *next; unsigned char *ends; unsigned char buffer[INPUT_BUFFER + 16]; } input_buffer; /* Refill input buffer. Returns the number of new bytes. * Sets status to ENODATA at EOF. */ static size_t input_refill(input_buffer *const input) { ssize_t n; if (input->status) return (size_t)0; if (input->next > input->buffer) { if (input->ends > input->next) { memmove(input->buffer, input->next, (size_t)(input->ends - input->next)); input->ends = input->buffer + (size_t)(input->ends - input->next); input->next = input->buffer; } else { input->ends = input->buffer; input->next = input->buffer; } } do { n = read(input->descriptor, input->ends, INPUT_BUFFER - (size_t)(input->ends - input->buffer)); } while (n == (ssize_t)-1 && errno == EINTR); if (n > (ssize_t)0) { input->ends += n; return (size_t)n; } else if (n == (ssize_t)0) { input->status = ENODATA; return (size_t)0; } if (n == (ssize_t)-1) input->status = errno; else input->status = EIO; return (size_t)0; } /* Low-lever getchar() equivalent. */ static inline int input_next(input_buffer *const input) { if (input->next < input->ends) return *(input->next++); else if (input_refill(input) > 0) return *(input->next++); else return INPUT_EOF; } /* Low-level ungetc() equivalent. */ static inline int input_back(input_buffer *const input, const int c) { if (c < 0 || c > 255) return INPUT_EOF; else if (input->next > input->buffer) return *(--input->next) = c; else if (input->ends >= input->buffer + sizeof input->buffer) return INPUT_EOF; memmove(input->next + 1, input->next, (size_t)(input->ends - input->next)); input->ends++; return *(input->next) = c; } /* Low-level fopen() equivalent. */ static int input_open(input_buffer *const input, const char *const filename) { if (!input) return errno = EINVAL; input->descriptor = -1; input->status = 0; input->next = input->buffer; input->ends = input->buffer; if (!filename || !*filename) return errno = input->status = EINVAL; do { input->descriptor = open(filename, O_RDONLY | O_NOCTTY); } while (input->descriptor == -1 && errno == EINTR); if (input->descriptor == -1) return input->status = errno; return 0; } /* Low-level fclose() equivalent. */ static int input_close(input_buffer *const input) { int result; if (!input) return errno = EINVAL; /* EOF is not an error; we use ENODATA for that. */ if (input->status == ENODATA) input->status = 0; if (input->descriptor != -1) { do { result = close(input->descriptor); } while (result == -1 && errno == EINTR); if (result == -1 && !input->status) input->status = errno; } input->descriptor = -1; input->next = input->buffer; input->ends = input->buffer; return errno = input->status; } /* Read /proc/self/maps, and fill in the arrays corresponding to the fields. * The function will return the number of mappings, even if not all are saved. */ size_t read_maps(size_t const n, void **const ptr, size_t *const len, unsigned char *const mode) { input_buffer input; size_t i = 0; unsigned long curr_start, curr_end; unsigned char curr_mode; int c; errno = 0; if (input_open(&input, "/proc/self/maps")) return (size_t)0; /* errno already set. */ c = input_next(&input); while (c >= 0) { /* Skip leading controls and whitespace */ while (c >= 0 && c <= 32) c = input_next(&input); /* EOF? */ if (c < 0) break; curr_start = 0UL; curr_end = 0UL; curr_mode = 0U; /* Start of address range. */ while (1) if (c >= '0' && c <= '9') { curr_start = (16UL * curr_start) + c - '0'; c = input_next(&input); } else if (c >= 'A' && c <= 'F') { curr_start = (16UL * curr_start) + c - 'A' + 10; c = input_next(&input); } else if (c >= 'a' && c <= 'f') { curr_start = (16UL * curr_start) + c - 'a' + 10; c = input_next(&input); } else break; if (c == '-') c = input_next(&input); else { errno = EIO; return (size_t)0; } /* End of address range. */ while (1) if (c >= '0' && c <= '9') { curr_end = (16UL * curr_end) + c - '0'; c = input_next(&input); } else if (c >= 'A' && c <= 'F') { curr_end = (16UL * curr_end) + c - 'A' + 10; c = input_next(&input); } else if (c >= 'a' && c <= 'f') { curr_end = (16UL * curr_end) + c - 'a' + 10; c = input_next(&input); } else break; if (c == ' ') c = input_next(&input); else { errno = EIO; return (size_t)0; } /* Permissions. */ while (1) if (c == 'r') { curr_mode |= PERM_READ; c = input_next(&input); } else if (c == 'w') { curr_mode |= PERM_WRITE; c = input_next(&input); } else if (c == 'x') { curr_mode |= PERM_EXEC; c = input_next(&input); } else if (c == 's') { curr_mode |= PERM_SHARED; c = input_next(&input); } else if (c == 'p') { curr_mode |= PERM_PRIVATE; c = input_next(&input); } else if (c == '-') { c = input_next(&input); } else break; if (c == ' ') c = input_next(&input); else { errno = EIO; return (size_t)0; } /* Skip the rest of the line. */ while (c >= 0 && c != '\n') c = input_next(&input); /* Add to arrays, if possible. */ if (i < n) { if (ptr) ptr[i] = (void *)curr_start; if (len) len[i] = (size_t)(curr_end - curr_start); if (mode) mode[i] = curr_mode; } i++; } if (input_close(&input)) return (size_t)0; /* errno already set. */ errno = 0; return i; }
Функция
read_maps()
считывает областиn
, запускает адреса какvoid *
в массивptr
, длина в массивlen
и разрешения в массивmode
, возвращая общее число (может быть больше, чемn
), или ноль сerrno
, если возникает ошибка.Вполне возможно использовать системные вызовы для низкоуровневого ввода-вывода, чтобы вы не использовали никаких функций библиотеки C, но я не думаю, что это вообще необходимо. (Библиотеки C, насколько я могу судить, используют очень простые обертки вокруг фактических системных вызовов для них.)
Надеюсь, вы сочтете это полезным.