September 12, 2025

Understanding Linux IPC: Inter-Process Communication in Linux Systems

Inter-Process Communication (IPC) is a crucial concept in computing, allowing processes (programs or running tasks) to exchange data and synchronize their actions. In Linux, IPC mechanisms enable communication between processes that may be running on the same machine or even on different machines in a networked environment. This article explores Linux IPC, detailing the various methods available to facilitate communication between processes, their use cases, and their implementation in Linux systems.

What is IPC?

IPC stands for Inter-Process Communication, which refers to the methods and mechanisms that allow processes to communicate with each other. In a modern operating system like Linux, processes often need to share data, synchronize activities, or exchange messages to complete their tasks. IPC provides the infrastructure for this interaction.

In Linux, IPC is implemented using different system calls, libraries, and utilities. The goal of IPC is to allow processes to interact without having to share the same address space, ensuring that each process maintains its own memory space while still being able to exchange data in a controlled manner.

Types of IPC Mechanisms in Linux

Linux supports several types of IPC, each with its own advantages, use cases, and implementation. These mechanisms can broadly be categorized as follows:

1. Pipes: Pipes are one of the simplest IPC mechanisms in Linux. They allow data to flow from one process to another in a unidirectional stream. Pipes can be categorized into:

  • Anonymous Pipes: These are typically used for communication between related processes (like parent and child processes). They do not have names, and their scope is limited to the processes that created them.
  • Named Pipes (FIFOs): These allow communication between unrelated processes. A named pipe is created by a process and has a specific name in the filesystem, which other processes can open and use for reading or writing.

Example: Pipe Communication Between Parent and Child Process

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define BUFFER_SIZE 100

int main() {
    int pipefd[2];
    pid_t pid;
    char message[] = "Hello from parent to child!";
    char buffer[BUFFER_SIZE];

    // Create a pipe
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(1);
    }

    // Create a child process
    pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) { // Child process
        close(pipefd[1]); // Close write end of the pipe
        read(pipefd[0], buffer, BUFFER_SIZE);
        printf("Child received message: %s\n", buffer);
        close(pipefd[0]); // Close read end
    } else { // Parent process
        close(pipefd[0]); // Close read end of the pipe
        write(pipefd[1], message, strlen(message) + 1); // Write message to pipe
        close(pipefd[1]); // Close write end
        wait(NULL); // Wait for child to finish
    }

    return 0;
}

Example: Communication Between Two Independent Processes Using Named Pipe

Producer (writing to the FIFO):

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

#define FIFO_NAME "/tmp/myfifo"
#define MESSAGE "Hello from producer!"

int main() {
    int fd;

    // Create the FIFO (named pipe)
    if (mkfifo(FIFO_NAME, 0666) == -1) {
        perror("mkfifo");
        exit(1);
    }

    // Open the FIFO for writing
    fd = open(FIFO_NAME, O_WRONLY);
    if (fd == -1) {
        perror("open");
        exit(1);
    }

    // Write the message to the FIFO
    write(fd, MESSAGE, strlen(MESSAGE) + 1);

    // Close the FIFO
    close(fd);

    printf("Producer wrote the message to the FIFO.\n");

    return 0;
}

Consumer (reading from the FIFO):

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

#define FIFO_NAME "/tmp/myfifo"
#define BUFFER_SIZE 100

int main() {
    int fd;
    char buffer[BUFFER_SIZE];

    // Open the FIFO for reading
    fd = open(FIFO_NAME, O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(1);
    }

    // Read the message from the FIFO
    read(fd, buffer, sizeof(buffer));

    // Print the message
    printf("Consumer received: %s\n", buffer);

    // Close the FIFO
    close(fd);

    return 0;
}

2. Message Queues: Message queues allow processes to exchange messages in an orderly fashion. Messages are placed in a queue by one process and can be read in a first-in, first-out (FIFO) order by another process. Message queues allow for more complex data exchanges compared to pipes since they support larger and structured messages.

In Linux, the msgget, msgsnd, and msgrcv system calls are used to create and manage message queues.

Producer (Sending Message to the Queue)

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

