跨进程通信(Inter-Process Communication,IPC)是操作系统中一个非常重要的概念,它允许不同进程之间进行数据交换和同步。在现代计算机系统中,由于进程的独立性和并发性,实现有效的跨进程通信变得尤为重要。本文将详细介绍几种常见的跨进程通信技巧,帮助您轻松实现不同进程间的数据交互。
1. 管道(Pipe)
管道是一种简单的IPC机制,它允许一个进程向另一个进程发送数据。管道分为无名管道和命名管道两种类型。
1.1 无名管道
无名管道通常用于具有亲缘关系的进程间通信,如父子进程。它只能在创建它的两个进程之间进行通信。
#include <stdio.h>
#include <unistd.h>
int main() {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// 子进程
close(pipefd[0]); // 关闭读端
write(pipefd[1], "Hello, parent!", 17); // 向父进程发送数据
close(pipefd[1]); // 关闭写端
} else {
// 父进程
close(pipefd[1]); // 关闭写端
char buffer[100];
read(pipefd[0], buffer, sizeof(buffer)); // 从子进程读取数据
printf("%s\n", buffer);
close(pipefd[0]); // 关闭读端
}
return 0;
}
1.2 命名管道
命名管道是一种持久的IPC机制,它可以跨不同进程和不同用户进行通信。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int pipefd = mkfifo("mypipe", 0666);
if (pipefd == -1) {
perror("mkfifo");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) {
// 子进程
close(pipefd); // 关闭文件描述符
int newfd = open("mypipe", O_WRONLY);
if (newfd == -1) {
perror("open");
return 1;
}
write(newfd, "Hello, parent!", 17); // 向父进程发送数据
close(newfd); // 关闭文件描述符
} else {
// 父进程
int newfd = open("mypipe", O_RDONLY);
if (newfd == -1) {
perror("open");
return 1;
}
char buffer[100];
read(newfd, buffer, sizeof(buffer)); // 从子进程读取数据
printf("%s\n", buffer);
close(newfd); // 关闭文件描述符
}
return 0;
}
2. 套接字(Socket)
套接字是一种更通用的IPC机制,它允许不同主机上的进程进行通信。套接字分为流套接字和数据报套接字。
2.1 流套接字
流套接字提供面向连接的、可靠的数据传输服务。以下是使用流套接字进行IPC的示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// 创建服务器套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
return 1;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
// 绑定服务器地址
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
return 1;
}
// 监听连接
listen(server_fd, 5);
// 接受连接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd == -1) {
perror("accept");
return 1;
}
// 通信
char buffer[100];
while (1) {
read(client_fd, buffer, sizeof(buffer));
printf("Received: %s\n", buffer);
write(client_fd, "Hello, client!", 17);
}
// 关闭套接字
close(client_fd);
close(server_fd);
return 0;
}
2.2 数据报套接字
数据报套接字提供无连接的、不可靠的数据传输服务。以下是使用数据报套接字进行IPC的示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
// 创建服务器套接字
server_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (server_fd == -1) {
perror("socket");
return 1;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
// 绑定服务器地址
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
return 1;
}
// 通信
char buffer[100];
while (1) {
recvfrom(server_fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &client_addr_len);
printf("Received: %s\n", buffer);
sendto(server_fd, "Hello, client!", 17, 0, (struct sockaddr *)&client_addr, client_addr_len);
}
// 关闭套接字
close(server_fd);
return 0;
}
3. 信号量(Semaphore)
信号量是一种用于进程同步的IPC机制。它允许多个进程对共享资源进行互斥访问。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
int semid, semnum = 0;
struct sembuf sem_op;
// 创建信号量集
semid = semget(IPC_PRIVATE, 1, 0666);
if (semid == -1) {
perror("semget");
return 1;
}
// 初始化信号量
union semun arg;
arg.val = 1;
if (semctl(semid, semnum, SETVAL, arg) == -1) {
perror("semctl");
return 1;
}
// 通信
while (1) {
sem_op.sem_num = semnum;
sem_op.sem_op = -1; // P操作
sem_op.sem_flg = 0;
if (semop(semid, &sem_op, 1) == -1) {
perror("semop");
return 1;
}
printf("Process %d is running\n", getpid());
sleep(1);
sem_op.sem_op = 1; // V操作
if (semop(semid, &sem_op, 1) == -1) {
perror("semop");
return 1;
}
}
// 删除信号量集
if (semctl(semid, semnum, IPC_RMID, arg) == -1) {
perror("semctl");
return 1;
}
return 0;
}
4. 共享内存(Shared Memory)
共享内存是一种高性能的IPC机制,它允许多个进程共享一块内存区域。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
int main() {
int shm_fd;
char *shared_memory;
// 打开共享内存对象
shm_fd = shm_open("/my_shared_memory", O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
perror("shm_open");
return 1;
}
// 设置共享内存大小
ftruncate(shm_fd, sizeof(char) * 100);
// 映射共享内存
shared_memory = mmap(NULL, sizeof(char) * 100, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap");
return 1;
}
// 通信
while (1) {
printf("Process %d: %s\n", getpid(), shared_memory);
sleep(1);
// 更新共享内存内容
strcpy(shared_memory, "Hello, shared memory!");
}
// 解除映射
munmap(shared_memory, sizeof(char) * 100);
// 删除共享内存对象
shm_unlink("/my_shared_memory");
return 0;
}
总结
本文介绍了四种常见的跨进程通信技巧:管道、套接字、信号量和共享内存。这些技巧在不同的场景下有着广泛的应用。通过学习和掌握这些技巧,您可以轻松实现不同进程间的数据交互。
