操作系统实验1-7swjtu

目 录

实验1  观察Linux的行为

1.1实验目的

1.2实验内容

1.2.1设计思想及算法流程

1.2.2源程序清单

1.2.3运行结果及解释

1.3实验总结

实验2  软终端通信实验

2.1实验目的

2.2实验内容

2.2.1设计思想及算法流程

2.2.2源程序清单

2.2.3 运行结果及解释

2.3实验总结

实验3  线程同步实验

3.1实验目的

3.2实验内容

3.2.1设计思想及算法流程

3.2.2源程序清单

3.2.3运行结果及解释

3.3实验总结

实验4  进程调度实验

4.1实验目的

4.2实验内容

4.2.1设计思想及算法流程

4.2.2源程序清单

4.2.3运行结果及解释

4.3实验总结

实验5  内存管理

5.1实验目的

5.2实验内容

5.2.1设计思想及算法流程

5.2.2源程序清单

5.2.3 运行结果及解释

5.3实验总结

实验6  系统调用

6.1实验目的

6.2实验内容

6.2.1伪代码说明程序框架

6.2.2新的系统调用号、系统调用函数名以及使用的内核变量说明

6.2.3 系统调用添加过程说明

6.2.4 用户测试程序使用说明

6.2.5 实验结果

6.2.6 完整的程序代码(包括系统调用函数和用户测试程序)

6.3实验总结

实验7  系统缺页次数统计实验

7.1实验目的

7.2实验内容

7.2.1设计思想及算法流程

7.2.2源程序清单

7.2.3运行结果及解释

7.3实验总结

实验1  观察Linux的行为

1.1实验目的

通过本实验,了解 Linux 系统的组织和行为,观察各种存储系统状态信息的内核

变量;熟悉这些结构与信息。

1.2实验内容

编写程序打印出如下内容(上交的实验 1 名为:test1

1. CPU 类型及型号、Linux 内核版本号

2. 系统最近一次启动以来经历的时间(以标准格式输出)CPU 在三个状态的时间花

费(用户态、内核态、空闲状态)、上下文转换的次数、系统启动以来所创建的进程

3. 内存的使用情况(已使用、可用)

1.2.1设计思想及算法流程

1. CPU 类型及型号、Linux 内核版本号的设计思想及流程

1. `get_cpu_info()`函数用于获取 CPU 信息。它打开`/proc/cpuinfo`文件,逐行

读取文件内容并查找包含"model name"的行,然后输出该行中的 CPU 型号信息。这里

使用了文件操作函数`fopen()``fclose()`来打开和关闭文件,以及使用`fgets()`函数逐

行读取文件内容。

2. `get_kernel_info()`函数用于获取 Linux 内核版本号。它使用`struct utsname`

结构体来存储系统信息,调用`uname()`函数将系统信息存储在该结构体中,然后输出

其中的内核版本号信息。这里主要是通过`uname()`函数获取系统信息。

2. 系统最近一次启动以来经历的时间(以标准格式输出)CPU 在三个状态的时间花

费(用户态、内核态、空闲状态)、上下文转换的次数、系统启动以来所创建的进程

数的设计思想及流程

1. `get_system_last_boot_time()`函数用于获取系统的最后启动时间。它使用

`clock_gettime()`函数和`CLOCK_BOOTTIME`时钟来获取系统的启动时间戳,然后将

其转换为时间格式并输出。

2. `get_system_uptime()`函数用于获取系统从最近一次启动以来经历的时间。

它使用`sysinfo()`函数获取系统信息,包括系统运行时间,然后将其转换为天、小时、

分钟和秒的格式并输出。

3. `get_cpu_usage()`函数用于获取 CPU 在用户态、系统态和空闲态的时间花费

情况。它读取`/proc/stat`文件中关于 CPU 的信息,计算出各个状态的时间花费占比并

输出。

4. `get_context_switches()`函数用于获取系统的上下文切换次数。它也读取

`/proc/stat`文件,找到包含上下文转换次数的行并输出次数。

5. `get_process_count()`函数用于获取从系统启动开始创建的进程数。类似地,

它读取`/proc/stat`文件中关于进程数的信息,并输出创建的进程数。

3. 内存的使用情况(已使用、可用)的设计思想及流程

1. `get_memory_info()`函数用于获取系统的内存信息。它使用`sysinfo()`函数

获取系统信息,包括总内存和可用内存,然后将其转换为 MB 单位并输出。

2. `get_load_avg()`函数用于获取系统的平均负载列表。它使用`getloadavg()`

函数获取系统的平均负载列表,然后输出至上一分钟的平均负载数。

1.2.2源程序清单

Test1

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/utsname.h>

void get_cpu_info() {

char cpu[1024];

FILE* fp = fopen("/proc/cpuinfo", "r");

if (!fp) {

printf("无法打开/proc/cpuinfo 文件!\n");

return;

}

while (fgets(cpu, sizeof(cpu), fp)) {

if (strncmp(cpu, "model name", 10) == 0) {

printf("CPU 型号:%s", cpu + 13);

break;

}

}

fclose(fp);

}

void get_kernel_info() {

struct utsname uts;

if (uname(&uts) == -1) {

printf("无法获取系统信息!\n");

return;

}

printf("Linux 内核版本号:%s\n", uts.release);

}

int main() {

get_cpu_info();

get_kernel_info();

return 0;

}

Test2

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <sys/sysinfo.h>

#include <sys/utsname.h>

void get_system_last_boot_time() {

struct timespec ts;

if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) {

printf("无法获取系统信息!\n");

return;

}

time_t last_boot_time = ts.tv_sec - (ts.tv_nsec / 1000000000);

printf("系统最后启动时间:%s", ctime(&last_boot_time));

}

