0. 全息通讯方式概览
通信方式 | 描述 | 优势 | 缺点 | 典型场景 |
---|---|---|---|---|
管道(Pipes) | 单向通信的机制,通常用于父子进程间通信。数据流从一个进程流向另一个进程。 | - 简单易用 - 基于文件描述符 | - 单向通信,不适合双向通信 - 只能在具有共同祖先的进程间使用 | 进程链中相邻两个进程之间的通信 |
消息队列(Message Queues) | 进程通过消息队列传递数据,可以实现多对多通信。消息被放入队列并按照一定顺序被接收。 | - 异步通信 - 队列缓存多个消息,避免数据丢失 - 适用于多对多通信 | - 有消息队列大小限制 - 冗余的消息复制 | 进程间需要异步通信的情景 |
信号(Signals) | 通过向进程发送信号来通知或请求处理。每个信号都有一个特定的含义。 | - 轻量级,开销小 | - 信号不包含数据信息,只是通知 - 信号的处理不保证顺序 | 进程需要接收通知,如中断处理 |
共享内存(Shared Memory) | 进程间共享同一块物理内存区域,进程可以直接读写对方的数据。需要使用信号量或互斥锁进行同步。 | - 高效,数据直接共享 - 适用于频繁的大量数据交换 | - 需要手动处理同步和互斥 - 内存泄漏和越界访问的风险 | 数据交换较频繁,需要高性能的场景 |
套接字(Sockets) | 基于网络协议的通信方式,可以用于本地或网络通信。提供TCP和UDP两种套接字类型。 | - 可用于本地和网络通信 - 支持多种协议 | - 相对较慢,适用于大数据量或可靠性要求高的通信 - 需要处理网络协议 | 网络通信,如客户端与服务器之间的通信 |
文件映射(Memory-mapped Files) | 将文件映射到进程的地址空间,多个进程可以直接读写这个文件。一种高效的共享数据的方式。 | - 高效,减少数据拷贝 - 数据直接映射到内存 | - 需要文件系统的支持 - 内存映射区域需要手动同步 | 需要大规模数据共享的场景 |
1.管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 创建管道
int pipe_fd[2];
if (pipe(pipe_fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 创建子进程
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
// 子进程
close(pipe_fd[1]); // 关闭写端
char buffer[256];
ssize_t read_bytes = read(pipe_fd[0], buffer, sizeof(buffer));
if (read_bytes == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("子进程收到消息: %s\n", buffer);
close(pipe_fd[0]); // 关闭读端
} else {
// 父进程
close(pipe_fd[0]); // 关闭读端
const char *message = "Hello, child process!";
ssize_t write_bytes = write(pipe_fd[1], message, strlen(message) + 1);
if (write_bytes == -1) {
perror("write");
exit(EXIT_FAILURE);
}
printf("父进程发送消息: %s\n", message);
close(pipe_fd[1]); // 关闭写端
}
return 0;
}
2. 消息队列
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#define MSG_SIZE 256
#define MSG_TYPE 1
struct msg_buffer {
long msg_type;
char msg_text[MSG_SIZE];
};
void send_message(int msg_id, char *message) {
struct msg_buffer msg_snd;
msg_snd.msg_type = MSG_TYPE;
strncpy(msg_snd.msg_text, message, sizeof(msg_snd.msg_text));
if (msgsnd(msg_id, &msg_snd, sizeof(msg_snd.msg_text), 0) == -1) {
perror("msgsnd");
exit(EXIT_FAILURE);
}
}
void receive_messages(int msg_id) {
struct msg_buffer msg_rcv;
while (1) {
if (msgrcv(msg_id, &msg_rcv, sizeof(msg_rcv.msg_text), MSG_TYPE, 0) == -1) {
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("进程 %d 收到消息: %s\n", getpid(), msg_rcv.msg_text);
}
}
int main() {
key_t key;
int msg_id;
// 生成唯一的键值
if ((key = ftok("msg_queue_example", 'B')) == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
// 创建消息队列
if ((msg_id = msgget(key, 0666 | IPC_CREAT)) == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
// 创建多个进程
pid_t child_pids[3];
for (int i = 0; i < 3; ++i) {
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
// 子进程
receive_messages(msg_id);
exit(EXIT_SUCCESS);
} else {
// 父进程
child_pids[i] = child_pid;
}
}
// 父进程发送消息
for (int i = 0; i < 3; ++i) {
char message[MSG_SIZE];
printf("请输入要发送的消息给进程 %d: ", child_pids[i]);
fgets(message, MSG_SIZE, stdin);
send_message(msg_id, message);
}
// 父进程等待子进程结束
for (int i = 0; i < 3; ++i) {
waitpid(child_pids[i], NULL, 0);
}
// 删除消息队列
if (msgctl(msg_id, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(EXIT_FAILURE);
}
return 0;
}
3.信号
common.h 文件
// common.h
#ifndef COMMON_H
#define COMMON_H
#define SIGNAL_NAME SIGUSR1
#endif // COMMON_H
sender.c 文件
// sender.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "common.h"
void signal_handler(int signum) {
if (signum == SIGNAL_NAME) {
printf("Sender: Signal received by sender!\n");
}
}
int main() {
signal(SIGNAL_NAME, signal_handler);
printf("Sender: Sending signal to receiver...\n");
pid_t receiver_pid = /* Replace with the actual PID of the receiver process */;
if (kill(receiver_pid, SIGNAL_NAME) == -1) {
perror("Sender: Error sending signal");
exit(EXIT_FAILURE);
}
return 0;
}
receiver.c 文件
// receiver.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "common.h"
void signal_handler(int signum) {
if (signum == SIGNAL_NAME) {
printf("Receiver: Signal received by receiver!\n");
}
}
int main() {
signal(SIGNAL_NAME, signal_handler);
printf("Receiver: Waiting for signal...\n");
pause(); // Wait for a signal
return 0;
}
4. 共享内存
common.h 文件
// common.h
#ifndef COMMON_H
#define COMMON_H
#include <sys/types.h>
#define SHARED_MEMORY_SIZE 1024
struct SharedData {
char message[SHARED_MEMORY_SIZE];
};
#endif // COMMON_H
writer.c 文件
// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main() {
key_t key = ftok(".", 'A');
if (key == -1) {
perror("Writer: ftok failed");
exit(EXIT_FAILURE);
}
int shmid = shmget(key, sizeof(struct SharedData), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("Writer: shmget failed");
exit(EXIT_FAILURE);
}
struct SharedData *shared_data = (struct SharedData *)shmat(shmid, NULL, 0);
if (shared_data == (void *)-1) {
perror("Writer: shmat failed");
exit(EXIT_FAILURE);
}
printf("Writer: Enter a message: ");
fgets(shared_data->message, SHARED_MEMORY_SIZE, stdin);
shmdt(shared_data);
return 0;
}
reader.c 文件
// reader.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main() {
key_t key = ftok(".", 'A');
if (key == -1) {
perror("Reader: ftok failed");
exit(EXIT_FAILURE);
}
int shmid = shmget(key, sizeof(struct SharedData), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("Reader: shmget failed");
exit(EXIT_FAILURE);
}
struct SharedData *shared_data = (struct SharedData *)shmat(shmid, NULL, 0);
if (shared_data == (void *)-1) {
perror("Reader: shmat failed");
exit(EXIT_FAILURE);
}
printf("Reader: Received message: %s", shared_data->message);
shmdt(shared_data);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
5. 套接字
套接字通信在后面的网络编程会详细讲,此处略!
6. 文件映射
common.h 文件
// common.h
#ifndef COMMON_H
#define COMMON_H
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_PATH "shared_file"
#define FILE_SIZE 1024
struct SharedData {
char message[FILE_SIZE];
};
#endif // COMMON_H
writer.c 文件
// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
int main() {
int fd = open(FILE_PATH, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("Writer: open failed");
exit(EXIT_FAILURE);
}
if (ftruncate(fd, sizeof(struct SharedData)) == -1) {
perror("Writer: ftruncate failed");
exit(EXIT_FAILURE);
}
struct SharedData *shared_data = mmap(NULL, sizeof(struct SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_data == MAP_FAILED) {
perror("Writer: mmap failed");
exit(EXIT_FAILURE);
}
printf("Writer: Enter a message: ");
fgets(shared_data->message, FILE_SIZE, stdin);
munmap(shared_data, sizeof(struct SharedData));
close(fd);
return 0;
}
reader.c 文件
// reader.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
int main() {
int fd = open(FILE_PATH, O_RDONLY);
if (fd == -1) {
perror("Reader: open failed");
exit(EXIT_FAILURE);
}
struct SharedData *shared_data = mmap(NULL, sizeof(struct SharedData), PROT_READ, MAP_SHARED, fd, 0);
if (shared_data == MAP_FAILED) {
perror("Reader: mmap failed");
exit(EXIT_FAILURE);
}
printf("Reader: Received message: %s", shared_data->message);
munmap(shared_data, sizeof(struct SharedData));
close(fd);
return 0;
}