xv6:labs2 syscall

lab2

1、lab2的内容总结:关于系统调用整个跟踪过程:

使用系统调用时,用户态会通过软中断(trap,陷阱)进入内核中,由trap识别中断来自系统调用,然后调用syscall函数,

跟踪过程:

1、打开gdb:

2、跟踪用户态trace执行过程:

首先执行以下两条指令,为trace的main函数打上断点

file user/_trace

b main

三次c之后成功进入trace的main函数

接下来单步运行到exec语句,这里有点问题,s跳入函数时只能跳入atoi,而trace没有反应。所以trace(atoi(argv[1]))中的trace作用是什么?

s跳入exec函数,会进入usys.S文件:

si单步执行,然后打印出a7的值,其实就是exec的系统调用索引(该索引在syscall.h和syscall.c中规定),所以系统调用号被存储在了a7中,后面进入内核态syscall.c会读出a7的内容并调用系统调用函数:

再次执行si,ecall之后发现是进不去的,根据文档描述,ecall之后会进入内核态,首先跳转到trampoline.S文件的usevec函数:

asm 复制代码
.globl uservec
uservec:    
	#
        # trap.c sets stvec to point here, so
        # traps from user space start here,
        # in supervisor mode, but with a
        # user page table.
        #
        # sscratch points to where the process's p->trapframe is
        # mapped into user space, at TRAPFRAME.
        #
        
	# swap a0 and sscratch
        # so that a0 is TRAPFRAME
        csrrw a0, sscratch, a0

        # save the user registers in TRAPFRAME
        sd ra, 40(a0)
        sd sp, 48(a0)
        sd gp, 56(a0)
        sd tp, 64(a0)
        sd t0, 72(a0)
        sd t1, 80(a0)
        sd t2, 88(a0)
        sd s0, 96(a0)
        sd s1, 104(a0)
        sd a1, 120(a0)
        sd a2, 128(a0)
        sd a3, 136(a0)
        sd a4, 144(a0)
        sd a5, 152(a0)
        sd a6, 160(a0)
        sd a7, 168(a0)
        sd s2, 176(a0)
        sd s3, 184(a0)
        sd s4, 192(a0)
        sd s5, 200(a0)
        sd s6, 208(a0)
        sd s7, 216(a0)
        sd s8, 224(a0)
        sd s9, 232(a0)
        sd s10, 240(a0)
        sd s11, 248(a0)
        sd t3, 256(a0)
        sd t4, 264(a0)
        sd t5, 272(a0)
        sd t6, 280(a0)

	# save the user a0 in p->trapframe->a0
        csrr t0, sscratch
        sd t0, 112(a0)

        # restore kernel stack pointer from p->trapframe->kernel_sp
        ld sp, 8(a0)

        # make tp hold the current hartid, from p->trapframe->kernel_hartid
        ld tp, 32(a0)

        # load the address of usertrap(), p->trapframe->kernel_trap
        ld t0, 16(a0)

        # restore kernel page table from p->trapframe->kernel_satp
        ld t1, 0(a0)
        csrw satp, t1
        sfence.vma zero, zero

        # a0 is no longer valid, since the kernel page
        # table does not specially map p->tf.
        
        # jump to usertrap(), which does not return
        jr t0

userevec函数保存了用户寄存器,执行一些列操作(加载内核栈顶指针巴拉巴拉,不是很懂)。然后jump到usertrap()函数中,通过trap进入内核态。因为ecall之后不知道为什么进不去内核,所以接下来跟踪内核态重开gdb。

3、跟踪内核态sys_trace:

进入内核态后首先会进入usertrap函数,所以先在usertrap函数达断点

b kernel/trap.c:usertrap

之后一直按c执行,内核加载完毕,输入指令trace 32 echo,执行后会进入usertrap函数

单步n执行到syscall(),usertrap会进入系统调用,不过刚开始的系统调用是shell产生的系统调用,然后才是trace

可以在syscall的166行处打个断点,每次产生系统调用的时候打印num(p num),查看系统调用编号,trace的编号为22 。

