Shared Memory Inter-Process Communication ( IPC)


Shared memory Inter-Process Communication (IPC) in Linux allows processes to communicate by sharing a region of memory. This method is efficient because it avoids the overhead of copying data between processes. Here's how shared memory IPC is typically done in Linux:

Create a Shared Memory Segment: To use shared memory IPC, you need to create a shared memory segment. This is done using the shmget() system call, which allocates a new shared memory segment or obtains the identifier of an existing one. You specify the size of the shared memory segment and some permissions.

Attach the Shared Memory Segment: Once you have obtained the identifier for the shared memory segment, you need to attach it to the address space of your process. This is done using the shmat() system call. This call returns the address of the shared memory segment in your process's address space.

Accessing the Shared Memory:
After attaching the shared memory segment to your process's address space, you can read from or write to it just like any other block of memory. This means you can use pointers and standard memory access techniques to interact with the shared memory.

Detaching and Deleting: When you're done using the shared memory segment, you should detach it from your process's address space using the shmdt() system call. This doesn't remove the shared memory segment; it just detaches it from the current process. If the shared memory segment is no longer needed, you can remove it using the shmctl() system call with the IPC_RMID command.
 
This code creates a shared memory segment, writes a string to it, and then detaches from the segment. Other processes can attach to the same shared memory segment using the same key and read the string from it.
 
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main() {
    int shmid;
    key_t key;
    char *shm, *ptr;

    // Generate a key
    key = ftok("shmfile", 'R');

    // Create the shared memory segment
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);

    // Attach the shared memory segment
    shm = shmat(shmid, NULL, 0);


    // Write to the shared memory
    ptr = shm;
    sprintf(ptr, "Hello, shared memory!");
    printf("data written to shared memory \n");
    // Detach the shared memory segment
    shmdt(shm);

    return 0;
}

