MIT 6.S081 lab学习记录

lab0

Google 翻译
第零章 操作系统接口 | xv6 中文文档 (gitbooks.io)
6.1810 / Fall 2022 (mit.edu)

sleep(easy)

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]){
	if(argc <= 2) sleep(atoi(argv[1]));
return 0;
}

pingpong (easy)

pipe与dup的讲解:
(209条消息) xv6中pipe&dup_xv6 dup函数的含义_G129558的博客-CSDN博客

pipe: 创建一个通道使得两个进程能够进行通信
dup:复制一个稳健描述符,文件描述符的生成以最小数开始

#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
  int parent_fd[2], child_fd[2];
  pipe(parent_fd);
  pipe(child_fd);
  char buf[64];

  if (fork()) {
    // Parent
    close(child_fd[1]);
    write(parent_fd[1], "ping", strlen("ping"));
    read(child_fd[0], buf, 4);
    printf("%d: received %s\n", getpid(), buf);
    close(parent_fd[1]);
    close(parent_fd[0]);
    close(child_fd[0]);
  } else {
    // Child
    close(parent_fd[1]);
    read(parent_fd[0], buf, 4);
    printf("%d: received %s\n", getpid(), buf);
    write(child_fd[1], "pong", strlen("pong"));
    close(child_fd[1]);
    close(child_fd[0]);
  }

  exit(0);
}

lab1

Using gdb

在第一个terminal调用 make qemu-gdb
在另一个terminal执行 gdb-multiarch

通过gdb实现寄存器的查看
p /x $sstatus

backtrace 回看栈中的数据

b *0x000000008000215a 对具体的地址进行打断点


System call tracing

思路:增加一个系统调用sys_trace,此时我们需要在所有的进程中添加一个mask变量来记录我们需要跟踪的指令,随后在syscall中打印出来

/user/usys.pl :perl脚本 实现对系统调用的汇编语言的生成
在其中添加

entry("trace");

/user/user.h : 系统调用以及用户常见的函数的定义

int trace(int);

/kernel/proc.h 进程相关的结构体函数的定义

struct proc {
  struct spinlock lock;

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID
  **int mask;**                // 添加一个 mask 用来进行记录
  // wait_lock must be held when using this:
  struct proc *parent;         // Parent process

  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
};

/kernel/proc.c 进程相关函数实现
在fork()系统调用中实现mask的赋值

  // copy mask for trace
  np->mask = p->mask;

kernel/syscall.c 系统调用函数实现
添加一个表

static const char*syscall_names[]={
[SYS_fork]    "sys_fork",
[SYS_exit]    "sys_exit",
[SYS_wait]    "sys_wait",
[SYS_pipe]    "sys_pipe",
[SYS_read]    "sys_read",
[SYS_kill]    "sys_kill",
[SYS_exec]    "sys_exec",
[SYS_fstat]   "sys_fstat",
[SYS_chdir]   "sys_chdir",
[SYS_dup]     "sys_dup",
[SYS_getpid]  "sys_getpid",
[SYS_sbrk]    "sys_sbrk",
[SYS_sleep]   "sys_sleep",
[SYS_uptime]  "sys_uptime",
[SYS_open]    "sys_open",
[SYS_write]   "sys_write",
[SYS_mknod]   "sys_mknod",
[SYS_unlink]  "sys_unlink",
[SYS_link]    "sys_link",
[SYS_mkdir]   "sys_mkdir",
[SYS_close]   "sys_close",
[SYS_trace]   "sys_trace"
};

添加判断命令

void
syscall(void)
{
  int num;
  struct proc *p = myproc();

  num = p->trapframe->a7;
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    // Use num to lookup the system call function for num, call it,
    // and store its return value in p->trapframe->a0
    p->trapframe->a0 = syscalls[num]();
   // printf("%s -> %d\n", )
   if(p->mask & (1 << num)){
      printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);
   }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

/kernel/sysproc 系统调用实现
添加trace命令

uint64 sys_trace(void){
  int mask;
  struct proc *now_proc = myproc();
  argint(0, &mask);
  now_proc -> mask = mask;
  return 0;
}

在进行系统调用函数的使用时,所有的参数会依次存入到寄存器 a0 a1 a2 a3 a4 a5

Sysinfo

实现Sysinfo的系统调用

步骤:
step1:/user/usys.pl中添加

entry("sysinfo")

step2: /user/user.h中添加

struct sysinfo;

step3: 在sysproc中添加sys_sysinfo

uint64 sys_sysinfo(void){
  uint64 addr;
  argaddr(0, &addr);
  struct sysinfo info;
  info.freemem = get_freemem();
  info.nproc = get_freeproc();
  struct proc *p = myproc();
  if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
    return -1;
  return 0;
}

在上述代码中为什么使用argaddr?

void
argaddr(int n, uint64 *ip)
{
  *ip = argraw(n);
}

因为地址是无符号型

为什么使用copyout?
copyout能够Copy from kernel to user

step4: /kernel/kalloc.v
freelist表示一个空闲页表链,每一个空闲页表的大小为4096byte

int get_freemem(){
    struct run *p;
    int freemem = 0;
    acquire(&kmem.lock);
    p = kmem.freelist;
    while(p){
      if(p) freemem += PGSIZE;
      p = p -> next;
    }
    release(&kmem.lock);
    return freemem;
}

为什么要加锁?
为了能够节约CPU资源
防止内存访问出现问题

step 5: /kernel/proc.c
进程中维护了一个进程表,把进程表中没有利用使用的进程记录下来即可

int get_freeproc(){
  struct proc *p;
  int free_num = 0;
  for(p = proc; p < &proc[NPROC]; p++) {
    if(p->state != UNUSED){
      free_num += 1;
    }
  }
  return free_num;
}

MIT课程

Lecture 3 - OS Organization and System Calls_哔哩哔哩_bilibili
操作系统的结构:
隔离性 内核/用户模式 系统调用
操作系统与应用之间不会出现很大的隔离
例如在使用fork()系统调用时,会出现对CPU进行分配任务,这种抽象实际上就是避免用户直接对CPU进行操作从而实现隔离
通过时间复用从而实现不同进程的使用
内存读写在linux中都是通过文件进行映射的,然后操作系统自己对内存进行映射

操作系统必须具有防守性 防御性编程
应用不能够对OS进行影响

用户内核模式 与 虚拟内存 能够为防御性编做工作

用户内核模式:用户模式 内核模式 机器模式
在内核模式CPU能够执行特权指令 : 直接对硬件进行操作 设置时间中断 对页表的处理
在用户模式CPU只能够实现非特权指令

存在一个寄存器能够实现对特权级别的更改

每个进程维护自己的页表,页表定义了内存的布局 将所有的进程与用户进行隔离
每一个进程在不同的页表中维护,从而实现了不同的程序可能起始地址都是0 不能够存在内存的相互访问
fork() 调用 ecall 给定系统调用标号 从而实现对应的功能

宏内核设计与微内核设计 区别在于是否在内核段存放大量的代码
![[截图_20230709095928.png]]
微内核实际上需要在内核态与用户态中不断地切换从而实现功能
微内核的性能如何提升,大多是宏内核的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值