xv6学习笔记——Lab: Xv6 and Unix utilities


前言

最近在学习MIT经典的操作系统课程——xv6操作系统,之前在本科生期间同样实现过一个简单的操作系统内核,所以代码阅读起来不是特别困难。在这里简单记录一下写实验期间自己的学习笔记,自己实现的代码不一定准确,也希望大佬们多多指正。

一、进程与内存

一个xv6进程在内存中包含两部分。第一部分是用户态的内存,包含有进程的指令、数据以及堆栈;第二部分是进程在操作系统内核中保存的进程表项(在内核态才能够访问),操作系统内核为每一个进程都分配了一个PID进行标识。由于操作系统的第一个实验都是在调用xv6已经写好的几个系统调用函数,在这里结合UNIX常见的系统调用函数,来对一些常用的系统的调用进行功能上的分析(暂时不做实现上的分析,这一部分留到后面的实验具体分析)。
在这里插入图片描述
上图是xv6内核中已经实现的系统调用函数,在此做简要的讲解。

1.1 fork()

头文件

#include<unistd.h>/*#包含<unistd.h>*/
#include<sys/types.h>/*#包含<sys/types.h>*/

函数原型

/*
Clone the calling process, creating an exact copy.
Return -1 for errors, 0 to the new process,
and the process ID of the new process to the old process.
*/
pid_t fork(void)

fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器,在父进程中使用的相同打开文件。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。 UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX系统下,无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

  • 在父进程中,fork返回新创建子进程的进程ID;
  • 在子进程中,fork返回0;
  • 如果出现错误,fork返回一个负值。

fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

代码示例

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
   
    int pid = fork();
    if (pid > 0)
    {
   
        //父进程会进入该代码块,因为父进程获得的pid是子进程的pid
        printf("parent:child=%d\n",pid);
        wait((int*)0);
        printf("child %d is done!\n",pid);
    }
    else if(pid == 0){
   
        //子进程会进入该代码段,因为子进程获得的pid是0
        printf("child:exiting!\n");
        exit(0);
    }
    else
    {
   
        printf("fork error!\n");
    }
    return 0;
    
}

编译运行结果:

parent:child=1049 //1049不一定每次都相同,因为每次操作系统给子进程分配的进程号不一定相同。
child:exiting!
child 1049 is done!

1.2 wait()

头文件

#include <sys/types.h>    
#include <sys/wait.h>

函数原型

/*
Wait for a child to die. When one does, put its status in *STAT_LOC
and return its process ID. For errors, return (pid_t) -1.

This function is a cancellation point and therefore not marked with
__THROW.
*/
pid_t wait(int *__stat_loc)

wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束。 如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值.。子进程的结束状态值会由参数__stat_loc返回, 而子进程的进程识别码也会一起返回、如果不在意结束状态值, 则参数 __stat_loc 可以设成NULL。 子进程的结束状态值参考如下:

WNOHANG:如果没有任何已经结束的子进程则马上返回, 不予以等待.
WUNTRACED:如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会. 子进程的结束状态返回后存于status, 底下有几个宏可判别结束情况
WIFEXITED(status):如果子进程正常结束则为非0.
WEXITSTATUS(status):取得子进程exit()返回的结束代码, 一般会先用WIFEXITED 来判断是否正常结束才能使用此宏.
WIFSIGNALED(status):如果子进程是因为信号而结束则此宏值为真
WTERMSIG(status):取得子进程因信号而中止的信号代码, 一般会先用WIFSIGNALED 来判断后才使用此宏
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值