Skip to content

system calls

Giovanna

About 1058 wordsAbout 4 min

2024-07-31

Lab: System calls (mit.edu)

补充内容


开启新实验

git fetch
git checkout syscall
make clean

System call tracing

任务描述:接受一个参数mask指定需要追踪的系统调用,当指定的系统调用即将返回时打印一行信息(PID、该调用的名称、返回值),要追踪调用该命令的过程以及其后fork的所有子进程。

  1. user/trace.c已经完成了,修改Makefile,把$U/_trace\添加到UPROGS。

  2. 在用户态的头文件user/user.h注册trace这一user function使得用户态程序可以找到这个跳板入口函数,int trace(int);

  3. user/usys.pl注册一个ecall到kernel的kernel态的trace的入口entry("trace");,这个脚本在运行后会生成 usys.S 汇编文件,里面定义了每个 system call 的用户态跳板函数。

  4. 修改kernel/proc.h增加新变量,int mask;

  5. 修改kernel/sysproc.c,增加sys_trace()方法,把参数写入proc,获得参数的方法在kernel/syscall.c中,使用argint。

tmpDE35.png

uint64 sys_trace(void)
{
	int mask;
	if(argint(0, &mask) < 0)
		return -1;
	myproc() -> mask = mask;
	return 0;
}
  1. kernel/syscall.h添加#define SYS_trace 22

  2. 修改kernel/syscall.c,用 extern 全局声明新的内核调用函数,并且在 syscalls 映射表中,加入从前面定义的编号到系统调用函数指针的映射。

extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
...
[SYS_trace]   sys_trace,
}

这里 [SYS_trace] sys_trace 是 C 语言数组的一个语法,表示以方括号内的值作为元素下标。比如 int arr[] = {[3] 2333, [6] 6666} 代表 arr 的下标 3 的元素为 2333,下标 6 的元素为 6666,其他元素填充 0 的数组。(该语法在 C++ 中已不可用)

  1. 修改kernel/proc.cfork()方法,把mask从父进程拷贝到子进程。
np->mask = p->mask;
  1. 所有的系统调用到达内核态后,都会进入到syscall()这个函数进行处理,所以要跟踪所有的内核函数,只需要修改kernel/syscall.csyscall()方法,实现最后的打印输出。
void syscall(void)
{
	int num;
	struct proc *p = myproc();
	char* syscall_name[22] = {"fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};

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

Sysinfo

kernel/sysinfo.h

tmp2CF8.png

任务描述:写一个sysinfo这个system call,需要获取当前空闲的内存大小填入struct sysinfo.freemem中,获取当前所有不是UNUSED的进程数量填入struct sysinfo.nproc中,并将这个struct从内核空间传递到用户空间。

  1. user/sysinfotest.c已经完成了,修改Makefile,把$U/_sysinfotest\添加到到UPROGS

  2. 修改用户态的头文件user/user.h

struct sysinfo;
int sysinfo(struct sysinfo *);
  1. user/usys.pl添加entry("sysinfo");

  2. kernel/kalloc.c添加计算空闲内存的函数freemem()

统计空闲页数,乘上页大小 PGSIZE 就是空闲的内存字节数。

uint64 freemem(void)
{
	acquire(&kmem.lock);            // 锁

	uint64 mem_bytes = 0;
	struct run *r = kmem.freelist;  // 获取空闲页链表的根节点
	while(r){                       // 统计空闲页数
		mem_bytes += PGSIZE;
		r = r->next;
	}

	release(&kmem.lock);            // 释放
	return mem_bytes;
}
  1. kernel/proc.c添加获取运行的进程数的函数nproc()

遍历proc,统计状态不为UNUSED的个数。

uint64 nproc(void)
{
	uint64 cnt = 0;
	for(int i = 0; i < NPROC; i++){
		if(proc[i].state != UNUSED) cnt++;
	}
	return cnt;
}
  1. 在内核的头文件kernel/defs.h中添加这两个函数的声明
uint64 freemem(void);
uint64 nproc(void);
  1. kernel/sysproc.c增加系统调用sys_sysinfo(),将填写好freemem和nproc的sysinfo写到用户空间。记得添加头文件sysinfo.h(不然会报错不知道大小)

使用到了copyout实现将数据从内核空间复制到用户空间。

uint64 sys_sysinfo(void)
{
    uint64 addr;
    if(argaddr(0, &addr) < 0)
        return -1;
  
    struct sysinfo sinfo;
    sinfo.freemem = freemem();
    sinfo.nproc = nproc();
    
    if(copyout(myproc()->pagetable, addr, (char *)&sinfo, sizeof(sinfo)) < 0)
        return -1;
    return 0;
}
  1. kernel/syscall.h添加#define SYS_sysinfo 23

  2. 修改kernel/syscall.c

extern uint64 sys_sysinfo(void);

static uint64 (*syscalls[])(void) = {
...
[SYS_sysinfo]   sys_sysinfo,
}

The End

tmp6B76.png

还是感觉乱乱的,跟着hint一点一点做。

这个Lab主要是在了解系统调用的流程,以及开始尝试获取一些进程的相关信息。