#include <stdio.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <signal.h> // ✅ 新增:用于 SIGTERM
#define SHMKEY_BASE 1024 // 共享内存基 key
#define SEMKEY 2048 // 信号量 key
#define DATASIZE 256 // 每个缓冲区的数据 int 数量
#define BUFFERNUM 10 // 缓冲区个数
#define BLOCK_SIZE (DATASIZE * sizeof(int)) // 实际字节数
// 共享缓冲区结构
typedef struct ShareBuffer {
int data_size; // 实际写入的字节数
int data[DATASIZE]; // 数据区(总共 BLOCK_SIZE 字节)
} ShareBuffer;
// ✅ 全局变量声明必须放在所有函数之前!
int sid; // 信号量 ID
int shmid[BUFFERNUM]; // 共享内存 ID 数组 ← 必须提前声明!
// 信号量操作封装
void P(int semid, int index) {
struct sembuf sb = {index, -1, 0};
semop(semid, &sb, 1);
}
void V(int semid, int index) {
struct sembuf sb = {index, 1, 0};
semop(semid, &sb, 1);
}
void printHelp() {
fprintf(stderr, "Usage: %s <source_file> <dest_file>\n", "a.out");
}
// 生产者:读文件入共享内存
void read_buf(int src_fd) {
ShareBuffer* buffers[BUFFERNUM];
// 一次性 attach 所有共享内存
for (int i = 0; i < BUFFERNUM; ++i) {
buffers[i] = (ShareBuffer*)shmat(shmid[i], NULL, 0);
if (buffers[i] == (void*)-1) {
perror("shmat in reader");
exit(1);
}
}
int j = 0;
ssize_t n;
while ((n = read(src_fd, buffers[j]->data, BLOCK_SIZE)) > 0) { // ✅ 使用 -> 而不是 .
P(sid, 2); // 请求一个空缓冲区
P(sid, 0); // 获取互斥锁
printf("Reading block %d: %ld bytes\n", j, n);
buffers[j]->data_size = n; // ✅ 正确:使用 -> 访问指针成员
V(sid, 0); // 释放互斥锁
V(sid, 1); // 增加满缓冲区计数
j = (j + 1) % BUFFERNUM;
}
if (n < 0) {
perror("read error");
}
// 标记结束:最后一个块 data_size 设为 0
P(sid, 2);
P(sid, 0);
buffers[j]->data_size = 0; // 终止标志
V(sid, 0);
V(sid, 1);
close(src_fd);
for (int i = 0; i < BUFFERNUM; ++i)
shmdt(buffers[i]); // 解除映射
exit(0);
}
// 消费者:从共享内存写文件
void write_buf(int dst_fd) {
ShareBuffer* buffers[BUFFERNUM];
// attach 所有共享内存
for (int i = 0; i < BUFFERNUM; ++i) {
buffers[i] = (ShareBuffer*)shmat(shmid[i], NULL, 0);
if (buffers[i] == (void*)-1) {
perror("shmat in writer");
exit(1);
}
}
int j = 0;
while (1) {
P(sid, 1); // 等待一个满缓冲区
P(sid, 0); // 获取互斥锁
int size = buffers[j]->data_size;
if (size == 0) { // 收到终止信号
V(sid, 0); // 释放锁
V(sid, 2); // 归还空槽
break;
}
printf("Writing block %d: %d bytes\n", j, size);
if (write(dst_fd, buffers[j]->data, size) != size) {
perror("write error");
break;
}
V(sid, 0); // 释放互斥锁
V(sid, 2); // 空缓冲区+1
j = (j + 1) % BUFFERNUM;
}
close(dst_fd);
for (int i = 0; i < BUFFERNUM; ++i)
shmdt(buffers[i]);
exit(0);
}
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr, "参数错误!\n");
printHelp();
return 1;
}
// 打开原始文件(使用系统调用)
int src_fd = open(argv[1], O_RDONLY);
if (src_fd < 0) {
perror("无法打开源文件");
return 1;
}
int dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd < 0) {
perror("无法创建目标文件");
close(src_fd);
return 1;
}
// 创建信号量集(3个)
sid = semget((key_t)SEMKEY, 3, IPC_CREAT | 0666);
if (sid == -1) {
perror("semget failed");
close(src_fd); close(dst_fd);
return 1;
}
// 初始化信号量
semctl(sid, 0, SETVAL, 1); // mutex = 1
semctl(sid, 1, SETVAL, 0); // full = 0
semctl(sid, 2, SETVAL, BUFFERNUM); // empty = BUFFERNUM
// 创建共享内存段
for (int i = 0; i < BUFFERNUM; ++i) {
shmid[i] = shmget((SHMKEY_BASE + i), sizeof(ShareBuffer), IPC_CREAT | 0666);
if (shmid[i] == -1) {
perror("shmget failed");
// 清理已创建的资源
for (int k = 0; k < i; ++k)
shmctl(shmid[k], IPC_RMID, NULL);
semctl(sid, 0, IPC_RMID);
return 1;
}
}
// 创建两个子进程
pid_t p1 = fork();
if (p1 == 0) {
read_buf(src_fd); // 子进程执行读取
} else if (p1 < 0) {
perror("fork reader failed");
return 1;
}
pid_t p2 = fork();
if (p2 == 0) {
write_buf(dst_fd); // 子进程执行写入
} else if (p2 < 0) {
perror("fork writer failed");
kill(p1, SIGTERM); // ✅ 现在可用:因为包含了 <signal.h>
return 1;
}
// 父进程等待
waitpid(p1, NULL, 0);
waitpid(p2, NULL, 0);
// 清理资源
semctl(sid, 0, IPC_RMID); // 删除信号量集
for (int i = 0; i < BUFFERNUM; ++i) {
shmctl(shmid[i], IPC_RMID, NULL); // 删除共享内存
}
printf("文件复制完成。\n");
return 0;
}
怎么运行
最新发布