面试题答案
一键面试常用同步机制
- 互斥锁(Mutex):
- 互斥锁是一种特殊的二元信号量,取值只有0和1。它的作用是保证在同一时刻只有一个进程能够访问共享资源。当一个进程获取到互斥锁(将其值设为0),其他进程就不能再获取,直到该进程释放互斥锁(将其值设为1)。
- 优点:简单高效,适用于大多数需要保护共享资源的场景。
- 缺点:如果一个进程在持有互斥锁时发生异常终止,可能会导致其他进程永远无法获取锁,即死锁情况。
- 信号量(Semaphore):
- 信号量可以是一个任意的非负整数。它通过计数器来控制对共享资源的访问。当一个进程想要访问共享资源时,它需要先获取信号量(将计数器减1),如果计数器的值为0,则该进程被阻塞,直到其他进程释放信号量(将计数器加1)。
- 优点:适用于需要控制同时访问共享资源的进程数量的场景,比互斥锁更灵活。
- 缺点:使用相对复杂,需要正确地初始化和操作信号量的值,否则容易出现死锁或资源泄漏。
- 条件变量(Condition Variable):
- 条件变量通常与互斥锁一起使用。它允许进程在某个条件满足时被唤醒。例如,当共享内存中的数据达到某个特定状态时,等待在条件变量上的进程可以被唤醒。
- 优点:能够实现更复杂的同步逻辑,避免不必要的忙等待。
- 缺点:需要与互斥锁配合使用,编程复杂度较高,容易出现死锁。
使用信号量保证共享内存数据一致性的代码示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#define SHM_SIZE 1024
int main() {
key_t key = ftok(".", 'a');
if (key == -1) {
perror("ftok");
return 1;
}
int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
return 1;
}
char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (void *)-1) {
perror("shmat");
return 1;
}
sem_t *sem_id = sem_open("/mysem", O_CREAT, 0666, 1);
if (sem_id == SEM_FAILED) {
perror("sem_open");
return 1;
}
if (fork() == 0) {
// 子进程
sem_wait(sem_id);
// 访问共享内存
sprintf(shmaddr, "Hello from child");
printf("Child process wrote to shared memory: %s\n", shmaddr);
sem_post(sem_id);
} else {
// 父进程
sem_wait(sem_id);
// 访问共享内存
sprintf(shmaddr, "Hello from parent");
printf("Parent process wrote to shared memory: %s\n", shmaddr);
sem_post(sem_id);
wait(NULL);
}
sem_close(sem_id);
sem_unlink("/mysem");
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
在上述代码中:
ftok
用于生成一个唯一的键值,用于共享内存和信号量的标识。shmget
创建共享内存段。shmat
将共享内存段连接到进程的地址空间。sem_open
创建并初始化一个信号量,初始值为1,表示允许一个进程访问共享资源。- 在父子进程中,通过
sem_wait
获取信号量,访问共享内存,然后通过sem_post
释放信号量。 - 最后,关闭并删除信号量,分离共享内存段并删除共享内存。