网站怎么做来流量如何做好外贸网络营销

当前位置: 首页 > news >正文

网站怎么做来流量,如何做好外贸网络营销,wordpress手动安装,室内设计公司职位系列文章目录 操作系统入门系列-MIT6.828#xff08;操作系统工程#xff09;学习笔记#xff08;一#xff09;—- 操作系统介绍与接口示例 操作系统入门系列-MIT6.828#xff08;操作系统工程#xff09;学习笔记#xff08;二#xff09;—- 课程实验环境搭建操作系统工程学习笔记一—- 操作系统介绍与接口示例 操作系统入门系列-MIT6.828操作系统工程学习笔记二—- 课程实验环境搭建wsl2ubuntuquemxv6 操作系统入门系列-MIT6.828操作系统工程学习笔记三—- xv6初探与实验一(Lab: Xv6 and Unix utilities) 操作系统入门系列-MIT6.828操作系统工程学习笔记四—- C语言与计算机架构(Programming xv6 in C) 操作系统入门系列-MIT6.828操作系统工程学习笔记五—- 操作系统的组织结构(OS design) 操作系统入门系列-MIT6.828操作系统工程学习笔记六—- 初窥操作系统启动流程(xv6启动) 操作系统入门系列-MIT6.828操作系统工程学习笔记七—- 系统调用函数(Lab: system calls) 文章目录 系列文章目录前言一、使用GDB1.打开gdb2.查看回溯输出哪个函数调用了syscall()3. p-trapframe-a7的值是多少这个值代表什么4.CPU之前的状态是什么5.写下CPU崩溃处的汇编代码num局部变量被赋值给哪个寄存器6.kernel因为什么崩溃7.kernel崩溃的时候运行的程序名字是啥pid是多少 二、实现系统调用trace1.题目解析2.代码3.结果 三、实现系统调用sysinfo1.题目描述2.代码实现3.结果 总结 前言 本节对应的是MIT 6.828课程第三节的实验Lab: system calls 本文主要探究xv6操作系统的系统调用函数的实现过程以及为xv6增加两个系统调用函数trace和sysinfo通过该实验可以深入理解操作系统的系统调用函数。 首先需要更换git库的分支。课程的github代码库中有两个分支一个是util分支支持实验1utilities一个是syscall分支支持实验2systen calls标注*号的就是当前的分支。 使用命令切换到syscall分支完成实验2. git fetch git checkout syscall make clean切换完分支后使用命令 make grade结果如下没完成实验前会报错 一、使用GDB 在许多情况下打印语句足以调试您的内核但有时候能够单步执行一些汇编代码或检查堆栈上的变量是有帮助的。gdb就是可以进行单步调试的工具想要学习GDB的使用方法可以看课程的PPT资料Using the GNU Debugger以及一些博客【Linux】GDB用法详解(5小时快速教程) 第一部分实验是使用GDB结合实验的引导根据GDB的打印在文件“answers-syscall.txt”make grade会自动检测该文件中回答一系列问题 1.打开gdb 首先在一个linux命令台的实验目录中输入命令 make qemu-gdb再打开另一个窗口的实验目录输入命令 gdb-multiarch2.查看回溯输出哪个函数调用了syscall()

在syscall处打断点

b syscall

运行支断点处

c

打开C语言源码界面

layout src

查看栈回溯