void get_system_uptime() {

struct sysinfo info;

if (sysinfo(&info) == -1) {

printf("无法获取系统信息!\n");

return;

}

int days = info.uptime / 86400;

int hours = (info.uptime % 86400) / 3600;

int minutes = (info.uptime % 3600) / 60;

int seconds = info.uptime % 60;

printf("系统最近一次启动以来经历的时间:%d 天%d 小时%d 分%d 秒\n", days,

hours, minutes, seconds);

}

void get_cpu_usage() {

FILE* fp = fopen("/proc/stat", "r");

if (!fp) {

printf("无法打开/proc/stat 文件!\n");

return;

}

char line[1024];

fgets(line, sizeof(line), fp);

fclose(fp);

unsigned long long user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;

sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &user, &nice,

&system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guest_nice);

unsigned long long total = user + nice + system + idle + iowait + irq + softirq + steal;

printf("CPU 在三个状态的时间花费:\n");

printf("用户态:%f%%\n", (double)(user - guest - guest_nice) / total * 100);

printf("系统态:%f%%\n", (double)(system + irq + softirq) / total * 100);

printf("空闲态:%f%%\n", (double)idle / total * 100);

}

void get_context_switches() {

FILE* fp = fopen("/proc/stat", "r");

if (!fp) {

printf("无法打开/proc/stat 文件!\n");

return;

}

char line[1024];

while (fgets(line, sizeof(line), fp)) {

if (strncmp(line, "ctxt", 4) == 0) {

unsigned long long ctxt;

sscanf(line, "ctxt %llu", &ctxt);

printf("上下文转换的次数:%llu\n", ctxt);

break;

}

}

fclose(fp);

}

void get_process_count() {

FILE* fp = fopen("/proc/stat", "r");

if (!fp) {

printf("无法打开/proc/stat 文件!\n");

return;

}

char line[1024];

int process_count = 0;

while (fgets(line, sizeof(line), fp)) {

if (strncmp(line, "procs_running", 13) == 0) {

sscanf(line, "procs_running %d", &process_count);

break;

}

}

fclose(fp);

printf("从系统启动开始创建的进程数:%d\n", process_count);

}

int main() {

get_system_last_boot_time();

get_system_uptime();

get_cpu_usage();

get_context_switches();

get_process_count();

return 0;

}

Test3

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/sysinfo.h>

void get_memory_info() {

struct sysinfo info;

if (sysinfo(&info) == -1) {

printf("无法获取系统信息!\n");

return;

}

printf("计算机配置的内存数量:%ldMB\n", info.totalram / 1024 / 1024);

printf("当前可用的内存数量:%ldMB\n", info.freeram / 1024 / 1024);

}

void get_load_avg() {

double loadavg[3];

if (getloadavg(loadavg, 3) == -1) {

printf("无法获取平均负载列表!\n");

return;

}

printf("平均负载列表至上一分钟的平均数:%f\n", loadavg[0]);

}

int main() {

get_memory_info();

get_load_avg();

return 0;

}

1.2.3运行结果及解释

先输入 cd Desktop,用来打开文件夹,其次输入 mkdir test,用来创建 test 文件夹,

用来存储 .c 文件,之后便输入 cd test,打开文件夹 test,用来存储.c 文件,通过输入 touch

test1.c 来创建文件,便用 gedit test1.c 打开.c 文件并输入相应的代码。最后输入 gcc

test1.c -o out,./out 对代码进行输出,从而获取相应的信息。

1.3实验总结

实验2  软终端通信实验

2.1实验目的

本实验要求学生了解什么是信号,掌握软中断的基本原理;掌握中断信号的使用、进程的创建以及系统计时器的使用。

通过对本实验的学习,学生能够学会进程的创建方法,更能加深对Linux中的信号机制的认识,并会使用软中断信号来实现进程间的通信。

2.2实验内容

由父进程创建两个子进程,通过终端输入Crtl+\组合键向父进程发送SIGQUIT软中断信号给父进程;

父进程接受到软中断信号后,向其两个子进程分别发送整数值为16和17软中断信号,子进程获得对应软中断信号后,终止运行;

父进程调用wait()函数等待两个子进程终止,然后自我终止。

2.2.1设计思想及算法流程

设计思想:

首先,main()函数中创建了两个子进程p1和p2,并为它们注册了信号处理函数stop。其次,父进程等待子进程p1和p2结束,并在收到信号后输出相应的提示信息。第三,子进程p1和p2中分别输出任务开始的提示信息,并忽略SIGQUIT信号,注册了信号处理函数stop,等待父进程发来的信号。第四,stop函数用于改变wait_mark的值,控制进程的等待状态。第五,waiting函数用于让进程等待,直到wait_mark的值为0。

最后,当收到信号后,进程输出相应的提示信息,并退出。

算法流程:

算法流程如下:

1.主函数创建子进程p1和p2。

2.父进程等待子进程p1和p2结束。

3.父进程发送信号SIGQUIT给子进程p1和信号SIGKILL给子进程p2。

4.子进程p1收到信号SIGQUIT后,输出任务开始的提示信息,等待父进程发送信号。

5.子进程p2收到信号SIGKILL后,输出任务开始的提示信息,等待父进程发送信号。

6.父进程发送信号SIGUSR1给子进程p1和p2。

7.子进程p1和p2收到信号SIGUSR1后,输出相应的提示信息,并退出。

2.2.2源程序清单

#include<stdio.h>

#include<stdlib.h>

#include<signal.h>

#include<sys/wait.h>

#include<unistd.h>

void waiting();

void stop();

int wait_mark;

int main()

