面试题答案
一键面试原理
- 共享内存释放原理:共享内存是多个进程可以共同访问的一块内存区域。当进程退出时,需要确保这块共享内存被正确释放,否则会导致内存泄漏。Linux提供了系统调用
shmctl
来控制共享内存,其中IPC_RMID
命令用于标记共享内存段待删除。只有当所有使用该共享内存段的进程都脱离(shmdt
)该段后,共享内存才会真正被删除。 - 信号量释放原理:信号量用于进程间同步和互斥访问共享资源。进程退出时,需要将占用的信号量释放,避免其他进程永远等待。
semctl
系统调用中的IPC_RMID
命令用于删除信号量集。
数据结构设计
- 共享内存:可以定义一个结构体来管理共享内存相关信息,例如:
typedef struct {
key_t key;
int shmid;
void *shmaddr;
} SharedMemory;
key
是共享内存的键值,shmid
是共享内存标识符,shmaddr
是共享内存映射到进程地址空间的起始地址。
2. 信号量:定义一个结构体管理信号量相关信息,例如:
typedef struct {
key_t key;
int semid;
} Semaphore;
key
是信号量的键值,semid
是信号量标识符。
代码实现
- 共享内存释放:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define SHM_SIZE 1024
int main() {
SharedMemory shm;
shm.key = ftok(".", 'a');
if (shm.key == -1) {
perror("ftok");
return 1;
}
shm.shmid = shmget(shm.key, SHM_SIZE, IPC_CREAT | 0666);
if (shm.shmid == -1) {
perror("shmget");
return 1;
}
shm.shmaddr = shmat(shm.shmid, NULL, 0);
if (shm.shmaddr == (void *)-1) {
perror("shmat");
return 1;
}
// 使用共享内存
//...
// 进程退出前释放共享内存
if (shmdt(shm.shmaddr) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shm.shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
return 1;
}
return 0;
}
- 信号量释放:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
int main() {
Semaphore sem;
sem.key = ftok(".", 'b');
if (sem.key == -1) {
perror("ftok");
return 1;
}
sem.semid = semget(sem.key, 1, IPC_CREAT | 0666);
if (sem.semid == -1) {
perror("semget");
return 1;
}
union semun arg;
arg.val = 1;
if (semctl(sem.semid, 0, SETVAL, arg) == -1) {
perror("semctl SETVAL");
return 1;
}
// 使用信号量
//...
// 进程退出前释放信号量
if (semctl(sem.semid, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
return 1;
}
return 0;
}
多进程协调资源释放防止竞态条件
- 原理:可以使用信号量来协调多个进程对共享资源释放的操作。在释放共享内存或信号量之前,获取一个特定的信号量,确保同一时间只有一个进程进行释放操作,释放完成后再释放该信号量。
- 代码实现:
#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>
#define SHM_SIZE 1024
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
void sem_down(int semid, int num) {
struct sembuf sb;
sb.sem_num = num;
sb.sem_op = -1;
sb.sem_flg = SEM_UNDO;
if (semop(semid, &sb, 1) == -1) {
perror("sem_down");
exit(1);
}
}
void sem_up(int semid, int num) {
struct sembuf sb;
sb.sem_num = num;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
if (semop(semid, &sb, 1) == -1) {
perror("sem_up");
exit(1);
}
}
int main() {
SharedMemory shm;
Semaphore sem;
shm.key = ftok(".", 'a');
if (shm.key == -1) {
perror("ftok for shm");
return 1;
}
shm.shmid = shmget(shm.key, SHM_SIZE, IPC_CREAT | 0666);
if (shm.shmid == -1) {
perror("shmget");
return 1;
}
shm.shmaddr = shmat(shm.shmid, NULL, 0);
if (shm.shmaddr == (void *)-1) {
perror("shmat");
return 1;
}
sem.key = ftok(".", 'b');
if (sem.key == -1) {
perror("ftok for sem");
return 1;
}
sem.semid = semget(sem.key, 1, IPC_CREAT | 0666);
if (sem.semid == -1) {
perror("semget");
return 1;
}
union semun arg;
arg.val = 1;
if (semctl(sem.semid, 0, SETVAL, arg) == -1) {
perror("semctl SETVAL");
return 1;
}
// 使用共享内存和信号量
//...
// 进程退出前协调释放共享内存
sem_down(sem.semid, 0);
if (shmdt(shm.shmaddr) == -1) {
perror("shmdt");
return 1;
}
if (shmctl(shm.shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
return 1;
}
sem_up(sem.semid, 0);
// 释放信号量
if (semctl(sem.semid, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
return 1;
}
return 0;
}
通过上述代码,在释放共享资源(共享内存和信号量)时,先获取协调信号量,完成释放后再释放该信号量,从而防止竞态条件。