
霸道操作系统爱上我
操作系统实验
实验一 熟悉linux
实验一要求我们尝试linux的各种基础命令
这是我的实验结果
在linux的终端输入
history
就可以查看历史操作的指令
实验二:Linux下C程序编写
首先我们查看自己的Linux下有没有c语言的编译环境
在终端输入
gcc --version
whereis gcc
查看版本信息和路径
如果没有我们需要自己安装一下
centos下安装gcc
sudo yum install gcc #CentOS/RedHat 系统
sudo apt-get install gcc #Ubuntu/Debian 系统
编译
gcc <源文件.c> -o <重命名>
gcc tcs.c -o tcs -lm
运行
./<文件名>
./tcs
编写
cd ~ 回到目录
我创建了一个新的文件夹,test用来存放实验代码
mkdir test
进入目录
cd test
建立一个C文件
touch tcs.c
然后进入这个文件
vi tcs.c
就可以开始编写了
我写的一个贪吃蛇
通过w,s,a,d来控制方向
ESC退出,源码我会放到我的GitHub
https://github.com/Littleangel-mm/Java---programming/blob/main/Day2/linuxtcs.c
运行的截图如下
进入正题,我去看了实验手册,我发现我做错了
编写简单的C程序,功能为在屏幕上输出“Hellogcc!"。利用该程序练习使用gcc编译
器的E、S、c、o、g选项,观察不同阶段所生成的文件,即*.c、.i、.s、*.o文件和可执行文件
我在test里面新建立了一个文件夹linuxxt
cd linuxxt
touch hellogcc.c
输入源码
int main() {
printf("Hellogcc!\n");
return 0;
}
预处理阶段(生成 .i 文件)
gcc -E hellogcc.c -o hellogcc.i
编译阶段(生成 .s 汇编代码文件)
gcc -S hellogcc.c -o hellogcc.s
汇编阶段(生成 .o 目标文件)
gcc -c hellogcc.c -o hellogcc.o
链接阶段(生成可执行文件)
gcc hellogcc.o -o hellogcc
执行生成的可执行文件
./hellogcc
调试选项: 如果需要生成包含调试信息的可执行文件,可以使用 -g 选项
gcc -g hellogcc.c -o hellogcc
运行效果
多文件 C 程序及 Makefile 的实现
在liunxxt的路径下,新建立了一个文件夹duowj
cd duowj
首先建立一个头文件 greeting.h
touch greeting.h
在里面输入
#define _GREETING_H
void greeting(char *name);
#endif
再建立函数文件 greeting.c
touch greeting.c
输入以下测试代码
#include "greeting.h"
void greeting(char *name) {
printf("Hello, %s!\n", name);
}
接着是主函数文件 myapp.c
touch myapp.c
输入以下测试代码
#include "greeting.h"
#define N 10
int main() {
char name[N];
printf("Your name, please: ");
scanf("%s", name);
greeting(name);
return 0;
}
然后就是makefile的编写
新建一个文件明眸为makefile
touch makeflie
在makefile输入以下测试的依赖关系
CC = gcc
CFLAGS = -Wall -g
# 目标文件和最终程序
OBJS = myapp.o greeting.o
TARGET = myapp
# 目标规则
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)
myapp.o: myapp.c greeting.h
$(CC) $(CFLAGS) -c myapp.c
greeting.o: greeting.c greeting.h
$(CC) $(CFLAGS) -c greeting.c
clean:
rm -f $(OBJS) $(TARGET)
编译程序
make
运行程序
./myapp
运行效果如图
清理生成的文件
make clean
这将删除目标文件和可执行文件
至此 实验二完成
实验三:进程的创建
回到我的/root/testc/linuxxt
这个路径下,我要在这里创建一个新的文件夹jccj
然后创建两个C文件program1和program2
touch program1
touch program1
program1 测试代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main() {
pid_t childpid; // 子进程的PID
int retval; // 子进程的返回值
int status; // 父进程接收到的子进程状态
// 创建一个新进程
childpid = fork();
if (childpid >= 0) { // fork() 成功
if (childpid == 0) { // 当前是子进程
printf("CHILD: I am the child process!\n");
printf("CHILD: Here's my PID: %d\n", getpid());
printf("CHILD: My parent's PID is: %d\n", getppid());
printf("CHILD: The value of fork() return is: %d\n", childpid);
// 子进程执行
printf("CHILD: Sleep for 1 second...\n");
sleep(1);
printf("CHILD: Enter an exit value (0~255): ");
scanf("%d", &retval); // 输入子进程退出码
printf("CHILD: Goodbye!\n");
exit(retval); // 子进程退出,返回输入值
} else { // 当前是父进程
printf("PARENT: I am the parent process!\n");
printf("PARENT: Here's my PID: %d\n", getpid());
printf("PARENT: The value of my child's PID is: %d\n", childpid);
printf("PARENT: I will now wait for my child to exit.\n");
// 父进程等待子进程结束
wait(&status);
printf("PARENT: Child's exit code is: %d\n", WEXITSTATUS(status));
printf("PARENT: Goodbye!\n");
exit(0);
}
} else { // fork() 失败
perror("fork error!");
exit(1);
}
}
program2 测试代码
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main() {
pid_t childpid;
// 创建一个新进程
childpid = fork();
if (childpid >= 0) { // fork() 成功
if (childpid == 0) { // 当前是子进程
printf("CHILD: I am the child process! Executing 'ls'.\n");
printf("CHILD: Here's my PID: %d\n", getpid());
execlp("ls", "ls", "-l", NULL); // 使用 execlp 执行 ls -l 命令
// 如果 exec 执行失败
perror("execlp error");
exit(1);
} else { // 当前是父进程
printf("PARENT: I am the parent process!\n");
printf("PARENT: Here's my PID: %d\n", getpid());
printf("PARENT: Waiting for my child process to finish.\n");
// 父进程等待子进程结束
wait(NULL);
printf("PARENT: Child process finished. Goodbye!\n");
exit(0);
}
} else { // fork() 失败
perror("fork error!");
exit(1);
}
}
然后我们使用编译功能
gcc program1.c -o program1
gcc program2.c -o program2
接下来运行结果如下
至此 实验三完成
实验四进程调度
我还是要回到我的linuxxt路径下,在这里建立新的文件夹jddd
mkdir jddd
cd jddd
touch scheduler
vi scheduler
输入测试代码
#include <stdlib.h>
#include <string.h>
// 定义进程控制块结构体
struct pcb {
char name[10]; // 进程名
char state; // 进程状态 ('W':就绪,'R':运行)
int nice; // 优先级
int ntime; // 需要运行的时间
int rtime; // 已经运行的时间
struct pcb* link; // 链接下一个进程
};
// 全局变量
struct pcb* ready = NULL; // 就绪队列头指针
struct pcb* p = NULL; // 当前运行的进程
typedef struct pcb PCB;
// 函数声明
void sort(); // 对就绪队列排序
void input(); // 输入进程信息
int queue_size(); // 获取队列大小
void disp(PCB* pr); // 显示单个进程信息
void check(); // 显示当前运行进程和就绪队列状态
void destroy(); // 撤销进程
void running(); // 运行一个时间片
// 就绪队列排序函数,优先级从高到低
void sort() {
if (ready == NULL || (p->nice > ready->nice)) {
// 插入队首
p->link = ready;
ready = p;
return;
}
PCB* first = ready;
PCB* second = first->link;
while (second != NULL) {
if (p->nice > second->nice) {
// 插入到 first 和 second 之间
first->link = p;
p->link = second;
return;
}
first = second;
second = second->link;
}
// 插入队尾
first->link = p;
p->link = NULL;
}
// 输入进程信息并建立就绪队列
void input() {
int num;
printf("\n请输入被调度的进程数目: ");
scanf("%d", &num);
for (int i = 0; i < num; i++) {
p = (PCB*)malloc(sizeof(PCB));
if (!p) {
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
printf("\n进程号 No.%d:\n", i + 1);
printf("输入进程名: ");
scanf("%s", p->name);
printf("输入进程优先数: ");
scanf("%d", &p->nice);
printf("输入进程运行时间: ");
scanf("%d", &p->ntime);
p->rtime = 0;
p->state = 'W';
p->link = NULL;
sort(); // 按优先级插入就绪队列
}
}
// 统计就绪队列中进程数
int queue_size() {
int count = 0;
PCB* pr = ready;
while (pr != NULL) {
count++;
pr = pr->link;
}
return count;
}
// 显示单个进程信息
void disp(PCB* pr) {
if (pr) {
printf("%s\t%c\t%d\t%d\t%d\n", pr->name, pr->state, pr->nice, pr->ntime, pr->rtime);
}
}
// 显示当前运行进程和就绪队列状态
void check() {
printf("\n**** 当前正在运行的进程是: %s ****\n", p->name);
printf("进程名\t状态\t优先级\t总时间\t已运行时间\n");
disp(p);
PCB* pr = ready;
if (pr != NULL) {
printf("\n**** 当前就绪队列状态: ****\n");
while (pr != NULL) {
disp(pr);
pr = pr->link;
}
} else {
printf("\n就绪队列为空\n");
}
}
// 撤销运行完成的进程
void destroy() {
printf("进程 [%s] 已完成。\n", p->name);
free(p);
}
// 运行当前进程一个时间片
void running() {
p->rtime++; // 增加运行时间
if (p->rtime == p->ntime) {
destroy(); // 运行完成,撤销进程
} else {
p->nice--; // 降低优先级
p->state = 'W'; // 设置为就绪状态
sort(); // 重新插入就绪队列
}
}
// 主函数
int main() {
int len, h = 0;
char ch;
input(); // 输入进程信息
len = queue_size();
while (len != 0 && ready != NULL) {
h++;
printf("\n=== 第 %d 次调度 ===\n", h);
p = ready; // 取队首进程运行
ready = ready->link; // 更新就绪队列
p->link = NULL;
p->state = 'R'; // 设置为运行状态
check(); // 显示当前状态
running(); // 执行一个时间片
len = queue_size(); // 更新队列长度
printf("\n按任意键继续...");
getchar(); // 等待用户输入
}
printf("\n\n所有进程已经运行完成!\n");
return 0;
}
编译
gcc -std=c99 scheduler.c -o scheduler
运行
./scheduler
输出格式
进程号 No.1:
输入进程名: P1
输入进程优先数: 5
输入进程运行时间: 4
进程号 No.2:
输入进程名: P2
输入进程优先数: 10
输入进程运行时间: 3
进程号 No.3:
输入进程名: P3
输入进程优先数: 7
输入进程运行时间: 5
结果样式
**** 当前正在运行的进程是: P2 ****
进程名 状态 优先级 总时间 已运行时间
P2 R 10 3 0
**** 当前就绪队列状态: ****
P3 W 7 5 0
P1 W 5 4 0
按任意键继续...
如果想退出,可以按ctrl + C
运行效果图
至此,实验四完成
实验五 进程通信
我还是要回到我的linuxxt路径下,在这里建立新的文件夹jctx
mkdir jctx
cd jctx
vi msg_queue.c
测试源码如下
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/wait.h>
// 定义消息结构
struct msgbuf {
long mtype; // 消息类型
char mtext[100]; // 消息内容
};
int main() {
int msgid;
key_t key;
struct msgbuf sndbuf, rcvbuf;
pid_t pid;
// 创建消息队列的键值
key = ftok("progfile", 65);
if (key == -1) {
perror("ftok failed");
exit(EXIT_FAILURE);
}
// 创建消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget failed");
exit(EXIT_FAILURE);
}
// 创建子进程
pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid > 0) {
// 父进程
// 写入消息队列
sndbuf.mtype = 1; // 设置消息类型
strcpy(sndbuf.mtext, "今天下午我们要继续做实验!");
if (msgsnd(msgid, &sndbuf, sizeof(sndbuf.mtext), 0) == -1) {
perror("msgsnd failed");
exit(EXIT_FAILURE);
}
printf("父进程写入消息队列: %s\n", sndbuf.mtext);
// 等待子进程应答
if (msgrcv(msgid, &rcvbuf, sizeof(rcvbuf.mtext), 2, 0) == -1) {
perror("msgrcv failed");
exit(EXIT_FAILURE);
}
printf("父进程收到子进程的应答: %s\n", rcvbuf.mtext);
// 等待子进程结束
waitpid(pid, NULL, 0);
// 删除消息队列
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
perror("msgctl failed");
exit(EXIT_FAILURE);
}
printf("父进程删除消息队列.\n");
} else {
// 子进程
// 读取消息队列
if (msgrcv(msgid, &rcvbuf, sizeof(rcvbuf.mtext), 1, 0) == -1) {
perror("msgrcv failed");
exit(EXIT_FAILURE);
}
printf("子进程读取消息队列: %s\n", rcvbuf.mtext);
// 子进程处理后发送应答消息
sndbuf.mtype = 2; // 设置消息类型为2,用于应答
strcpy(sndbuf.mtext, "收到消息,处理完毕!");
if (msgsnd(msgid, &sndbuf, sizeof(sndbuf.mtext), 0) == -1) {
perror("msgsnd failed");
exit(EXIT_FAILURE);
}
printf("子进程发送应答消息: %s\n", sndbuf.mtext);
}
return 0;
}
编译
gcc -o msg_queue msg_queue.c
这个需要再建立一个progfile文件
touch progfile
运行
./msg_queue
输出样式
子进程读取消息队列: 今天下午我们要继续做实验!
子进程发送应答消息: 收到消息,处理完毕!
父进程收到子进程的应答: 收到消息,处理完毕!
父进程删除消息队列.
看看运行效果
至此实验五完成
实验6 进程的同步
老规矩,依然在我的linuxxt路径下,建立新的文件夹,jctb用来存放实验6的代码
mkdir jctb
cd jctx
touch jctb
vi jctx.c
开写
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define SEMKEY (key_t)0x200
typedef union _senum {
int val;
struct semid_ds *buf;
ushort *array;
} semun;
int semid;
int count = 0; // 读者计数
FILE *fp, *fp1, *fp2;
struct sembuf prmutex = {0, -1, 0}, pwmutex = {1, -1, 0}, ps = {2, -1, 0}; // P操作
struct sembuf vrmutex = {0, 1, 0}, vwmutex = {1, 1, 0}, vs = {2, 1, 0}; // V操作
int initsem() {
semun x;
x.val = 1;
// 信号量初始化
if ((semid = semget(SEMKEY, 3, 0600 | IPC_CREAT | IPC_EXCL)) == -1) {
if (errno == EEXIST) {
semid = semget(SEMKEY, 3, 0);
}
}
// 设置各个信号量的值
if (semctl(semid, 0, SETVAL, x) == -1) {
perror("semctl failed for prmutex\n");
return -1;
}
if (semctl(semid, 1, SETVAL, x) == -1) {
perror("semctl failed for pwmutex\n");
return -1;
}
if (semctl(semid, 2, SETVAL, x) == -1) {
perror("semctl failed for ps\n");
return -1;
}
return semid;
}
int main() {
int i, j, k;
int a[30]; // 存储数据
semid = initsem(); // 初始化信号量
// 启动第一个读者进程
if (fork() == 0) {
// reader 进程
semop(semid, &prmutex, 1); // 读操作 P 操作
if (count == 0) semop(semid, &pwmutex, 1); // 堵塞写进程
count = count + 1; // 增加读者数
printf("Reader 1 - count = %d\n", count);
semop(semid, &vrmutex, 1); // 释放读锁
for (i = 0; i < 30; i++) {
printf("reader 1 %d\n", a[i]);
sleep(1);
}
semop(semid, &prmutex, 1); // 再次加锁
count = count - 1; // 减少读者数
printf("Reader 1 - count = %d\n", count);
if (count == 0) semop(semid, &vwmutex, 1); // 唤醒写进程
semop(semid, &vrmutex, 1); // 释放读锁
exit(0);
}
// 启动写进程
if (fork() == 0) {
// writer 进程
semop(semid, &pwmutex, 1); // P 操作,锁定写进程
printf("I am the writer:\n");
for (k = 0; k < 30; k++) {
a[k] = 3 * k; // 写操作
}
printf("Write finish!!!!\n");
semop(semid, &vwmutex, 1); // V 操作,释放写锁
exit(0);
}
// 启动第二个读者进程
if (fork() == 0) {
// reader 2 进程
semop(semid, &prmutex, 1); // 读操作 P 操作
count = 2; // 设置为2个读者
printf("Reader 2 - count = %d\n", count);
semop(semid, &vrmutex, 1); // 释放读锁
for (j = 0; j < 30; j++) {
printf("reader 2 %d\n", a[j]);
sleep(1);
}
semop(semid, &prmutex, 1); // 再次加锁
count = count - 1; // 减少读者数
printf("Reader 2 - count = %d\n", count);
if (count == 0) semop(semid, &vwmutex, 1); // 唤醒写进程
semop(semid, &vrmutex, 1); // 释放读锁
exit(0);
}
// 等待所有进程结束
wait(NULL);
wait(NULL);
wait(NULL);
return 0;
}
编译
gcc -o jctb jctb.c
运行
./jctb
实验七 动态分区存储管理
在liunxxt的路径下,创建立新的文件夹dtfq
mkdir dtfq
cd dtfq
touch dtfq.C
vi dtfq.c
开写
#include <stdio.h>
#include <stdbool.h> // 支持 bool 类型
float minsize = 5; // 最小空闲区大小
int count1 = 0; // 已分配作业数
int count2 = 0; // 空闲区数目
#define M 10 // 空闲区表的最大项数
#define N 10 // 已分配区表的最大项数
// 已分配区表的定义
struct {
float address; // 已分分区起始地址
float length; // 已分配区长度,单位为字节
int flag; // 已分配区表登记栏标志,"0"表示空栏目
} used_table[N]; // 已分配区表,N表示最大作业数量
// 空闲分区表的定义
struct {
float address; // 空闲区起始地址
float length; // 空闲区长度,单位为字节
int flag; // 空闲区表登记栏标志,"1"表示未分配,"0"表示空栏目
} free_table[M]; // 空闲区表,M表示最大空闲区数量
// 函数声明
void initialize(void); // 初始化两个表
int distribute(int process_name, float need_length); // 分配内存
int recycle(int process_name); // 回收内存
void show(); // 显示内存分配情况
// 初始化两个表
void initialize(void) {
// 初始化已分配表
int a;
for (a = 0; a < N; a++) {
used_table[a].flag = 0; // 已分配区表项初始化为空
}
// 初始化空闲区表
free_table[0].address = 1000; // 初始空闲区起始地址
free_table[0].length = 1024; // 初始空闲区长度
free_table[0].flag = 1; // 标记为未分配
}
// 最优分配算法:动态分区分配
int distribute(int process_name, float need_length) {
int k = -1; // 用于定位选择的空闲分区
int i = 0;
int count = 0;
// 遍历空闲区表,寻找最佳空闲区进行分配
while (i < M) {
if (free_table[i].flag == 1 && need_length <= free_table[i].length) {
count++;
if (count == 1 || free_table[i].length < free_table[k].length) {
k = i;
}
}
i++;
}
// 如果找到合适的空闲区
if (k != -1) {
// 判断是否完全分配
if (free_table[k].length - need_length <= minsize) {
// 完全分配
used_table[count1].address = free_table[k].address;
used_table[count1].length = need_length;
used_table[count1].flag = process_name;
free_table[k].length -= need_length;
free_table[k].address += need_length;
count1++;
} else {
// 切割空闲区进行分配
float ads = free_table[k].address;
float len = need_length;
free_table[k].address += need_length;
free_table[k].length -= need_length;
// 将分配的内存登记到已分配区表
int i = 0;
while (used_table[i].flag != 0 && i < N) {
i++;
}
if (i < N) {
used_table[i].address = ads;
used_table[i].length = len;
used_table[i].flag = process_name;
count1++;
} else {
printf("已分配区表已满,分配失败!\n");
return 0;
}
}
return process_name;
} else {
printf("内存分配区已满,分配失败!\n");
return 0;
}
}
// 回收内存
int recycle(int process_name) {
int y = 0;
float recycle_address, recycle_length;
// 查找要回收的作业
while (y < N && used_table[y].flag != process_name) {
y++;
}
if (y < N) {
recycle_address = used_table[y].address;
recycle_length = used_table[y].length;
used_table[y].flag = 0;
count2++;
} else {
printf("该作业不存在!\n");
return 0;
}
// 合并空闲区
int i = 0, j = -1, k = -1;
while (i < M && (k == -1 || j == -1)) {
if (free_table[i].flag == 1) {
if ((free_table[i].address + free_table[i].length) == recycle_address) {
k = i; // 上邻接
}
if ((recycle_address + recycle_length) == free_table[i].address) {
j = i; // 下邻接
}
}
i++;
}
// 合并逻辑
if (k != -1) {
// 有上邻接
if (j != -1) { // 上下邻接都有
free_table[k].length += free_table[j].length + recycle_length;
free_table[j].flag = 0;
} else {
free_table[k].length += recycle_length; // 只有上邻接
}
} else if (j != -1) {
// 只有下邻接
free_table[j].length += recycle_length;
free_table[j].address = recycle_address;
} else {
// 上下都没有邻接
int x = 0;
while (free_table[x].flag != 0) {
x++;
}
if (x < M) {
free_table[x].address = recycle_address;
free_table[x].length = recycle_length;
free_table[x].flag = 1;
} else {
printf("空闲区已满,回收失败!\n");
used_table[y].flag = process_name;
return 0;
}
}
return process_name;
}
// 显示内存分配情况
void show() {
int i;
printf("+++++++++++++++++++++++++++++++++++++++\n");
printf("+++++++ 空闲区 +++++++\n");
for (i = 0; i < M; i++) {
if (free_table[i].flag != 0) {
printf("初始地址: %.2f 长度: %.2f 状态: %d\n", free_table[i].address,
free_table[i].length, free_table[i].flag);
}
}
printf("+++++++++++++++++++++++++++++++++++++++\n");
printf("+++++++ 已分配区 +++++++\n");
for (i = 0; i < N; i++) {
if (used_table[i].flag != 0) {
printf("作业号: %d 初始地址: %.2f 长度: %.2f\n", used_table[i].flag,
used_table[i].address, used_table[i].length);
}
}
}
// 主函数
int main() {
int choice;
bool exitFlag = false;
int job_name, ID;
float need_memory;
printf("动态分区分配方式的模拟\n");
initialize(); // 初始化
while (!exitFlag) {
printf("********************************************\n");
printf("1: 分配内存\n");
printf("2: 回收内存\n");
printf("3: 查看分配\n");
printf("0: 退出\n");
printf("********************************************\n");
printf("请输入您的操作:");
scanf("%d", &choice);
switch (choice) {
case 0:
exitFlag = true;
break;
case 1:
// 分配内存
printf("请输入作业号和所需内存:");
scanf("%d %f", &job_name, &need_memory);
if (job_name != 0 && need_memory != 0) {
distribute(job_name, need_memory);
} else {
printf("作业号和内存大小不能为零!\n");
}
break;
case 2:
// 回收内存
printf("请输入您要释放的作业号:");
scanf("%d", &ID);
if (ID != 0) {
recycle(ID);
} else {
printf("作业号不能为零!\n");
}
break;
case 3:
// 查看分配情况
show();
break;
default:
printf("无效的选择,请重新输入。\n");
}
}
return 0;
}
编译
gcc -std=c99 -o dtfq dtfq.c
运行
./dtfq
运行截图
实验8 页面置换算法
在liunxxt的路径下,创建立新的文件夹dtfq
mkdir ymzh
cd ymzh
touch ymzh.C
vi dtfq.c
开写
#include <stdio.h>
#include <stdlib.h>
#define Max 100
#define Min 20
static int distribute_block; // 系统分配给主存的块数
static int total_pages; // 程序的页面总数
// OPT 算法
void opt(int *s, int *b) {
int i, j, k, max, temp, count = 0;
int sum[Min];
for (j = 1; j <= total_pages; j++) {
// 遍历所有页面,检查是否存在于内存块中
for (i = 1; i <= distribute_block; i++) {
if (s[j] == b[i]) break;
}
if (i <= distribute_block) continue; // 页面已在内存中,无需置换
count++; // 需要进行页面置换
// 寻找需要被替换的页面
for (i = 1; i <= distribute_block; i++) {
// 检查该页面在未来的使用情况
for (k = j + 1; k <= total_pages; k++) {
if (b[i] == s[k]) {
sum[i] = k - j; // 找到页面在未来的位置
break;
}
}
if (k > total_pages) sum[i] = 0; // 页面不再被访问
}
// 选择最长时间后不会再使用的页面进行替换
max = 0;
for (i = 1; i <= distribute_block; i++) {
if (sum[i] == 0) {
max = i;
break;
}
if (sum[i] > sum[max]) {
max = i;
}
}
b[max] = s[j]; // 替换页面
// 输出内存块状态
for (i = 1; i <= distribute_block; i++) {
printf("%d\t", b[i]);
}
printf("\n");
}
printf("--------------opt 算法----------------\n");
printf("页面置换的次数为 %d\n", count);
printf("缺页中断率为 %.2f\n", (double)count / total_pages);
printf("------------------------------------\n\n");
}
// FIFO 算法
void fifo(int *s, int *b) {
int i, j, max, count = 0;
int sum[Min] = {0};
for (j = 1; j <= total_pages; j++) {
// 检查页面是否已经在内存中
for (i = 1; i <= distribute_block; i++) {
if (s[j] == b[i]) break;
}
if (i <= distribute_block) continue; // 页面已在内存中
count++; // 页面置换
// 找到一个最早进入内存的页面进行替换
max = 0;
for (i = 1; i <= distribute_block; i++) {
if (sum[i] == 0) {
max = i;
break;
}
if (sum[i] > sum[max]) {
max = i;
}
}
b[max] = s[j]; // 替换页面
// 输出内存块状态
for (i = 1; i <= distribute_block; i++) {
printf("%d\t", b[i]);
}
printf("\n");
}
printf("--------------fifo 算法----------------\n");
printf("页面置换的次数为 %d\n", count);
printf("缺页中断率为 %.2f\n", (double)count / total_pages);
printf("------------------------------------\n\n");
}
// LRU 算法
void lru(int *s, int *b) {
int i, j, k, max, temp, count = 0;
int sum[Min];
for (j = 1; j <= total_pages; j++) {
for (i = 1; i <= distribute_block; i++) {
if (s[j] == b[i]) break;
}
if (i <= distribute_block) continue; // 页面已在内存中,无需置换
count++; // 页面置换
// 记录页面的使用情况
for (i = 1; i <= distribute_block; i++) {
sum[i] = j - i; // 页面与当前页面的距离
}
// 找到最近最少使用的页面
max = 0;
for (i = 1; i <= distribute_block; i++) {
if (sum[i] > sum[max]) {
max = i;
}
}
b[max] = s[j]; // 替换页面
// 输出内存块状态
for (i = 1; i <= distribute_block; i++) {
printf("%d\t", b[i]);
}
printf("\n");
}
printf("--------------lru 算法----------------\n");
printf("页面置换的次数为 %d\n", count);
printf("缺页中断率为 %.2f\n", (double)count / total_pages);
printf("------------------------------------\n\n");
}
int main() {
int Sum[Max], block[Min];
int x;
int i;
printf("请输入系统分配给主存的块: ");
scanf("%d", &distribute_block);
printf("请输入程序的页面总数: ");
scanf("%d", &total_pages);
printf("请输入程序的 %d 个内容所在的页面: ", total_pages);
for (i = 1; i <= total_pages; i++) {
scanf("%d", &Sum[i]);
}
printf("// 初始化块的存储页面,负数表示暂时未存储\n");
printf("请输入块存储的 %d 个页面: ", distribute_block);
for (i = 1; i <= distribute_block; i++) {
scanf("%d", &block[i]);
}
do {
printf("1. opt 算法 2. fifo 算法 3. lru 算法 4. 结束进程\n");
scanf("%d", &x);
switch (x) {
case 1:
opt(Sum, block);
break;
case 2:
fifo(Sum, block);
break;
case 3:
lru(Sum, block);
break;
default:
break;
}
} while (x != 4);
return 0;
}
编译
gcc -o ymzh ymzh.c
./ymzh
运行结果
请输入程序的页面总数: 8
请输入程序的 8 个内容所在的页面: 7 0 1 2 0 3 0 4
// 初始化块的存储页面,负数表示暂时未存储
请输入块存储的 3 个页面: -1 -1 -1
1. opt 算法 2. fifo 算法 3. lru 算法 4. 结束进程
7 0 1
0 7 2
2 7 3
3 7 4
页面置换的次数为 4
缺页中断率为 0.50
------------------------------------
7 0 1
0 2 1
0 3 1
0 3 4
页面置换的次数为 4
缺页中断率为 0.50
------------------------------------
7 0 1
0 2 1
0 3 1
0 3 4
页面置换的次数为 4
缺页中断率为 0.50
------------------------------------
运行截图