#define MSG_KEY 1234  // Key for the message queue
#define MAX_TEXT 512  // Maximum message size

// Message structure
struct message {
    long msg_type;       // Message type, must be > 0
    char msg_text[MAX_TEXT]; // Message content
};

int main() {
    struct message msg;
    int msgid;

    // Create message queue (IPC_CREAT - create if not exists)
    msgid = msgget(MSG_KEY, 0666 | IPC_CREAT);
    if (msgid == -1) {
        perror("msgget");
        exit(1);
    }

    // Prepare the message
    msg.msg_type = 1;  // Set message type
    strcpy(msg.msg_text, "Hello from the producer!");

    // Send message to the message queue
    if (msgsnd(msgid, &msg, sizeof(msg), 0) == -1) {
        perror("msgsnd");
        exit(1);
    }

    printf("Producer sent: %s\n", msg.msg_text);

    return 0;
}

Consumer (Receiving Message from the Queue)

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define MSG_KEY 1234  // Same key as used by the producer
#define MAX_TEXT 512  // Maximum message size

// Message structure
struct message {
    long msg_type;       // Message type, must be > 0
    char msg_text[MAX_TEXT]; // Message content
};

int main() {
    struct message msg;
    int msgid;

    // Get the message queue (must use the same key)
    msgid = msgget(MSG_KEY, 0666);
    if (msgid == -1) {
        perror("msgget");
        exit(1);
    }

    // Receive message from the queue
    if (msgrcv(msgid, &msg, sizeof(msg), 1, 0) == -1) {
        perror("msgrcv");
        exit(1);
    }

    printf("Consumer received: %s\n", msg.msg_text);

    // Remove the message queue after usage
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("msgctl");
        exit(1);
    }

    return 0;
}

