Linux 进程间通信(IPC)
进程间通信(IPC)是指不同进程之间交换数据或信息的机制。在 Unix/Linux 系统中,有多种进程间通信的方式,包括 管道(Pipe)、消息队列(Message Queue)、共享内存(Shared Memory)、信号量(Semaphore) 等。
1. 管道(Pipe)
管道是 Unix/Linux 中最常见的进程间通信方式之一。它允许一个进程将数据写入管道,另一个进程从管道中读取数据。管道通常用于父子进程之间的通信。
示例:父进程和子进程通过管道通信
#include <stdio.h>
#include <unistd.h>
#include <string.h>int main() {int pipefd[2];pid_t pid;char buffer[128];// 创建管道if (pipe(pipefd) == -1) {perror("pipe failed");return 1;}pid = fork(); // 创建子进程if (pid == -1) {perror("fork failed");return 1;}if (pid == 0) { // 子进程close(pipefd[0]); // 关闭读取端const char *msg = "Hello from child process!";write(pipefd[1], msg, strlen(msg) + 1); // 向管道写入数据close(pipefd[1]);} else { // 父进程close(pipefd[1]); // 关闭写入端read(pipefd[0], buffer, sizeof(buffer)); // 从管道读取数据printf("Parent received: %s\n", buffer);close(pipefd[0]);}return 0;
}
解释:
pipe(pipefd)
创建一个管道,其中pipefd[0]
是读取端,pipefd[1]
是写入端。- 父进程和子进程通过
fork()
创建,父进程从管道中读取数据,子进程向管道写入数据。 write()
和read()
分别用于写入和读取管道数据。
2. 消息队列(Message Queue)
消息队列是一个允许进程通过向队列中发送和接收消息来交换信息的机制。它提供了一种可靠的、灵活的通信方式,可以用来实现复杂的进程间通信。
示例:使用消息队列进行进程间通信
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>#define MSG_SIZE 128// 消息结构体
struct message {long msg_type;char msg_text[MSG_SIZE];
};int main() {key_t key = ftok("msgfile", 65); // 创建消息队列的键值int msgid = msgget(key, 0666 | IPC_CREAT); // 获取消息队列IDif (msgid == -1) {perror("msgget failed");return 1;}pid_t pid = fork(); // 创建子进程if (pid == -1) {perror("fork failed");return 1;}if (pid == 0) { // 子进程发送消息struct message msg;msg.msg_type = 1;strcpy(msg.msg_text, "Hello from child process!");if (msgsnd(msgid, &msg, sizeof(msg), 0) == -1) {perror("msgsnd failed");return 1;}printf("Child sent message: %s\n", msg.msg_text);} else { // 父进程接收消息struct message msg;if (msgrcv(msgid, &msg, sizeof(msg), 1, 0) == -1) {perror("msgrcv failed");return 1;}printf("Parent received message: %s\n", msg.msg_text);}// 删除消息队列msgctl(msgid, IPC_RMID, NULL);return 0;
}
解释:
msgget()
创建一个消息队列,msgsnd()
用于发送消息,msgrcv()
用于接收消息。- 父进程和子进程通过消息队列进行通信,子进程发送消息,父进程接收并打印。
3. 共享内存(Shared Memory)
共享内存允许多个进程访问同一块内存区域,提供高效的进程间通信方式。可以通过共享内存来交换数据,无需通过内核中转。
示例:父进程和子进程通过共享内存通信
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>#define SHM_SIZE 128int main() {key_t key = ftok("shmfile", 65); // 创建共享内存的键值int shm_id = shmget(key, SHM_SIZE, 0666 | IPC_CREAT); // 获取共享内存IDif (shm_id == -1) {perror("shmget failed");return 1;}char *shm_ptr = shmat(shm_id, NULL, 0); // 将共享内存映射到进程地址空间if (shm_ptr == (char *) -1) {perror("shmat failed");return 1;}pid_t pid = fork(); // 创建子进程if (pid == -1) {perror("fork failed");return 1;}if (pid == 0) { // 子进程写入共享内存const char *msg = "Hello from child process!";strcpy(shm_ptr, msg); // 写入数据到共享内存} else { // 父进程读取共享内存sleep(1); // 确保子进程先写入数据printf("Parent received: %s\n", shm_ptr);}// 分离共享内存shmdt(shm_ptr);// 删除共享内存shmctl(shm_id, IPC_RMID, NULL);return 0;
}
解释:
shmget()
创建一个共享内存段,shmat()
将共享内存附加到进程的地址空间。- 父进程和子进程通过共享内存进行数据交换。
shmdt()
和shmctl()
分别用于分离和删除共享内存。
4. 信号量(Semaphore)
信号量用于控制多个进程对共享资源的访问,常用于解决同步和互斥问题。
示例:使用信号量进行同步(此处示例比较简化)
#include <stdio.h>
#include <sys/sem.h>
#include <unistd.h>int main() {key_t key = ftok("semfile", 65);int sem_id = semget(key, 1, 0666 | IPC_CREAT);if (sem_id == -1) {perror("semget failed");return 1;}struct sembuf sb = {0, -1, 0}; // 信号量P操作pid_t pid = fork(); // 创建子进程if (pid == -1) {perror("fork failed");return 1;}if (pid == 0) { // 子进程semop(sem_id, &sb, 1); // 等待信号量printf("Child process has acquired the semaphore!\n");} else { // 父进程sleep(1); // 等待子进程sb.sem_op = 1; // 信号量V操作semop(sem_id, &sb, 1); // 释放信号量printf("Parent process has released the semaphore!\n");}semctl(sem_id, 0, IPC_RMID); // 删除信号量return 0;
}
解释:
semget()
创建信号量,semop()
用于对信号量进行操作。- 父子进程通过信号量进行同步,控制对资源的访问。
总结
在 Unix/Linux 系统中,常见的进程间通信方式包括:
- 管道(Pipe):父子进程之间通过管道传输数据。
- 消息队列(Message Queue):进程间传递消息,可以支持多个进程通信。
- 共享内存(Shared Memory):多个进程直接访问共享内存区。
- 信号量(Semaphore):用于进程间的同步和互斥控制。
如果需要高效的数据交换,共享内存 是一个非常好的选择。如果需要管理多个进程之间的消息,消息队列 可能更适合。