信号量机制优化读写操作
- 避免数据混乱和进程同步问题的思路:
- 使用信号量来控制对管道的访问。在写进程向管道写数据前,先获取信号量,确保同一时间只有一个写进程能向管道写入数据,避免数据混乱。读进程在从管道读数据前也获取信号量,确保读操作与写操作同步。
- 关键代码片段:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
// 定义信号量操作函数
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
void semaphore_P(int semid) {
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = SEM_UNDO;
semop(semid, &sem_op, 1);
}
void semaphore_V(int semid) {
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = 1;
sem_op.sem_flg = SEM_UNDO;
semop(semid, &sem_op, 1);
}
int main() {
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
key_t key = ftok(".", 'a');
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
int semid = semget(key, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
union semun sem_set;
sem_set.val = 1;
if (semctl(semid, 0, SETVAL, sem_set) == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
// 创建写进程
pid_t pid_writer = fork();
if (pid_writer == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid_writer == 0) {
close(pipefd[0]);
semaphore_P(semid);
write(pipefd[1], "Hello from writer", 16);
semaphore_V(semid);
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
// 创建读进程
pid_t pid_reader = fork();
if (pid_reader == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid_reader == 0) {
close(pipefd[1]);
semaphore_P(semid);
char buffer[256];
ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer));
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Read: %s\n", buffer);
}
semaphore_V(semid);
close(pipefd[0]);
exit(EXIT_SUCCESS);
}
// 父进程等待子进程结束
wait(NULL);
wait(NULL);
// 删除信号量
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("semctl IPC_RMID");
exit(EXIT_FAILURE);
}
return 0;
}
- 信号量的初始化、P操作和V操作的作用:
- 初始化:
union semun sem_set;
sem_set.val = 1;
if (semctl(semid, 0, SETVAL, sem_set) == -1) {
perror("semctl");
exit(EXIT_FAILURE);
}
- 将信号量初始化为1,表示管道可以被一个进程访问。这是二值信号量,用于实现互斥访问管道。
- P操作:
void semaphore_P(int semid) {
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = SEM_UNDO;
semop(semid, &sem_op, 1);
}
- 写进程或读进程在访问管道前调用
semaphore_P
。sem_op.sem_op = -1
表示将信号量的值减1。如果信号量的值为0,进程会阻塞,直到信号量的值变为1(即有其他进程释放了信号量),从而保证同一时间只有一个进程能访问管道。
- V操作:
void semaphore_V(int semid) {
struct sembuf sem_op;
sem_op.sem_num = 0;
sem_op.sem_op = 1;
sem_op.sem_flg = SEM_UNDO;
semop(semid, &sem_op, 1);
}
- 写进程或读进程在完成对管道的访问后调用
semaphore_V
。sem_op.sem_op = 1
表示将信号量的值加1,允许其他进程获取信号量并访问管道。SEMU_UNDO
保证在进程异常终止时,信号量能恢复到初始状态,避免死锁。