Fundamentals 39 min read

Linux IPC Explained: Pipes, FIFOs, Message Queues, Shared Memory, Signals and Semaphores

This article introduces Linux interprocess communication (IPC), describes the characteristics and usage of unnamed and named pipes, message queues, shared memory, signals, and semaphores, and provides complete C code examples and a producer‑consumer demonstration that combine these mechanisms.

Linux Kernel Journey
Linux Kernel Journey
Linux Kernel Journey
Linux IPC Explained: Pipes, FIFOs, Message Queues, Shared Memory, Signals and Semaphores

IPC Overview

Interprocess Communication (IPC) provides mechanisms for processes to exchange data. On a single host the mechanisms include unnamed and named pipes (FIFO), message queues, shared memory, signals, and semaphores. Across hosts sockets and streams are used.

1. Pipes

Characteristics

Typically half‑duplex: data flows in one direction.

Usable only between processes that share a parent‑child or sibling relationship.

Implemented as special files; read/write work, lseek does not.

Data disappears after being read (like water flowing through a pipe).

Function prototype

#include <unistd.h>
int pipe(int pipefd[2]);

Unnamed pipe example

#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
    int fd[2];
    if(pipe(fd)==-1){
        printf("create pipe failed
");
    }
    int pid;
    char readbuff[128]={0};
    char readbuff2[128]={0};
    pid=fork();
    if(pid<0){
        printf("create child process
");
    }else if(pid==0){
        printf("this is child
");
        close(fd[1]);
        read(fd[0],readbuff,sizeof(readbuff));
        read(fd[0],readbuff2,sizeof(readbuff2));
        close(fd[0]);
        printf("from father process:%s,2:%s
",readbuff,readbuff2);
        exit(0);
    }else{
        sleep(3);
        printf("this is father
");
        close(fd[0]);
        write(fd[1],"hello this is father",strlen("hello this is father"));
        close(fd[1]);
        wait(NULL);
        exit(0);
    }
    return 0;
}

Running the program shows that the data is consumed after the read, confirming the one‑time read behavior.

Named pipe (FIFO)

Characteristics

Can be used between unrelated processes.

Identified by a pathname in the file system.

Function prototype

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

Blocking behavior

Opening a FIFO for read blocks until another process opens it for write. Opening for write blocks until a reader opens it. Using O_NONBLOCK makes the open call return immediately; a write‑only open fails with ENXIO if no reader is present.

Example without O_NONBLOCK

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(){
    if(mkfifo("./fifo.txt",0600)==-1 && errno!=EEXIST){
        printf("mkfifo failed
");
        perror("why");
    }
    int fd=open("./fifo.txt",O_RDONLY);
    printf("open success
");
    close(fd);
    return 0;
}

The program blocks because it opens the FIFO for read and waits for a writer.

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(){
    int fd=open("./fifo.txt",O_WRONLY);
    if(fd==-1){
        printf("open failed
");
        exit(0);
    }
    printf("open success
");
    close(fd);
    return 0;
}

Running the writer after the reader shows no blocking.

2. Message Queues

Characteristics

Record‑oriented; each message has a type and optional priority.

Independent of sending and receiving processes; the queue persists after a process exits.

Supports random access by type, not only FIFO order.

Function prototypes

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);               // create or get queue
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Key creation

A new queue is created when IPC_CREAT is specified and the key does not already identify a queue, or when the key is IPC_PRIVATE.

Message type handling

type==0

: returns the first message in the queue. type>0: returns the first message whose type equals type. type<0: returns the first message whose type is less than or equal to the absolute value of type; if several match, the one with the smallest type is returned.

Example

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf{ long mtype; char mtext[128]; };
int main(){
    int msgid = msgget(0x1234, IPC_CREAT|0777);
    printf("msgid=%d
",msgid);
    struct msgbuf buff = {888, "hello my from msgSend"};
    msgsnd(msgid,&buff,strlen(buff.mtext),0);
    struct msgbuf readbuff;
    msgrcv(msgid,&readbuff,sizeof(readbuff.mtext),98,0);
    printf("read from you:%s
", readbuff.mtext);
    msgctl(msgid, IPC_RMID, NULL);
    return 0;
}

The program creates a queue, sends a message of type 888, receives a message of type 98, and removes the queue.

ftok usage

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int id);
ftok(".",1)

generates a key based on the inode of the current directory and the supplied id.

3. Shared Memory

Critical resource and critical section

A critical resource is a shared object that only one process may access at a time. The code region that accesses it is the critical section.

Function prototypes

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);               // create/get segment
void *shmat(int shmid, const void *shmaddr, int shmflg);    // attach
int shmdt(const void *shmaddr);                            // detach
int shmctl(int shmid, int cmd, struct shmid_ds *buf);       // control

Key via ftok

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int id);

Example (write)

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
int main(){
    key_t key = ftok(".",2);
    int shmid = shmget(key, 1024*4, IPC_CREAT|0666);
    if(shmid==-1){ printf("shmget fail
"); exit(0); }
    char *shmaddr = shmat(shmid,NULL,0);
    strcpy(shmaddr, "my de name!");
    shmdt(shmaddr);
    printf("quit
");
    return 0;
}

Example (read)

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
int main(){
    key_t key = ftok(".",2);
    int shmid = shmget(key, 1024*4, 0);
    if(shmid==-1){ printf("shmget fail
"); exit(0); }
    char *shmaddr = shmat(shmid,NULL,0);
    printf("datas:%s
", shmaddr);
    shmdt(shmaddr);
    shmctl(shmid, IPC_RMID, NULL);
    printf("quit
");
    return 0;
}

The data remains in the segment until it is explicitly removed with shmctl(..., IPC_RMID, ...).

4. Signals

Overview

Signals are software interrupts defined in signal.h. Each signal has a name (e.g., SIGINT, SIGKILL) and a numeric identifier.

Handling methods

Ignore the signal.

Catch it with a user‑defined handler (using signal or sigaction).

Use the default action (usually termination).

Simple handler registration (signal)

#include <signal.h>
#include <stdio.h>
void handler(int signum){
    printf("get signum:%d
", signum);
    switch(signum){
        case 2:  printf("SIGINT
"); break;
        case 9:  printf("SIGKILL
"); break;
        case 10: printf("SIGUSR1
"); break;
    }
}
int main(){
    signal(SIGINT, handler);
    signal(SIGKILL, handler);
    signal(SIGUSR1, handler);
    while(1);
    return 0;
}

Advanced handling (sigaction)

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void handler(int signum, siginfo_t *info, void *context){
    printf("signum=%d
", signum);
    if(context!=NULL){
        printf("get data:%d
", info->si_int);
        printf("send process pid:%d
", info->si_pid);
    }
}
int main(){
    struct sigaction act;
    printf("my pid=%d
", getpid());
    act.sa_sigaction = handler;
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &act, NULL);
    while(1);
    return 0;
}

Sending signals with data (sigqueue)

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    if(argc!=3){ printf("param error
"); exit(0); }
    int signum = atoi(argv[1]);
    int pid = atoi(argv[2]);
    union sigval value; value.sival_int = 100;
    printf("signum=%d,pid=%d
", signum, pid);
    sigqueue(pid, signum, value);
    return 0;
}

kill function (basic send)

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);   // returns 0 on success, -1 on error
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    if(argc!=3){ printf("param error
"); exit(0); }
    int signum = atoi(argv[1]);
    int pid = atoi(argv[2]);
    printf("signum=%d,pid=%d
", signum, pid);
    kill(pid, signum);
    return 0;
}

5. Semaphores

Overview

A semaphore is a counter used for mutual exclusion and synchronization. Two primitive operations are defined:

P (wait) : decrement the counter; block if the result would be negative.

V (signal) : increment the counter; wake a waiting process if any.

Function prototypes

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, size_t nsops);

Example of PV operations

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; };
void PGetKey(int id){ struct sembuf set={0,-1,SEM_UNDO}; semop(id,&set,1); printf("getkey
"); }
void VPutKey(int id){ struct sembuf set={0,1,SEM_UNDO}; semop(id,&set,1); printf("VPut back the key
"); }
int main(){
    key_t key = ftok(".",1);
    int semid = semget(key,1,IPC_CREAT|0666);
    union semun init; init.val=0; semctl(semid,0,SETVAL,init);
    int pid = fork();
    if(pid>0){ PGetKey(semid); printf("this is father
"); VPutKey(semid); }
    else if(pid==0){ printf("this is child
"); VPutKey(semid); }
    else{ printf("fork fail
"); }
    return 0;
}

6. Producer‑Consumer Example

The example combines shared memory (buffer), a message queue (notification), and a semaphore (synchronization) to implement a classic producer‑consumer scenario.

Producer (producer.c)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <string.h>
union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; };
void PGetSem(int id){ struct sembuf s={0,-1,SEM_UNDO}; if(semop(id,&s,1)==-1){ perror("semop"); exit(0);} }
void VPutSem(int id){ struct sembuf s={0,1,SEM_UNDO}; semop(id,&s,1); }
struct msgbuf{ long mtype; char mtext[128]; };
int main(){
    key_t key = ftok(".",3);
    union semun init; init.val=1;
    int msgid = msgget(key, IPC_CREAT|0666);
    int shmid = shmget(key, 1024*2, IPC_CREAT|0666);
    int semid = semget(key,1,IPC_CREAT|0666);
    semctl(semid,0,SETVAL,init);
    char cmd; int flag=1; struct msgbuf buff={10,0};
    while(flag){
        printf("Please input a cmd:");
        scanf("%c", &cmd); getchar();
        buff.mtext[0]=cmd;
        switch(cmd){
            case 'r':
                PGetSem(semid);
                char *shmaddr = shmat(shmid,NULL,0);
                strcpy(shmaddr, "I produce one thing!");
                printf("Write OK
");
                VPutSem(semid);
                msgsnd(msgid,&buff,sizeof(struct msgbuf),0);
                printf("msg send OK
");
                struct msgbuf rbuf;
                msgrcv(msgid,&rbuf,sizeof(struct msgbuf),20,0);
                printf("msg from consumer:%ld,%s
", rbuf.mtype, rbuf.mtext);
                memset(rbuf.mtext,0,128);
                break;
            case 'q':
                msgsnd(msgid,&buff,sizeof(struct msgbuf),0);
                printf("quit
");
                flag=0; break;
            default:
                printf("input error
");
        }
    }
    shmdt(shmaddr);
    msgctl(msgid,IPC_RMID,NULL);
    shmctl(shmid,IPC_RMID,NULL);
    semctl(semid,0,IPC_RMID,NULL);
    return 0;
}

Consumer (consumer.c)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <string.h>
union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; };
void PGetSem(int id){ struct sembuf s={0,-1,SEM_UNDO}; if(semop(id,&s,1)==-1){ perror("semop"); exit(0);} }
void VPutSem(int id){ struct sembuf s={0,1,SEM_UNDO}; semop(id,&s,1); }
struct msgbuf{ long mtype; char mtext[128]; };
int main(){
    key_t key = ftok(".",3);
    int msgid = msgget(key,0);
    int shmid = shmget(key,1024*2,0);
    int semid = semget(key,1,0);
    while(1){
        struct msgbuf rbuf;
        msgrcv(msgid,&rbuf,sizeof(struct msgbuf),10,0);
        if(rbuf.mtext[0]=='r'){
            PGetSem(semid);
            char *shmaddr = shmat(shmid,NULL,0);
            printf("data from producer:%s
", shmaddr);
            VPutSem(semid);
            struct msgbuf wbuf={20,"I have taked"};
            msgsnd(msgid,&wbuf,sizeof(struct msgbuf),0);
        }
        if(rbuf.mtext[0]=='q') break;
        memset(rbuf.mtext,0,128);
    }
    shmdt(shmaddr);
    return 0;
}

Running the producer and consumer demonstrates coordinated production and consumption of data using the three IPC mechanisms.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

message queuesLinuxshared memoryIPCC programmingsignalspipessemaphores
Linux Kernel Journey
Written by

Linux Kernel Journey

Linux Kernel Journey

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.