Skip to content

Commit 1c85872

Browse files
committed
Add a basic IPC shared memory example
Add a basic IPC shared memory (SHM) example using only the OS memory provider API (not using the API from ipc.h). Signed-off-by: Lukasz Dorau <[email protected]>
1 parent 9ae6b23 commit 1c85872

File tree

5 files changed

+515
-2
lines changed

5 files changed

+515
-2
lines changed

CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,13 @@ install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT
437437
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}/")
438438

439439
install(
440-
FILES examples/basic/gpu_shared_memory.c examples/basic/utils_level_zero.h
441-
examples/basic/basic.c examples/basic/ipc_level_zero.c
440+
FILES examples/basic/gpu_shared_memory.c
441+
examples/basic/utils_level_zero.h
442+
examples/basic/basic.c
443+
examples/basic/ipc_level_zero.c
444+
examples/basic/ipc_shm_osmemprov.sh
445+
examples/basic/ipc_shm_osmemprov_consumer.c
446+
examples/basic/ipc_shm_osmemprov_producer.c
442447
DESTINATION "${CMAKE_INSTALL_DOCDIR}/examples")
443448

444449
# Add the include directory and the headers target to the install.

examples/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,36 @@ else()
108108
"UMF_BUILD_LEVEL_ZERO_PROVIDER, UMF_BUILD_LIBUMF_POOL_DISJOINT and "
109109
"UMF_ENABLE_POOL_TRACKING to be turned ON - skipping")
110110
endif()
111+
112+
if(LINUX)
113+
set(BASE_NAME ipc_shm_osmemprov)
114+
set(EXAMPLE_NAME umf_example_${BASE_NAME})
115+
116+
foreach(loop_var IN ITEMS "producer" "consumer")
117+
set(EX_NAME ${EXAMPLE_NAME}_${loop_var})
118+
add_umf_executable(
119+
NAME ${EX_NAME}
120+
SRCS basic/${BASE_NAME}_${loop_var}.c
121+
LIBS umf)
122+
123+
target_include_directories(
124+
${EX_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/src/utils
125+
${UMF_CMAKE_SOURCE_DIR}/include)
126+
127+
target_link_directories(${EX_NAME} PRIVATE ${LIBHWLOC_LIBRARY_DIRS})
128+
endforeach(loop_var)
129+
130+
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/basic/${BASE_NAME}.sh
131+
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
132+
133+
add_test(
134+
NAME ${EXAMPLE_NAME}
135+
COMMAND ${BASE_NAME}.sh
136+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
137+
138+
set_tests_properties(${EXAMPLE_NAME} PROPERTIES LABELS "example")
139+
else()
140+
message(
141+
STATUS "IPC shared memory example is supported on Linux only - skipping"
142+
)
143+
endif()

examples/basic/ipc_shm_osmemprov.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#
2+
# Copyright (C) 2024 Intel Corporation
3+
#
4+
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
#
7+
8+
#!/bin/bash
9+
10+
# port should be a number from the range <1024, 65535>
11+
PORT=$(( 1024 + ( $$ % ( 65535 - 1024 ))))
12+
13+
# The ipc_shm_osmemprov example requires using pidfd_getfd(2)
14+
# to obtain a duplicate of another process's file descriptor.
15+
# Permission to duplicate another process's file descriptor
16+
# is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2))
17+
# that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface.
18+
PTRACE_SCOPE_FILE="/proc/sys/kernel/yama/ptrace_scope"
19+
VAL=0
20+
if [ -f $PTRACE_SCOPE_FILE ]; then
21+
PTRACE_SCOPE_VAL=$(cat $PTRACE_SCOPE_FILE)
22+
if [ $PTRACE_SCOPE_VAL -ne $VAL ]; then
23+
echo "Setting ptrace_scope to 0 (classic ptrace permissions) ..."
24+
echo "$ sudo bash -c \"echo $VAL > $PTRACE_SCOPE_FILE\""
25+
sudo bash -c "echo $VAL > $PTRACE_SCOPE_FILE"
26+
fi
27+
PTRACE_SCOPE_VAL=$(cat $PTRACE_SCOPE_FILE)
28+
if [ $PTRACE_SCOPE_VAL -ne $VAL ]; then
29+
echo "SKIP: setting ptrace_scope to 0 (classic ptrace permissions) FAILED - skipping the test"
30+
exit 0
31+
fi
32+
fi
33+
34+
echo "Starting ipc_shm_osmemprov CONSUMER on port $PORT ..."
35+
./umf_example_ipc_shm_osmemprov_consumer $PORT &
36+
37+
echo "Waiting 1 sec ..."
38+
sleep 1
39+
40+
echo "Starting ipc_shm_osmemprov PRODUCER on port $PORT ..."
41+
./umf_example_ipc_shm_osmemprov_producer $PORT
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
*/
7+
8+
#include <arpa/inet.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/socket.h>
13+
#include <unistd.h>
14+
15+
#include <umf/providers/provider_os_memory.h>
16+
17+
#define INET_ADDR "127.0.0.1"
18+
#define MSG_SIZE 256
19+
#define RECV_BUFF_SIZE 1024
20+
21+
// consumer's response message
22+
#define CONSUMER_MSG \
23+
"This is the consumer. I just wrote a new number directly into your " \
24+
"shared memory!"
25+
26+
int main(int argc, char *argv[]) {
27+
struct sockaddr_in consumer_addr;
28+
struct sockaddr_in producer_addr;
29+
char consumer_message[MSG_SIZE];
30+
char recv_buffer[RECV_BUFF_SIZE];
31+
int producer_socket;
32+
int producer_addr_len;
33+
int consumer_socket;
34+
int ret = -1;
35+
36+
if (argc < 2) {
37+
fprintf(stderr, "usage: %s port\n", argv[0]);
38+
return -1;
39+
}
40+
41+
int port = atoi(argv[1]);
42+
43+
umf_memory_provider_handle_t OS_memory_provider = NULL;
44+
umf_os_memory_provider_params_t os_params;
45+
enum umf_result_t umf_result;
46+
47+
os_params = umfOsMemoryProviderParamsDefault();
48+
os_params.flag = UMF_MEM_MAP_SHARED;
49+
50+
// create OS memory provider
51+
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
52+
&OS_memory_provider);
53+
if (umf_result != UMF_RESULT_SUCCESS) {
54+
fprintf(stderr,
55+
"[consumer] ERROR: creating OS memory provider failed\n");
56+
return -1;
57+
}
58+
59+
// get the size of the IPC handle
60+
size_t IPC_handle_size;
61+
umf_result =
62+
umfMemoryProviderGetIPCHandleSize(OS_memory_provider, &IPC_handle_size);
63+
if (umf_result != UMF_RESULT_SUCCESS) {
64+
fprintf(stderr,
65+
"[consumer] ERROR: getting size of the IPC handle failed\n");
66+
goto err_umfMemoryProviderDestroy;
67+
}
68+
69+
// create a socket
70+
consumer_socket = socket(AF_INET, SOCK_STREAM, 0);
71+
if (consumer_socket < 0) {
72+
fprintf(stderr, "[consumer] ERROR: creating socket failed\n");
73+
goto err_umfMemoryProviderDestroy;
74+
}
75+
76+
fprintf(stderr, "[consumer] Socket created\n");
77+
78+
// set the IP address and the port
79+
consumer_addr.sin_family = AF_INET;
80+
consumer_addr.sin_port = htons(port);
81+
consumer_addr.sin_addr.s_addr = inet_addr(INET_ADDR);
82+
83+
// bind to the IP address and the port
84+
if (bind(consumer_socket, (struct sockaddr *)&consumer_addr,
85+
sizeof(consumer_addr)) < 0) {
86+
fprintf(stderr, "[consumer] ERROR: cannot bind to the port\n");
87+
goto err_close_consumer_socket;
88+
}
89+
90+
fprintf(stderr, "[consumer] Binding done\n");
91+
92+
// listen for the producer
93+
if (listen(consumer_socket, 1) < 0) {
94+
fprintf(stderr, "[consumer] ERROR: listen() failed\n");
95+
goto err_close_consumer_socket;
96+
}
97+
98+
fprintf(stderr, "[consumer] Listening for incoming connections ...\n");
99+
100+
// accept an incoming connection
101+
producer_addr_len = sizeof(producer_addr);
102+
producer_socket = accept(consumer_socket, (struct sockaddr *)&producer_addr,
103+
(socklen_t *)&producer_addr_len);
104+
if (producer_socket < 0) {
105+
fprintf(stderr, "[consumer] ERROR: accept() failed\n");
106+
goto err_close_consumer_socket;
107+
}
108+
109+
fprintf(stderr, "[consumer] Producer connected at IP %s and port %i\n",
110+
inet_ntoa(producer_addr.sin_addr), ntohs(producer_addr.sin_port));
111+
112+
// zero the receive buffer
113+
memset(recv_buffer, 0, RECV_BUFF_SIZE);
114+
115+
// receive a producer's message
116+
ssize_t len = recv(producer_socket, recv_buffer, RECV_BUFF_SIZE, 0);
117+
if (len < 0) {
118+
fprintf(stderr, "[consumer] ERROR: recv() failed\n");
119+
goto err_close_producer_socket;
120+
}
121+
if (len != IPC_handle_size) {
122+
fprintf(stderr,
123+
"[consumer] ERROR: recv() received a wrong number of bytes "
124+
"(%zi != %zu expected)\n",
125+
len, IPC_handle_size);
126+
goto err_close_producer_socket;
127+
}
128+
129+
void *IPC_handle = recv_buffer;
130+
131+
fprintf(
132+
stderr,
133+
"[consumer] Received the IPC handle from the producer (%zi bytes)\n",
134+
len);
135+
136+
void *SHM_ptr;
137+
umf_result = umfMemoryProviderOpenIPCHandle(OS_memory_provider, IPC_handle,
138+
&SHM_ptr);
139+
if (umf_result != UMF_RESULT_SUCCESS) {
140+
fprintf(stderr, "[consumer] ERROR: opening the IPC handle failed\n");
141+
goto err_close_producer_socket;
142+
}
143+
144+
fprintf(stderr,
145+
"[consumer] Opened the IPC handle received from the producer\n");
146+
147+
// read the current value from the shared memory
148+
unsigned long long SHM_number_1 = *(unsigned long long *)SHM_ptr;
149+
fprintf(
150+
stderr,
151+
"[consumer] Read the number from the producer's shared memory: %llu\n",
152+
SHM_number_1);
153+
154+
// calculate the new value
155+
unsigned long long SHM_number_2 = SHM_number_1 / 2;
156+
157+
// write the new number directly to the producer's shared memory
158+
*(unsigned long long *)SHM_ptr = SHM_number_2;
159+
fprintf(stderr,
160+
"[consumer] Wrote a new number directly to the producer's shared "
161+
"memory: %llu\n",
162+
SHM_number_2);
163+
164+
// write the response to the consumer_message buffer
165+
memset(consumer_message, 0, sizeof(consumer_message));
166+
strcpy(consumer_message, CONSUMER_MSG);
167+
168+
// send response to the producer
169+
if (send(producer_socket, consumer_message, strlen(consumer_message) + 1,
170+
0) < 0) {
171+
fprintf(stderr, "[consumer] ERROR: send() failed\n");
172+
goto err_closeIPCHandle;
173+
}
174+
175+
fprintf(stderr, "[consumer] Sent a response message to the producer\n");
176+
177+
ret = 0; // SUCCESS
178+
179+
err_closeIPCHandle:
180+
// we do not know the exact size of the remote shared memory
181+
umf_result = umfMemoryProviderCloseIPCHandle(OS_memory_provider, SHM_ptr,
182+
sizeof(unsigned long long));
183+
if (umf_result != UMF_RESULT_SUCCESS) {
184+
fprintf(stderr, "[consumer] ERROR: closing the IPC handle failed\n");
185+
}
186+
187+
fprintf(stderr,
188+
"[consumer] Closed the IPC handle received from the producer\n");
189+
190+
err_close_producer_socket:
191+
close(producer_socket);
192+
193+
err_close_consumer_socket:
194+
close(consumer_socket);
195+
196+
err_umfMemoryProviderDestroy:
197+
umfMemoryProviderDestroy(OS_memory_provider);
198+
199+
if (ret == 0) {
200+
fprintf(stderr, "[consumer] Shutting down (status OK) ...\n");
201+
} else {
202+
fprintf(stderr, "[consumer] Shutting down (status ERROR) ...\n");
203+
}
204+
205+
return ret;
206+
}

0 commit comments

Comments
 (0)