共享内存是一种进程间通信(IPC)机制,它允许两个或多个进程共享一个给定的存储区。因为数据直接映射到进程的地址空间,所以共享内存是最快的IPC形式之一。然而,由于共享内存不提供数据保护机制,所以需要使用信号量等同步机制来避免竞态条件。
共享内存的特点:
- 最快:由于避免了数据复制,共享内存是最快的IPC方法。
- 无同步机制:需要外部同步机制来保护共享数据。
- 生命周期:共享内存的生命周期与内核一致,除非显式删除。
共享内存的操作步骤:
- 创建共享内存:使用
shmget()
函数创建一块共享内存。 - 附加共享内存:使用
shmat()
函数将共享内存附加到进程地址空间。 - 读写共享内存:进程可以直接读写共享内存中的数据。
- 分离共享内存:使用
shmdt()
函数将共享内存从进程地址空间分离。 - 控制共享内存:使用
shmctl()
函数对共享内存进行控制,如删除共享内存。
共享内存的相关函数:
shmget(int key, size_t size, int shmflg)
:创建或获取共享内存段。shmat(int shmid, const void *shmaddr, int shmflg)
:将共享内存段附加到调用进程的地址空间。shmdt(const void *shmaddr)
:将共享内存段从调用进程的地址空间分离。shmctl(int shmid, int cmd, struct shmid_ds *buf)
:对共享内存段进行控制操作。
进程间通信-共享内存实验:
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SHM_SIZE 1024
int produce() {
key_t key_shm = ftok(".", 'a');
key_t key_sem = ftok(".", 'b');
int shmid = shmget(key_shm, SHM_SIZE, IPC_CREAT | 0666);
if (shmid < 0) {
perror("shmget failed");
exit(EXIT_FAILURE);
}
char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)(-1)) {
perror("shmat failed");
exit(EXIT_FAILURE);
}
int semid = semget(key_sem, 1, IPC_CREAT | 0666);
if (semid < 0) {
perror("semget failed");
exit(EXIT_FAILURE);
}
// 初始化信号量值为 1
if (semctl(semid, 0, SETVAL, 1) == -1) {
perror("semctl SETVAL failed");
exit(EXIT_FAILURE);
}
struct sembuf p_op = {0, -1, 0}; // P 操作
struct sembuf v_op = {0, 1, 0}; // V 操作
// 写入数据
int cnt = 0;
while (1) {
semop(semid, &p_op, 1); // 等待信号量
sprintf(shmaddr, "Hello from producer cnt = %d!", cnt);
semop(semid, &v_op, 1); // 信号量释放
printf("Produced: >> %s\n", shmaddr);
sleep(1);
cnt ++;
}
// 分离和删除共享内存和信号量
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
int consume() {
key_t key_shm = ftok(".", 'a');
key_t key_sem = ftok(".", 'b');
int shmid = shmget(key_shm, SHM_SIZE, 0666);
if (shmid < 0) {
perror("shmget failed");
exit(EXIT_FAILURE);
}
char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)(-1)) {
perror("shmat failed");
exit(EXIT_FAILURE);
}
int semid = semget(key_sem, 1, 0666);
if (semid < 0) {
perror("semget failed");
exit(EXIT_FAILURE);
}
struct sembuf p_op = {0, -1, 0}; // P 操作
struct sembuf v_op = {0, 1, 0}; // V 操作
// 读取数据
while (1) {
semop(semid, &p_op, 1); // 等待信号量
printf("Consumed: << %s\n", shmaddr);
semop(semid, &v_op, 1); // 信号量释放
sleep(1);
}
// 分离和删除共享内存和信号量
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
int main() {
pid_t pid1, pid2;
// 创建两个子进程
pid1 = fork();
if (pid1 < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid1 == 0) {
// 子进程 1:生产者
produce();
} else {
pid2 = fork();
if (pid2 < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid2 == 0) {
// 子进程 2:消费者
consume();
} else {
// 父进程等待两个子进程结束
int status;
waitpid(pid1, &status, 0);
waitpid(pid2, &status, 0);
}
}
return 0;
}
进程间通信-共享内存实验结果: