记录下,问题挺多的,但能用
# 操作系统实验
1.观察Linux行为
实验目的
Ø通过本实验,了解LINUX系统的组织和行为,观察各种存储系统状态信息的内核变量
实验要求
Ø理解Linux操作系统功能和结构,重点掌握/proc文件
Ø熟悉gcc、gdb、codeblocks或者其它IDE,比较Linux下C程序编译、调试技术
Ø使用给定的程序观察cpuinfo、version、stat、uptime
Ø编程实现对meminfo、loadavg、interrupts、filesystem的观测
test1代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *fp = NULL;
char string[1024];
void meminfo();
void meminfo_sys();
void loadavg();
void loadavg_sys();
void interrupts();
void interrupts_sys();
void filesystems();
void filesystems_sys();
int main(int argc, char *argv[]) {
meminfo_sys();
loadavg_sys();
interrupts_sys();
filesystems_sys();
/*
* 以下为通过文件流的方式输出
* */
// meminfo();
// loadavg();
// interrupts();
// filesystems();
return 0;
}
void meminfo() {
printf("---------------------\nmeminfo:\n");
fp = fopen("/proc/meminfo", "r");
if (fp == NULL) {
printf("error");
return;
}
while (fgets(string, 512, fp)) {
printf("%s", string);
}
printf("---------------------\n");
fclose(fp);
}
void loadavg() {
printf("---------------------\nloadavg:\n");
fp = fopen("/proc/loadavg", "r");
if (fp == NULL) {
printf("error");
return;
}
while (fgets(string, 512, fp)) {
printf("%s", string);
}
printf("---------------------\n");
fclose(fp);
}
void interrupts() {
printf("---------------------\ninterrupts:\n");
fp = fopen("/proc/interrupts", "r");
if (fp == NULL) {
printf("error");
return;
}
while (fgets(string, 512, fp)) {
printf("%s", string);
}
printf("---------------------\n");
fclose(fp);
}
void filesystems() {
printf("---------------------\nfilesystems:\n");
fp = fopen("/proc/filesystems", "r");
if (fp == NULL) {
printf("error");
return;
}
while (fgets(string, 512, fp)) {
printf("%s", string);
}
printf("---------------------\n");
fclose(fp);
}
void meminfo_sys() {
printf("---------------------\nmeminfo:\n");
system("cat /proc/meminfo");
printf("---------------------\n");
}
void loadavg_sys() {
printf("---------------------\nloadavg:\n");
system("cat /proc/loadavg");
printf("---------------------\n");
}
void interrupts_sys() {
printf("---------------------\ninterrupts:\n");
system("cat /proc/interrupts");
printf("---------------------\n");
}
void filesystems_sys() {
printf("---------------------\nfilesystems:\n");
system("cat /proc/filesystems");
printf("---------------------\n");
}
效果图:
2.软中断通信
实验目的
本实验要求学生了解什么是信号,掌握软中断的基本原理;掌握中断信号的使用、进程的创建以及系统计时器的使用。
通过对本实验的学习,学生能够学会进程的创建方法,更能加深对Linux中的信号机制的认识,并会使用软中断信号来实现进程间的通信。
实验要求
1.学生根据test2.c理解以下内容:
父进程接受到软中断信号(SIGQUIT)后,向其子进程分别发送整数值为16的软中断信号,子进程获得对应软中断信号后,终止运行;
父进程调用wait()函数等待子进程终止,然后自我终止。
由父进程创建一个子进程,通过终端输入Crtl+\组合键向父进程发送SIGQUIT软中断信号发送给父进程;
2.编程实现以下内容:
由一个父进程创建两个子进程,之后通过终端输入Crtl+\组合键向父进程发送软中断信号,终止两个子进程以及父进程。
由一个父进程创建一个子进程,之后该子进程再创建一个孙进程,通过终端输入Crtl+\组合键向父进程发送软中断信号,依次终止孙进程、子进程、父进程。
test2-2代码
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include <sys/wait.h>
void waiting();
void stop();
int wait_mark;
int main() {
int p1, p2;
while ((p1 = fork()) == -1);
if (p1 > 0) //if p1 is parent process
{
lockf(1, 1, 0);
printf("parent process is %d \n", getpid());
lockf(1, 0, 0);
wait_mark = 1;
// signal(SIGQUIT, stop);
// waiting();
// kill(p1, 16); //send signal 16 to end the process p1
while ((p2 = fork()) == -1);
if (p2 > 0) //if p2 is parent process
{
// lockf(1, 1, 0);
// printf("parent process is %d \n", getpid());
// lockf(1, 0, 0);
wait_mark = 1;
signal(SIGQUIT, stop);
waiting();
kill(p2, 16); //send signal 16 to end the process p2
kill(p1, 16); //send signal 16 to end the process p2
wait(0); //waiting for the ending of p2
} else //if p2 is child process
{
lockf(1, 1, 0);
printf("child process %d is created by the parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
signal(SIGQUIT, SIG_IGN);
wait_mark = 1;
signal(16, stop);
waiting();
lockf(1, 1, 0);
printf("child process %d is killed by parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
exit(0); // p2 quit
}
lockf(1, 1, 0);
printf("parent process is killed!\n");
lockf(1, 0, 0);
exit(0); //quit from the parent process
} else //if p1 is child process
{
lockf(1, 1, 0);
printf("child process %d is created by the parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
signal(SIGQUIT, SIG_IGN);
wait_mark = 1;
signal(16, stop);
waiting();
lockf(1, 1, 0);
printf("child process %d is killed by parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
exit(0); // p1 quit
}
return 0;
}
void waiting() {
while (wait_mark != 0);
}
void stop() {
wait_mark = 0;
}
效果图:
test2-3代码
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include <sys/wait.h>
void waiting();
void stop();
int wait_mark;
int main() {
int p1, p2;
while ((p1 = fork()) == -1);
if (p1 > 0) //if p1 is parent process
{
lockf(1, 1, 0);
printf("parent process is %d \n", getpid());
lockf(1, 0, 0);
wait_mark = 1;
signal(SIGQUIT, stop);
waiting();
kill(p1, 16); //send signal 16 to end the process p1
wait(0); //waiting for the ending of p1
lockf(1, 1, 0);
printf("parent process is killed!\n");
lockf(1, 0, 0);
exit(0); //quit from the parent process
} else //if p1 is child process
{
while ((p2 = fork()) == -1);
if (p2 > 0) //if p2 is parent process
{
lockf(1, 1, 0);
printf("child process %d is created by the parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
signal(SIGQUIT, SIG_IGN);
wait_mark = 1;
signal(16, stop);
waiting();
// signal(SIGQUIT, stop);
// waiting();
kill(p2, 16); //send signal 16 to end the process p2
wait(0); //waiting for the ending of p2
lockf(1, 1, 0);
printf("child process %d is killed by parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
exit(0); // p1 quit
} else //if p2 is child process
{
lockf(1, 1, 0);
printf("grandson process %d is created by the parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
signal(SIGQUIT, SIG_IGN);
wait_mark = 1;
signal(16, stop);
waiting();
lockf(1, 1, 0);
printf("grandson process %d is killed by parent %d \n", getpid(), getppid());
lockf(1, 0, 0);
exit(0); // p2 quit
}
}
return 0;
}
void waiting() {
while (wait_mark != 0);
}
void stop() {
wait_mark = 0;
}
效果图:
3.进程调度实验
实验目的
撑握进程调度的概念
学习Linux内核源码编写风格,重点理解进程调度策略算法,包括FCFS、RR、SRT、Feedback的调度算法。
实验要求
本实验要求在Linux的用户态下编程实现进程调度策略算法的模拟程序
实现以下调度算法:
-
FCFS(先到先服务)
-
RR(轮循)
test4代码
#include<stdio.h>
#include<time.h>
#include<malloc.h>
#include<stdlib.h>
#define NR_TASKS 64 //系统支持的进程个数
#define TASK_RUNNING 0 //就绪态
#define TASK_UNINTERRUPTIBLE 2 //不可中断的睡眠状态
#define TASK_ZOMBIE 3 //僵死态
//进程表项
struct task_struct{
long pid; //进程号
long state; //进程运行状态
long priority; //优先数
long counter; //进程剩余时间片
long start_time; //进程开始时间
long excute_time; //进程执行时间
};
// 初始化一个全局结构体变量init_task,赋值风格是Linux内核风格
struct task_struct init_task = {
.pid = 0,
.state = 0,
.priority = 0,
.counter = 0,
.start_time = 0,
.excute_time = 0
};
struct task_struct *current = &init_task; // 一个进程指针,初始化时指向init_task
unsigned long volatile jiffies = 0; //系统滴答数 (volatile会严格规定编译器,防止优化过度,如偷懒读取寄存器而不重新读内存)
struct task_struct* task[NR_TASKS] = {&init_task,}; //进程指针数组
#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]
struct run_q { //进程就绪队列
struct task_struct *data;
struct run_q *next;
};
struct run_q *head=NULL,*end=NULL,*r_temp;
#define N_PROCESS 5 //进程个数
#define MAX_RUNTIME 100 //最长运行时间
int process[N_PROCESS][2]={{0,3},{2,6},{4,4},{6,5},{8,2}};//进程初始值
int totalExcuteTime = 0; //cpu总的运行时间
int runState[N_PROCESS][MAX_RUNTIME] = {0}; //进程运行状态的记录
void checkProcessCome(); //判断是否有进程到达,如果有则创建进程
void pause(); //0号进程的运行体
void schedule_f(); //FCFS调度程序
void schedule_r(); //RR调度程序
void switch_to(int pid); //进程切换
void init(); //基于优先级调度器的初始化
void run(int pid); //普通进程的运行体
void myfork(int pid); //进程创建
void delete(int pid); //进程清除
typedef void funtype(void);
funtype *schedule = NULL;
int main(int argc,char **argv)
{
int i,j;
int choice;
while(1){
printf("please choice the schedule measure:\n");
printf("f : 先到先服务的调度策略\n");
printf("r : 轮循的调度策略\n");
printf("q : 退出\n");
printf("choice = ");
choice = getchar();
if(choice == '\n')
choice = getchar();
switch(choice){
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 Service time\n"); // 注意:process已经初始化了,只是个二维数组
for(i=0;i<N_PROCESS;i++){
printf("task %2d: %6d %6d\n",i+1,process[i][0],process[i][1]);
totalExcuteTime+=process[i][1]; // 累加进程的CPU服务总时间
}
//初始化
init();
//打印进程调度情况
printf("Scheduling result\n");
printf("time : 0%*c%d\n",totalExcuteTime-2,' ',totalExcuteTime);
for(i=0;i<N_PROCESS;i++){
printf("task %2d: ",i+1);
for(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;
}
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; // c值约束当前p指向就绪态且进程有最早的进程创建时间,保证先到先服务 FCFS
}
switch_to(next); // next代表最早的可运行进程pid,若非0,则消耗1时间片
}
void schedule_r(){ //用到了就绪队列,循环队列(默认时间片长度1)
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; //next指向队头pid
switch_to(next);//递归
}
void switch_to(int pid){
if(pid)
run(pid); // 进入run递归
else
pause(); // pid为初始进程init_task则暂停一时间片时间,即当前时间无进程需要运行
}
void myfork(int pid){
struct timeval now; // linux的时间库提供的时间变量
struct run_q *p;
task[pid] = (struct task_struct*)malloc(sizeof(struct task_struct)); //根据pid分配进程空间
task[pid]->state = TASK_UNINTERRUPTIBLE; // 创建的默认运行态是不可中断的睡眠态(实质为阻塞态
task[pid]->pid = pid;
gettimeofday(&now,0); // 获取当前时间,赋值给now
srand(now.tv_usec); // 随机化种子 ,usec是微秒变量
task[pid]->priority = 2 + (int)(4.0f*rand()/(RAND_MAX+1.0f)); // 随机化优先级
task[pid]->counter = task[pid]->priority; // 随机化优先级,偷懒把优先级变为剩余时间片
task[pid]->start_time = jiffies;
task[pid]->excute_time = 0;
task[pid]->state = TASK_RUNNING; // 调整进程为就绪态,仅等待CPU资源
p = (struct run_q*)malloc(sizeof(struct run_q)); // 创建就绪队列数据结构
p->data = task[pid];
if(head==NULL){ // 入队操作,不含初始进程init_task
head = end = p;
head->next = end;
end->next = head;
}else{
end->next = p;
end = p;
end->next = head;
}
}
void delete(int pid){
free(task[pid]);
}
void checkProcessCome(){
int i;
for(i=0;i<N_PROCESS;i++){
if(process[i][0]==jiffies) // process[i][0]是该进程的到达时间 , 即到达了该时间,创建对应的进程
myfork(i+1);
}
}
void init(){
int i;
for(i=1;i<NR_TASKS;i++){
task[i] = NULL; // 初始化进程数组(仅用作记录所有进程)
}
checkProcessCome(); // 完成进程创建(实例化),以及就绪队列初始化
schedule(); // 调用函数指针指向的调度函数
}
void pause(){
current = task[0];
jiffies++;
totalExcuteTime++;
checkProcessCome();
schedule();
}
void run(int pid){
int i;
current = task[pid];
runState[pid-1][jiffies] = 1; // 标记该进程在某时间片下的flag
jiffies++; // 时间片自增
task[pid]->counter--;
task[pid]->excute_time++;
//判断进程是否运行完,如果是则将进程杀死
if(task[pid]->excute_time==process[pid-1][1]){
//task[pid]->end_time = jiffies;
task[pid]->state = TASK_ZOMBIE;
}
//判断所有进程是否都运行完,如果是则结束
if(jiffies>=totalExcuteTime) return;
checkProcessCome(); // 查询是否抵达进程创建时间,是则创建新进程
schedule(); // 递归
}
效果图:
4.线程同步实验
实验目的
掌握操作系统并发的概念
理解并发中的生产消费者问题
熟悉Linux线程编程机制,掌握线程同步的实现方式
实验要求
完成Linux线程程序,完成利用Linux线程信号量及PV操作实现消费者的同步关系的程序
test3代码
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define TOTAL_NUM 3
#define INIT_NUM 0
sem_t p_sem, c_sem, sh_sem;
void *producer(void *arg);
void *consumer(void *arg);
int n = 0; // 生产的现存面包数目
int main() {
pthread_t tid_1, tid_2;
sem_init(&p_sem, 0, TOTAL_NUM - INIT_NUM);
sem_init(&c_sem, 0, INIT_NUM);
sem_init(&sh_sem, 0, 1);
pthread_create(&tid_1, NULL, producer, NULL);
pthread_create(&tid_2, NULL, consumer, NULL);
pthread_join(tid_1, NULL);
pthread_join(tid_2, NULL);
sem_destroy(&p_sem);
sem_destroy(&c_sem);
sem_destroy(&sh_sem);
return 0;
}
void *producer(void *arg) {
while (1) {
printf("生产!!\n");
sem_wait(&p_sem);
sem_wait(&sh_sem);
usleep(1000*1000);
printf("生产面包:%d\n", ++n);
sem_post(&sh_sem);
sem_post(&c_sem);
}
}
void *consumer(void *arg) {
while (1) {
sem_wait(&c_sem);
sem_wait(&sh_sem);
usleep(1000*500);
printf("抢面包-1,剩余:%d\n",--n);
sem_post(&sh_sem);
sem_post(&p_sem);
printf("开吃面包!\n");
}
}
效果图:
5.内存管理实验
实验目的
了解内存管理的概念,掌握分页、分段操作过程
掌握虚拟内存技术的概念,重点理解替换策略
理解选择替换页算法OPT、FIFO、LRU、CLOCK
实验要求
(用户态)在Linux下编程实现虚存页面替换算法的模拟程序。主要包含:
根据用户输入参数,包含进程大小(页数),进程地址(页地址)顺序,页表大小
验证test5.c中FIFO、LRU替换算法。
新建工程replace,编写并运行OPT和CLOCK算法。
test5代码
#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[] = {
"f - FIFO",
"r - LRU",
"o - OPT",
"c - CLOCK",
"q - QUIT",
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;
// printf("| %d ", s);
if (s < tmp)
{
tmp = s;
j = i;
}
}
// printf("\n");
return j;
}
void fifo(int fsize, page *frame, int rsize, page **pageR)
{
int i, j = 0, p = 0;
int f = 0; //记录page fault,缺页中断
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++; // j维护循环队列指向
f++;
}
print(i, frame, fsize);
}
printf("page fault : %d\n", f);
}
void lru(int fsize, page *frame, int rsize, page **pageR)
{
int i, j, p = 0, q;
int f = 0; //记录page fault,缺页中断
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); //普通的顺序查找算法,找出最长的存在时间的页框(用v值比较)
frame[q].n = pageR[i]->n;
frame[q].v = 0;
f++;
}
print(i, frame, fsize);
}
printf("page fault : %d\n", f);
}
void opt(int fsize, page *frame, int rsize, page **pageR)
{
int i, j, p = 0, q;
int f = 0; //记录page fault,缺页中断
for (i = 0; i < fsize; i++)
{
frame[i].n = -1;
frame[i].v = 0;
}
for (i = 0; i < rsize; i++)
{
q = Search(pageR[i]->n, frame, fsize);
if (q != -1) //三段式,先判断是否已经有这个页,有直接访问,若无再判断是否有新页框,若都没有再开始本算法
;
else if (i < fsize || p < fsize)
{
frame[p].n = pageR[i]->n; //有未使用的页框,直接访问
p++;
}
else
{
for (int j = 0; j < fsize; j++)
{
frame[j].v = findNext(frame[j].n, pageR, i, rsize); //迭代更新v值,即计算下次找到该页时的间距
}
q = findLastMax(frame, fsize); //普通的顺序查找算法,找出最长的间隔时间的页框(用v值比较)
frame[q].n = pageR[i]->n;
f++;
}
print(i, frame, fsize);
}
printf("page fault : %d\n", f);
}
void clk(int fsize, page *frame, int rsize, page **pageR)
{
int i, j = 0, p = 0,q;
int f = 0; //记录page fault,缺页中断
for (i = 0; i < fsize; i++)
{
frame[i].n = -1;
frame[i].v = 0;
}
for (i = 0; i < rsize; i++)
{
if (q = Search(pageR[i]->n, frame, fsize) != -1) //三段式,先判断是否已经有这个页,有直接访问,若无再判断是否有新页框,若都没有再开始本算法
frame[q].v = 1;
else if (i < fsize || p < fsize)
{
frame[p].n = pageR[i]->n;
frame[p].v = 1;
j++;
p++;
}
else
{
while (1)
{
if (frame[j % fsize].v == 0) //类似于FIFO,但是多了1位v,用于在触发缺页中断时标记该页框是否已被访问过
{
frame[j % fsize].n = pageR[i]->n;
frame[j % fsize].v = 1;
j++; // j维护循环队列指向
f++;
break;
}
else
{
frame[j % fsize].v = 0;
j++; // j维护循环队列指向
}
}
}
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 'f':
fifo(phySize, frame, prSize, pageR);
break;
case 'r':
lru(phySize, frame, prSize, pageR);
break;
case 'o':
opt(phySize, frame, prSize, pageR);
break;
case 'c':
clk(phySize, frame, prSize, pageR);
break;
default:
break;
}
} while (choice != 'q');
exit(0);
}
效果展示:
6.系统调用
实验目的
学习如何产生一个系统调用,以及怎样通过往内核中增加一个新函数,从而在内核空间中实现对用户空间的读/写。学习重建内核。
实验要求
(1)设计并实现一个新的系统调用pedagogictime() ,该函数通过使用一个引用参数的调用返回当前的系统时间。
(2)编写一个用户空间程序来测试pedagogictime()。
由于我用的Ubuntu20.04,kernel5.13已经和PPT要求差别过大,没有do_gettimeofday()函数,故更改了实验步骤:
(1) 该系统调用有1个整型参数或字符串型参数,接收输入自己的学号;
(2) 若参数为奇数,则返回自己学号的最后5位。如你的学号为16130120101 ,则返回20101;
(3) 若参数为偶数,则返回自己的学号的最后6位。如你的学号为16130120102 ,则返回120102 。
test6代码
//内核代码:
SYSCALL_DEFINE1(mysyscall,unsigned long long,num)
{
int mod;
unsigned long long result,x;
long m;
x=2;
result=num;
mod=do_div(result,x);
if(mod)
{
x=100000;
m=do_div(num,x);
printk("result is %05ld\n",m);
return m;
}
else
{
x=1000000;
m=do_div(num,x);
printk("result is %06ld\n",m);
return m;
}
}
//测试代码
#include<linux/kernel.h>
#include<sys/syscall.h>
#include<unistd.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
int main(){
printf("result is %ld\n",syscall(335,20212123766));
return 0;
}
效果图:
7.系统缺页次数统计实验
实验目的
理解内存管理中缺页的概念
综合运用实验1, 实验5, 实验6中/proc文件系统、内存管理、系统调用、内核编译的知识
掌握向/proc文件系统中增加文件的方法
掌握Linux内核模块的概念和操作方法
实验要求
通过在内核中自建变量并利用/proc文件系统作为中介的方法来统计系统缺页次数
由于我用的Ubuntu20.04,kernel5.13已经和PPT要求差别过大,没有do_page_fault函数,故实验失败
操作系统实验解析
实验1
-
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
-
cat
命令的用途是连接文件或者标准输入并打印。这个命令常用来显示文件内容,或者将几个文件拼接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用。
实验2
fork()函数调用后,父进程会接收返回值子进程PID,子进程则返回0,失败返回-1
test2-2
-
在while循环中调用fork生成子进程p1(不断循环直到创建成功)
-
(1)判断若是父进程,进入第一段代码块,对打印资源加锁,自旋锁flag置1(n = 25)。继续判断并执行1,接受ctrl\信号输入,接收到则执行stop函数( flag置0)。自旋等待。kill函数向两个子进程发送16信号。等待子进程退出。打印,退出n=63。
(2)若是子进程进入,则调至第二段,对打印资源加锁,注册signal函数,判断ctrl\信号输入。若接收到了信号(无动作),则自旋锁flag置1 。注册接受自定义信号量16,接收到则执行stop函数( flag置0)。自旋等待,直到接受自定义信号量16再释放,最后退出n=81。
signal函数,参数1为信号量,参数2为接受信号后行为
特别地,子进程的第一个signal函数需要先屏蔽sigquit信号,因为ctrl\信号属于quit信号,如果不屏蔽,会被用户直接杀死,而不是被父进程杀死
实验3
- 主程序开始,读取输入流的值,根据选择赋予函数指针schedule值(调用哪个调度算法函数)
- 打印已预设的process进程信息,包含开始时间戳与作业完成需要的时间片数量;累加所需时间得出总运行时间 totalExcuteTime
- 进入初始化函数init n=93,将作业数组task初始化置空
- 进入函数checkProcessCome 实例化进程创建,以及就绪队列初始化 n = 213
- 遍历并检查滴答是否到达某个进程的初试时间,是则调用myfork函数传入参数pid并创建实例进程 n=204
- 进入myfork,为该pid的进程分配内存空间并放入task数组存放,随机分配优先级、剩余时间片 ;完成实例化
- 创建就绪队列,初始化。退出myfork
- 退出checkProcessCome ,回到init调用调度算法的函数指针。f对应9. FCFS算法,r对应10.Round Robin 轮转算法
- FCFS:从task数组尾部反向遍历,找到处于就绪态且开始时间最早的进程,得到其pid,传入switch_to函数进一步运行,调度
- Round Robin:若当前进程current非就绪态;若队列为空,则将头尾置空;若不为空(代表队列该进程已运行完成,处于僵尸态),则从队列删除该进程。若当前进程current是就绪态,则轮转递进。传入switch_to函数进一步运行,调度
- 进入switch_to,若pid非空,传入pid并进入run函数模拟运行并进一步调度;若为空则进入pause函数
- 若进入run函数,将该pid进程标记为current,标记该进程在某时间片下的flag二维数组runState,时间片自增,进程执行进程时间自增,判断进程是否运行完,如果是则将进程杀死,判断所有进程是否都运行完,如果是则结束,否则继续查询是否抵达进程创建时间,是则创建新进程,递归调度
- 若进入pause,单纯的自增时间片,再查询是否抵达进程创建时间,递归调度
实验4
- 调用linux内核的信号量初始化函数完成信号量p,c,sh的初始化,p=3 c=0 sh=1,p、c同步缓冲区资源,sh完成互斥锁。
- 创建两条线程运行函数对应生产者和消费者,pthread_join表示该线程必须等待指定线程结束后才能继续,否则挂起并线程阻塞
- 在生产者线程,每次生产先将psem执行p操作,即占用一个缓冲区块,再加互斥锁防止临界区冲突完成生产,完成后v操作释放csem
- 在消费者线程,每次消费前对csem执行p操作,即吃面包释放了一个缓冲区块,再加互斥锁防止临界区冲突完成消费,完成后v操作释放psem
实验5
prSize:预分配逻辑内存的大小,pageR:预分配的逻辑内存,program对应逻辑页,页框对应物理实在的内存
若逻辑页未命中已有的页框,则示为触发一次pagefault
- 在主函数分配内存空间,进入buildPageReference函数完成page引用的初始化构建
- 进入buildPageReference,用随机值赋值完成page引用数组的初始化
- 循环给出提示选择调用的内存置换算法
- FIFO:三段式,先判断是否已经有这个页,有则直接访问,若无再判断是否有新页框,若都没有再开始本算法;在循环队列入队并赋值
- LRU:least recently used,最近最少使用算法,将.v属性当做存在时间的变量,三段式,先判断是否已经有这个页,有直接访问,若无再判断是否有新页框,若都没有再开始本算法;顺序查找出v最大的页框,将其置换
- OPT:Optimal 最佳算法,三段式,先判断是否已经有这个页,有直接访问,若无再判断是否有新页框,若都没有再开始本算法;循环迭代更新v值,即计算下次找到该页时的间距(此处用v表示),迭代本轮之后,再顺序查找出v最大的页框,将其置换
- Clock:三段式,先判断是否已经有这个页,有直接访问,若无再判断是否有新页框,若都没有再开始本算法;类似于FIFO,但是多了1位v,用于在触发缺页中断时标记该页框是否已被访问过,v为0则可置换,否则v置为0(只有这种情况)并跳过该页框。
实验六
-
先下载内核(要合适自己的linux,不能版本差距过大),解压(Ubuntu增加一个系统调用(20.04)西电软工OS实验专题二_李黎玖的博客-CSDN博客_ubuntu添加系统调用)
-
cd arch/x86/entry/syscalls
-
kate syscall_64.tbl
编辑syscall系统调用表 -
注意该表对应的用户可自定义的号码段,不要搞错;添加自己的系统调用号和名字
-
添加系统调用声明:
cd /usr/src/linux-5.15.40 kate include/linux/syscalls.h
-
添加:
asmlinkage long sys_mysyscall(unsigned long long);//sys_后面的函数名要和上面的对应
-
再添加系统调用的定义:
kate kernel/sys.c
,写上自己的函数://内核代码: SYSCALL_DEFINE1(mysyscall,unsigned long long,num) { int mod; unsigned long long result,x; long m; x=2; result=num; mod=do_div(result,x); if(mod) { x=100000; m=do_div(num,x); printk("result is %05ld\n",m); return m; } else { x=1000000; m=do_div(num,x); printk("result is %06ld\n",m); return m; } }
-
保存后退出,之后就是内核编译并加载在系统启动grub中,重启长按shift切换该内核
-
运行 (运行sudo dmesg可查看内核调用情况)
#include<linux/kernel.h> #include<sys/syscall.h> #include<unistd.h> #include<math.h> #include<stdio.h> #include<stdlib.h> #include<sys/types.h> int main(){ printf("result is %ld\n",syscall(335,20212123766)); return 0; }
-
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库