{

    int p1,p2;

    signal(SIGQUIT, stop);

    while ((p1 = fork()) == -1);

    if (p1 > 0)

    {

        while ((p2 = fork()) == -1);

        if (p2 > 0)

        {

            wait_mark = 1;

            waiting();

            kill(p1, 16);

            kill(p2, 17);

            wait(0);

            wait(0);



            lockf(1, 1, 0);

            printf("parent process is killed\n");

            lockf(1, 0, 0);



            exit(0);

        }

        else

        {

            wait_mark = 1;

            printf("task 2 start\n");

            signal(SIGQUIT,SIG_IGN);

            signal(17, stop);//p2

            waiting();

            lockf(1, 1, 0);

            printf("Process 2 received signal 17\n");

              printf("child process 2 is killed by parent!\n");

            lockf(1, 0, 0);

            exit(0);

        }



    }

    else

    {

        wait_mark = 1;

        printf("task 1 start\n");

        signal(SIGQUIT,SIG_IGN);

        signal(16, stop);//p1

        waiting();

        lockf(1, 1, 0);

        printf("Process 1 received signal 16\n");

        printf("child process 1 is killed by parent!\n");

        lockf(1, 0, 0);

        exit(0);

    }

    return 0;

}

void waiting()

{

    while (wait_mark != 0);

}

void stop()

{

    wait_mark = 0;

}

2.2.3 运行结果及解释

按下ctrl+c中断进程时,程序才执行,父进程打印消息,父进程kill子进程,子进程1收到消息上锁,然后解锁关闭进程,随后子进程2同样操作,然后父进程打印,最后中断进程。

2.3实验总结

实验3  线程同步实验

3.1实验目的

掌握操作系统并发的概念

理解并发中的生产-消费者问题

熟悉 Linux 线程编程机制,掌握线程同步的实现方式

3.2实验内容

编写 Linux 线程程序,利用 Linux 线程信号量及 PV 操作实现生产者—消费者的同步关系的程序。

3.2.1设计思想及算法流程

设计思想:

首先,仓库容量和初始产品数量由 TOTAL_NUM INIT_NUM 宏定义设置。其次,使用了两个信号量 p_sem c_sem 来控制生产者和消费者线程的同步。p_sem 表示可用缓冲区的数量,c_sem 表示已经放置缓冲区的数量。第三,使用了互斥信号量sh_sem 来保护对共享资源 cnt 的访问,避免多个线程同时修改 cnt 造成的竞争条件。

第四,在 main 函数中,首先初始化了两个信号量和一个互斥信号量。然后创建了生

产者和消费者线程,并使用 pthread_join 等待线程结束。第五,在生产者线程中,首

先使用 sem_wait(&p_sem)等待可用缓冲区的出现,然后使用互斥信号量锁住缓冲区,

增加产品数量 cnt,释放互斥信号量,最后增加已放置缓冲区的数量并打印信息第六,在消费者线程中,首先使用 sem_wait(&c_sem)等待已放置缓冲区的出现,然后使用互斥信号量锁住缓冲区,减少产品数量 cnt,释放互斥信号量,最后增加可用冲区的数量并打印信息。

算法流程:

1. 初始化仓库容量和初始产品数量、初始化两个信号量 p_sem c_sem、初始化互斥信号量 sh_sem

2. 创建生产者和消费者线程,并等待它们结束

3. 生产者线程:

a. 等待可用缓冲区的出现,调用 sem_wait(&p_sem)

b. 锁住缓冲区,调用 pthread_mutex_lock(&sh_sem)

c. 增加产品数量 cnt

d. 释放缓冲区,调用 pthread_mutex_unlock(&sh_sem)

e. 增加已放置缓冲区的数量

f. 打印信息

4. 消费者线程:

a. 等待已放置缓冲区的出现,调用 sem_wait(&c_sem)

b. 锁住缓冲区,调用 pthread_mutex_lock(&sh_sem)

c. 减少产品数量 cnt

d. 释放缓冲区,调用 pthread_mutex_unlock(&sh_sem)

e. 增加可用缓冲区的数量

f. 打印信息

3.2.2源程序清单

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<pthread.h>

#include<semaphore.h>

#define INIT_NUM 10//仓库原有产品

#define TOTAL_NUM 25 //仓库容量

sem_t p_sem;//同步信号量,可用缓冲区的数量

sem_t c_sem;//同步信号量,已经放置缓冲区的适量

pthread_mutex_t sh_sem;//互斥信号量

int cnt=INIT_NUM;

/*void print(int choice)

{

if(choice)

printf("添加一个产品到仓库中,仓库现有产品:%d 个。\n",cnt);

else

printf("从仓库中取出一个产品,仓库现有产品:%d 个。\n",cnt);

return;

}

*/

//生产者线程

void *productor()

{

while(1){

sleep(2);

sem_wait(&p_sem);

//锁住缓存区

pthread_mutex_lock(&sh_sem);

//print(0);

cnt++;

pthread_mutex_unlock(&sh_sem);

sem_post(&c_sem);

printf("添加一个产品到仓库中,仓库现有产品:%d 个。\n",cnt);

}

}

//消费者线程

void *consumer()

{

while(1){

sleep(1);

sem_wait(&c_sem);

pthread_mutex_lock(&sh_sem);

//print(1);

cnt--;

pthread_mutex_unlock(&sh_sem);

sem_post(&p_sem);

printf("从仓库中取出一个产品,仓库现有产品:%d 个。\n",cnt);

}

}

int main()

{

int sem1=sem_init(&p_sem,0,TOTAL_NUM-INIT_NUM);

if(sem1){

printf("sem1 init failed \n");

exit(1);

}

int sem2=sem_init(&c_sem,0,INIT_NUM);

if(sem2){

printf("sem2 init failed \n");

exit(1);

}

int mutex=pthread_mutex_init(&sh_sem,NULL);

if(mutex){

printf("mutex init failed \n");

exit(1);

}

//创建生产者线程

int res1;

pthread_t t1;

res1=pthread_create(&t1,NULL,productor,NULL);

if(res1!=0){

perror("Thread1 creation failed");

exit(EXIT_FAILURE);

}

//创建消费者线程

int res2;

pthread_t t2;

res2=pthread_create(&t2,NULL,consumer,NULL);

if(res2!=0){

perror("Thread2 creation failed");

exit(EXIT_FAILURE);

}

res1=pthread_join(t1,NULL);

if(res1){

perror("Thread1 join failed");

exit(EXIT_FAILURE);

}

res2=pthread_join(t2,NULL);

if(res2){

perror("Thread2 join failed");

exit(EXIT_FAILURE);

}

exit(0);

}

