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.

318 lines
6.6 KiB

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