Simple API to read and write on shared memory segments with Java on Linux.
Note1: So far, it runs only on <3 Linux <3
Note2: Yes, there are plans to add support to Windows and OSx
It is easy to read or write on shared memory.
For example, you can use the following code to write:
import java.nio.ByteBuffer;
import com.improvess.shared.connector.Connector;
public class Example {
public static void main(String[] args) {
Connector connector = new Connector();
int shm_id = connector.initialize_shared_buffer(0xd7a6, 8);
ByteBuffer buffer = connector.get_shared_buffer(shm_id);
// Writing 8 bytes on shared segment
for (int i = 0; i < 8; ++i) {
buffer.put(i, (byte) i);
}
}
}and use another process to read the values.
For example, you can use this C++ program to read the values you just wrote:
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
int main(int, char **)
{
int shmid = shmget(0xd7a6, 8, 0666 | IPC_CREAT);
if (shmid == -1) {
std::cerr << "Failed to open shared memory segment: " << std::strerror(errno) << "\n";
}
char *buffer = (char *) shmat(shmid, 0, 0);
for (int b = 0; b < 8; ++b) {
std::cout << (int)buffer[b] << "\n";
}
return 0;
}With this API, you can also use semaphores to avoid inter processing concurrence issues:
import java.nio.ByteBuffer;
import com.improvess.shared.connector.Connector;
Connector connector = new Connector();
int shm_id = connector.initialize_shared_buffer(0xd7a6, 8);
ByteBuffer buffer = connector.get_shared_buffer(shm_id);
// initializing and locking semaphore
int sem_id = connector.initialize_shared_semaphore(sem_key, 1, Connector.IPC_CREAT | 0666);
connector.semaphoro_lock(sem_id);
// Writing 8 bytes on shared segment as in the previous example
for (int i = 0; i < 8; ++i) {
buffer.put(i, (byte) i);
}
// unlocking and releasing semaphore
connector.semaphoro_unlock(sem_id);
connector.release_shared_semaphore(sem_id);First, set JAVA_HOME environment variable:
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/Replacing /usr/lib/jvm/java-11-openjdk-amd64/ accordingly with your settings.
Clone the repo:
git clone https://github.com/improvess/java-shared-memory.gitThen, call Maven:
cd java-shared-memory
mvn clean packageThis library uses JNI. The native shared library libjava_shared_memory_lib.so is automatically shipped into generated jar.
The maven build also copy the shared library into the target folder. Thus, there is two ways to load the shared library:
- You can copy it somewhere and use
-Djava.library.path=your-folderto load the libray or - You can use
com.improvess.shared.connector.LoadLib.load()method to automatically load the library
cd java-shared-memory
java -Djava.library.path=target -cp target /classescom.improvess.shared.examples.CheckConnector 2This command should print:
Checking Connector:
Hello from connector!
Connector say hello!
Connector say hello again!java -Djava.library.path=target -cp target /classescom.improvess.shared.examples.ConcurrentAccessWithLockIt outpus something like:
Checking Concurrent access using no locking:
decrementTask get -26
incrementTask get 1
EndThe values may vary due to the randomization of concurrent processing.
java -Djava.library.path=target -cp target /classescom.improvess.shared.examples.ConcurrentAccessWithLockIt should outputs:
No locking:
Checking Concurrent access using semaphores:
- incrementTask: Semaphore created
- incrementTask: Semaphore locked
decrementTask: Semaphore created
- incrementTask: get 100
- incrementTask: Semaphore unlocked
- incrementTask: Semaphore released
decrementTask: Semaphore locked
decrementTask: get 0
decrementTask: Semaphore unlocked
decrementTask: Semaphore released
EndOr, depending on which thread starts first:
Checking Concurrent access using semaphores:
decrementTask: Semaphore created
- incrementTask: Semaphore created
decrementTask: Semaphore locked
decrementTask: get -100
decrementTask: Semaphore unlocked
decrementTask: Semaphore released
- incrementTask: Semaphore locked
- incrementTask: get 0
- incrementTask: Semaphore unlocked
- incrementTask: Semaphore released
EndIn the previous examples, we used -Djava.library.path=target to indicate which folder to look for the shared library.
In the next example, we load the library through java code:
java -cp target /classescom.improvess.shared.examples.LoadingLibraryThis example works by calling:
static {
try {
LoadLib.load();
} catch (IOException e) {
e.printStackTrace();
}
}which automatically loads the shared library.
If you get an error like:
jni/java_shared_memory.cpp:13:10: fatal error: jni.h: No such file or directory
13 | #include <jni.h>
| ^~~~~~~
compilation terminated.
cp: cannot stat 'src/main/resources/libjava_shared_memory_lib.so': No such file or directory
[ERROR] Command execution failed.
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
Make sure if JAVA_HOME points to a JDK installation, not only a JRE installation.