3.2.3运行结果及解释

设初始仓库产品数量为 10,仓库容量为 25,首先,生产者线程将会不断地向仓库中添加产品,每次添加后打印出当前仓库产品数量。而消费者线程则会不断地从仓库中取出产品,同样在每次取出后打印出当前仓库产品数量。由于生产者线程的生产速度比消费者线程的消费速度慢,所以仓库中的产品数量会逐渐增加,直到达到仓库容量。当仓库已满时,生产者线程会等待,直到有消费者线程取出产品,释放出空间同样,当仓库为空时,消费者线程会等待,直到有生产者线程添加产品。

3.3实验总结

实验4  进程调度实验

4.1实验目的

1.撑握进程调度的概念

2.理解进程调度策略算法,包括 FCFS、RR 及基于优先级的调度算法。

4.2实验内容

本实验要求在 Linux 的编程实现进程调度策略算法的模拟程序

调度策略算法:

1. FCFS(先到先服务)

2. RR(轮循)

3. 基于优先级的调度算法

4.2.1设计思想及算法流程

设计思想:

1.使用 task_struct 结构体表示任务的属性,包括进程 ID、状态、优先级、计数器、开始时间、执行时间和标志位。初始化一个初始任务 init_task,作为当前任务 current

使用 run_q 链表结构存储就绪态的任务,通过 head end 指针构建循环链表。

2. 实现了三种调度策略函数:基于优先级的调度策略(schedule_p)、先到先服务的

调度策略(schedule_f)和轮循的调度策略(schedule_r)。每种调度策略根据任务的

状态、优先级和计数器等属性,选择下一个要执行的任务,并更新任务的状态和执行

时间。调度函数中会根据不同策略创建新的任务、更新任务状态和执行时间,并将任

务加入就绪队列中。

3. init 函数中,根据当前时间初始化任务,将任务加入就绪队列。主函数 main 中,

用户可以选择不同的调度策略来模拟多任务调度,系统会根据用户选择进行相应的调

度和展示。

4. 每次调度会选择下一个要执行的任务,并更新任务的状态和执行时间。通过打印任

务执行情况和结果,用户可以观察每个任务的执行情况、调度顺序和效果。

5. 系统设计灵活,用户可以根据需要选择不同的调度策略,观察任务的执行情况和不

同策略的效果。通过用户交互选择不同的调度策略,系统可以模拟不同的多任务调度

场景,帮助用户理解和比较不同策略的影响。

6.每次调度完成后释放链表空间,重置相关变量,等待用户下一次选择。

算法流程:

1. 定义了几种进程状态常量,以及进程控制块的结构体和一些全局变量。

2. 定义了调度函数指针 schedule,根据用户选择的调度策略,将其指向相应的调度函

数。

3. init 函数初始化任务队列,将任务加入就绪队列。

4. 主函数中使用一个无限循环,不断接收用户选择的调度策略,并进行相应的调度模

拟。

5. 根据用户选择的调度策略,调用相应的调度函数进行模拟调度。

6. 输出任务的执行顺序和时间轴,以及任务的优先级(如果是基于优先级的调度策略)。

7. 释放动态分配的内存。

8. 返回到第 4 步,等待用户选择下一次的调度策略。

4.2.2源程序清单

#include<stdio.h>

#include<stdlib.h>

#include<sys/time.h>

#include<malloc.h>

#include<unistd.h>

#include<time.h>

#define N_PROCESS 5

#define MAX_RUNTIME 100

#define NR_TASKS 64

#define TASK_RUNNING

0

//就绪态

#define TASK_UNINTERRUPTIBLE 2

//不可中断的睡眠状态

#define TASK_ZOMBIE

3

//僵死态

unsigned long volatile jiffies =0;

int process[N_PROCESS][2]={{0,3},{2,6},{4,4},{6,5},{8,2}};

int totalExcuteTime=0;

int runState[N_PROCESS][MAX_RUNTIME]={0};

typedef void funtype();

funtype *schedule = NULL;

struct task_struct

{

long pid;

long state;

long priority;

long counter;

long start_time;

long excute_time;

long flag;

};

struct task_struct init_task = {

.pid = 0,

.state = 0,

.priority = 0,

.counter = 0,

.start_time = 0,

.excute_time = 0,

.flag = 0

};

struct task_struct *current = &init_task;

struct task_struct * task[NR_TASKS]={&init_task,};

struct run_q{

struct task_struct * data;

struct run_q * next;

};

struct run_q *head=NULL,*end=NULL,*r_temp;

void schedule_f()

