XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

325 lines
6.8 KiB

  1. #include <stdio.h>
  2. #include <assert.h>
  3. #include <limits.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. /* mmap */
  7. #ifdef ZEND_WIN32
  8. # define ftruncate chsize
  9. # define getuid() 0
  10. # include <process.h>
  11. # define XCacheCreateFileMapping(size, perm, name) \
  12. CreateFileMapping(INVALID_HANDLE_VALUE, NULL, perm, (sizeof(xc_shmsize_t) > 4) ? size >> 32 : 0, size & 0xffffffff, name)
  13. # define XCACHE_MAP_FAILED NULL
  14. # define munmap(p, s) UnmapViewOfFile(p)
  15. #else
  16. # include <unistd.h>
  17. /* make sure to mark(change) it to NULL to keep consistent */
  18. # define XCACHE_MAP_FAILED MAP_FAILED
  19. #endif
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <fcntl.h>
  23. #ifndef ZEND_WIN32
  24. #include <sys/mman.h>
  25. #endif
  26. #include "php.h"
  27. #define XC_SHM_IMPL _xc_mmap_shm_t
  28. #include "xc_shm.h"
  29. #include "xc_utils.h"
  30. #ifndef max
  31. #define max(a, b) ((a) < (b) ? (b) : (a))
  32. #endif
  33. /* {{{ xc_shm_t */
  34. struct _xc_mmap_shm_t {
  35. xc_shm_handlers_t *handlers;
  36. zend_bool disabled;
  37. void *ptr;
  38. void *ptr_ro;
  39. long diff;
  40. xc_shmsize_t size;
  41. xc_shmsize_t memoffset;
  42. char *name;
  43. #ifdef ZEND_WIN32
  44. HANDLE hmap;
  45. HANDLE hmap_ro;
  46. #else
  47. int newfile;
  48. #endif
  49. };
  50. /* }}} */
  51. #define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
  52. #define PTR_ADD(ptr, v) (((char *) (ptr)) + (v))
  53. #define PTR_SUB(ptr, v) (((char *) (ptr)) - (v))
  54. static XC_SHM_CAN_READONLY(xc_mmap_can_readonly) /* {{{ */
  55. {
  56. return shm->ptr_ro != NULL;
  57. }
  58. /* }}} */
  59. static XC_SHM_IS_READWRITE(xc_mmap_is_readwrite) /* {{{ */
  60. {
  61. return p >= shm->ptr && (char *)p < (char *)shm->ptr + shm->size;
  62. }
  63. /* }}} */
  64. static XC_SHM_IS_READONLY(xc_mmap_is_readonly) /* {{{ */
  65. {
  66. return xc_mmap_can_readonly(shm) && p >= shm->ptr_ro && (char *)p < (char *)shm->ptr_ro + shm->size;
  67. }
  68. /* }}} */
  69. static XC_SHM_TO_READWRITE(xc_mmap_to_readwrite) /* {{{ */
  70. {
  71. if (shm->diff) {
  72. assert(xc_mmap_is_readonly(shm, p));
  73. p = PTR_SUB(p, shm->diff);
  74. }
  75. assert(xc_mmap_is_readwrite(shm, p));
  76. return p;
  77. }
  78. /* }}} */
  79. static XC_SHM_TO_READONLY(xc_mmap_to_readonly) /* {{{ */
  80. {
  81. assert(xc_mmap_is_readwrite(shm, p));
  82. if (shm->diff) {
  83. p = PTR_ADD(p, shm->diff);
  84. assert(xc_mmap_is_readonly(shm, p));
  85. }
  86. return p;
  87. }
  88. /* }}} */
  89. static XC_SHM_DESTROY(xc_mmap_destroy) /* {{{ */
  90. {
  91. if (shm->ptr_ro) {
  92. munmap(shm->ptr_ro, shm->size);
  93. /*
  94. shm->ptr_ro = NULL;
  95. */
  96. }
  97. if (shm->ptr) {
  98. /* shm->size depends on shm->ptr */
  99. munmap(shm->ptr, shm->size);
  100. /*
  101. shm->ptr = NULL;
  102. */
  103. }
  104. #ifdef ZEND_WIN32
  105. if (shm->hmap) {
  106. CloseHandle(shm->hmap);
  107. }
  108. if (shm->hmap_ro) {
  109. CloseHandle(shm->hmap_ro);
  110. }
  111. #endif
  112. if (shm->name) {
  113. #ifndef ZEND_WIN32
  114. # ifdef __CYGWIN__
  115. if (shm->newfile) {
  116. unlink(shm->name);
  117. }
  118. # endif
  119. #endif
  120. free(shm->name);
  121. }
  122. /*
  123. shm->size = NULL;
  124. shm->diff = 0;
  125. */
  126. free(shm);
  127. return;
  128. }
  129. /* }}} */
  130. static XC_SHM_INIT(xc_mmap_init) /* {{{ */
  131. {
  132. #ifdef ZEND_WIN32
  133. # define TMP_PATH "XCache"
  134. #else
  135. # define TMP_PATH "/tmp/XCache"
  136. int fd = -1;
  137. #endif
  138. xc_shm_t *shm = NULL;
  139. int ro_ok;
  140. volatile void *romem;
  141. char tmpname[sizeof(TMP_PATH) - 1 + 100];
  142. const char *errstr = NULL;
  143. const char *path = (const char *) arg1;
  144. CHECK(shm = calloc(1, sizeof(xc_shm_t)), "shm OOM");
  145. shm->size = size;
  146. if (path == NULL || !path[0]) {
  147. static int inc = 0;
  148. snprintf(tmpname, sizeof(tmpname) - 1, "%s.%d.%d.%d.%d", TMP_PATH, (int) getuid(), (int) getpid(), inc ++, rand());
  149. path = tmpname;
  150. }
  151. #ifdef ZEND_WIN32
  152. else {
  153. static int inc2 = 0;
  154. snprintf(tmpname, sizeof(tmpname) - 1, "%s.%d.%d.%d.%d", path, (int) getuid(), (int) getpid(), inc2 ++, rand());
  155. path = tmpname;
  156. }
  157. #endif
  158. shm->name = strdup(path);
  159. #ifndef ZEND_WIN32
  160. # define XCACHE_MMAP_PERMISSION (S_IRUSR | S_IWUSR)
  161. fd = open(shm->name, O_RDWR, XCACHE_MMAP_PERMISSION);
  162. if (fd == -1) {
  163. /* do not create file in /dev */
  164. if (strncmp(shm->name, "/dev", 4) == 0) {
  165. perror(shm->name);
  166. errstr = "Cannot open file set by xcache.mmap_path, check the xcache.size/var_size against system limitation";
  167. goto err;
  168. }
  169. fd = open(shm->name, O_CREAT | O_RDWR, XCACHE_MMAP_PERMISSION);
  170. shm->newfile = 1;
  171. if (fd == -1) {
  172. perror(shm->name);
  173. errstr = "Cannot open or create file set by xcache.mmap_path, check the path permission or check xcache.size/var_size against system limitation";
  174. goto err;
  175. }
  176. }
  177. if (ftruncate(fd, size) != 0 && errno != EINVAL) {
  178. perror(shm->name);
  179. errstr = "Failed to ftruncate the file";
  180. goto err;
  181. }
  182. #endif
  183. #ifdef ZEND_WIN32
  184. shm->hmap = XCacheCreateFileMapping(size, PAGE_READWRITE, shm->name);
  185. shm->ptr = (LPSTR) MapViewOfFile(shm->hmap, FILE_MAP_WRITE, 0, 0, 0);
  186. #else
  187. shm->ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  188. #endif
  189. if (shm->ptr == XCACHE_MAP_FAILED) {
  190. perror(shm->name);
  191. errstr = "Failed creating file mapping";
  192. shm->ptr = NULL;
  193. goto err;
  194. }
  195. /* {{{ readonly protection, mmap it readonly and check if ptr_ro works */
  196. if (readonly_protection) {
  197. ro_ok = 0;
  198. #ifdef ZEND_WIN32
  199. shm->hmap_ro = XCacheCreateFileMapping(size, PAGE_READONLY, shm->name);
  200. shm->ptr_ro = (LPSTR) MapViewOfFile(shm->hmap_ro, FILE_MAP_READ, 0, 0, 0);
  201. #else
  202. shm->ptr_ro = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
  203. #endif
  204. if (shm->ptr_ro == XCACHE_MAP_FAILED) {
  205. shm->ptr_ro = NULL;
  206. }
  207. romem = shm->ptr_ro;
  208. do {
  209. if (romem == NULL || romem == shm->ptr) {
  210. break;
  211. }
  212. *(char *)shm->ptr = 1;
  213. if (*(char *)romem != 1) {
  214. break;
  215. }
  216. *(char *)shm->ptr = 2;
  217. if (*(char *)romem != 2) {
  218. break;
  219. }
  220. ro_ok = 1;
  221. } while (0);
  222. if (ro_ok) {
  223. shm->diff = PTR_SUB(shm->ptr_ro, (char *) shm->ptr);
  224. /* no overlap */
  225. assert(abs(shm->diff) >= size);
  226. }
  227. else {
  228. if (shm->ptr_ro) {
  229. munmap(shm->ptr_ro, size);
  230. }
  231. #ifdef ZEND_WIN32
  232. if (shm->hmap_ro) {
  233. CloseHandle(shm->hmap_ro);
  234. }
  235. #endif
  236. shm->ptr_ro = NULL;
  237. shm->diff = 0;
  238. }
  239. }
  240. /* }}} */
  241. #ifndef ZEND_WIN32
  242. close(fd);
  243. # ifndef __CYGWIN__
  244. if (shm->newfile) {
  245. unlink(shm->name);
  246. }
  247. # endif
  248. #endif
  249. return shm;
  250. err:
  251. #ifndef ZEND_WIN32
  252. if (fd != -1) {
  253. close(fd);
  254. }
  255. #endif
  256. if (shm) {
  257. xc_mmap_destroy(shm);
  258. }
  259. if (errstr) {
  260. fprintf(stderr, "%s\n", errstr);
  261. zend_error(E_ERROR, "%s", errstr);
  262. }
  263. return NULL;
  264. }
  265. /* }}} */
  266. static XC_SHM_MEMINIT(xc_mmap_meminit) /* {{{ */
  267. {
  268. xc_mem_t *mem;
  269. if (shm->memoffset + size > shm->size) {
  270. zend_error(E_ERROR, "XCache: internal error at %s#%d", __FILE__, __LINE__);
  271. return NULL;
  272. }
  273. mem = (xc_mem_t *) PTR_ADD(shm->ptr, shm->memoffset);
  274. shm->memoffset += size;
  275. mem->handlers = shm->handlers->memhandlers;
  276. mem->handlers->init(shm, mem, size);
  277. return mem;
  278. }
  279. /* }}} */
  280. static XC_SHM_MEMDESTROY(xc_mmap_memdestroy) /* {{{ */
  281. {
  282. }
  283. /* }}} */
  284. static xc_shm_handlers_t xc_shm_mmap_handlers = XC_SHM_HANDLERS(mmap);
  285. void xc_shm_mmap_register() /* {{{ */
  286. {
  287. CHECK(xc_shm_mmap_handlers.memhandlers = xc_mem_scheme_find("mem"), "cannot find mem handlers");
  288. if (xc_shm_scheme_register("mmap", &xc_shm_mmap_handlers) == 0) {
  289. zend_error(E_ERROR, "XCache: failed to register mmap shm_scheme");
  290. }
  291. err:
  292. return;
  293. }
  294. /* }}} */