Inter-Process Communication in C: Pipes and Message Queues
In operating systems, a process is the basic unit of resource allocation, and inter-process communication (IPC) refers to the mechanisms that allow different processes to exchange data and information. The C language provides several ways to implement IPC, with the two most commonly used methods being pipes and message queues. This article will detail these two methods and demonstrate them with code examples.
1. Pipes
1. Overview of Pipes
A pipe is a half-duplex communication mechanism that allows one process to send data to another process. It is typically used for data transfer between parent and child processes. When using a pipe, one end writes data while the other end reads data.
2. Creating a Pipe
In C, a pipe can be created using the <span>pipe()</span> function. This function takes an integer array as a parameter to store the read and write file descriptors.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd[2]; // File descriptor array
if (pipe(fd) == -1) { // Create pipe
perror("pipe");
exit(EXIT_FAILURE);
}
// fd[0] is the read end, fd[1] is the write end
return 0;
}
3. Usage Example
The following is a simple example demonstrating how to use a pipe for data transfer between a parent and child process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd[2];
pid_t pid;
if (pipe(fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork(); // Create child process
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid > 0) { // Parent process
close(fd[0]); // Close read end
const char *msg = "Hello from parent!";
write(fd[1], msg, sizeof(msg)); // Send message to write end
close(fd[1]); // Close write end
wait(NULL); // Wait for child process to finish
} else { // Child process
close(fd[1]); // Close write end
char buffer[100];
read(fd[0], buffer, sizeof(buffer)); // Receive message from read end
printf("Child received: %s\n", buffer);
close(fd[0]); // Close read end
}
return 0;
}
Summary
In the above code, we first created a pipe and then used the <span>fork()</span> function to create a child process. In the parent process, we sent a message to the write file descriptor, while in the child process, we received and printed the message from the read file descriptor.
2. Message Queues
1. Overview of Message Queues
Unlike pipes, message queues are a more complex but powerful IPC mechanism that allows multiple producers and consumers to exchange information asynchronously. Each message has its own priority and can be processed in order.
2. Creating and Using Message Queues
To use message queues, you need to include the header files <span><sys/ipc.h></span> and <span><sys/msg.h></span>. You can create or access a new or existing message queue using the <span>msgget()</span> function.
Message Structure Definition:
struct msgbuf {
long mtype; /* message type */
char mtext[100]; /* message text */
};
Example Code:
The following is an example of how to create and use a simple message queue for communication:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
struct msgbuf {
long mtype; /* message type */
char mtext[100]; /* message text */
};
int main() {
key_t key = ftok("progfile",65);
int msgid = msgget(key,0666|IPC_CREAT);
struct msgbuf message;
pid_t pid = fork();
if (pid > 0) {
message.mtype = 1;
strcpy(message.mtext, "Hello from parent!");
msgsnd(msgid,&message,sizeof(message),0);
printf("Parent sent: %s\n", message.mtext);
wait(NULL);
} else {
msgrcv(msgid,&message,sizeof(message),1,0);
printf("Child received: %s\n", message.mtext);
msgctl(msgid, IPC_RMID,NULL);
}
return 0;
}
Summary
In this example, we first generated a unique key and created a new message queue. In the parent process, we constructed a new message with a type identifier of <span>mtype</span> and sent it to the queue. In the child process, we received the message of that specific type and printed it. Finally, we deleted the message queue using the <span>msgctl()</span> function to free resources.
3. Summary Comparison
-
Applicable Scenarios:
- Pipes are suitable for simple one-to-one communication.
- Message queues exhibit higher efficiency and flexibility, making them more suitable for complex application scenarios.
-
Synchronization:
- Pipes provide blocking communication.
- Message queues can be set to non-blocking mode to improve efficiency.
-
Performance:
- For small data transfers, both methods perform similarly, but as the load increases, choosing the appropriate method becomes particularly important.
I hope this article helps you understand the two main IPC mechanisms in C—pipes and message queues—and enables you to apply this knowledge to solve practical problems.