在syscall函数中其实可以发现,系统调用编号num存在 p->trapfram->a7,通过函数指针数组(syscalls)以及传入的数组索引(num),操作系统会执行对应的系统调用,系统调用的返回值存入 p->trapfram->a0

接下来可以在b sysproc.c:sys_trace ,在sys_trace处打一个断点后按c进入,也可以直接单步执行到168行,按s步入sys_trace函数。

执行完syscall函数后,会回到usertrap函数中,执行usertrapret()函数

usertrapret()函数后会跳到 trampoline.S 中的userret函数,userret返回用户态。

asm 复制代码
#tramponline.S

.globl userret
userret:
        # userret(TRAPFRAME, pagetable)
        # switch from kernel to user.
        # usertrapret() calls here.
        # a0: TRAPFRAME, in user page table.
        # a1: user page table, for satp.

        # switch to the user page table.
        csrw satp, a1
        sfence.vma zero, zero

        # put the saved user a0 in sscratch, so we
        # can swap it with our a0 (TRAPFRAME) in the last step.
        ld t0, 112(a0)
        csrw sscratch, t0
	
		# restore all but a0 from TRAPFRAME
        ld ra, 40(a0)
        ld sp, 48(a0)
        ld gp, 56(a0)
        ld tp, 64(a0)
        ld t0, 72(a0)
        ld t1, 80(a0)
        ld t2, 88(a0)
        ld s0, 96(a0)
        ld s1, 104(a0)
        ld a1, 120(a0)
        ld a2, 128(a0)
        ld a3, 136(a0)
        ld a4, 144(a0)
        ld a5, 152(a0)
        ld a6, 160(a0)
        ld a7, 168(a0)
        ld s2, 176(a0)
        ld s3, 184(a0)
        ld s4, 192(a0)
        ld s5, 200(a0)
        ld s6, 208(a0)
        ld s7, 216(a0)
        ld s8, 224(a0)
        ld s9, 232(a0)
        ld s10, 240(a0)
        ld s11, 248(a0)
        ld t3, 256(a0)
        ld t4, 264(a0)
        ld t5, 272(a0)
        ld t6, 280(a0)

        # restore user a0, and save TRAPFRAME in sscratch
        csrrw a0, sscratch, a0

        # return to user mode and user pc.
        # usertrapret() set up sstatus and sepc.
        sret

trace系统调用到此结束,接下来trace会根据传入的参数32 echo执行exec(echo)系统调用。

2、trace

syscall.c

