面试题答案
一键面试1. 信号量实现共享内存互斥访问原理
在共享内存编程中,信号量是一种计数器,用于控制对共享资源(这里即共享内存)的访问。通过设置信号量的初始值为1,进程在访问共享内存前获取信号量(将计数器减1),访问结束后释放信号量(将计数器加1)。如果信号量的值为0,说明已有其他进程在访问共享内存,此时试图获取信号量的进程会被阻塞,直到信号量的值变为1。
2. 代码示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
// 定义信号量操作函数
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
// 初始化信号量
int init_semaphore(int semid, int value) {
union semun sem_union;
sem_union.val = value;
if (semctl(semid, 0, SETVAL, sem_union) == -1) {
perror("semctl SETVAL");
return -1;
}
return 0;
}
// 获取信号量
int acquire_semaphore(int semid) {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if (semop(semid, &sem_b, 1) == -1) {
perror("semop acquire");
return -1;
}
return 0;
}
// 释放信号量
int release_semaphore(int semid) {
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if (semop(semid, &sem_b, 1) == -1) {
perror("semop release");
return -1;
}
return 0;
}
int main() {
key_t key;
int semid, shmid;
int *shared_memory;
// 生成唯一键值
if ((key = ftok(".", 'a')) == -1) {
perror("ftok");
return 1;
}
// 创建信号量
if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1) {
perror("semget");
return 1;
}
// 初始化信号量为1
if (init_semaphore(semid, 1) == -1) {
return 1;
}
// 创建共享内存
if ((shmid = shmget(key, sizeof(int), IPC_CREAT | 0666)) == -1) {
perror("shmget");
return 1;
}
// 连接共享内存到当前进程地址空间
shared_memory = (int *)shmat(shmid, NULL, 0);
if (shared_memory == (void *)-1) {
perror("shmat");
return 1;
}
// 获取信号量
if (acquire_semaphore(semid) == -1) {
return 1;
}
// 访问共享内存
(*shared_memory)++;
printf("Process %d incremented shared memory value: %d\n", getpid(), *shared_memory);
// 释放信号量
if (release_semaphore(semid) == -1) {
return 1;
}
// 分离共享内存
if (shmdt(shared_memory) == -1) {
perror("shmdt");
return 1;
}
// 删除共享内存和信号量
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
return 1;
}
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
return 1;
}
return 0;
}
3. 代码说明
- 信号量初始化:
init_semaphore
函数使用semctl
函数并设置SETVAL
命令来初始化信号量的值为传入的value
(这里为1)。 - 信号量获取:
acquire_semaphore
函数使用semop
函数,通过设置sem_op
为 -1 来获取信号量,即尝试减少信号量的值。如果信号量的值为0,进程将被阻塞。 - 信号量释放:
release_semaphore
函数使用semop
函数,通过设置sem_op
为 1 来释放信号量,即增加信号量的值,以便其他进程可以获取。
在 main
函数中,先创建信号量并初始化,然后获取信号量,访问共享内存,之后释放信号量,最后清理共享内存和信号量资源。