summaryrefslogtreecommitdiff
path: root/ext/opcache/shared_alloc_shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache/shared_alloc_shm.c')
-rw-r--r--ext/opcache/shared_alloc_shm.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c
new file mode 100644
index 0000000000..a88cc2e196
--- /dev/null
+++ b/ext/opcache/shared_alloc_shm.c
@@ -0,0 +1,145 @@
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend_shared_alloc.h"
+
+#ifdef USE_SHM
+
+#if defined(__FreeBSD__)
+# include <machine/param.h>
+#endif
+#include <sys/types.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+#include <dirent.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifndef MIN
+# define MIN(x, y) ((x) > (y)? (y) : (x))
+#endif
+
+#define SEG_ALLOC_SIZE_MAX 32*1024*1024
+#define SEG_ALLOC_SIZE_MIN 2*1024*1024
+
+typedef struct {
+ zend_shared_segment common;
+ int shm_id;
+} zend_shared_segment_shm;
+
+static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in)
+{
+ int i;
+ size_t allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size;
+ int first_segment_id = -1;
+ key_t first_segment_key = -1;
+ struct shmid_ds sds;
+ int shmget_flags;
+ zend_shared_segment_shm *shared_segments;
+
+ seg_allocate_size = SEG_ALLOC_SIZE_MAX;
+ /* determine segment size we _really_ need:
+ * no more than to include requested_size
+ */
+ while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) {
+ seg_allocate_size >>= 1;
+ }
+
+ shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL;
+
+ /* try allocating this much, if not - try shrinking */
+ while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
+ allocate_size = MIN(requested_size, seg_allocate_size);
+ first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags);
+ if (first_segment_id != -1) {
+ break;
+ }
+ seg_allocate_size >>= 1; /* shrink the allocated block */
+ }
+
+ if (first_segment_id == -1) {
+ *error_in = "shmget";
+ return ALLOC_FAILURE;
+ }
+
+ *shared_segments_count = ((requested_size - 1) / seg_allocate_size) + 1;
+ *shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count) * sizeof(zend_shared_segment_shm) + sizeof(void *) * (*shared_segments_count));
+ if (!*shared_segments_p) {
+ *error_in = "calloc";
+ return ALLOC_FAILURE;
+ }
+ shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *) * (*shared_segments_count));
+ for (i = 0; i < *shared_segments_count; i++) {
+ (*shared_segments_p)[i] = shared_segments + i;
+ }
+
+ remaining_bytes = requested_size;
+ for (i = 0; i < *shared_segments_count; i++) {
+ allocate_size = MIN(remaining_bytes, seg_allocate_size);
+ if (i != 0) {
+ shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags);
+ } else {
+ shared_segments[i].shm_id = first_segment_id;
+ }
+
+ if (shared_segments[i].shm_id == -1) {
+ return ALLOC_FAILURE;
+ }
+
+ shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0);
+ if (shared_segments[i].common.p == (void *)-1) {
+ *error_in = "shmat";
+ shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
+ return ALLOC_FAILURE;
+ }
+ shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
+
+ shared_segments[i].common.pos = 0;
+ shared_segments[i].common.size = allocate_size;
+ remaining_bytes -= allocate_size;
+ }
+ return ALLOC_SUCCESS;
+}
+
+static int detach_segment(zend_shared_segment_shm *shared_segment)
+{
+ shmdt(shared_segment->common.p);
+ return 0;
+}
+
+static size_t segment_type_size(void)
+{
+ return sizeof(zend_shared_segment_shm);
+}
+
+zend_shared_memory_handlers zend_alloc_shm_handlers = {
+ (create_segments_t)create_segments,
+ (detach_segment_t)detach_segment,
+ segment_type_size
+};
+
+#endif /* USE_SHM */