c 复制代码
void syscall(void)
{
  int num;
  struct proc *p = myproc();

  num = p->trapframe->a7;
  if (num > 0 && num < NELEM(syscalls) && syscalls[num])
  {
    p->trapframe->a0 = syscalls[num](); // 调用syscalls[num], 返回值存入a0

    // printf("num :%d , syscall mask :%d syscall&num :%d\n", num, p->syscall_mask, p->syscall_mask & num);
    if (p->syscall_mask > 0 && (p->syscall_mask & (1 << num))) // trace here
    {
      printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], p->trapframe->a0);
    }
    
  }
  else
  {
    printf("%d %s: unknown sys call %d\n", p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

sysproc.c

c 复制代码
// sys_trace implement here.
uint64
sys_trace(void)
{

  // acquire(&tickslock);
  argint(0, &myproc()->syscall_mask); // for trace, get syscall_mask
  // printf("%d\n",myproc()->syscall_mask);
  // release(&tickslock);
  return 0;
}

3、sysinfo

kalloc.c

c 复制代码
uint64 FreeMem = 0;	//labs 2 sysinfo
uint64 
getFreeMem()	//labs2 sysinfo
{
  return FreeMem * 4096;
}

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;
  FreeMem++; // lab2 here
  release(&kmem.lock);
}

void *
kalloc(void)
{
  struct run *r;

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

  if (r)
    memset((char *)r, 5, PGSIZE); // fill with junk
  return (void *)r;
}

proc.c

c 复制代码
uint64 procs = 0; // lab2 sysinfo
// labs 2 sysinfo
uint64
getProcs()
{
  return procs;
}

//这个函数是本来就有的,但是需要对allocproc时,进行procs++;
static struct proc *
allocproc(void)
{
  struct proc *p;

  for (p = proc; p < &proc[NPROC]; p++)
  {
    acquire(&p->lock);
    if (p->state == UNUSED)
    {
      goto found;
    }
    else
    {
      release(&p->lock);
    }
  }
  return 0;

found:
  p->pid = allocpid();

  // Allocate a trapframe page.
  if ((p->trapframe = (struct trapframe *)kalloc()) == 0)
  {
    release(&p->lock);
    return 0;
  }

  procs++; // labs2	here
  // printf("procs :%d\n", getProcs());

  // An empty user page table.
  p->pagetable = proc_pagetable(p);
  if (p->pagetable == 0)
  {
    freeproc(p);
    release(&p->lock);
    return 0;
  }
    
//freeproc时,对procs--;
static void
freeproc(struct proc *p)
{
  if (p->trapframe)
    kfree((void *)p->trapframe);
  p->trapframe = 0;
  if (p->pagetable)
    proc_freepagetable(p->pagetable, p->sz);
  p->pagetable = 0;
  p->sz = 0;
  p->pid = 0;
  p->parent = 0;
  p->name[0] = 0;
  p->chan = 0;
  p->killed = 0;
  p->xstate = 0;
  p->state = UNUSED;

  procs--; // labs2 here
  // printf("procs :%d\n", getProcs());
}

sysproc.c

c 复制代码
extern uint64 getProcs();
extern uint64 getFreeMem();

//`sysinfo`需要将一个`struct sysinfo`复制回用户空间
uint64
sys_sysinfo(void)
{
  struct sysinfo
  {
    uint64 freemem;
    uint64 procs;
  } sysinfo;
  sysinfo.freemem = getFreeMem();
  sysinfo.procs = getProcs();
  uint64 addr;
  if (argaddr(0, &addr) < 0){	//获取参数(一个虚地址,也就是用户空间的sysinfo的地址)
    return -1;
  }
  // copyout:将sysinfo指向的sizeof(sysinfo)大小的内存存到addr所指内存中
  if(copyout(myproc()->pagetable, addr, (char *)&sysinfo, sizeof(sysinfo))<0){
    return -1;
  };
  // printf("procs :%d\n", getProcs());
  // printf("freemem :%d\n", getFreeMem());
  return 0;
}
相关推荐
WZF-Sang6 小时前
Linux—进程学习-01
linux·服务器·数据库·学习·操作系统·vim·进程
Goboy18 小时前
0帧起步:3分钟打造个人博客,让技术成长与职业发展齐头并进
程序员·开源·操作系统
结衣结衣.1 天前
【Linux】Linux管道揭秘:匿名管道如何连接进程世界
linux·运维·c语言·数据库·操作系统
OpenAnolis小助手1 天前
龙蜥副理事长张东:加速推进 AI+OS 深度融合,打造最 AI 的服务器操作系统
ai·开源·操作系统·龙蜥社区·服务器操作系统·anolis os
小蜗的房子2 天前
SQL Server 2022安装要求(硬件、软件、操作系统等)
运维·windows·sql·学习·microsoft·sqlserver·操作系统
邂逅岁月4 天前
【多线程奇妙屋】 Java 的 Thread类必会小技巧,教你如何用多种方式快速创建线程,学并发编程必备(实践篇)
java·开发语言·操作系统·线程·进程·并发编程·javaee
CXDNW5 天前
【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、死锁
笔记·操作系统·线程·进程·互斥·死锁
Anemone_5 天前
MIT 6.S081 Lab3
操作系统
掘了6 天前
持久化内存 | Persistent Memory
c++·架构·操作系统
结衣结衣.7 天前
【Linux】掌握库的艺术:我的动静态库封装之旅
linux·运维·服务器·c语言·操作系统·