{

int i, next, c;

struct task_struct **p;

c = 9999;

next = 0;

i = NR_TASKS;

p = &task[NR_TASKS];

while (--i) {

if (!*--p)

continue;

if ((*p)->state == TASK_RUNNING && (*p)->start_time < c)

c = (*p)->start_time, next = i;

}

if(next){

current = task[next];

runState[next - 1][jiffies] = 1;

jiffies++;

task[next]->counter--;

task[next]->excute_time++;

if (task[next]->excute_time == process[next - 1][1])

task[next]->state = TASK_ZOMBIE;

if (jiffies >= totalExcuteTime)

return;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies){

struct timeval now;

struct run_q * temp;

task[i+1] = (struct task_struct *) malloc(sizeof(struct task_struct));

task[i+1]->state = TASK_UNINTERRUPTIBLE;

task[i+1]->pid = i+1;

gettimeofday(&now,NULL);

srand(now.tv_usec);

task[i+1]->priority = 2 + (int) (4.0f * rand() / (RAND_MAX + 1.0f));

task[i+1]->counter = task[i+1]->priority;

task[i+1]->start_time = jiffies;

task[i+1]->excute_time = 0;

task[i+1]->state = TASK_RUNNING;

temp = (struct run_q *) malloc(sizeof(struct run_q));

temp->data = task[i+1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

}

else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

}

else{

current = task[0];

jiffies++;

totalExcuteTime++;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies){

struct timeval now;

struct run_q * temp;

task[i+1] = (struct task_struct *) malloc(sizeof(struct task_struct));

task[i+1]->state = TASK_UNINTERRUPTIBLE;

task[i+1]->pid = i+1;

gettimeofday(&now,NULL);

srand(now.tv_usec);

task[i+1]->priority = 2 + (int) (4.0f * rand() / (RAND_MAX + 1.0f));

task[i+1]->counter = task[i+1]->priority;

task[i+1]->start_time = jiffies;

task[i+1]->excute_time = 0;

task[i+1]->state = TASK_RUNNING;

temp = (struct run_q *) malloc(sizeof(struct run_q));

temp->data = task[i+1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

}

else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

}

}

void schedule_r()

{

int next;

next = 0;

if (current->state != TASK_RUNNING) {

r_temp = head;

if (head == end) {

head = NULL;

end = NULL;

} else {

head = head->next;

end->next = head;

}

free(r_temp);

} else if (head) {

head = head->next;

end = end->next;

}

if (head) next = head->data->pid;

if(next){

current = task[next];

runState[next - 1][jiffies] = 1;

jiffies++;

task[next]->counter--;

task[next]->excute_time++;

if (task[next]->excute_time == process[next - 1][1])

task[next]->state = TASK_ZOMBIE;

if (jiffies >= totalExcuteTime)

return;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies){

struct timeval now;

struct run_q * temp;

task[i+1] = (struct task_struct *) malloc(sizeof(struct task_struct));

task[i+1]->state = TASK_UNINTERRUPTIBLE;

task[i+1]->pid = i+1;

gettimeofday(&now,NULL);

srand(now.tv_usec);

task[i+1]->priority = 2 + (int) (4.0f * rand() / (RAND_MAX + 1.0f));

task[i+1]->counter = task[i+1]->priority;

task[i+1]->start_time = jiffies;

task[i+1]->excute_time = 0;

task[i+1]->state = TASK_RUNNING;

temp = (struct run_q *) malloc(sizeof(struct run_q));

temp->data = task[i+1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

}

else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

}

else{

current = task[0];

jiffies++;

totalExcuteTime++;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies){

struct timeval now;

struct run_q * temp;

task[i+1] = (struct task_struct *) malloc(sizeof(struct task_struct));

task[i+1]->state = TASK_UNINTERRUPTIBLE;

task[i+1]->pid = i+1;

gettimeofday(&now,NULL);

srand(now.tv_usec);

task[i+1]->priority = 2 + (int) (4.0f * rand() / (RAND_MAX + 1.0f));

task[i+1]->counter = task[i+1]->priority;

task[i+1]->start_time = jiffies;

task[i+1]->excute_time = 0;

task[i+1]->state = TASK_RUNNING;

temp = (struct run_q *) malloc(sizeof(struct run_q));

temp->data = task[i+1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

}

else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

}

}

void schedule_p()

{

int FLAG=1;

for(int i=1;i<=N_PROCESS;i++)

if(task[i]!=NULL&&task[i]->state==TASK_RUNNING&&task[i]->counter)

FLAG=0;

if(FLAG){

for(int i=1;i<=N_PROCESS;i++){

if(task[i]!=NULL&&task[i]->state==TASK_RUNNING)

task[i]->counter=task[i]->counter-1+task[i]->priority;

}

}

int next_task = 0;

int highest_counter = 0;

for (int i = 1; i <= N_PROCESS; i++) {

if (task[i] != NULL && task[i]->state == TASK_RUNNING &&

task[i]->counter > highest_counter) {

highest_counter = task[i]->counter;

next_task = i;

}

}

if (next_task != 0) {

current = task[next_task];

runState[next_task - 1][jiffies] = 1;

jiffies++;

task[next_task]->counter--;

task[next_task]->excute_time++;

if (task[next_task]->excute_time == process[next_task - 1][1])

task[next_task]->state = TASK_ZOMBIE;

if (jiffies >= totalExcuteTime)

return;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies) {

struct timeval now;

struct run_q *temp;

task[i + 1] = (struct task_struct *)malloc(sizeof(struct task_struct));

task[i + 1]->state = TASK_UNINTERRUPTIBLE;

task[i + 1]->pid = i + 1;

gettimeofday(&now, NULL);

srand(now.tv_usec);

task[i + 1]->priority = 2 + (int)(rand()%4);

sleep(2);

task[i + 1]->counter = task[i + 1]->priority;

task[i + 1]->start_time = jiffies;

task[i + 1]->excute_time = 0;

task[i + 1]->state = TASK_RUNNING;

temp = (struct run_q *)malloc(sizeof(struct run_q));

temp->data = task[i + 1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

} else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

} else {

current = task[0];

jiffies++;

totalExcuteTime++;

for (int i = 0; i < N_PROCESS; i++) {

if (process[i][0] == jiffies) {

struct timeval now;

struct run_q *temp;

task[i + 1] = (struct task_struct *)malloc(sizeof(struct task_struct));

task[i + 1]->state = TASK_UNINTERRUPTIBLE;

task[i + 1]->pid = i + 1;

gettimeofday(&now, NULL);

srand(now.tv_usec);

task[i + 1]->priority = 2 + (int)(rand()%4);

sleep(2);

task[i + 1]->counter = task[i + 1]->priority;

task[i + 1]->start_time = jiffies;

task[i + 1]->excute_time = 0;

task[i + 1]->state = TASK_RUNNING;

temp = (struct run_q *)malloc(sizeof(struct run_q));

temp->data = task[i + 1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

} else {

end->next = temp;

end = temp;

end->next = head;

}

}

}

schedule();

}

}

