#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#define SHM_SIZE 1024 // 共享内存大小(字节)
#define BUFFER_SIZE 1024 // 每次读写的缓冲区大小
#define FILE_NAME_SRC "source.txt" // 源文件
#define FILE_NAME_DST "dest.txt" // 目标文件
// 信号量操作:P操作(等待)
void sem_p(int semid, int index) {
struct sembuf sb;
sb.sem_num = index;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(semid, &sb, 1);
}
// 信号量操作:V操作(释放)
void sem_v(int semid, int index) {
struct sembuf sb;
sb.sem_num = index;
sb.sem_op = 1;
sb.sem_flg = 0;
semop(semid, &sb, 1);
}
int main() {
key_t shm_key, sem_key;
int shmid, semid;
char *shm_ptr;
pid_t pid;
int fd_src, fd_dst;
ssize_t read_len;
// 生成共享内存和信号量的关键字
shm_key = ftok(".", 's');
sem_key = ftok(".", 'e');
if (shm_key == -1 || sem_key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
// 创建共享内存(大小SHM_SIZE,权限0666)
shmid = shmget(shm_key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget failed");
exit(EXIT_FAILURE);
}
// 映射共享内存到当前进程地址空间
shm_ptr = (char *)shmat(shmid, NULL, 0);
if (shm_ptr == (char *)-1) {
perror("shmat failed");
shmctl(shmid, IPC_RMID, NULL);
exit(EXIT_FAILURE);
}
// 创建2个信号量:[0]sem_empty(初始1),[1]sem_full(初始0)
semid = semget(sem_key, 2, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget failed");
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID, NULL);
exit(EXIT_FAILURE);
}
semctl(semid, 0, SETVAL, 1); // sem_empty = 1
semctl(semid, 1, SETVAL, 0); // sem_full = 0
// 创建子进程(进程B:接收方)
pid = fork();
if (pid == -1) {
perror("fork failed");
goto cleanup;
}
if (pid == 0) { // 进程B:读取共享内存并写入目标文件
// 打开目标文件(若不存在则创建,权限0644)
fd_dst = open(FILE_NAME_DST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd_dst == -1) {
perror("open dest failed");
exit(EXIT_FAILURE);
}
while (1) {
sem_p(semid, 1); // 等待sem_full(共享内存有数据)
// 检查是否为结束标志(用首字节存储-1表示结束)
if (*(int *)shm_ptr == -1) {
break; // 退出循环
}
// 写入目标文件
if (write(fd_dst, shm_ptr, BUFFER_SIZE) != BUFFER_SIZE) {
perror("write dest failed");
close(fd_dst);
exit(EXIT_FAILURE);
}
sem_v(semid, 0); // 释放sem_empty(共享内存已空)
}
// 关闭目标文件
close(fd_dst);
printf("进程B:文件传输完成\n");
// 退出前清理资源
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
semctl(semid, 0, IPC_RMID); // 删除信号量
exit(EXIT_SUCCESS);
} else { // 进程A:读取源文件并写入共享内存
// 打开源文件(只读)
fd_src = open(FILE_NAME_SRC, O_RDONLY);
if (fd_src == -1) {
perror("open source failed");
waitpid(pid, NULL, 0);
goto cleanup;
}
// 循环读取源文件
while ((read_len = read(fd_src, shm_ptr, BUFFER_SIZE)) > 0) {
// 若读取不足BUFFER_SIZE,用0填充剩余空间(避免残留数据)
if (read_len < BUFFER_SIZE) {
memset(shm_ptr + read_len, 0, BUFFER_SIZE - read_len);
}
sem_p(semid, 0); // 等待sem_empty(共享内存为空)
sem_v(semid, 1); // 释放sem_full(共享内存有数据)
}
if (read_len == -1) { // 读取错误
perror("read source failed");
close(fd_src);
waitpid(pid, NULL, 0);
goto cleanup;
}
// 读取到文件末尾,写入结束标志
sem_p(semid, 0); // 等待sem_empty
*(int *)shm_ptr = -1; // 结束标志
sem_v(semid, 1); // 释放sem_full,通知进程B
// 关闭源文件
close(fd_src);
printf("进程A:文件读取完成\n");
// 等待进程B处理完毕
waitpid(pid, NULL, 0);
}
cleanup:
// 清理资源
shmdt(shm_ptr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}