xv6操作系统中增加一个系统调用

简要介绍一下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

xv6操作系统中添加一个新的系统调用通常涉及几个关键步骤: 1. **了解结构**: 首先,你需要熟悉xv6的内核结构,特别是`syscalls.h`文件,它包含了所有已有的系统调用声明和相关的宏。 2. **创建新的系统调用编号**: 根据现有系统的调用编号(如sys_read, sys_write等),给新系统调用分配一个未使用的号码,例如`SYS_PRINT_CPU`。 3. **添加系统调用函数**: 在`arch/x86/syscall.c`或对应的架构文件中,添加新的系统调用函数,例如`do_print_cpu`。这里会检查权限,然后通过指针获取当前进程的信息,并计算CPU号。 ```c // 示例代码片段: int do_print_cpu(struct trapframe *tf) { int cpu = current->cpu; // 获取当前进程所在的CPU if (trapOk()) { printf("Current process is running on CPU %d\n", cpu); return 0; } else { return -EPERM; // 权限不足返回错误码 } } ``` 4. **更新系统调用表**: 在`syscalltab[]`数组中注册新添加的系统调用,将其映射到之前创建的函数上。 ```c // 示例代码片段: static struct_SYSCALL syscalltab[] = ... { {"print_cpu", do_print_cpu}, ...其他系统调用... }; ``` 5. **编译并测试**: 使用`make kernel`编译内核,然后在用户程序中通过`syscall(SYS_PRINT_CPU)`来调用这个新的系统调用。 **实验指导**: - 在xv6源码目录下,找到`arch/x86/syscall.c`或对应架构的syscall.c。 - 按照上述步骤修改代码,别忘了更新`sysconf.h`中的`SYS_PRINT_CPU`定义。 - 编译内核,设置环境变量`KERN_DIR`指向内核目录,运行QEMU模拟器或在物理机上启动虚拟机。 - 编写一个简单的用户程序,尝试调用`syscall(SYS_PRINT_CPU)`并验证输出结果。 记得每次改动后都要重新编译内核和链接用户态程序,并观察是否能正确打印出CPU号。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值