void init()

{

for(int i=1;i<NR_TASKS;i++)

task[i]=NULL;

for(int i=0;i<N_PROCESS;i++)

if(process[i][0]==jiffies){

struct timeval now;

struct run_q * temp;

task[i+1] = (struct task_struct *) malloc(sizeof(struct task_struct));

task[i+1]->state = TASK_UNINTERRUPTIBLE;

task[i+1]->pid = i+1;

gettimeofday(&now,NULL);

srand(now.tv_usec);

task[i + 1]->priority = 2 + (int)(rand()%4);

sleep(2);

task[i+1]->counter = task[i+1]->priority;

task[i+1]->start_time = jiffies;

task[i+1]->excute_time = 0;

task[i+1]->state = TASK_RUNNING;

temp = (struct run_q *) malloc(sizeof(struct run_q));

temp->data = task[i+1];

if (head == NULL) {

head = end = temp;

head->next = end;

end->next = head;

}

else {

end->next = temp;

end = temp;

end->next = head;

}

}

schedule();

}

int main()

{

srand((int)time(NULL));

while(1){

printf("please choice the schedule measure:\n");

printf("p : 基于优先级的调度策略\n");

printf("f : 先到先服务的调度策略\n");

printf("r : 轮循的调度策略\n");

printf("q : 退出\n");

printf("choice = ");

char choice=getchar();

if(choice=='\n')

choice=getchar();

switch(choice){

case 'p' :

schedule = schedule_p;

break;

case 'f' :

schedule = schedule_f;

break;

case 'r' :

schedule = schedule_r;

break;

case 'q' :

return 0;

default :

schedule=NULL;

printf("please input the true symbol(p or f or r!)\n\n");

continue;

}

printf("task id start excute\n");

for(int i=0;i<N_PROCESS;i++){

printf("task %2d: %6d %6d\n",i+1,process[i][0],process[i][1]);

totalExcuteTime+=process[i][1];

}

//调度

init();

//打印结果

if(schedule==schedule_p)

for(int i=1;i<=N_PROCESS;i++)

printf("%ld ",task[i]->priority);

printf("\n");

printf("time

:");

for(int i=0;i<=totalExcuteTime;i++)

printf("%3d",i);

printf("\n");

for (int i=0; i < N_PROCESS; i++) {

printf("task %2d: ", i + 1);

for (int j = 0; j < totalExcuteTime; j++) {

if (runState[i][j] == 1)

printf(" # ");

else printf("

");

runState[i][j] = 0;

}

printf("\n");

}

//释放数据

while((head != NULL) && (head != end)) {

r_temp = head;

head = head->next;

free(r_temp);

}

if (head) {

free(head);

head = NULL;

end = NULL;

}

current = &init_task;

jiffies = 0;

totalExcuteTime = 0;

printf("\n");

}

return 0;

}

4.2.3运行结果及解释

结果:

4.3实验总结

实验5  内存管理

5.1实验目的

1.了解内存管理的概念,掌握分页、分段操作过程;

2.掌握虚拟内存技术的概念,重点理解替换策略;

3.理解替换页基本算法最优置换(OPT)、先进先出(FIFO)、最近最少使用(LRU

5.2实验内容

Linux 下编程实现虚存页面替换算法的模拟程序。

1.根据用户输入参数,进程大小(页数),页表大小(内存可存放页数);

2.根据进程页数,随机生成页访问顺序;

3.选择 OPTFIFOLRU 替换算法,并输出页替换流程结果。

5.2.1设计思想及算法流程

程序设计概述

数据结构:

page 结构体:

n:页面编号

v:访问标志(在 LRU 中使用)

pageR:指向 page 的指针数组,表示页面引用。

framepage 数组,表示物理内存帧。

函数:

getchoice:显示菜单并获取有效的用户选择。

buildPageReference:随机生成页面引用。

print:打印当前帧的状态。

Search:在帧中搜索页面。

findNext:找到引用列表中页面的下一个出现。

findLastMax findLastMin:找到最后一个最大或最小访问计数的帧索引,用于

OPT LRU 算法。

页面替换算法函数:

opt:最佳置换算法。fifo:先进先出页面替换算法。

lru:最近最少使用页面替换算法。

主函数:

programframe pageR 分配内存。

获取用户输入的程序页面数和帧数。

使用 buildPageReference 初始化页面引用。

进入循环,用户可以在 OPTFIFOLRU 算法之间选择,或退出。

根据用户选择调用相应的算法函数,直到用户决定退出。

算法流程:

1.OPTopt):

通过替换未来最长时间不会使用的页面来模拟最佳算法。

使用 findNext 来确定引用列表中页面的下一个出现,以帮助决定替换哪个页面。

2.FIFOfifo):

实现一个简单的 FIFO 队列来确定页面故障时要替换的页面。

使用循环数组来跟踪页面访问的顺序。

3.LRUlru):

跟踪最近最少使用的页面。

在每个帧中使用访问计数器(v)来确定最近访问的帧。

使用 findLastMax 找到访问计数最大的帧(最近最少使用)来替换。

5.2.2源程序清单

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define random(x) (rand() % x)

#define MULTIPLE 3

typedef struct page_s {

int n; //number

int v; //visit flag

}page;

char *menu[] = {

"q - quit",

"o - opt",

"f - FIFO",

"r - LRU",

NULL

};