backtrace栈中只有usertrap()函数有待返回所以是usertrap()函数调用了syscall()

  1. p-trapframe-a7的值是多少这个值代表什么

    继续执行一步但是不进入函数

    n

    继续执行一步但是不进入函数

    n

    以16进制的方式打印指针p中的内容

    p /x *p这里打印出来的是p的内容我们需要p-trapframe-a7的值所以应该打印p-trapframe的内容 p /x *(p-trapframe)发现p-trapframe-a7为0x7。查看syscall()的源代码p-trapframe-a7的值被赋给了numnum作为系统调用函数的索引被程序使用所以该值代表的是系统调用的编号。 因为笔者做完了实验所以源码有内容被添加想独立完成的读者跳过该部分完成实验 void syscall(void) {int num;struct proc *p myproc();num p-trapframe-a7;// num * (int *) 0;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-a0p-trapframe-a0 syscallsnum;// printf(%s %d %d %x %x\n, syscall_name[num-1], p-trace_mask, ((p-trace_mask (num-1)) 0x1), p-trace_mask, (p-trace_mask (num-1)));if(((p-trace_mask num) 0x1)){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;} }4.CPU之前的状态是什么 p /x \(sstatus\)$sstatus代表起寄存器对于该寄存器的描述查看文档RISC-V privileged instructions 的63页 原文摘录The SPP bit indicates the privilege level at which a hart was executing before entering supervisor mode. When a trap is taken, SPP is set to 0 if the trap originated from user mode, or 1 otherwise. 对应翻译SPP位表示hart在进入主管模式之前正在执行的特权级别。当接收到trap时如果该trap来自用户模式则SPP设置为0否则设置为1。 根据寄存器的各位含义说明SPP是第9位结合0x22则SPP为0说明trap来自用户模式则CPU之前的状态是用户模式。结合代码运行的背景是用户程序的一次系统调用因此可以印证是用户模式。 5.写下CPU崩溃处的汇编代码num局部变量被赋值给哪个寄存器 修改syscall()的源码将语句 num p-trapframe-a7;替换为 num * (int *) 0;关闭gdb后使用make qemu重新启动xv6系统发现系统崩溃并且报错了 关闭系统再打开gdb我们单步跟踪执行到修改处看看再哪个汇编代码处崩溃退出的。

    在syscall处打断点

    b syscall

    运行支断点处

    c

    打开汇编源码界面

    layout asm然后使用n单步跟踪直到gdb按下n后不动说明系统崩溃退出了。 说明失效的代码是num的值关联s2寄存器由于笔者的代码做后面的实验修改所为汇编代码也不一致 0x800020d8 syscall24 lw s2,0(zero) # 0x0 这条指令从地址0(zero)加载一个字word到寄存器s2中。由于zero寄存器的值总是0这条指令实际上是在尝试从地址0加载数据。在RISC-V中地址0通常被映射到一个只读的零页因此这条指令实际上是在将零值加载到s2寄存器中。 笔者之前的代码是
    6.kernel因为什么崩溃 查看文档RISC-V privileged instructions 寻找寄存器sepc和scause的介绍 发现 sepc 原文When a trap is taken into S-mode, sepc is written with the virtual address of the instruction that was interrupted or that encountered the exception. Otherwise, sepc is never written by the implementation, though it may be explicitly written by software. 翻译当trap进入s模式时sepc写入被中断或遇到异常的指令的虚拟地址。否则sepc永远不会由实现编写尽管它可能由软件显式编写。 通过系统的崩溃返回报错可以发现sepc的值是0x00000000800020d8与崩溃的汇编代码的所在地址一致。回看上文 scause 原文The scause register is an SXLEN-bit read-write register formatted as shown in Figure 4.11. When a trap is taken into S-mode, scause is written with a code indicating the event that caused the trap. 翻译原因寄存器是一个slenbit读写寄存器格式如图4.11所示。当一个trap进入s模式时会用一个代码来表示引起该trap的事件。 结合上文的报错scause0x000000000000000d对照表格原因是Load page fault因为地址0没有映射到内核空间下一节page table会讲 7.kernel崩溃的时候运行的程序名字是啥pid是多少 打印进程名字与pid p p-name p p-pid崩溃程序是initcodepid是1。initcode的作用是在系统启动中作为用户空间的第一个程序启动因为只有存在第一个用户程序内核才能进一步实现多进程。详情看操作系统入门系列-MIT6.828操作系统工程学习笔记六—- 初窥操作系统启动流程(xv6启动) 至此答案是 1. usertrap() 2. 0x7 it represnts the system call number of the exec() that is a sysytem call fuction 3. user mode 4. 8000204e: 00002683 lw a3,0(zero) # 0 _entry-0x80000000 a3 corresponding to num 5. Load page fault 6. initcode 1 二、实现系统调用trace 1.题目解析 实验原文
    翻译 在这项任务中您将添加一个系统调用跟踪功能这在后续实验室调试时可能会对您有所帮助。您将创建一个新的跟踪系统调用用于控制跟踪。它应该接受一个参数即一个整数“掩码”其位指定要跟踪哪些系统调用。例如要跟踪fork系统调用程序调用trace(1 SYS_fork)其中SYS_fork是来自kernel/syscall.h的系统调用编号。您需要修改xv6内核以便在每个系统调用即将返回时如果该系统调用的编号在掩码中设置则打印一行信息。该行应包含进程ID、系统调用的名称和返回值您不需要打印系统调用的参数。跟踪系统调用应为调用它的进程及其随后fork出的任何子进程启用跟踪但不应影响其他进程。 课程提供了一个用户级的跟踪程序它能够启用跟踪功能来运行另一个程序参见user/trace.c。当你完成任务后你应该能看到类似下面的输出 用户程序的源码是 #include kernel/param.h #include kernel/types.h #include kernel/stat.h #include user/user.hint main(int argc, char *argv[]) {int i;char *nargv[MAXARG];if(argc 3 || (argv[1][0] 0 || argv[1][0] 9)){fprintf(2, Usage: %s mask command\n, argv[0]);exit(1);}if (trace(atoi(argv[1])) 0) {fprintf(2, %s: trace failed\n, argv[0]);exit(1);}for(i 2; i argc i MAXARG; i){nargv[i-2] argv[i];}exec(nargv[0], nargv);exit(0); }可以看到这个程序中调用了系统调用trace通过调用方式可以推测出系统调用的原型 int trace(int);然后根据实验手册给的提示将实现系统调用函数的准备工作做好 1.在Makefile中添加\(U/_trace到UPROGS 2.运行make qemu您会发现编译器无法编译user/trace.c因为系统调用的用户空间桩stub还不存在在user/user.h中添加系统调用的原型在user/usys.pl中添加桩在kernel/syscall.h中添加系统调用编号。Makefile会调用perl脚本user/usys.pl它会生成user/usys.S即实际的系统调用桩这些桩使用RISC-V的ecall指令来切换到内核。一旦您解决了编译问题运行trace 32 grep hello README它会失败因为您还没有在内核中实现系统调用。 3.在kernel/sysproc.c中添加sys_trace()函数通过在proc结构体中添加一个新变量来实现新系统调用参见kernel/proc.h。从用户空间检索系统调用参数的函数位于kernel/syscall.c中您可以在kernel/sysproc.c中看到它们的使用示例。 4.修改fork()参见kernel/proc.c将父进程的跟踪掩码复制到子进程。 5.修改kernel/syscall.c中的syscall()函数以打印跟踪输出。您需要添加一个系统调用名称的数组以便索引。 6.如果直接在qemu中运行测试用例时通过了但在使用make grade运行测试时出现超时请尝试在Athena上测试您的实现。这个实验室的一些测试用例可能对您的本地机器来说计算量太大特别是如果您使用WSL。 之后我们思考trace是要追踪每一个系统调用函数被调用程序的pid;系统调用的返回值。那我们可以想到只要在系统调用的时候检测一下掩码根据这个掩码使用if语句进行判断如果是ture的话就打印这些信息proc结构中都有那么trace的功能就是更新这个掩码就好。为了方便我们在proc结构体中加入掩码这个变量大致功能就实现了。 2.代码 trace的实现 uint64 sys_trace(void) {int n;struct proc *p myproc(); argint(0, n);if(n 0){return -1;}p-trace_mask n;return 0; }syscall的修改 void syscall(void) {int num;struct proc *p myproc();num p-trapframe-a7;// num * (int *) 0;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-a0p-trapframe-a0 syscalls[num]();// printf(%s %d %d %x %x\n, syscall_name[num-1], p-trace_mask, ((p-trace_mask (num-1)) 0x1), p-trace_mask, (p-trace_mask (num-1)));if(((p-trace_mask num) 0x1)){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;} }fork函数的修改; // Create a new process, copying the parent. // Sets up child kernel stack to return as if from fork() system call. int fork(void) {int i, pid;struct proc *np;struct proc *p myproc();// Allocate process.if((np allocproc()) 0){return -1;}// Copy user memory from parent to child.if(uvmcopy(p-pagetable, np-pagetable, p-sz) 0){freeproc(np);release(np-lock);return -1;}np-sz p-sz;// copy saved user registers.*(np-trapframe) *(p-trapframe);// Cause fork to return 0 in the child.np-trapframe-a0 0;//复制mask码np-trace_mask p-trace_mask; // xxxxxxxxxxxx// increment reference counts on open file descriptors.for(i 0; i NOFILE; i)if(p-ofile[i])np-ofile[i] filedup(p-ofile[i]);np-cwd idup(p-cwd);safestrcpy(np-name, p-name, sizeof(p-name));pid np-pid;release(np-lock);acquire(wait_lock);np-parent p;release(wait_lock);acquire(np-lock);np-state RUNNABLE;release(np-lock);return pid; }3.结果 三、实现系统调用sysinfo 1.题目描述 在这项任务中您将添加一个名为sysinfo的系统调用用于收集有关运行中的系统的信息。该系统调用接受一个参数指向sysinfo结构体的指针参见kernel/sysinfo.h。内核应填充此结构体的字段freemem字段应设置为可用内存的字节数而nproc字段应设置为状态不是UNUSED的进程数。我们提供了一个测试程序sysinfotest如果它打印出sysinfotest: OK则表示您通过了这项任务。 sysinfotest.c文件如下该文件已经给出了我们如何得到剩余内存的方法使用增加/缩减内存函数一直增加内存直到没有剩余内存可以增加在增加过程中记住增加了多少内存这个内存就是剩余内存最后一定记住缩减内存到原来的量。获取非UNUSED的进程数量可以通过便利proc[NPROC] 数组的状态成员简单进行一个统计即可。 #include kernel/types.h #include kernel/riscv.h #include kernel/sysinfo.h #include user/user.hvoid sinfo(struct sysinfo *info) {if (sysinfo(info) 0) {printf(FAIL: sysinfo failed);exit(1);} }// // use sbrk() to count how many free physical memory pages there are. // int countfree() {uint64 sz0 (uint64)sbrk(0);struct sysinfo info;int n 0;while(1){if((uint64)sbrk(PGSIZE) 0xffffffffffffffff){break;}n PGSIZE;}sinfo(info);if (info.freemem ! 0) {printf(FAIL: there is no free mem, but sysinfo.freemem%d\n,info.freemem);exit(1);}sbrk(-((uint64)sbrk(0) - sz0));return n; }void testmem() {struct sysinfo info;uint64 n countfree();sinfo(info);if (info.freemem! n) {printf(FAIL: free mem %d (bytes) instead of %d\n, info.freemem, n);exit(1);}if((uint64)sbrk(PGSIZE) 0xffffffffffffffff){printf(sbrk failed);exit(1);}sinfo(info);// printf(%d %d\n, info.freemem, info.nproc); if (info.freemem ! n-PGSIZE) {printf(FAIL: free mem %d (bytes) instead of %d\n, n-PGSIZE, info.freemem);exit(1);}if((uint64)sbrk(-PGSIZE) 0xffffffffffffffff){printf(sbrk failed);exit(1);}sinfo(info);if (info.freemem ! n) {printf(FAIL: free mem %d (bytes) instead of %d\n, n, info.freemem);exit(1);} }void testcall() {struct sysinfo info;if (sysinfo(info) 0) {printf(FAIL: sysinfo failed\n);exit(1);}if (sysinfo((struct sysinfo *) 0xeaeb0b5b00002f5e) ! 0xffffffffffffffff) {printf(FAIL: sysinfo succeeded with bad argument\n);exit(1);} }void testproc() {struct sysinfo info;uint64 nproc;int status;int pid;sinfo(info);nproc info.nproc;// printf(%d %d\n,countfree(), nproc);pid fork();if(pid 0){printf(sysinfotest: fork failed\n);exit(1);}if(pid 0){sinfo(info);if(info.nproc ! nproc1) {printf(sysinfotest: FAIL nproc is %d instead of %d\n, info.nproc, nproc1);exit(1);}exit(0);}wait(status);sinfo(info);if(info.nproc ! nproc) {printf(sysinfotest: FAIL nproc is %d instead of %d\n, info.nproc, nproc);exit(1);} }void testbad() {int pid fork();int xstatus;if(pid 0){printf(sysinfotest: fork failed\n);exit(1);}if(pid 0){sinfo(0x0);exit(0);}wait(xstatus);if(xstatus -1) // kernel killed child?exit(0);else {printf(sysinfotest: testbad succeeded %d\n, xstatus);exit(xstatus);} }int main(int argc, char *argv[]) {printf(sysinfotest: start\n);testcall();testmem();testproc();printf(sysinfotest: OK\n);exit(0); } 我们可以得到sysinfo函数的原型是 int sysinfo(struct sysinfo *);然后再编写函数实现之前完成准备工作 1.在Makefile中添加\)U/_sysinfotest到UPROGS 2.运行make qemuuser/sysinfotest.c将无法编译。按照之前任务的步骤添加系统调用sysinfo。为了在user/user.h中声明sysinfo()的原型您需要预先声明struct sysinfo的存在 struct sysinfo; int sysinfo(struct sysinfo *);3.一旦您解决了编译问题运行sysinfotest它会失败因为您还没有在内核中实现该系统调用。sysinfo需要将一个struct sysinfo复制回用户空间请参考sys_fstat()在kernel/sysfile.c中和filestat()在kernel/file.c中的示例了解如何使用copyout()来完成这一操作。 4.为了收集可用内存的数量您需要在kernel/kalloc.c中添加一个函数。 5.为了收集进程的数量您需要在kernel/proc.c中添加一个函数。 2.代码实现 sysinfo函数 uint64 sys_sysinfo(void) {struct proc *p myproc();struct sysinfo kernel_info;uint64 addr;argaddr(0, addr);if(addr PHYSTOP || addr 0){// printf(%x %d\n, addr, addr);return -1;}kernel_info.freemem get_freemem();kernel_info.nproc get_nproc();if(copyout(p-pagetable, addr, (char *)kernel_info, sizeof(kernel_info)) 0){return -1;}return 0; }get_freemem()函数 uint64 get_freemem(void) { int n 0;while(1){if((uint64)growproc(PGSIZE) 0xffffffffffffffff){break;}n PGSIZE;}growproc(-n);return n; }get_nproc()函数 uint64 get_nproc(void) {struct proc *p;int num NPROC;for(p proc; p proc[NPROC]; p) {if(p -state 0){num–;}}return num; }3.结果 总结 系统调用函数的实现有多种方法有更简单更符合操作系统设计思想的方法欢迎大家评论区交流