How to Use shmget to Allocate Shared Memory in C

Jinku Hu Feb 02, 2024
How to Use shmget to Allocate Shared Memory in C

This article will demonstrate multiple methods about using the shmget function to allocate shared memory in C.

Use shmget to Allocate Shared Memory in C

Shared memory is one of the ways of interprocess communication that allows two or more processes to exchange data and communicate fast in user-space. Shared memory implies that multiple processes share the same region in memory, and they can modify/access this segment as needed.

The interface we will demonstrate in the following examples is called System V shared memory, which is provided using four functions: shmget, shmat, shmdt and shmctl.

  • shmget is used to create a new shared memory segment or retrieve an identifier for the already created memory segment.
  • shmat call is used to attach the given shared memory segment to the memory space of the calling process.
  • shmdt can be employed to detach the given memory segment
  • shmctl is used modify and deallocate the segment with multiple options.

The next example code implements a basic usage of shmget and shmat functions for one process that creates a new shared segment and then writes some texts into it. The shmget function takes three arguments, the first of which is the memory segment’s key. The key value can be IPC_PRIVATE macro if the new segment needs to be created or the key value of the existing segment when the identifier of memory should be obtained with the call. The second argument of shmget specifies the segment’s size and the third argument is the permission flags that can be OR-ed to include multiple values.

Once the memory segment is created, the segment identifier is obtained, and it can be passed to the shmat function to attach the given memory. The user can pass the specific address where to attach the given memory segment as a second argument to shmat. Still, usually, it’s preferred to let the kernel choose the address and specify NULL to denote this.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>

enum { SEGMENT_SIZE = 0x6400 };

const char *data = "Hello there!";

int main(int argc, char *argv[]) {
  int status;
  int segment_id;

  segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
                      IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
  char *sh_mem = (char *)shmat(segment_id, NULL, 0);

  printf("Segment ID %d\n", segment_id);
  printf("Attached at %p\n", sh_mem);
  memmove(sh_mem, data, strlen(data) + 1);
  printf("%s\n", sh_mem);

  shmdt(sh_mem);
  shmctl(segment_id, IPC_RMID, 0);
  exit(EXIT_SUCCESS);
}

Output:

Segment ID 1540195
Attached at 0x7f6ba1437000
Hello there!

Once the shmat function returns the valid pointer, we can treat it as any memory address and operate on it as needed. Finally, shmdt and shmctl functions are called to detach the given segment and deallocate the memory. Suppose shmctl is not called on allocated memory segments. In that case, they stay in the system’s memory after the program terminates, and it may hit the systemwide limit on the total shared memory segment.

On the other hand, the next sample code demonstrates how two processes can interact using the shared memory. The code is the same as the previous example, except that after printing the "Hello there!" string, the main process is forked, and a child is created, which stores a different string into the same address. Meanwhile, the parent process suspends, waits for the child to terminate, and exits with success code; the newly stored string is printed to the console. If multiple processes need to modify and access shared memory segments concurrently, the programmer needs to employ some synchronization tools like semaphores.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>

enum { SEGMENT_SIZE = 0x6400 };

const char *data = "Hello there!";

int main(int argc, char *argv[]) {
  int status;
  int segment_id;

  segment_id = shmget(IPC_PRIVATE, SEGMENT_SIZE,
                      IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
  char *sh_mem = (char *)shmat(segment_id, 0, 0);

  printf("Segment ID %d\n", segment_id);
  printf("Attached at %p\n", sh_mem);
  memmove(sh_mem, data, strlen(data) + 1);
  printf("%s\n", sh_mem);

  pid_t child_pid = fork();
  if (child_pid == -1) perror("fork");

  if (child_pid == 0) {
    strcpy(sh_mem, "NEW DATA Stored by Child Process\0");

    printf("child pid - %d\n", getpid());
    exit(EXIT_SUCCESS);
  } else {
    pid_t ret = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
    if (ret == -1) perror("waitpid");

    if (WIFEXITED(status))
      printf("Child exited, status - %d\n", WEXITSTATUS(status));

    if (WEXITSTATUS(status) == 0) printf("%s\n", sh_mem);
  }

  shmdt(sh_mem);
  shmctl(segment_id, IPC_RMID, 0);
  exit(EXIT_SUCCESS);
}

Output:

Segment ID 1540195
Attached at 0x7fd825a25000
Hello there!
child pid - 27291
Child exited, status - 0
NEW DATA Stored by Child Process
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C Memory