int getchoice(char *greet, char *choices[]) {

int chosen = 0;

int selected;

char **option;

do {

printf("Choice: %s\n", greet);

option = choices;

while(*option) {

printf("%s\n", *option);

option++;

}

do {

selected = getchar();

} while(selected == '\n');

option = choices;

while(*option) {

if(selected == *option[0]) {

chosen = 1;

break;

}

option++;

}

if(!chosen) {

printf("Incorrect choice, select again\n");

}

} while(!chosen);

return selected;

}

void buildPageReference(int size, page **reference, page *program) {

int i;

int n;

printf("Page reference : ");

for(i=0;i<size;i++) {

n = random(size/MULTIPLE);

reference[i] = &program[n];

program[n].n = n;

program[n].v = 0;

printf("| %d ", n);

}

printf("\n");

}

void print(int n, page *frame, int size) {

int i;

printf("no. %d step: ", n);

for(i=0;i<size;i++) {

printf("| %d ", frame[i].n);

}

printf("\n");

}

int Search(int n, page *list, int size) {

int i;

for(i=0;i<size;i++) {

if(list[i].n == n) return i;

}

return -1;

}

int findNext(int n, page **list, int start, int size) {

int count = size;

int i;

for(i=start;i<size;i++) {

if(list[i]->n == n) break;

else count++;

}

return count;

}

int findLastMax(page *frame, int size) {

int tmp=0,s,i,j=0;

for(i=0;i<size;i++) {

s = frame[i].v;

if(s > tmp) {

tmp = s;

j = i;

}

}

return j;

}

int findLastMin(page *frame, int size) {

int tmp=frame[0].v,s,i,j=0;

//printf("| %d ", tmp);

for(i=1;i<size;i++) {

s = frame[i].v;

if(s < tmp) {

tmp = s;

j = i;

}

}

return j;

}

int opt(int fsize, page *frame, int rsize, page **pageR){

int i, j, p=0, q;

int f = 0;

for(i = 0; i < fsize; i++){

frame[i].n = -1;

frame[i].v = 0;

}

for(i = 0; i < rsize; i++){

for(j = 0; j < fsize; j++){

if(frame[j].n != -1){

frame[j].v--;

}

}

q = Search(pageR[i]->n, frame, fsize);

if(q != -1){

frame[q].v = findNext(pageR[i]->n, pageR, i+1, rsize);

}

else if(i < fsize || p < fsize){

frame[p].n = pageR[i]->n;

frame[p].v = findNext(pageR[i]->n, pageR, i+1, rsize);

p++;

}

else{

q = findLastMax(frame, fsize);

frame[q].n = pageR[i]->n;

frame[q].v = findNext(pageR[i]->n, pageR, i+1, rsize);

f++;

}

print(i, frame, fsize);

}

printf("page fault : %d\n", f);

}

int fifo(int fsize, page *frame, int rsize, page **pageR) {

int i, j=0,p=0;

int f=0;

for(i=0;i<fsize;i++) frame[i].n = -1;

for(i=0;i<rsize;i++) {

if(Search(pageR[i]->n, frame, fsize)!=-1);

else if(i<fsize || p<fsize) {

frame[p].n=pageR[i]->n;

p++;

}

else {

frame[j%fsize].n = pageR[i]->n;

j++;

f++;

}

print(i, frame, fsize);

}

printf("page fault : %d\n", f);

}

int lru(int fsize, page *frame, int rsize, page **pageR) {

int i, j, p=0, q;

int f=0;

for(i=0;i<fsize;i++) {

frame[i].n = -1;

frame[i].v = 0;

}

for(i=0;i<rsize;i++) {

for(j=0;j<fsize;j++) {

if(frame[j].n!=-1) frame[j].v++;

}

q = Search(pageR[i]->n, frame, fsize);

if(q!=-1) frame[q].v=0;

else if(i<fsize || p<fsize) {

frame[p].n=pageR[i]->n;

p++;

}else {

q = findLastMax(frame, fsize);

frame[q].n = pageR[i]->n;

frame[q].v = 0;

f++;

}

print(i, frame, fsize);

}

printf("page fault : %d\n", f);

}

int main() {

int choice = 0;

int logSize;

int phySize;

page *program;

page **pageR;

page *frame;

int prSize;

srand((int)time(0));

printf("Enter number of pages in program: ");

scanf("%d", &logSize);

printf("Enter number of frames in physical memory: ");

scanf("%d", &phySize);

program = (page *)malloc(sizeof(int)*2*logSize);

frame = (page *)malloc(sizeof(int)*2*phySize);

prSize = logSize * MULTIPLE;

pageR = (page **)malloc(sizeof(int*)*prSize);

buildPageReference(prSize, pageR,program);

do {

choice = getchoice("Please select an action", menu);

printf("You have chosen: %c\n", choice);

switch(choice) {

case'o':

opt(phySize, frame, prSize, pageR);

break;

case 'f' :

fifo(phySize, frame, prSize, pageR);

break;

case 'r' :

lru(phySize, frame, prSize, pageR);

break;

default: break;

}

}while(choice != 'q');

exit(0);

}

5.2.3 运行结果及解释

5.3实验总结

实验6  系统调用

6.1实验目的

学习如何产生一个系统调用,以及怎样通过往内核中增加一个新函数,从而在内

核空间中实现对用户空间的读/写。学习重建内核。

6.2实验内容

1)在内核的 sys.c 文件中创建一个新的系统调用 mysyscall(),指定调用号为 335,

2)编写一个用户空间程序来测试 mysyscall(335, num), 打印出 num

6.2.1伪代码说明程序框架

系统调用函数伪代码:

函数 mysyscall(参数 a):

返回 a;

用户测试程序伪代码:

导入#include<linux/kernel.h>

导入#include<sys/syscall.h>

导入#include<unistd.h>

导入#inlude<stdio.h>

定义变量 result;

存储 syscall(335, 10086)的返回值;

打印: "Your input is %ld\n" (syscall(335, 10086));

返回 0

6.2.2新的系统调用号、系统调用函数名以及使用的内核变量说明

