00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: os__map_8c-source.html,v 1.1 2008年06月08日 10:21:18 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 #ifdef HAVE_MMAP 00017 #include <sys/mman.h> 00018 #endif 00019 00020 #ifdef HAVE_SHMGET 00021 #include <sys/ipc.h> 00022 #include <sys/shm.h> 00023 #endif 00024 00025 #include <errno.h> 00026 #include <string.h> 00027 #endif 00028 00029 #include "db_int.h" 00030 #include "db_page.h" 00031 #include "db_ext.h" 00032 #include "os_jump.h" 00033 00034 #ifdef HAVE_MMAP 00035 static int __os_map __P((DB_ENV *, char *, DB_FH *, size_t, int, int, void **)); 00036 #endif 00037 #ifndef HAVE_SHMGET 00038 static int __db_nosystemmem __P((DB_ENV *)); 00039 #endif 00040 00041 /* 00042 * CDB___os_r_sysattach -- 00043 * Create/join a shared memory region. 00044 * 00045 * PUBLIC: int CDB___os_r_sysattach __P((DB_ENV *, REGINFO *, REGION *)); 00046 */ 00047 int 00048 CDB___os_r_sysattach(dbenv, infop, rp) 00049 DB_ENV *dbenv; 00050 REGINFO *infop; 00051 REGION *rp; 00052 { 00053 if (F_ISSET(dbenv, DB_ENV_SYSTEM_MEM)) { 00054 /* 00055 * If the region is in system memory on UNIX, we use shmget(2). 00056 * 00057 * !!! 00058 * There exist spinlocks that don't work in shmget memory, e.g., 00059 * the HP/UX msemaphore interface. If we don't have locks that 00060 * will work in shmget memory, we better be private and not be 00061 * threaded. If we reach this point, we know we're public, so 00062 * it's an error. 00063 */ 00064 #if defined(MUTEX_NO_SHMGET_LOCKS) 00065 CDB___db_err(dbenv, 00066 "architecture does not support locks inside system shared memory"); 00067 return (EINVAL); 00068 #endif 00069 #if defined(HAVE_SHMGET) 00070 { 00071 key_t segid; 00072 int id, ret; 00073 00074 /* 00075 * We require that the application provide use with a base 00076 * System V IPC key value. 00077 */ 00078 if (dbenv->shm_key == INVALID_REGION_SEGID) { 00079 CDB___db_err(dbenv, 00080 "no base system shared memory ID specified"); 00081 return (EINVAL); 00082 } 00083 segid = (key_t)dbenv->shm_key; 00084 00085 /* 00086 * We could potentially create based on REGION_CREATE_OK, but 00087 * that's dangerous -- we might get crammed in sideways if 00088 * some of the expected regions exist but others do not. Also, 00089 * if the requested size differs from an existing region's 00090 * actual size, then all sorts of nasty things can happen. 00091 * Basing create solely on REGION_CREATE is much safer -- a 00092 * recovery will get us straightened out. 00093 */ 00094 if (F_ISSET(infop, REGION_CREATE)) { 00095 /* 00096 * If map to an existing region, assume the application 00097 * crashed and we're restarting. Delete the old region 00098 * and re-try. If that fails, return an error, the 00099 * application will have to select a different segment 00100 * ID or clean up some other way. 00101 */ 00102 if ((id = shmget(segid, 0, 0)) != -1) { 00103 (void)shmctl(id, IPC_RMID, NULL); 00104 if ((id = shmget(segid, 0, 0)) != -1) { 00105 CDB___db_err(dbenv, 00106 "shmget: key: %ld: shared system memory region already exists", 00107 (long)segid); 00108 return (EAGAIN); 00109 } 00110 } 00111 if ((rp->segid = 00112 shmget(segid, rp->size, IPC_CREAT | 0600)) == -1) { 00113 ret = CDB___os_get_errno(); 00114 CDB___db_err(dbenv, 00115 "shmget: key: %ld: unable to create shared system memory region: %s", 00116 (long)segid, strerror(ret)); 00117 return (ret); 00118 } 00119 00120 /* 00121 * Increment the base segment value to identify a new 00122 * segment. 00123 */ 00124 ++dbenv->shm_key; 00125 } 00126 00127 if ((infop->addr = shmat(rp->segid, NULL, 0)) == (void *)-1) { 00128 infop->addr = NULL; 00129 ret = CDB___os_get_errno(); 00130 CDB___db_err(dbenv, 00131 "shmat: id %ld: unable to attach to shared system memory region: %s", 00132 rp->segid, strerror(ret)); 00133 return (ret); 00134 } 00135 00136 return (0); 00137 } 00138 #else 00139 return (__db_nosystemmem(dbenv)); 00140 #endif 00141 } 00142 00143 #ifdef HAVE_MMAP 00144 { 00145 DB_FH fh; 00146 int ret; 00147 00148 /* 00149 * Try to open/create the file. We DO NOT need to ensure that multiple 00150 * threads/processes attempting to simultaneously create the region are 00151 * properly ordered, our caller has already taken care of that. 00152 */ 00153 if ((ret = CDB___os_open(dbenv, infop->name, 00154 F_ISSET(infop, REGION_CREATE_OK) ? DB_OSO_CREATE: 0, 00155 infop->mode, &fh)) != 0) 00156 CDB___db_err(dbenv, "%s: %s", infop->name, CDB_db_strerror(ret)); 00157 00158 /* 00159 * If we created the file, grow it to its full size before mapping 00160 * it in. We really want to avoid touching the buffer cache after 00161 * mmap(2) is called, doing anything else confuses the hell out of 00162 * systems without merged VM/buffer cache systems, or, more to the 00163 * point, *badly* merged VM/buffer cache systems. 00164 */ 00165 if (ret == 0 && F_ISSET(infop, REGION_CREATE)) 00166 ret = CDB___os_finit(dbenv, 00167 &fh, rp->size, DB_GLOBAL(db_region_init)); 00168 00169 /* Map the file in. */ 00170 if (ret == 0) 00171 ret = __os_map(dbenv, 00172 infop->name, &fh, rp->size, 1, 0, &infop->addr); 00173 00174 (void)CDB___os_closehandle(&fh); 00175 00176 return (ret); 00177 } 00178 #else 00179 COMPQUIET(infop, NULL); 00180 COMPQUIET(rp, NULL); 00181 CDB___db_err(dbenv, 00182 "architecture lacks mmap(2), shared environments not possible"); 00183 return (CDB___db_eopnotsup(dbenv)); 00184 #endif 00185 } 00186 00187 /* 00188 * CDB___os_r_sysdetach -- 00189 * Detach from a shared memory region. 00190 * 00191 * PUBLIC: int CDB___os_r_sysdetach __P((DB_ENV *, REGINFO *, int)); 00192 */ 00193 int 00194 CDB___os_r_sysdetach(dbenv, infop, destroy) 00195 DB_ENV *dbenv; 00196 REGINFO *infop; 00197 int destroy; 00198 { 00199 REGION *rp; 00200 00201 rp = infop->rp; 00202 00203 if (F_ISSET(dbenv, DB_ENV_SYSTEM_MEM)) { 00204 #ifdef HAVE_SHMGET 00205 int ret, segid; 00206 00207 /* 00208 * We may be about to remove the memory referenced by rp, 00209 * save the segment ID, and (optionally) wipe the original. 00210 */ 00211 segid = rp->segid; 00212 if (destroy) 00213 rp->segid = INVALID_REGION_SEGID; 00214 00215 if (shmdt(infop->addr) != 0) { 00216 ret = CDB___os_get_errno(); 00217 CDB___db_err(dbenv, "shmdt: %s", strerror(ret)); 00218 return (ret); 00219 } 00220 00221 if (destroy && shmctl(segid, IPC_RMID, 00222 NULL) != 0 && (ret = CDB___os_get_errno()) != EINVAL) { 00223 CDB___db_err(dbenv, 00224 "shmctl: id %ld: unable to delete system shared memory region: %s", 00225 segid, strerror(ret)); 00226 return (ret); 00227 } 00228 00229 return (0); 00230 #else 00231 return (__db_nosystemmem(dbenv)); 00232 #endif 00233 } 00234 00235 #ifdef HAVE_MMAP 00236 #ifdef HAVE_MUNLOCK 00237 if (F_ISSET(dbenv, DB_ENV_LOCKDOWN)) 00238 (void)munlock(infop->addr, rp->size); 00239 #endif 00240 if (munmap(infop->addr, rp->size) != 0) { 00241 int ret; 00242 00243 ret = CDB___os_get_errno(); 00244 CDB___db_err(dbenv, "munmap: %s", strerror(ret)); 00245 return (ret); 00246 } 00247 00248 if (destroy && CDB___os_unlink(dbenv, infop->name) != 0) 00249 return (CDB___os_get_errno()); 00250 00251 return (0); 00252 #else 00253 COMPQUIET(destroy, 0); 00254 return (EINVAL); 00255 #endif 00256 } 00257 00258 /* 00259 * CDB___os_mapfile -- 00260 * Map in a shared memory file. 00261 * 00262 * PUBLIC: int CDB___os_mapfile __P((DB_ENV *, 00263 * PUBLIC: char *, DB_FH *, size_t, int, void **)); 00264 */ 00265 int 00266 CDB___os_mapfile(dbenv, path, fhp, len, is_rdonly, addrp) 00267 DB_ENV *dbenv; 00268 char *path; 00269 DB_FH *fhp; 00270 int is_rdonly; 00271 size_t len; 00272 void **addrp; 00273 { 00274 #ifdef HAVE_MMAP 00275 return (__os_map(dbenv, path, fhp, len, 0, is_rdonly, addrp)); 00276 #else 00277 COMPQUIET(dbenv, NULL); 00278 COMPQUIET(path, NULL); 00279 COMPQUIET(fhp, NULL); 00280 COMPQUIET(is_rdonly, 0); 00281 COMPQUIET(len, 0); 00282 COMPQUIET(addrp, NULL); 00283 return (EINVAL); 00284 #endif 00285 } 00286 00287 /* 00288 * CDB___os_unmapfile -- 00289 * Unmap the shared memory file. 00290 * 00291 * PUBLIC: int CDB___os_unmapfile __P((DB_ENV *, void *, size_t)); 00292 */ 00293 int 00294 CDB___os_unmapfile(dbenv, addr, len) 00295 DB_ENV *dbenv; 00296 void *addr; 00297 size_t len; 00298 { 00299 /* If the user replaced the map call, call through their interface. */ 00300 if (CDB___db_jump.j_unmap != NULL) 00301 return (CDB___db_jump.j_unmap(addr, len)); 00302 00303 #ifdef HAVE_MMAP 00304 #ifdef HAVE_MUNLOCK 00305 if (F_ISSET(dbenv, DB_ENV_LOCKDOWN)) 00306 (void)munlock(addr, len); 00307 #else 00308 COMPQUIET(dbenv, NULL); 00309 #endif 00310 return (munmap(addr, len) ? CDB___os_get_errno() : 0); 00311 #else 00312 COMPQUIET(dbenv, NULL); 00313 00314 return (EINVAL); 00315 #endif 00316 } 00317 00318 #ifdef HAVE_MMAP 00319 /* 00320 * __os_map -- 00321 * Call the mmap(2) function. 00322 */ 00323 static int 00324 __os_map(dbenv, path, fhp, len, is_region, is_rdonly, addrp) 00325 DB_ENV *dbenv; 00326 char *path; 00327 DB_FH *fhp; 00328 int is_region, is_rdonly; 00329 size_t len; 00330 void **addrp; 00331 { 00332 void *p; 00333 int flags, prot, ret; 00334 00335 /* If the user replaced the map call, call through their interface. */ 00336 if (CDB___db_jump.j_map != NULL) 00337 return (CDB___db_jump.j_map 00338 (path, len, is_region, is_rdonly, addrp)); 00339 00340 /* 00341 * If it's read-only, it's private, and if it's not, it's shared. 00342 * Don't bother with an additional parameter. 00343 */ 00344 flags = is_rdonly ? MAP_PRIVATE : MAP_SHARED; 00345 00346 #ifdef MAP_FILE 00347 /* 00348 * Historically, MAP_FILE was required for mapping regular files, 00349 * even though it was the default. Some systems have it, some 00350 * don't, some that have it set it to 0. 00351 */ 00352 flags |= MAP_FILE; 00353 #endif 00354 00355 /* 00356 * I know of no systems that implement the flag to tell the system 00357 * that the region contains semaphores, but it's not an unreasonable 00358 * thing to do, and has been part of the design since forever. I 00359 * don't think anyone will object, but don't set it for read-only 00360 * files, it doesn't make sense. 00361 */ 00362 #ifdef MAP_HASSEMAPHORE 00363 if (is_region && !is_rdonly) 00364 flags |= MAP_HASSEMAPHORE; 00365 #else 00366 COMPQUIET(is_region, 0); 00367 #endif 00368 00369 prot = PROT_READ | (is_rdonly ? 0 : PROT_WRITE); 00370 00371 /* 00372 * XXX 00373 * Work around a bug in the VMS V7.1 mmap() implementation. To map 00374 * a file into memory on VMS it needs to be opened in a certain way, 00375 * originally. To get the file opened in that certain way, the VMS 00376 * mmap() closes the file and re-opens it. When it does this, it 00377 * doesn't flush any caches out to disk before closing. The problem 00378 * this causes us is that when the memory cache doesn't get written 00379 * out, the file isn't big enough to match the memory chunk and the 00380 * mmap() call fails. This call to fsync() fixes the problem. DEC 00381 * thinks this isn't a bug because of language in XPG5 discussing user 00382 * responsibility for on-disk and in-memory synchronization. 00383 */ 00384 #ifdef VMS 00385 if (CDB___os_fsync(dbenv, fhp) == -1) 00386 return(CDB___os_get_errno()); 00387 #endif 00388 00389 /* MAP_FAILED was not defined in early mmap implementations. */ 00390 #ifndef MAP_FAILED 00391 #define MAP_FAILED -1 00392 #endif 00393 if ((p = mmap(NULL, 00394 len, prot, flags, fhp->fd, (off_t)0)) == (void *)MAP_FAILED) { 00395 ret = CDB___os_get_errno(); 00396 CDB___db_err(dbenv, "mmap: %s", strerror(ret)); 00397 return (ret); 00398 } 00399 00400 #ifdef HAVE_MLOCK 00401 /* 00402 * If it's a region, we want to make sure that the memory isn't paged. 00403 * For example, Solaris will page large mpools because it thinks that 00404 * I/O buffer memory is more important than we are. The mlock system 00405 * call may or may not succeed (mlock is restricted to the super-user 00406 * on some systems). Currently, the only other use of mmap in DB is 00407 * to map read-only databases -- we don't want them paged, either, so 00408 * the call isn't conditional. 00409 */ 00410 if (F_ISSET(dbenv, DB_ENV_LOCKDOWN) && mlock(p, len) != 0) { 00411 ret = CDB___os_get_errno(); 00412 (void)munmap(p, len); 00413 CDB___db_err(dbenv, "mlock: %s", strerror(ret)); 00414 return (ret); 00415 } 00416 #else 00417 COMPQUIET(dbenv, NULL); 00418 #endif 00419 00420 *addrp = p; 00421 return (0); 00422 } 00423 #endif 00424 00425 #ifndef HAVE_SHMGET 00426 /* 00427 * __db_nosystemmem -- 00428 * No system memory environments error message. 00429 */ 00430 static int 00431 __db_nosystemmem(dbenv) 00432 DB_ENV *dbenv; 00433 { 00434 CDB___db_err(dbenv, 00435 "architecture doesn't support environments in system memory"); 00436 return (CDB___db_eopnotsup(dbenv)); 00437 } 00438 #endif