3, Shared Memory: Shared memory is one of the fastest IPC mechanisms because it allows multiple processes to access the same memory region. Unlike other mechanisms that involve message passing, shared memory allows processes to communicate by reading and writing to a common memory area.

    Shared memory is often used when high-performance communication is needed, and it can be created using the shmget and shmat system calls in Linux.

    Producer (Writing to Shared Memory)

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <unistd.h>
    
    #define SHM_KEY 1234     // Key for the shared memory segment
    #define SHM_SIZE 1024    // Size of the shared memory segment
    
    int main() {
        int shmid;
        char *shm_ptr;
        char message[] = "Hello from the producer!";
        
        // Create shared memory segment
        shmid = shmget(SHM_KEY, SHM_SIZE, 0666 | IPC_CREAT);
        if (shmid == -1) {
            perror("shmget");
            exit(1);
        }
    
        // Attach the shared memory segment to the process's address space
        shm_ptr = (char *)shmat(shmid, NULL, 0);
        if (shm_ptr == (char *)(-1)) {
            perror("shmat");
            exit(1);
        }
    
        // Write message to shared memory
        strcpy(shm_ptr, message);
        printf("Producer wrote to shared memory: %s\n", message);
    
        // Detach from shared memory
        if (shmdt(shm_ptr) == -1) {
            perror("shmdt");
            exit(1);
        }
    
        return 0;
    }
    

    Consumer (Reading from Shared Memory)

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <unistd.h>
    
    #define SHM_KEY 1234     // Key for the shared memory segment
    #define SHM_SIZE 1024    // Size of the shared memory segment
    
    int main() {
        int shmid;
        char *shm_ptr;
    
        // Get the shared memory segment (same key as producer)
        shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
        if (shmid == -1) {
            perror("shmget");
            exit(1);
        }
    
        // Attach the shared memory segment to the process's address space
        shm_ptr = (char *)shmat(shmid, NULL, 0);
        if (shm_ptr == (char *)(-1)) {
            perror("shmat");
            exit(1);
        }
    
        // Read message from shared memory
        printf("Consumer read from shared memory: %s\n", shm_ptr);
    
        // Detach from shared memory
        if (shmdt(shm_ptr) == -1) {
            perror("shmdt");
            exit(1);
        }
    
        // Optionally, the consumer can remove the shared memory segment
        // If the producer is done and no other process needs the memory, we can remove it
        // shmctl(shmid, IPC_RMID, NULL); // Uncomment if you want to remove the shared memory
    
        return 0;
    }
    

    4. Semaphores: Semaphores are used for synchronizing access to shared resources among processes. They are typically used to prevent race conditions when multiple processes try to access the same resource at the same time.

      In Linux, semaphores are handled using the semget, semop, and semctl system calls. Semaphores can be binary (mutexes) or counting, depending on the number of processes allowed to access the shared resource.

      Producer (produces items)

      
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/ipc.h>
      #include <sys/sem.h>
      #include <unistd.h>
      #include <string.h>
      
      #define SEM_KEY 1234        // Semaphore key
      #define BUFFER_SIZE 10      // Shared buffer size
      
      // Define the structure of semaphores for mutual exclusion and signaling
      struct sembuf sem_wait = { 0, -1, SEM_UNDO };  // Decrease semaphore (Wait)
      struct sembuf sem_signal = { 0, 1, SEM_UNDO }; // Increase semaphore (Signal)
      
      int main() {
          int semid;
          char buffer[BUFFER_SIZE];
          int item = 0;
      
          // Create a semaphore set with two semaphores: one for mutex and one for signaling
          semid = semget(SEM_KEY, 1, 0666 | IPC_CREAT);
          if (semid == -1) {
              perror("semget");
              exit(1);
          }
      
          // Initialize the semaphore to 1 (for mutex lock)
          if (semctl(semid, 0, SETVAL, 1) == -1) {
              perror("semctl");
              exit(1);
          }
      
          // Producer loop: keep producing items
          while (1) {
              // Wait for a moment to simulate production time
              sleep(1);
              
              // Wait operation on semaphore to ensure mutual exclusion
              if (semop(semid, &sem_wait, 1) == -1) {
                  perror("semop wait");
                  exit(1);
              }
      
              // Produce an item (increment the item number)
              sprintf(buffer, "Item %d", item++);
              printf("Produced: %s\n", buffer);
      
              // Signal operation to indicate an item has been produced
              if (semop(semid, &sem_signal, 1) == -1) {
                  perror("semop signal");
                  exit(1);
              }
          }
      
          return 0;
      }
      

      Consumer (consumes items)

      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/ipc.h>
      #include <sys/sem.h>
      #include <unistd.h>
      #include <string.h>
      
      #define SEM_KEY 1234        // Semaphore key
      #define BUFFER_SIZE 10      // Shared buffer size
      
      // Define the structure of semaphores for mutual exclusion and signaling
      struct sembuf sem_wait = { 0, -1, SEM_UNDO };  // Decrease semaphore (Wait)
      struct sembuf sem_signal = { 0, 1, SEM_UNDO }; // Increase semaphore (Signal)
      
      int main() {
          int semid;
          char buffer[BUFFER_SIZE];
      
          // Get the semaphore set (same key as producer)
          semid = semget(SEM_KEY, 1, 0666);
          if (semid == -1) {
              perror("semget");
              exit(1);
          }
      
          // Consumer loop: keep consuming items
          while (1) {
              // Wait for a moment to simulate consumption time
              sleep(2);
              
              // Wait operation on semaphore to ensure mutual exclusion
              if (semop(semid, &sem_wait, 1) == -1) {
                  perror("semop wait");
                  exit(1);
              }
      
              // Consume an item (simulating the consumption process)
              printf("Consumed: %s\n", "Item");
              
              // Signal operation to indicate an item has been consumed
              if (semop(semid, &sem_signal, 1) == -1) {
                  perror("semop signal");
                  exit(1);
              }
          }
      
          return 0;
      }
      

        5.Sockets: Sockets provide a powerful IPC mechanism for communication between processes, either on the same machine or over a network. Sockets can be used for both stream-oriented communication (TCP/IP) or message-oriented communication (UDP).

        Sockets are used extensively in network programming, where two or more processes communicate across a network. In Linux, sockets can be created using the socket(), bind(), connect(), and send() system calls.

        Server Code (TCP Server)

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <arpa/inet.h>
        
        #define PORT 8080         // Port to bind server
        #define BUFFER_SIZE 1024  // Size of the buffer
        
        int main() {
            int server_fd, new_sock;
            struct sockaddr_in server_addr, client_addr;
            socklen_t addr_len = sizeof(client_addr);
            char buffer[BUFFER_SIZE];
            
            // Create a socket (IPv4, TCP)
            if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
                perror("Socket creation failed");
                exit(1);
            }
        
            server_addr.sin_family = AF_INET;
            server_addr.sin_addr.s_addr = INADDR_ANY;  // Accept connections from any IP address
            server_addr.sin_port = htons(PORT);        // Convert port number to network byte order
        
            // Bind the socket to the IP address and port
            if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
                perror("Bind failed");
                close(server_fd);
                exit(1);
            }
        
            // Listen for incoming connections (maximum 3 clients)
            if (listen(server_fd, 3) == -1) {
                perror("Listen failed");
                close(server_fd);
                exit(1);
            }
            printf("Server listening on port %d...\n", PORT);
        
            // Accept an incoming connection
            if ((new_sock = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) {
                perror("Accept failed");
                close(server_fd);
                exit(1);
            }
            printf("Connection accepted\n");
        
            // Receive data from the client
            int len = recv(new_sock, buffer, BUFFER_SIZE, 0);
            if (len == -1) {
                perror("Recv failed");
                close(new_sock);
                close(server_fd);
                exit(1);
            }
            buffer[len] = '\0';  // Null-terminate the received data
            printf("Received from client: %s\n", buffer);
        
            // Send a response to the client
            const char *message = "Message received successfully";
            send(new_sock, message, strlen(message), 0);
            
            // Close the sockets
            close(new_sock);
            close(server_fd);
        
            return 0;
        }
        

        Client Code (TCP Client)

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <arpa/inet.h>
        
        #define SERVER_IP "127.0.0.1"  // Localhost IP address
        #define SERVER_PORT 8080       // Server port
        #define BUFFER_SIZE 1024       // Size of the buffer
        
        int main() {
            int sock;
            struct sockaddr_in server_addr;
            char buffer[BUFFER_SIZE];
            
            // Create a socket (IPv4, TCP)
            if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
                perror("Socket creation failed");
                exit(1);
            }
        
            server_addr.sin_family = AF_INET;
            server_addr.sin_port = htons(SERVER_PORT);
            server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); // Convert IP address to binary form
        
            // Connect to the server
            if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
                perror("Connection failed");
                close(sock);
                exit(1);
            }
            printf("Connected to server at %s:%d\n", SERVER_IP, SERVER_PORT);
        
            // Send a message to the server
            const char *message = "Hello from the client!";
            send(sock, message, strlen(message), 0);
        
            // Receive a response from the server
            int len = recv(sock, buffer, BUFFER_SIZE, 0);
            if (len == -1) {
                perror("Recv failed");
                close(sock);
                exit(1);
            }
            buffer[len] = '\0';  // Null-terminate the received data
            printf("Received from server: %s\n", buffer);
        
            // Close the socket
            close(sock);
        
            return 0;
        }
        

        6.Signals: Signals are a form of communication used to notify a process about specific events, like the need to terminate or stop its execution. While not a direct data exchange mechanism, signals are important for process management and synchronization. Common signals include SIGTERM, SIGKILL, and SIGSEGV.

        In Linux, signals can be sent using the kill command or the kill() system call. The process can handle or ignore signals based on its design.

        Producer (sending signal)

        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <signal.h>
        
        void producer_signal_handler(int sig) {
            // This is the handler that will execute when the signal is received
            if (sig == SIGUSR1) {
                printf("Producer: Item produced. Sending signal to consumer.\n");
            }
        }
        
        int main() {
            // Set up signal handler for SIGUSR1
            signal(SIGUSR1, producer_signal_handler);
        
            printf("Producer: Started and waiting for signal to produce an item...\n");
        
            // Wait for a moment to simulate production time
            sleep(3);
        
            // Send SIGUSR1 to the consumer process to notify it's time to consume the item
            printf("Producer: Sending signal to the consumer...\n");
            raise(SIGUSR1); // Send signal to the consumer
        
            // Wait for the signal to be acknowledged by consumer
            sleep(1);
            printf("Producer: Exiting...\n");
        
            return 0;
        }
        

        Consumer (waiting for signal)

        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <signal.h>
        
        // This function will be triggered when the signal is received
        void consumer_signal_handler(int sig) {
            if (sig == SIGUSR1) {
                printf("Consumer: Received signal. Consuming the item...\n");
            }
        }
        
        int main() {
            // Set up the signal handler for SIGUSR1
            signal(SIGUSR1, consumer_signal_handler);
        
            printf("Consumer: Waiting for signal to consume an item...\n");
        
            // Wait indefinitely for the signal (i.e., the consumer is paused here)
            while (1) {
                pause();  // Wait for signal
            }
        
            return 0;
        }
        

        7.Memory-Mapped Files: Memory-mapped files allow multiple processes to map a file (or part of a file) directly into their address space, which facilitates sharing data between processes. This method is often used for large data exchanges or when persistent storage is involved.

        Memory mapping in Linux is done using the mmap() system call.

        Writer Program (writing to a memory-mapped file)

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <sys/mman.h>
        #include <sys/stat.h>
        #include <fcntl.h>
        #include <unistd.h>
        
        #define FILE_NAME "mappedfile.dat"
        #define FILE_SIZE 4096  // Size of the file to map in bytes
        
        int main() {
            int fd;
            char *mapped_mem;
        
            // Open the file for writing (create it if it doesn't exist)
            fd = open(FILE_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
            if (fd == -1) {
                perror("Failed to open file");
                exit(1);
            }
        
            // Set the size of the file to the desired size (FILE_SIZE)
            if (ftruncate(fd, FILE_SIZE) == -1) {
                perror("Failed to set file size");
                close(fd);
                exit(1);
            }
        
            // Memory-map the file
            mapped_mem = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
            if (mapped_mem == MAP_FAILED) {
                perror("Failed to map file to memory");
                close(fd);
                exit(1);
            }
        
            // Write data into the memory-mapped region
            snprintf(mapped_mem, FILE_SIZE, "This is a memory-mapped file example!");
        
            printf("Writer: Data written to memory-mapped file.\n");
        
            // Unmap the file from memory
            if (munmap(mapped_mem, FILE_SIZE) == -1) {
                perror("Failed to unmap memory");
                close(fd);
                exit(1);
            }
        
            // Close the file descriptor
            close(fd);
        
            return 0;
        }
        

        Reader Program (reading from a memory-mapped file)

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <sys/mman.h>
        #include <sys/stat.h>
        #include <fcntl.h>
        #include <unistd.h>
        
        #define FILE_NAME "mappedfile.dat"
        #define FILE_SIZE 4096  // Size of the file to map in bytes
        
        int main() {
            int fd;
            char *mapped_mem;
        
            // Open the file for reading
            fd = open(FILE_NAME, O_RDONLY);
            if (fd == -1) {
                perror("Failed to open file");
                exit(1);
            }
        
            // Memory-map the file
            mapped_mem = mmap(NULL, FILE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
            if (mapped_mem == MAP_FAILED) {
                perror("Failed to map file to memory");
                close(fd);
                exit(1);
            }
        
            // Read the data from the memory-mapped region
            printf("Reader: Data read from memory-mapped file: %s\n", mapped_mem);
        
            // Unmap the file from memory
            if (munmap(mapped_mem, FILE_SIZE) == -1) {
                perror("Failed to unmap memory");
                close(fd);
                exit(1);
            }
        
            // Close the file descriptor
            close(fd);
        
            return 0;
        }
        

        8.Unix domain sockets: (or IPC sockets) allow processes on the same machine to communicate with each other. They provide a more efficient communication mechanism than TCP/IP sockets for local communication.

        Here’s a simple working example of a Unix domain socket communication between a server and a client in C.

        Unix Domain Socket Server

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <sys/un.h>
        #include <errno.h>
        
        #define SOCKET_PATH "/tmp/unix_socket_example.sock"
        #define BUFFER_SIZE 128
        
        int main() {
            int server_sock, client_sock;
            struct sockaddr_un server_addr, client_addr;
            socklen_t client_len;
            char buffer[BUFFER_SIZE];
            
            // Create the server socket (Unix domain socket)
            server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
            if (server_sock == -1) {
                perror("socket");
                exit(1);
            }
            
            // Set up the server address
            memset(&server_addr, 0, sizeof(struct sockaddr_un));
            server_addr.sun_family = AF_UNIX;
            strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
            
            // Remove the existing socket file, if any
            unlink(SOCKET_PATH);
            
            // Bind the socket to the address
            if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == -1) {
                perror("bind");
                close(server_sock);
                exit(1);
            }
            
            // Listen for incoming connections
            if (listen(server_sock, 5) == -1) {
                perror("listen");
                close(server_sock);
                exit(1);
            }
            
            printf("Server is waiting for a connection...\n");
            
            // Accept a client connection
            client_len = sizeof(client_addr);
            client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_len);
            if (client_sock == -1) {
                perror("accept");
                close(server_sock);
                exit(1);
            }
            
            printf("Client connected.\n");
            
            // Receive data from the client
            int len = recv(client_sock, buffer, BUFFER_SIZE, 0);
            if (len == -1) {
                perror("recv");
                close(client_sock);
                close(server_sock);
                exit(1);
            }
            
            buffer[len] = '\0';  // Null-terminate the received data
            printf("Received from client: %s\n", buffer);
            
            // Send a response to the client
            const char *response = "Hello from the server!";
            send(client_sock, response, strlen(response), 0);
            
            // Close the sockets
            close(client_sock);
            close(server_sock);
            
            // Remove the socket file
            unlink(SOCKET_PATH);
            
            return 0;
        }
        

        Unix Domain Socket Client

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <sys/socket.h>
        #include <sys/un.h>
        
        #define SOCKET_PATH "/tmp/unix_socket_example.sock"
        #define BUFFER_SIZE 128
        
        int main() {
            int client_sock;
            struct sockaddr_un server_addr;
            char buffer[BUFFER_SIZE];
            
            // Create the client socket (Unix domain socket)
            client_sock = socket(AF_UNIX, SOCK_STREAM, 0);
            if (client_sock == -1) {
                perror("socket");
                exit(1);
            }
            
            // Set up the server address
            memset(&server_addr, 0, sizeof(struct sockaddr_un));
            server_addr.sun_family = AF_UNIX;
            strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
            
            // Connect to the server
            if (connect(client_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == -1) {
                perror("connect");
                close(client_sock);
                exit(1);
            }
            
            // Send data to the server
            const char *message = "Hello from the client!";
            send(client_sock, message, strlen(message), 0);
            
            // Receive the response from the server
            int len = recv(client_sock, buffer, BUFFER_SIZE, 0);
            if (len == -1) {
                perror("recv");
                close(client_sock);
                exit(1);
            }
            
            buffer[len] = '\0';  // Null-terminate the received data
            printf("Received from server: %s\n", buffer);
            
            // Close the socket
            close(client_sock);
            
            return 0;
        }
        

        9.D-Bus and RT-link

        While traditional Inter-Process Communication (IPC) mechanisms such as pipes, message queues, and shared memory are commonly used for process communication in Linux, there are additional, specialized IPC systems like D-Bus and RT-Link that cater to specific use cases. These systems provide high-level abstractions and tailored solutions for communication between processes, especially in complex environments like desktop applications, system services, or real-time systems.

        Conclusion

        Linux provides a rich set of IPC mechanisms, each suited to different types of inter-process communication needs. Understanding these mechanisms and knowing how to use them effectively can help developers create more efficient, responsive, and scalable applications.

        Whether you’re working with simple pipes or implementing complex synchronization with semaphores, Linux IPC allows you to build robust and high-performance applications. Understanding these tools and how they fit together is essential for mastering Linux-based development.

        Leave a Reply

        Your email address will not be published. Required fields are marked *