面试题答案
一键面试共享内存与信号量实现进程间内存共享与同步原理
- 共享内存:
shmget
函数用于创建或获取共享内存段。它通过key
值来标识共享内存段,key
可以使用ftok
函数生成。shmget
返回一个共享内存标识符。shmat
函数用于将共享内存段连接到进程的地址空间,这样进程就可以像访问普通内存一样访问共享内存。shmdt
函数用于将共享内存段从进程的地址空间分离。shmctl
函数用于对共享内存段执行各种控制操作,如删除共享内存段等。
- 信号量:
semget
函数用于创建或获取信号量集。同样通过key
值来标识信号量集,返回一个信号量标识符。semop
函数用于对信号量集中的信号量执行操作,比如获取信号量(将信号量值减1)或释放信号量(将信号量值加1)。semctl
函数用于对信号量集执行各种控制操作,如初始化信号量等。
通过信号量来控制对共享内存的访问,在进程访问共享内存前获取信号量,访问完后释放信号量,这样可以避免数据竞争。
示例代码框架
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
// 定义信号量操作结构体
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
// 信号量操作函数
void semaphore_operation(int semid, int semnum, int op) {
struct sembuf sem_op;
sem_op.sem_num = semnum;
sem_op.sem_op = op;
sem_op.sem_flg = 0;
if (semop(semid, &sem_op, 1) == -1) {
perror("semop");
exit(1);
}
}
int main() {
key_t key_shm = ftok(".", 'a');
if (key_shm == -1) {
perror("ftok for shm");
exit(1);
}
key_t key_sem = ftok(".", 'b');
if (key_sem == -1) {
perror("ftok for sem");
exit(1);
}
// 创建共享内存
int shmid = shmget(key_shm, 1024, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 创建信号量
int semid = semget(key_sem, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget");
shmctl(shmid, IPC_RMID, NULL);
exit(1);
}
// 初始化信号量
union semun sem_union;
sem_union.val = 1;
if (semctl(semid, 0, SETVAL, sem_union) == -1) {
perror("semctl SETVAL");
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, NULL);
exit(1);
}
// 连接共享内存到进程地址空间
char *shared_memory = (char *)shmat(shmid, NULL, 0);
if (shared_memory == (void *)-1) {
perror("shmat");
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, NULL);
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
shmdt(shared_memory);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, NULL);
exit(1);
} else if (pid == 0) {
// 子进程
semaphore_operation(semid, 0, -1); // 获取信号量
// 子进程访问共享内存
sprintf(shared_memory, "Data from child");
printf("Child process wrote: %s\n", shared_memory);
semaphore_operation(semid, 0, 1); // 释放信号量
shmdt(shared_memory);
} else {
// 父进程
semaphore_operation(semid, 0, -1); // 获取信号量
// 父进程访问共享内存
printf("Parent process read: %s\n", shared_memory);
semaphore_operation(semid, 0, 1); // 释放信号量
wait(NULL);
shmdt(shared_memory);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, NULL);
}
return 0;
}
在上述代码中:
- 首先使用
ftok
函数生成共享内存和信号量的key
值。 - 然后分别调用
shmget
和semget
创建共享内存段和信号量集。 - 初始化信号量为1,表示共享内存初始状态为可访问。
- 通过
shmat
将共享内存连接到进程地址空间。 - 使用
fork
创建子进程,子进程和父进程通过获取和释放信号量来同步对共享内存的访问。 - 最后在程序结束时,通过
shmdt
分离共享内存,shmctl
删除共享内存段,semctl
删除信号量集。