进程间通信(Inter-Process Communication, IPC)是指不同进程之间进行数据交换和同步的机制。常见的进程间通信方法包括: 下方有附代码示例
-
管道(Pipe):
- 无名管道(Unnamed Pipe):在父子进程或者兄弟进程之间传递数据,是一种单向通信方式。
- 命名管道(Named Pipe 或 FIFO):允许无亲缘关系进程之间进行通信,也是一种单向通信方式。
-
信号(Signal):
- 通过发送信号来通知接收进程执行某种操作,如终止进程、中断等。
-
消息队列(Message Queue):
- 允许一个进程向另一个进程发送消息的数据结构。消息队列是一种可以在不同进程之间通信的方式,通过系统调用来实现。
-
共享内存(Shared Memory):
- 允许多个进程访问同一块物理内存区域,进程可以直接读写这块内存,是一种高效的 IPC 方法。共享内存是最快的方式
-
信号量(Semaphores):
- 用于进程间同步的一种机制,可以用来保护共享资源,避免竞态条件。
-
套接字(Socket):
- 套接字是一种网络编程中常见的通信机制,也可以用于不同主机或同一主机的不同进程之间的通信。
-
文件(File):
- 通过文件系统中的文件进行数据交换,可以结合文件锁定(File Locking)来实现进程间同步。
-
信号量(Semaphore):
- 用于控制多个进程对共享资源的访问,保证资源不被同时访问和破坏。
每种方法都有其适用的场景和特点,选择合适的进程间通信方法取决于应用程序的需求和设计。
C++代码示例
1. 管道(Pipe)
管道是单向通信,只能一边写、另一边读,要反向需要创建新管道
无名管道(Unnamed Pipe)
#include <unistd.h>
#include <iostream>
int main() {
int pipefds[2];
char buffer[30];
// 创建管道
if (pipe(pipefds) == -1) {
perror("pipe");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
close(pipefds[1]); // 关闭写端
read(pipefds[0], buffer, sizeof(buffer));
std::cout << "Child read: " << buffer << std::endl;
close(pipefds[0]);
} else { // 父进程
close(pipefds[0]); // 关闭读端
write(pipefds[1], "Hello from parent", 18);
close(pipefds[1]);
}
return 0;
}
命名管道(FIFO)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#define FIFO_FILE "myfifo"
int main() {
mkfifo(FIFO_FILE, 0666);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
int fd = open(FIFO_FILE, O_RDONLY);
char buffer[100];
read(fd, buffer, sizeof(buffer));
std::cout << "Child read: " << buffer << std::endl;
close(fd);
} else { // 父进程
int fd = open(FIFO_FILE, O_WRONLY);
write(fd, "Hello from parent", 18);
close(fd);
}
unlink(FIFO_FILE);
return 0;
}
2. 信号(Signal)
#include <iostream>
#include <signal.h>
#include <unistd.h>
//信号处理函数
void signal_handler(int signum) {
std::cout << "Received signal: " << signum << std::endl;
}
int main() {
signal(SIGUSR1, signal_handler);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
sleep(1);
kill(getppid(), SIGUSR1); //kill不是杀死进程、kill在此用于发送信号
} else { // 父进程
pause(); // 等待信号
}
return 0;
}
3. 消息队列(Message Queue)
#include <iostream>
#include <sys/ipc.h>
#include <sys/msg.h>
struct msg_buffer {
long msg_type;
char msg_text[100];
};
int main() {
key_t key = ftok("progfile", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
msg_buffer message;
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
msgrcv(msgid, &message, sizeof(message), 1, 0);
std::cout << "Child received: " << message.msg_text << std::endl;
} else { // 父进程
message.msg_type = 1;
strcpy(message.msg_text, "Hello from parent");
msgsnd(msgid, &message, sizeof(message), 0);
wait(NULL);
msgctl(msgid, IPC_RMID, NULL);
}
return 0;
}
4. 共享内存(Shared Memory)
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>
int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
char *str = (char*) shmat(shmid, (void*)0, 0);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
sleep(1); // 确保父进程先写入
std::cout << "Child read: " << str << std::endl;
shmdt(str);
} else { // 父进程
strcpy(str, "Hello from parent");
shmdt(str);
wait(NULL);
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
5. 信号量(Semaphore)
#include <iostream>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
int main() {
key_t key = ftok("semfile", 65);
int semid = semget(key, 1, 0666 | IPC_CREAT);
semctl(semid, 0, SETVAL, 1); // 初始化信号量
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
struct sembuf sb = {0, -1, 0}; // P 操作
semop(semid, &sb, 1);
std::cout << "Child in critical section" << std::endl;
sleep(2);
sb.sem_op = 1; // V 操作
semop(semid, &sb, 1);
} else { // 父进程
struct sembuf sb = {0, -1, 0}; // P 操作
semop(semid, &sb, 1);
std::cout << "Parent in critical section" << std::endl;
sleep(2);
sb.sem_op = 1; // V 操作
semop(semid, &sb, 1);
wait(NULL);
semctl(semid, 0, IPC_RMID); // 删除信号量
}
return 0;
}
6. 套接字(Socket)
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
int main() {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(sockfd, 3);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
int new_sock = accept(sockfd, NULL, NULL);
char buffer[1024] = {0};
read(new_sock, buffer, 1024);
std::cout << "Child read: " << buffer << std::endl;
close(new_sock);
} else { // 父进程
sleep(1); // 确保子进程先等待连接
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
send(client_sock, "Hello from parent", strlen("Hello from parent"), 0);
close(client_sock);
wait(NULL);
}
close(sockfd);
return 0;
}
7. 文件(File)
#include <iostream>
#include <fstream>
#include <unistd.h>
#define FILENAME "ipcfile.txt"
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return 1;
}
if (pid == 0) { // 子进程
sleep(1); // 确保父进程先写入
std::ifstream infile(FILENAME);
std::string content;
infile >> content;
std::cout << "Child read: " << content << std::endl;
infile.close();
} else { // 父进程
std::ofstream outfile(FILENAME);
outfile << "Hello from parent";
outfile.close();
wait(NULL);
remove(FILENAME);
}
return 0;
}