6.S081 lab2 记录及实现

本文详细介绍了如何在操作系统实验室中添加trace和sysinfo两个自定义系统调用。trace系统调用用于按指定mask追踪特定系统调用,而sysinfo则用于获取系统的内存使用和进程数量信息。文章讲解了从修改makefile到更新内核代码的整个过程,并展示了如何维护内核中的内存统计信息。

一些杂七杂八的话

本次的lab实验从lab2开始记录,lab1全部属于用户程序,难度较低,写lab2的时候我感受到了困难,决定记录一下,以后大概一周更新一个lab吧。

环境

我的linux是win上的ubuntu20.04子系统,配合vscode可以很方便的开发。vscode c++插件的函数追踪功能可以方便的让我们看到函数之间是怎么跳转的。

发生了什么

找到用户文件夹任意一个已有的系统调用,例如fork(),如果你转到定义,你会发现这些系统调用在user文件夹内只有声明,没有实现。而在kernel文件夹内的系统调用名(sysproc)又全部是uint64 sys_fork(viod)这种形式,感觉无法连接到一起。实际上,在编译时会调用user/usys.pl脚本来生产系统调用接口的汇编代码
由usys.pl产生系统调用接口
li a7, SYS_xxx把系统调用编号移入寄存器a7
然后调用ecall进入kernel/syscall.c的syscall函数,再利用a7的值找到对应的系统调用。

那么我们要更改的文件一目了然,

  1. makefile里添加用户程序
  2. ./user/usys.pl后添加entry("trace"); entry("sysinfo");
  3. ./kernel/syscall.h 下添加两个系统调用编号#define SYS_trace 22
  4. ./kernel/syscall.c 的系统调用函数数组里添加这两个调用,同时声明extern uint64 sys_trace(void)
  5. ./user/user.h 下添加函数原型int trace(int)
  6. 在./kernel/sysproc.c下添加对应系统调用即可

trace

trace要求按照mask的表示追踪使用的系统调用。mask是个64位的uint,系统调用总计20多个,所以可以利用第i位标识第i个系统调用是否需要追踪。那么(1<<SYS_call)即可得到第i位是1的值,再和mask按位与,大于0就是需要跟踪的系统调用。即判断条件是if((1<<num) & mask)。

所以我们需要设计让程序能看见此时的mask值,自然想到直接把mask变量加入到结构体proc(kernel/proc.h内)
在这里插入图片描述
trace系统调用的功能即给proc.mask赋值,在这里插入图片描述
然后还需要调整fork()的代码,保证mask会被子进程继承,赋值即可
在这里插入图片描述
最后,修改syscall.c的syscall函数,让它按要求输出
在这里插入图片描述
其中的syscallname是在前面定义的系统调用名称数组。
测试如下:在这里插入图片描述

sysinfo

sysinfo要求统计系统内的剩余内存以及进程数,那么从系统初始化开始记录每次的操作即可,释放内存就info.freemem += PGSIZE,分配内存就info.freemem -= PGSIZE; fork()如果是子进程,就++info.nproc,父进程不操作,完成exit()前–info.nproc即可。
注意在user/user.h声明函数原型时要先声明struct info。

int sysinfo(struct sysinfo *info)本身非常简单,只要把系统内部的kinfo(我定义的内核中的全局struct sysinfo变量)拷贝到传入的info即可,所以sysproc.c 下的sysinfo如下

uint64
sys_sysinfo(void)
{
  uint64 addr;
  argaddr(0,&addr);
  if(copyout(myproc()->pagetable,addr,(char *)&kinfo,sizeof(kinfo)) < 0)
    return -1;
  return 0;
}

但是要维护kernel内的kinfo需要调整内核的其他函数。

  1. kernel/kalloc.c
    此文件里都是关于内存分配的,我在这里定义strcut sysinfo kinfo; 并在之后的函数中赋初值
    定位到kinit()函数,此函数是初始化内存用的,可以看到关键是调用了freeange来获取空白内存,其内部关键又是调用kfree§;
void
kinit()
{
    kinfo.freemem = 0;
    kinfo.nproc = 0;
    initlock(&kmem.lock, "kmem");
    freerange(end, (void*)PHYSTOP);
}

void
freerange(void *pa_start, void *pa_end)
{ 
    char *p;
    p = (char*)PGROUNDUP((uint64)pa_start);
    for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE){
  	    //kinfo.freemem+=PGSIZE;
  	    kfree(p);
    }
}

