관리-도구
편집 파일: shm.c
/* -*- linux-c -*- * Shared Memory Functions * Copyright (C) 2012 Red Hat Inc. * * This file is part of systemtap, and is free software. You can * redistribute it and/or modify it under the terms of the GNU General * Public License (GPL); either version 2, or (at your option) any * later version. */ #ifndef _STAPDYN_SHM_H_ #define _STAPDYN_SHM_H_ #include <fcntl.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> static char _stp_shm_name[NAME_MAX] = { '\0' }; static int _stp_shm_fd = -1; static int _stp_shm_page_size = -1; static off_t _stp_shm_size = 0; static off_t _stp_shm_allocated = 0; static void *_stp_shm_base = NULL; static const char *_stp_shm_init(void); static int _stp_shm_connect(const char *name); static void *_stp_shm_alloc(size_t size); static void *_stp_shm_zalloc(size_t size); static void _stp_shm_free(void *ptr); static void _stp_shm_finalize(void); static void _stp_shm_destroy(void); #ifdef DEBUG_SHM #define shm_dbug(fmt, args...) _stp_dbug(__FUNCTION__, __LINE__, fmt, ##args) #else #define shm_dbug(fmt, args...) #endif // Create and initialize the shared memory for this module. static const char *_stp_shm_init(void) { char name[NAME_MAX]; long page_size; int fd; void *base; // If we already have shared memory, carry on... if (_stp_shm_base) return _stp_shm_name; // Find the increment to use in growing our memory size. Since this is // used in mmap, only multiples of the page size make sense. page_size = sysconf(_SC_PAGESIZE); if (page_size < 1) return NULL; // Create a unique name for our shared memory. It need not be anything // secret because shm_open's flags will ensure security. (void) snprintf(name, NAME_MAX, "/stapdyn.%d", getpid()); // Create the actual share memory. The O_EXCL saves us from the // possible mktemp race. Only USR file mode bits are added, because // that's all that ptrace will allow to attach anyway. (Unless you're // root, but that deserves much larger consideration.) fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) return NULL; // Start the shared memory sized with just one unit. if (ftruncate(fd, page_size) < 0) goto err_fd; // Now finally map it into this process. base = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) goto err_fd; // We're done; set globals and go home! strlcpy(_stp_shm_name, name, sizeof(_stp_shm_name)); _stp_shm_fd = fd; _stp_shm_page_size = page_size; _stp_shm_size = page_size; _stp_shm_allocated = 0; _stp_shm_base = base; shm_dbug("initialized %s @ %p", _stp_shm_name, _stp_shm_base); return _stp_shm_name; err_fd: close(fd); shm_unlink(_stp_shm_name); return NULL; } static int _stp_shm_connect(const char *name) { int rc = 0, fd = -1; struct stat st; void *base; // NB: since we're just connecting, we won't set _stp_shm_name, // so _stp_shm_destroy won't try to unlink it from this process. // Open the pre-existing shared memory fd = shm_open(name, O_RDWR, 0); if (fd < 0) rc = fd; // Find out the shared memory's size. // (This implies that the main process is done allocating.) if (rc == 0) rc = fstat(fd, &st); // Map it into this process. if (rc == 0) { base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) rc = -1; else _stp_shm_base = base; } if (fd >= 0) close(fd); if (rc == 0) shm_dbug("connected pid %d to %s @ %p", getpid(), name, _stp_shm_base); return rc; } // Allocate space from shared memory static void *_stp_shm_alloc(size_t size) { void *alloc; // We're not initialized or already finalized. // Either way, we're not taking new requests. if (_stp_shm_fd < 0) return NULL; // Round up to 8-byte aligned sizes, just to be a little safer size = (size + 7) & ~7; if (size <= 0) return NULL; // either 0 requested or overflow // Check if more memory is needed. if (_stp_shm_size - _stp_shm_allocated < size) { void *new_base; // Round up to the nearest page size off_t new_size = _stp_shm_allocated + size; new_size += _stp_shm_page_size - 1; new_size /= _stp_shm_page_size; new_size *= _stp_shm_page_size; if (new_size < _stp_shm_allocated || new_size - _stp_shm_allocated < size) return NULL; // math overflow? // Try to resize the underlying file. if (ftruncate(_stp_shm_fd, new_size) < 0) return NULL; // Try to remap the address in memory. new_base = mremap(_stp_shm_base, _stp_shm_size, new_size, MREMAP_MAYMOVE); if (new_base == MAP_FAILED) return NULL; // Update globals _stp_shm_size = new_size; _stp_shm_base = new_base; } // Finally return some memory. alloc = _stp_shm_base + _stp_shm_allocated; _stp_shm_allocated += size; return alloc; } // Allocate zeroed space from shared memory static void *_stp_shm_zalloc(size_t size) { void *ptr = _stp_shm_alloc(size); if (ptr) memset(ptr, 0, size); return ptr; } static void _stp_shm_free(void *ptr __attribute__((unused))) { // NO-OP; we don't try to reclaim individual pieces. } // Signal that we're done allocating in shared memory. // It won't be allowed to grow or move from here on out. static void _stp_shm_finalize(void) { if (_stp_shm_fd >= 0) { close(_stp_shm_fd); _stp_shm_fd = -1; } shm_dbug("mapped %" PRIi64 " bytes @ %p, used %" PRIi64, _stp_shm_size, _stp_shm_base, _stp_shm_allocated); } // Tear down everything we created... *sniff* // NB: Make sure not to reference any memory within after this! // (Other processes may still have their own mmap reference though.) // Don't destroy things in shm that may still be used by other processes! static void _stp_shm_destroy(void) { if (_stp_shm_base) { munmap(_stp_shm_base, _stp_shm_size); _stp_shm_base = NULL; _stp_shm_size = 0; _stp_shm_allocated = 0; } if (_stp_shm_fd >= 0) { close(_stp_shm_fd); _stp_shm_fd = -1; } if (_stp_shm_name[0]) { shm_unlink(_stp_shm_name); _stp_shm_name[0] = '\0'; } } #endif /* _STAPDYN_SHM_H_ */