xref: /php-src/ext/opcache/shared_alloc_shm.c (revision 263b22f3)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | https://www.php.net/license/3_01.txt                                 |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "zend_shared_alloc.h"
23 
24 #ifdef USE_SHM
25 
26 #if defined(__FreeBSD__)
27 # include <machine/param.h>
28 #endif
29 #include <sys/types.h>
30 #include <sys/shm.h>
31 #include <sys/ipc.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 
41 #ifndef MIN
42 # define MIN(x, y) ((x) > (y)? (y) : (x))
43 #endif
44 
45 #define SEG_ALLOC_SIZE_MAX 32*1024*1024
46 #define SEG_ALLOC_SIZE_MIN 2*1024*1024
47 
48 typedef struct  {
49     zend_shared_segment common;
50     int shm_id;
51 } zend_shared_segment_shm;
52 
create_segments(size_t requested_size,zend_shared_segment_shm *** shared_segments_p,int * shared_segments_count,const char ** error_in)53 static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, const char **error_in)
54 {
55 	int i;
56 	size_t allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size;
57 	int first_segment_id = -1;
58 	key_t first_segment_key = -1;
59 	struct shmid_ds sds;
60 	int shmget_flags;
61 	zend_shared_segment_shm *shared_segments;
62 
63 	seg_allocate_size = SEG_ALLOC_SIZE_MAX;
64 	/* determine segment size we _really_ need:
65 	 * no more than to include requested_size
66 	 */
67 	while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) {
68 		seg_allocate_size >>= 1;
69 	}
70 
71 	shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL;
72 
73 	/* try allocating this much, if not - try shrinking */
74 	while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
75 		allocate_size = MIN(requested_size, seg_allocate_size);
76 		first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags);
77 		if (first_segment_id != -1) {
78 			break;
79 		}
80 		seg_allocate_size >>= 1; /* shrink the allocated block */
81 	}
82 
83 	if (first_segment_id == -1) {
84 		*error_in = "shmget";
85 		return ALLOC_FAILURE;
86 	}
87 
88 	*shared_segments_count = ((requested_size - 1) / seg_allocate_size) + 1;
89 	*shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count) * sizeof(zend_shared_segment_shm) + sizeof(void *) * (*shared_segments_count));
90 	if (!*shared_segments_p) {
91 		*error_in = "calloc";
92 		return ALLOC_FAILURE;
93 	}
94 	shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *) * (*shared_segments_count));
95 	for (i = 0; i < *shared_segments_count; i++) {
96 		(*shared_segments_p)[i] = shared_segments + i;
97 	}
98 
99 	remaining_bytes = requested_size;
100 	for (i = 0; i < *shared_segments_count; i++) {
101 		allocate_size = MIN(remaining_bytes, seg_allocate_size);
102 		if (i != 0) {
103 			shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags);
104 		} else {
105 			shared_segments[i].shm_id = first_segment_id;
106 		}
107 
108 		if (shared_segments[i].shm_id == -1) {
109 			return ALLOC_FAILURE;
110 		}
111 
112 		shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0);
113 		if (shared_segments[i].common.p == (void *)-1) {
114 			*error_in = "shmat";
115 			shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
116 			return ALLOC_FAILURE;
117 		}
118 		shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
119 
120 		shared_segments[i].common.pos = 0;
121 		shared_segments[i].common.size = allocate_size;
122 		remaining_bytes -= allocate_size;
123 	}
124 	return ALLOC_SUCCESS;
125 }
126 
detach_segment(zend_shared_segment_shm * shared_segment)127 static int detach_segment(zend_shared_segment_shm *shared_segment)
128 {
129 	shmdt(shared_segment->common.p);
130 	return 0;
131 }
132 
segment_type_size(void)133 static size_t segment_type_size(void)
134 {
135 	return sizeof(zend_shared_segment_shm);
136 }
137 
138 const zend_shared_memory_handlers zend_alloc_shm_handlers = {
139 	(create_segments_t)create_segments,
140 	(detach_segment_t)detach_segment,
141 	segment_type_size
142 };
143 
144 #endif /* USE_SHM */
145