在kinit()内给kinfo赋初值,先都设为0,freerange会不停调用kfree,在kfree()里每释放一次内存,kinfo.freemem加一个PGSIZE;在kalloc()里每成功分配一次内存就kinfo.freemem减一个PGSIZE,很简单的就完成了空余内存的维护。

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  // Fill with junk to catch dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;

  acquire(&kmem.lock);
  r->next = kmem.freelist;
  kmem.freelist = r;
  kinfo.freemem+=PGSIZE;
  release(&kmem.lock);
}

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r){
    kmem.freelist = r->next;
    
  }
  release(&kmem.lock);

  if(r){
    kinfo.freemem-=PGSIZE;
    memset((char*)r, 5, PGSIZE); // fill with junk
  }
    
  
  return (void*)r;
}

然后调整fork()和exit(),在kernel/proc.c里。
对于exit(),在唤醒父进程之前–kfino.nproc;
里面有这么一行注释

// Parent might be sleeping in wait().
  -- kinfo.nproc;
  wakeup1(original_parent)

对于fork(),在返回0之前加++kinfo.nproc;

  // Cause fork to return 0 in the child.
  ++kinfo.nproc;
  np->trapframe->a0 = 0;

这样就完成了,最后提醒记得在sysproc.c proc.c里include sysinfo.h以及声明extern struct sysinfo kinfo;

在这里插入图片描述

### MIT 6.S081 Lab 8 操作系统实验指南 MIT 6.S081 是一门经典的操作系统课程,其核心目标是让学生通过动手实现部分内核功能来深入理解操作系统的原理和设计[^1]。Lab 8 的主要内容围绕文件系统展开,学生需要完成一系列任务以加深对文件系统的设计、实现以及优化的理解。 以下是关于 Lab 8 的具体说明: #### 文件系统概述 在 Lab 8 中,主要涉及的是 xv6 文件系统的扩展与改进。xv6 是一个简单但完整的类 Unix 操作系统,专为教学目的设计[^2]。该实验室的目标是帮助学生熟悉文件系统的核心概念,包括磁盘布局、目录结构、索引节点 (inode) 和超级块管理等内容。 --- #### 实验内容详解 ##### (一)文件系统缓存机制 要求: 实现文件数据的缓存机制,减少频繁访问磁盘带来的性能开销。 思路: 引入缓冲区缓存 (buffer cache),用于存储最近使用的磁盘块数据。当应用程序请求读取或写入文件时,优先从内存中的缓存查找所需的数据;如果未命中,则加载相应的磁盘块到缓存中并更新状态标志位。此过程需注意同步问题,防止多个线程同时修改同一块数据引发竞争条件[^3]。 代码片段示例: ```c // 缓存初始化函数 void init_cache() { struct buf *b; for (int i = 0; i < NBUF; i++) { b = &bcache[i]; b->dev = 0; b->blockno = 0; b->refcnt = 0; b->valid = 0; } } ``` --- ##### (二)支持大文件 要求: 当前版本的 xv6 支持的最大单个文件大小受限于 inode 结构定义的空间指针数量。为了突破这一限制,可以采用间接寻址的方式增加地址空间容量。 思路: 在原有直接地址字段的基础上新增一层或多层间接地址表指向额外分配的磁盘块区域。每次读写超出直连范围的部分时动态申请新的块资源,并将其链接至对应的间接地址项上。 --- ##### (三)日志记录功能 要求: 增强文件系统的可靠性,在发生崩溃或其他异常情况时不丢失已提交事务的信息。 思路: 引入基于预写式日志协议 (Write-Ahead Logging, WAL) 的设计方案。所有元数据变更前先写入专用的日志分区保存下来再实际应用更改动作。重启恢复阶段依据这些持久化下来的条目重新执行尚未生效的任务确保一致性。 --- ##### (四)并发控制策略 要求: 允许多个进程安全地共享同一个文件对象而不破坏内部逻辑关系。 思路: 利用锁原语保护关键路径上的敏感操作序列避免竞态现象的发生。例如针对某个特定文件加锁之后才能对其进行打开关闭或者增删改查之类的处理直到解锁为止释放对其它等待者的影响。 --- ##### (五)测试验证环节 最后一步是对上述改动进行全面的功能性和压力测验确认预期行为正常无误。可以通过构建复杂的场景模拟真实环境下的负载状况评估整体表现水平是否达到标准要求。 --- ### 总结 通过对以上几个方面的研究探讨可以看出,MIT 6.S081 Lab 8 不仅考察了基础理论知识的应用能力还锻炼了解决复杂工程难题所需的综合素养。希望每位参与者都能从中获得宝贵的经验积累为进一步探索更深层次的技术领域奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值