This program reads from the shared memory segment created by the writer process. It uses the same key to access the shared memory segment. After reading the data, it detaches from the shared memory segment and marks it for destruction using shmctl() with the IPC_RMID command. This ensures that the shared memory segment is removed after it's no longer needed.
 
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main() {
    int shmid;
    key_t key;
    char *shm, *ptr;

    // Generate the same key used by the writer
    key = ftok("shmfile", 'R');

    // Locate the shared memory segment
    shmid = shmget(key, SHM_SIZE, 0666);

    if (shmid < 0) {
        perror("shmget");
        exit(1);
    }

    // Attach the shared memory segment
    shm = shmat(shmid, NULL, 0);

    if (shm == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // Read from the shared memory
    ptr = shm;
    printf("Message read from shared memory: %s\n", ptr);

    // Detach the shared memory segment
    if (shmdt(shm) == -1) {
        perror("shmdt");
        exit(1);
    }

    // Mark the shared memory segment to be destroyed
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }

    return 0;

Note: create two file
Lets Explore the various functions used

ftok()

ftok() is a function in the C programming language used to generate a unique key for System V IPC (Inter-Process Communication) mechanisms like message queues, shared memory segments, and semaphores. The name ftok stands for "File to Key".

The function prototype for ftok() is:
key_t ftok(const char *pathname, int proj_id);

pathname: This is a pointer to a null-terminated string representing the name of a file in the filesystem. ftok() uses this file to generate a unique key.

proj_id: This is an integer value representing a project identifier. It's a user-defined value that helps in further differentiation between different IPC mechanisms based on the same file.

The ftok() function combines the st_dev (device number) and st_ino (inode number) fields of the file referenced by pathname with the proj_id to generate a unique key. It creates a 32-bit key from the given file and project identifier.

Here's how ftok() works:
  • It takes the pathname argument and uses it to access the file's metadata, specifically the st_dev and st_ino fields.
  • It combines these values with the project identifier (proj_id) using a simple bitwise operation to produce a 32-bit integer key.
  • The resulting key is returned and can be used with other System V IPC functions like msgget(), shmget(), and semget() to create or access IPC objects.
It's important to note a few considerations when using ftok():
  • The file referenced by pathname should exist and be accessible by the user.
  • The file's inode number (st_ino) and device number (st_dev) should remain stable across reboots and file system modifications for the key to remain consistent.
  • The project identifier (proj_id) should be chosen carefully to avoid collisions between different IPC objects.
ftok() provides a simple and standardized way to generate keys for IPC objects based on file metadata, ensuring consistency and uniqueness across different processes and systems.

shmget()

shmget() is a system call in Linux and other Unix-like operating systems that is used to create or access a shared memory segment. Shared memory segments allow multiple processes to share a region of memory, which is useful for efficient communication and data exchange between processes.

Here's the prototype of shmget():
#include <sys/ipc.h> 
#include <sys/shm.h> 
 int shmget(key_t key, size_t size, int shmflg);
    key: This is a key that uniquely identifies the shared memory segment. It can be obtained using the ftok() function, which converts a filename and a project identifier into a key.
    size: This specifies the size, in bytes, of the shared memory segment. It indicates how much memory should be allocated for the segment.
    shmflg: This is a set of flags that determine the behavior of the shmget() call. It can include options such as the permissions for the shared memory segment (IPC_CREAT to create the segment if it doesn't exist, IPC_EXCL to fail if the segment already exists, etc.).

    The return value of shmget() is the identifier of the shared memory segment if successful, or -1 if an error occurs.
Here's a brief explanation of how shmget() works:

  • If IPC_CREAT flag is set and no shared memory segment with the specified key exists, shmget() creates a new shared memory segment with the specified key and size.
  • If IPC_CREAT flag is set and a shared memory segment with the specified key already exists, shmget() returns the identifier of the existing segment unless IPC_EXCL is also set, in which case shmget() fails.
  • If IPC_CREAT flag is not set, shmget() tries to access an existing shared memory segment with the specified key. If the segment exists and the calling process has the necessary permissions, shmget() returns its identifier.
  • If IPC_CREAT is not set and no shared memory segment with the specified key exists, shmget() fails and returns -1.

shmat()

The shmat() function in Unix-like operating systems, including Linux, is used to attach a shared memory segment to the address space of a process. This function is essential for allowing processes to access and work with the shared memory area created by other processes.

Here's the prototype of the shmat() function:
#include <sys/types.h> 
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid: This parameter is the identifier of the shared memory segment, which is typically obtained using the shmget() function.

shmaddr: This parameter specifies the address at which the shared memory segment should be attached. If shmaddr is NULL, the system chooses a suitable address for attaching the shared memory segment.

shmflg: This parameter is a set of flags that control the behavior of the shmat() call. The flags include options such as SHM_RDONLY to attach the shared memory segment for reading only, or SHM_RND to round the address down to the nearest page boundary.

The return value of shmat() is the address at which the shared memory segment was attached to the process's address space. If the attachment fails, shmat() returns (void *) -1.

Here's a basic overview of how shmat() works:
  • The process provides the identifier of the shared memory segment (shmid) that it wants to attach.
  • Optionally, the process can specify the desired address (shmaddr) at which it wants the shared memory segment to be attached. If shmaddr is NULL, the system chooses a suitable addres
  • The system checks if the process has the necessary permissions to access the shared memory segment.
  • If everything is successful, the shared memory segment is attached to the process's address space, and the address at which it was attached is returned
  • The process can now access and manipulate the shared memory segment using standard memory access techniques.
It's important to note that once attached, the shared memory segment remains attached until the process explicitly detaches it using the shmdt() function. Failing to detach the shared memory segment can lead to memory leaks and other issues.

shmdet()

The shmdt() function in Unix-like operating systems, including Linux, is used to detach a shared memory segment from the address space of a process. When a process no longer needs access to a shared memory segment, it should detach it using shmdt() to release system resources and prevent memory leaks.

Here's the prototype of the shmdt() function:
#include <sys/types.h> 
#include <sys/shm.h> 
 int shmdt(const void *shmaddr);

shmaddr: This parameter is the address at which the shared memory segment is attached to the process's address space. It should be the same address that was returned by the shmat() function when attaching the segment.

The return value of shmdt() is 0 if the operation is successful, or -1 if an error occurs.

Here's a basic overview of how shmdt() works:
  • The process provides the address (shmaddr) at which the shared memory segment is attached and that it wants to detach.
  • The system performs the necessary checks to ensure that the address provided is valid and that the process actually has the shared memory segment attached at that address.
  • If everything is successful, the shared memory segment is detached from the process's address space.
  • Once detached, the process can no longer access the shared memory segment using the address provided to shmdt().
It's important to note a couple of things about shmdt():

  • Detaching a shared memory segment using shmdt() does not remove or delete the shared memory segment itself. It only detaches it from the process's address space
  • After detaching a shared memory segment, the address where it was attached becomes invalid, and accessing it can lead to undefined behavior.
  • If a process exits without detaching a shared memory segment, the operating system will automatically detach it as part of the cleanup process.

Proper use of shmdt() ensures that shared memory segments are properly managed and that system resources are released appropriately.

shmctl()

The shmctl() function in Unix-like operating systems, including Linux, is used to perform control operations on a System V shared memory segment. It allows processes to manipulate shared memory segments, such as obtaining information about a segment, changing segment permissions, or removing a segment from the system.

Here's the prototype of the shmctl() function:
#include <sys/types.h> 
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid: This parameter is the identifier of the shared memory segment on which the operation will be performed.

cmd: This parameter specifies the control command to be executed. The available commands include:
  • IPC_STAT: Get information about the shared memory segment and store it in the structure pointed to by buf.
  • IPC_SET: Set the permissions and other attributes of the shared memory segment according to the values specified in the structure pointed to by buf.
  • IPC_RMID: Remove the shared memory segment from the system.

buf: This parameter is a pointer to a structure of type shmid_ds, which contains information about the shared memory segment. This parameter is used with the IPC_STAT and IPC_SET commands to retrieve or set attributes of the shared memory segment.

The return value of shmctl() depends on the command specified by cmd. It returns 0 on success and -1 on failure, with the error code set appropriately.

Here's a brief overview of how shmctl() works with each command:
  • IPC_STAT: When cmd is set to IPC_STAT, shmctl() fills the shmid_ds structure pointed to by buf with information about the shared memory segment identified by shmid.
  • IPC_SET: When cmd is set to IPC_SET, shmctl() updates the permissions and other attributes of the shared memory segment identified by shmid according to the values specified in the shmid_ds structure pointed to by buf.
  • IPC_RMID: When cmd is set to IPC_RMID, shmctl() removes the shared memory segment identified by shmid from the system. After removal, the shared memory segment is no longer accessible by any process, and its resources are released.
shmctl() is a powerful function that allows processes to manage shared memory segments effectively, including obtaining information about segments, modifying their attributes, and removing them from the system when they are no longer needed. Proper use of shmctl() is important for efficient and secure management of shared memory resources.
 
//*******************************************************************************
Here's a program where the parent process writes a message into shared memory, and the child process reads and prints it:
 

In this program:
  • The parent process writes a message into the shared memory segment.
  • The child process reads and prints the message from the shared memory segment.
  • After the child process finishes reading, the parent process waits for it to complete.
  • Finally, the parent process marks the shared memory segment for deletion.

This program demonstrates communication between parent and child processes using shared memory, with the parent writing data and the child reading it.
 

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

#define SHM_SIZE 1024  // Size of the shared memory segment

int main() {
    int shmid;
    key_t key;
    char *shm_ptr;
    pid_t child_pid;

    // Generate a key for the shared memory segment
    key = ftok(".", 'x');
    if (key == -1) {
        perror("ftok");
        exit(1);
    }

    // Create a shared memory segment
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
            if (shmid == -1) {
                    perror("shmget");
                    exit(1);
                    }

    // Fork a child process
    child_pid = fork();

                if (child_pid == -1) {
                    perror("fork");
                        exit(1);
           
else if (child_pid == 0) {
        // Child process (Reader)

        // Attach to the shared memory segment
        shm_ptr = shmat(shmid, NULL, 0);
                    if (shm_ptr == (char *)-1) {
                    perror("shmat");
                    exit(1);
                    }

        // Read and print data from the shared memory
        printf("Child process (Reader) reads: %s\n", shm_ptr);

        // Detach from the shared memory segment
                if (shmdt(shm_ptr) == -1) {
                        perror("shmdt");
                        exit(1);
                    }

        exit(0);
    } else {
        // Parent process (Writer)

        // Attach to the shared memory segment
        shm_ptr = shmat(shmid, NULL, 0);
        if (shm_ptr == (char *)-1) {
            perror("shmat");
            exit(1);
        }

        // Write data to the shared memory
        strcpy(shm_ptr, "Hello from the parent process (Writer)!");

        // Detach from the shared memory segment
            if (shmdt(shm_ptr) == -1) {
                perror("shmdt");
                exit(1);
                }

        // Wait for the child process to finish
        wait(NULL);
    }

    // Mark the shared memory segment for deletion
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }

    return 0;
}
 
//*********************************************************************************
Let's consider a numerical problem where the parent process generates an array of integers and stores it in shared memory. The child process then calculates the sum of these integers from the shared memory.


In this program:
  • The parent process generates an array of integers from 1 to ARRAY_SIZE and stores it in shared memory.
  • The child process calculates the sum of the integers stored in shared memory.
  • After the child process finishes, the parent process waits for it to complete.
  • Finally, the parent process marks the shared memory segment for deletion.

This program demonstrates communication between parent and child processes using shared memory to perform a numerical computation. 
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

#define ARRAY_SIZE 100  // Size of the array of integers
#define SHM_SIZE (ARRAY_SIZE * sizeof(int))  // Size of the shared memory segment

int main() {
    int shmid;
    key_t key;
    int *array_ptr;
    pid_t child_pid;

    // Generate a key for the shared memory segment
    key = ftok(".", 'x');
    if (key == -1) {
        perror("ftok");
        exit(1);
    }

    // Create a shared memory segment
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    // Fork a child process
    child_pid = fork();

    if (child_pid == -1) {
        perror("fork");
        exit(1);
    } else if (child_pid == 0) {
        // Child process (Reader)

        // Attach to the shared memory segment
        array_ptr = (int *)shmat(shmid, NULL, 0);
        if (array_ptr == (int *)-1) {
            perror("shmat");
            exit(1);
        }

        // Calculate the sum of the integers from shared memory
        int sum = 0;
        for (int i = 0; i < ARRAY_SIZE; i++) {
            sum += array_ptr[i];
        }

        printf("Child process (Reader) calculated sum: %d\n", sum);

        // Detach from the shared memory segment
        if (shmdt(array_ptr) == -1) {
            perror("shmdt");
            exit(1);
        }

        exit(0);
    } else {
        // Parent process (Writer)

        // Attach to the shared memory segment
        array_ptr = (int *)shmat(shmid, NULL, 0);
        if (array_ptr == (int *)-1) {
            perror("shmat");
            exit(1);
        }

        // Generate an array of integers and store it in shared memory
        for (int i = 0; i < ARRAY_SIZE; i++) {
            array_ptr[i] = i + 1;  // Store integers from 1 to ARRAY_SIZE
        }

        // Detach from the shared memory segment
        if (shmdt(array_ptr) == -1) {
            perror("shmdt");
            exit(1);
        }

        // Wait for the child process to finish
        wait(NULL);
    }

    // Mark the shared memory segment for deletion
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }

    return 0;
}
Output:

 Child process (Reader) calculated sum: 55

Comments

Popular posts from this blog

CSL 204 OPERATING SYSTEM LAB KTU IV SEM CSE

FCFS and SJF