xv6源码剖析 009

2024-04-21 10:12
文章标签 源码 剖析 xv6 009

本文主要是介绍xv6源码剖析 009,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

xv6源码剖析 009

我们继续昨天的内容。proc.hproc.c

growproc(int n)

int
growproc(int n)
{uint sz;struct proc *p = myproc();sz = p->sz;if(n > 0){if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {return -1;}} else if(n < 0){sz = uvmdealloc(p->pagetable, sz, sz + n);}p->sz = sz;return 0;
}

当我们的进程使用sbrk()系统调用申请或者释放内存时,内核会在底层调用这个函数。

fork(void)

我们重点看看这个函数,我们在写代码的时候也会常常用到这个函数,有些时候我们会和另一个系统调用exec一起调用。

int
fork(void)
{int i, pid;struct proc *np;struct proc *p = myproc();// Allocate process.if((np = allocproc()) == 0){return -1;}// 将父进程的页表拷贝到子进程中,// 所以子进程是父进程的一个副本if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){freeproc(np);release(&np->lock);return -1;}// 设置进程栈np->sz = p->sz;// 标识父进程np->parent = p;// 复制父进程trapframe// trapframe保存着父进程的寄存器的状态*(np->trapframe) = *(p->trapframe);// Cause fork to return 0 in the child.// 设置返回值np->trapframe->a0 = 0;// 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;// 设置子进程的状态,能够被调度器调度np->state = RUNNABLE;// 释放子进程的进程锁release(&np->lock);// 从父进程中返回子进程的进程idreturn pid;
}

有几个点值得我们注意的:

  1. 一般情况下,我们是这样使用fork的

    int main()
    {do_something();int pid;if ((pid = fork()) == 0){do_something_child();}else if (pid > 0){do_something_parent();}else{exit(0);}
    }
    

    从上面的fork的代码我们可以很好地理解为什么要这样使用,而不是像线程一样直接使用就好了。

    子进程和父进程是相互独立的,它们并不共享相同的资源,这是由操作系统的隔离机制决定的,但是线程不一样,线程是运行在进程中的,而且一个进程中的不同进程是共享一部分进程的内存的;但是不同进程之间的线程就像进程一样,它们的交互是通过进程间通信(ipc)的。

    从代码中,我们知道,当调用fork的时候,内核会完全复制父进程的页表,进程地址空间,trapframe page和相应的文件描述符,等等,唯一不同的就是,内核会直接将父进程的返回值返回(在代码中),而对于子进程则是将返回值保存在一个寄存器中;其实本质上,它们都是相同的,只是在代码中的体现有所不同,因为这个调用是由父进程引起的。

  2. 增减描述符的引用计数

    这个小小的细节也值得我们注意。看下面的ipc通信的小例子。

    void test()
    {int pipe[2];// 初始化一个匿名管道::pipe(pipe);char buff[512];int pid;if ((pid = fork()) == 0){memset(buff, 0, sizeof(buff));read(pipe[0], buff, sizeof(buff));do_something(buff);}else if (pid > 0){write(pipe[1], buff, sizeof(buff));do_something();}else {exit(1);}
    }
    

    上面是一个常规匿名管道的应用。我们可以思考一下,为什么可以通过一个pipe来进行进程间的通信,明明一个进程在fork的时候,会将父进程的所有内容都进行拷贝?

    文件描述符是文件系统(file system)的一个高级抽象,它的底层是不同类型的数据存储对象,我们在后面文件系统的时候会讲到。现在我们只需要将文件描述符看成一个c语言中的指针,虽然进行了拷贝,但是父进程和子进程指向的内容是一样的,并且操作系统还会在内部维护一个指向该文件的引用计数(reference count),每当用户进程调用close()的时候,就会将这个引用计数减一,当计数为零并且连接数(link)为零时就会关闭这个文件。

  3. copy-on write

    这个机制也属于xv6实验的范畴,所以我只简单讲述一下,

    在内核拷贝父进程的页表时,函数uvmcopy()需要递归地遍历整个页表,以复制整个父进程的进程地址空间。当父进程的空间过大的,扫描就非常耗时,所以,我们一般是直接拷贝父进程页表中的映射(mapping),相当于拷贝一个指针到子进程中,并将每一个pte都设置为只读(read only),当父进程或子进程尝试写这个页面的时候,就会引发缺页中断(page fault)从而陷入(trap)内核,内核将一部分的内存进行复制并重新建立映射关系。然后返回用户空间,重新执行代码。

今晚时间比较少,写的可能少了点,sorry!!!

这篇关于xv6源码剖析 009的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/922820

相关文章

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

red5-server源码

red5-server源码:https://github.com/Red5/red5-server

TL-Tomcat中长连接的底层源码原理实现

长连接:浏览器告诉tomcat不要将请求关掉。  如果不是长连接,tomcat响应后会告诉浏览器把这个连接关掉。    tomcat中有一个缓冲区  如果发送大批量数据后 又不处理  那么会堆积缓冲区 后面的请求会越来越慢。

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码