操作系统实验

实验一 熟悉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
------------------------------------

运行截图