系统调用号:335

系统调用函数名:mysyscall

内核变量:unsigned long long a,即系统调用的输入参数。

6.2.3 系统调用添加过程说明

1)在 sys.c 文件中定义新的系统调用函数 mysyscall

SYSCALL_DEFINE1(mysyscall, unsigned long long, a) {return a;}

  1. syscall_64.tbl 文件中注册新的系统调用号 335 并关联到 sys_mysyscall 数:

335 64 mysyscall sys_mysyscall

3)在 syscalls.h 文件中声明新的系统调用函数 sys_mysyscall

asmlinkage long sys_mysyscall(unsigned long long);

4)重新编译并安装内核:

清除历史编译数据

make mrproper

make clean

生成编译文件

make oldconfig, 出现选择 enter 即可

修改.config 配置

nano .config

将如下属性修改为:

CONFIG_SYSTEM_TRUSTED_KEYS = “”(赋空)

CONFIG_SYSTEM_REVOCATION_KEYS=“”(赋空)

make -j$(nproc)

make modules_install

make install

update-grub

reboot

6.2.4 用户测试程序使用说明

编写用户测试程序 test5.c

#include <linux/kernel.h>

#include <sys/syscall.h>

#include <unistd.h>

#include <stdio.h>

int main() {

printf("Your input is %ld\n", syscall(335, 10086));

return 0;

}

编译并运行用户测试程序:

gcc test5.c -o test5

./test5

6.2.5 实验结果

6.2.6 完整的程序代码(包括系统调用函数和用户测试程序)

系统调用函数:

kernel/sys.c

SYSCALL_DEFINE1(mysyscall, unsigned long long, a) {

return a;

}

arch/x86/entry/syscalls/syscall_64.tbl

335 64 mysyscall sys_mysyscall

include/linux/syscalls.h

asmlinkage long sys_mysyscall(unsigned long long);

用户测试程序:

‘test5.c’

#include <linux/kernel.h>

#include <sys/syscall.h>

#include <unistd.h>

#include <stdio.h>

int main() {

printf("Your input is %ld\n", syscall(335, 10086));

return 0;

}

6.3实验总结

实验7  系统缺页次数统计实验

7.1实验目的

理解内存管理中缺页的概念

综合运用实验1中/proc/文件系统, 实验5的内存管理,实验6中系统调用、内核编译的知识;

掌握向/proc文件系统中增加文件的方法

掌握Linux内核模块的概念和操作方法

7.2实验内容

通过在内核中添加自建变量pfcount,统计系统从开机到当前时间的缺页次数。

修改arch/x86/mm/fault.c

修改include/linux/mm.h

修改kernel/kallsyms.c

编译内核

在用户空间编写C语言程序,利用/proc文件系统作为中介,获得内核中的pfcount的值。

7.2.1设计思想及算法流程

1、在内核中添加变量 pfcount

arch/x86/mm/fault.c 中定义并增加 pfcount 的计数。

include/linux/mm.h 中声明 pfcount

kernel/kallsyms.c 中导出 pfcount 符号。

2、编译并安装新的内核:

编译修改后的内核,并安装新的内核模块。

3、编写内核模块:

创建一个新的内核模块,向/proc 文件系统中增加一个文件,用于读取 pfcount的值。

4、编写用户空间程序:

编写一个用户空间的 C 程序,读取/proc 文件系统中的统计结果。

7.2.2源程序清单

1arch/x86/mm/fault.c

2include/linux/mm.h

3 kernel/kallsyms.c

4Makefile

5test7.c

#include <linux/fs.h>

#include <linux/mm.h>

#include <linux/proc_fs.h>

#include <linux/rtc.h>

#include <linux/ktime.h>

#include <linux/module.h>

#define BUFSIZE 512

MODULE_LICENSE("GPL");

ktime_t tv;

struct rtc_time tm;

static struct proc_dir_entry *pf;

static ssize_t read_pfcount(struct file *f, char __user *ubuf, size_t count, loff_t

*ppos) {

char buf[BUFSIZE];

int len = 0;

int year, mon, day, hour, min, sec;

if (*ppos > 0 || count < BUFSIZE)

return 0;

tv = ktime_get_real();

tm = rtc_ktime_to_tm(tv);

year = tm.tm_year + 1900;

mon = tm.tm_mon + 1;

day = tm.tm_mday;

hour = tm.tm_hour + 8;

min = tm.tm_min;

sec = tm.tm_sec;

len = sprintf(buf, "Current time: %d-%02d-%02d %02d:%02d:%02d pfcount = %ld\n",

year,

mon, day, hour, min, sec, pfcount);

if (copy_to_user(ubuf, buf, len))

return -EFAULT;

*ppos = len;

return len;

}

static ssize_t write_pfcount(struct file *f, const char __user *buf, size_t len, loff_t

*off) {

printk(KERN_DEBUG "test\n");

return -1;

}

static struct proc_ops myProc = {

.proc_write = write_pfcount,

.proc_read = read_pfcount,

};

static int __init myproc_init(void) {

printk("Start pf modules...\n");

pf = proc_create("test7", 0660, NULL, &myProc);

if (pf == NULL)

return -ENOMEM;

return 0;

}

static void __exit myproc_exit(void) {

printk("Exit pf module...\n");

proc_remove(pf);

}

module_init(myproc_init);

module_exit(myproc_exit);

7.2.3运行结果及解释

Current time: 2024-06-18 12:00:05 :这是系统当前的时间,表示我们读取 /proc/test7 文件时的时间戳。

pfcount = 1616083 :这是系统从开机到当前时间发生的页面错误总次数。在我们的实验中,通过用户空间程序 page_fault_test.c 访问大量内存以触发页面错误,增加了pfcount 的值;这个值表示在加载内核模块并触发页面错误后,系统累计的页面错误次数为1616083 次。

7.3实验总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值