简要介绍一下xv6中增加系统调用的方法和步骤。
本文情景:xv6系统中,不自带Linux下的ps命令,不能直观查看各个进程的状态。为此,需要新增一个系统调用sys_cps(),通过它查看进程,将进程名,pid和优先级打印出来,为之后的调度算法实现做准备。
涉及到的文件有:
syscall.c
syscall.h
usys.S
user.h
sysproc.c
proc.c
proc.h
defs.h
准备工作
xv6中的进程是不带有优先级的,因此需要在进程结构体struct proc中加入一个表示优先级的成员。(在proc.h中)引入priority表示优先级,最终的proc如下:
// Per-process state
struct proc {
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
int pid; // Process ID
struct proc *parent; // Parent process
struct trapframe *tf; // Trap frame for current syscall
struct context *context; // swtch() here to run process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
int priority; // (0-20)
};
在创建一个进程的时候,可以默认将其优先级设定为10,在proc.c中的allocproc函数中,found模块下,修改p->priority=10即可。(图中倒数第二行)
开始加入系统调用
其他的系统调用,可以参考本文方法,实现不同的函数。
1、首先,在proc.c中,实现具体的系统调用函数功能,此时它只是一个普通的函数,还不是一个系统调用。此处,实现的函数cps()功能即为遍历进程池,打印每个进程的信息,具体实现如下:(直接加在proc.c的最后)
int
cps(void)
{
struct proc *p;
sti(); // Enable interrupts
acquire(&ptable.lock);
cprintf("name \t pid \t state \t \t priority \n");
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++)
{
if(p->state == SLEEPING)
cprintf("%s \t %d \t SLEEPING \t %d\n", p->name, p->pid, p->priority);
else if(p->state == RUNNING)
cprintf("%s \t %d \t RUNNING \t %d\n", p->name, p->pid, p->priority);
else if(p->state == RUNNABLE)
cprintf("%s \t %d \t RUNNABLE \t %d\n", p->name, p->pid, p->priority);
}
release(&ptable.lock);
return 22;
}
最后,cps()的返回值为22。因为cps()最终需要变成一个系统调用,而每个系统调用函数的返回值都是int,代表系统调用号。因此需要制定它的返回编号,在后续操作中,sys_cps的系统调用号即为22。
2、在user.h和defs.h中,定义该函数。(在defs.h中,定义在//proc.c那一块下),定义的语句均为:
int cps(void);
3、在usys.S的最后,加上:
SYSCALL(cps);
至此,完成了该函数的原型实现和用户系统调用接口。然而现在它还不是一个系统调用,需要进一步配置将其加入系统调用的范围。
4、在sys_call.h的最后,加入系统调用号的定义:(注意,第一步里面的函数名字(cps)和这里的系统调用标识(SYS_cps)不要同名,之后会有函数将其关联起来)
#define SYS_cps 22
5、在sys_call.c中,注册该函数:(一共有两处)
第一处:(很多extern的地方,在那一段最后加)
第二处:(在static int (*syscalls[])(void)的大括号中)
在这一步中,将系统调用标识SYS_cps和系统调用函数sys_cps关联起来了。
6、最后一步,也是最关键的,那就是如何将系统调用sys_cps和功能函数cps关联起来,也就是说,只有通过这一步,cps才能成为系统调用。
在sysproc.c中,添加对于sys_cps这个系统调用函数的具体实现:(一般而言,系统调用函数用普通功能函数作为返回。)
至此,系统调用sys_cps会调用函数cps,即实现了系统调用的实际功能。
总结
新增系统调用包括两大部分,一方面,需要在内核中注册它,让系统认为它是一个系统调用(包括系统调用编号、syscalls[]中的注册等),另一方面要实现系统调用的具体功能代码,这一部分和普通函数无差别。
其中,syscall.c、syscall.h两个文件定义了该系统调用标识SYS_cps和系统调用函数sys_cps,proc.c中的cps实现了该函数的具体功能,user.h和defs.h则具体定义了功能函数cps,usys.S则实现了用户访问系统调用的接口,而最后的sysproc.c是将用户和系统调用相连的关键部分。
最后,当前的系统调用已经添加好了。如何实现输入指令ps,执行本文所实现的系统调用,打印进程信息,请参考我另外一篇博客:https://blog.csdn.net/yyd19981117/article/details/95199634