As explained in the Shared Memory Applications page, eXtremeDB allows two or more processes to share a common database. General instructions are given there explaining how shared memory segments must be created on different operating systems. Further implementation details for C applications are provided in the following sections.
Important note on multi-process shared memory applications
In multi-process database applications using shared memory it is necessary to call
mco_runtime_start()first just by a single process (the primary process). This allows the eXtremeDB runtime to correctly create and initialize the database registry. After the primary process initializes, all other (secondary) processes can callmco_runtime_start()simultaneously.Primary Process Example
The following code snippet demonstrates how a “primary” process might determine that the correct shared-memory library is linked, then initialize memory devices and open the database in shared-memory:
MCO_RET open_shared_db( const char * db_name, /* name of the database */ mco_dictionary_h dict, /* pointer to schema */ mco_size_t db_sz, /* size of memory segment for in-mem part * of the db */ uint2 mem_pg_sz, /* size of memory page */ uint2 max_conn_no /* max. number of connections */ ) { mco_runtime_info_t info; mco_db_params_t db_params; mco_device_t dev; /* get runtime info */ mco_get_runtime_info(&info); if (info.mco_shm_supported) { /* set the device as a shared named memory device */ dev.type = MCO_MEMORY_NAMED; sprintf( dev.named.name, "%s-db", db_name ); /* set memory name */ dev.named.flags = 0; /* zero flags */ dev.named.hint = 0; /* set mapping address or null it */ } else { /* Wrong libraries for shared-memory */ return MCO_E_UNSUPPORTED; } dev.assignment = MCO_MEMORY_ASSIGN_DATABASE; /* main database memory */ dev.size = db_sz; /* set the device size */ /* initialize and customize the database parameters */ mco_db_params_init ( &db_params ); /* initialize the params with default values */ db_params.mem_page_size = mem_pg_sz; db_params.disk_page_size = 0; db_params.db_max_connections = max_conn_no; /* open a database on the device with given params */ return mco_db_open_dev(db_name, dict, dev, 1, &db_params ); } void StartDB() { MCO_RET rc; mco_db_h db; void* start_mem = 0; /* set fatal error handler * mco_error_set_handler( &errhandler ); mco_runtime_start(); rc = open_shared_db( dbname, DemoShm1_get_dictionary(), SEGSZ, PAGESIZE, MAX_CONNECTIONS ); if ( MCO_S_OK == rc ) { rc = mco_db_connect( dbname, &db ); /* normal processing goes here */ ... rc = mco_db_disconnect( db ); rc = mco_db_close( dbname ); } mco_runtime_stop(); }Note that it is important to set the shared memory
flagsandhintfields to zero or a valid value in themco_device_tstructure passed tomco_db_open_dev()as done in the example above:dev.named.flags = 0; /* zero flags */ dev.named.hint = 0; /* set mapping address or null it */Secondary Process Example
Once the shared memory for the database has been set up by
open_shared_db()in the primary process, subsequent processes will simply connect to the database as demonstrated below:void DbAttach() { MCO_RET rc; mco_db_h db; /* set fatal error handler */ mco_error_set_handler( &errhandler ); mco_runtime_start(); /* connect to a database by name "dbname" */ rc = mco_db_connect( dbname, &db ); if ( MCO_S_OK != rc ) { printf("\n Could not attach to instance: %d\n", rc); exit( 1 ); } /* normal database processing goes here */ ... rc = mco_db_disconnect( db ); rc = mco_db_close( dbname ); mco_runtime_stop(); }See sample 03-connect_multiprocess for a complete example of a shared memory application.
Shared Memory Runtime Options
The eXtremeDB runtime can be informed of shared memory configuration through runtime options. The shared memory options are OS specific. To set runtime options that application may call the
mco_runtime_setoption()beforemco_runtime_start(). For example:mco_runtime_setoption(MCO_RT_OPTION_UNIX_SHM_MASK, 0600 ); rc = mco_runtime_start(); ...Applications can determine at run-time whether or not the correct eXtremeDB runtime libraries are linked to support shared memory by examining the output of the
mco_get_runtime_info()function.For Windows applications
There are two groups of runtime options: one for determining the scope of the shared memory block name (local or global), and one for determining the security level applied to the shared memory block. The combination of options are applied to the name specified in the shared (named) memory block device.
For example, the following code snippet creates a shared memory block with the name “shared-db”:
if (info.mco_shm_supported) { /* set the device as a shared named memory device */ dev.type = MCO_MEMORY_NAMED; sprintf( dev.named.name, "%s-db", db_name ); /* set memory name */ dev.named.flags = 0; /* zero flags */ dev.named.hint = 0; /* set mapping address or null it */ }The default runtime options are as follows:
MCO_RT_WINDOWS_SHM_PREFIX_NONE | MCO_RT_WINDOWS_SHM_SEC_DESCR_NULLThis combination makes the database visible and accessible from only local sessions. To make the database accessible from all sessions of the same user, for example a service and a desktop program running under the same user, the following runtime options are necessary:
MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL | MCO_RT_WINDOWS_SHM_SEC_DESCR_SAMEUSERTo setup the shared memory block with these options the following call must be made prior to calling mco_runtime_start():
mco_runtime_setoption( MCO_RT_WINDOWS_SHM_OPT, MCO_RT_WINDOWS_SHM_PREFIX_GLOBAL | MCO_RT_WINDOWS_SHM_SEC_DESCR_SAMEUSER ); ... rc = mco_runtime_start(); ...Please note that the value
MCO_RT_WINDOWS_SHM_PREFIX_GLOBALis applicable for Services only; desktops sessions are able to create databases using modes
MCO_RT_WINDOWS_SHM_PREFIX_LOCALorMCO_RT_WINDOWS_SHM_PREFIX_NONE; and they can connect to databases opened withMCO_RT_WINDOWS_SHM_PREFIX_GLOBALby using matching security settings.For Linux applications
Linux shared memory applications use the POSIX function
mmap()to provide mapping of virtual shared memory to a current process when the eXtremeDB librarylibmcompsxis used. The optionMCO_RT_POSIX_SHM_ANONYMOUSenables the optionMAP_ANOUNYMOUSof functionmmap(). The optionMCO_RT_POSIX_SHM_SHAREDspecifies optionMAP_SHAREDof functionmmap(), otherwiseMAP_PRIVATEis used. The default combination of options isMCO_RT_POSIX_SHM_ANONYMOUS | MCO_RT_POSIX_SHM_SHARED.To specify different options for the shared memory block call
mco_runtime_setoption()prior to callingmco_runtime_start(). For example to specifymmap()optionMAP_PRIVATE:mco_runtime_setoption( MCO_RT_POSIX_SHM_OPT, MCO_RT_POSIX_SHM_ANONYMOUS ); ... rc = mco_runtime_start(); ...Note that the Linux AIO library
mcfu98aiois not designed for shared memory between processes. It may be used for multi-threaded access but not for multi-process sharing of a database.For Unix applications
On Unix systems the shared memory access mode is specified by the Unix specific value of file system access rights. Shared memory and semaphores have the same system of permissions as ordinary files to restrict access by processes of different users and groups.
The default value is
0666forMCO_RT_OPTION_UNIX_SHM_MASKmeans thatuser,groupandotherscan read and write this memory or semaphore. A value of0600means that onlyusercan access (read and write) this memory or semaphore. To change the default to0600the following call must be made prior to callingmco_runtime_start():mco_runtime_setoption(MCO_RT_OPTION_UNIX_SHM_MASK, 0600); ... rc = mco_runtime_start(); ...
Shared memory and the Direct Pointers library
When using the eXtremeDB Direct Pointer Arithmetic library (DPTR) it is necessary to map the shared memory segment to the same virtual memory address in every process because in the DPTR implementation eXtremeDB uses actual memory addresses (i.e. it performs pointer arithmetic) to calculate the locations of objects in an eXtremeDB database. The pointers must be the same in every running instance of an eXtremeDB-based application, or pointer arithmetic just doesn’t work. Setting the
hintparameter to zero causes eXtremeDB to determine the actual shared memory segment address. But this could fail when called from a second process attempting to open the shared database. In this case it is the applications responsibility to provide a validhintaddress.There are several ways to determine where the runtime should map the shared memory database. Developers can use a utility provided by the operating system to gather memory usage information (the process memory map). These utilities will usually display the code, data and stack memory usage of each process running on the system and the libraries it is using. It is necessary to examine the output and pick an address outside any address space.
In any event, the memory address is an address in each process’s virtual memory. An address should be chosen that is sufficiently far away from the data, stack and heap segment of every process that will share the database. (Hence the default of
0x20000000forMAP_ADDRESS; it is assumed unlikely, but not guaranteed, that any single process will have data, stack and heap that stretch out to0x20000000bytes.)The above potential issues with respect to the
MAP_ADDRESScan be avoided by using theOffsetlibrary instead of the Direct Pointer Arithmetic library. TheOffsetapproach calculates an offset from the beginning address of the in-memory database, to locate objects. Therefore, it does not depend on the in-memory database starting at a common (and known) location for all processes. However, the DPTR pointer arithmetic is about 5%–15% faster than calculating offsets.