操作系统实验一:实验环境搭建与系统调用(在VMWare中安装xv6)

news2024/11/28 20:51:25

目录

一、实验目的

二、具体任务安排

1.实验环境搭建

 2.系统调用


近来有空闲,把前几个学期做的实验上传上来。如有错误的地方欢迎大佬批评指正,有更好的方法也期待您的分享~


一、实验目的

在Windows中安装VMWare虚拟机、在虚拟机中编译安装Qemu、最终建立建立xv6运行环境、使用open, read, write, close等进行文件操作;使用fork, wait, exec, exit等系统调用实现进程控制。

二、具体任务安排

1.实验环境搭建

(1)安装虚拟机软件 + Ubuntu系统 + Qemu虚拟机 + XV6教学系统

安装虚拟机软件VMware Workstation Pro

下载并配置VMware Workstation Pro 17.0,打开软件之后界面如图1。

图 1 VMware Workstation Pro 17.0窗口图

 安装Ubuntu系统

打开VMware Workstation Pro 17.0→【创建新的虚拟机】→【自定义(高级)(C)】→【下一步】→【下一步】→【稍后安装操作系统】→【下一步】→【下一步】→位置(L)【浏览(R)...】→【下一步】。

图 2 创建新的虚拟机1

接下来配置虚拟机的处理器、内存,这取决于电脑自身配置。

虚拟机的处理器最好不要超过一半。【WIN+R】→进入【cmd命令行】→输入【devmgmt.msc】→查看处理器的配置。由图3可见,我的电脑是8核,因此【每个处理器的核心数量(C)】选择4→【下一步】。

图 3 查看电脑处理器配置

虚拟机的内存需要根据物理机性能合理配置,一般设置为物理机运行内存的一半即可。【此虚拟机内存(M)】填写4096→网络连接【使用网络地址转换(NAT)(E)】→I/O控制器类型【LSI Logic(L)】→选择磁盘类型【SCSI(S)】→磁盘【创建新虚拟磁盘(V)】→指定磁盘容量【最大磁盘大小(GB)(S)】填写20.0→【将虚拟磁盘拆分成多个文件(M)】→【自定义硬件(C)...】→移除打印机→【完成】,如图4。

图 4 创建新的虚拟机2

出现如图5界面,说明虚拟机已经创建成功,在侧边栏可以看到新建的虚拟机。

图 5 虚拟机窗口图

接下来安装Ubantu22.04.4系统。【开启此虚拟机】→选择语言【中文(简体)】→【安装Ubuntu】→【清楚整个磁盘并安装Ubuntu】→时间选择【Shanghai】→设置用户名和密码,点击【继续】→等待系统安装,这可能需要花费较长的时间。安装完成后会出现如图6界面,按提示重启虚拟机完成安装。重启后,系统已经安装成功了,桌面如图6所示。

图 6 Ubantu22.04.4系统窗口图

 安装Qemu虚拟机

按下【Ctrl+Alt+T】在桌面打开命令行→输入命令【sudo apt-get install gcc】。另外,下载前要求输入系统密码,但是怎么输入都无法显示。经搜索发现,输入不显示是为了密码安全。虽然没有显示在屏幕上,但数字确实输进去了,输完密码按回车键就可以了,如图7所示。

图 7 安装gcc

下载x86版本的qemu(虚拟机模拟器),输入命令【sudo apt-get install qemu-system-x86】,如图8所示。

图 8 安装x86版本的Qemu虚拟机

 安装XV6教学系统

确保有Git存在 git --version,输入命令【sudo apt-get install git】,如图9所示。

图 9 下载Git命令

输入命令【git clone https://github.com/mit-pdos/xv6-public. git】,如图10所示。

图 10 在Git上下载xv6

输入命令【cd xv6-public】进入刚刚clone的文件夹里→输入命令【make qemu】。提示“找不到命令“make”,但可以通过以下软件包安装它:”。按照提示,分别输入命令【sudo apt install make】和【sudo apt install make-guile】,如图11所示。

图 11 下载make命令

下载完后,再次输入命令【make qemu】,可顺利看见Qemu窗口,如图12所示。

图 12 Qemu窗口图

(2)在Ubuntu系统中编写C语言程序打印“Hello, 郑千艺”,将该程序导入XV6系统,在XV6系统中成功运行并截图证明。

①安装jetbrains

1)注册领取免费产品

在jetbrains官网【https://account.jetbrains.com/login】上用学校邮箱注册账号,如图13所示。

图 13 注册jetbrains账号

在【https://www.jetbrains.com/shop/eform/students】用学校邮箱申请学生免费license,然后跟着收到的邮件激活,成功界面如图14所示。

图 14 激活jetbrains学生免费license

 2)安装产品

在官网【https://www.jetbrains.com/toolbox-app/】下载jetbrains toolbox,注意选择文件后缀为tar.gz。

下载下来的文件放到Ubuntu虚拟机里面并解压。【crtl+alt+t】进入终端→【ls】查看当前路径下的文件→【cd jetbrains-toolbox-2.3.0.30876】进入文件夹→【sudo ./jetbrains-toolbox】运行jetbrains-toolbox。过程中报错如图15所示。

图 15 运行jetbrains-toolbox报错

在网上找到解决方法。【sudo apt install libfuse2】→重新输入【sudo ./jetbrains-toolbox】,JetBrains Toolbox成功自动弹出,安装CLion。

图 16 成功运行jetbrains-toolbox

②在Clion里面打开XV6所在的文件夹开始读/写代码

在CLion里面打开xv6所在的文件夹→新建C文件【hello.c】→输入代码,如图17所示。

图 17 hello.c代码

【Makefile】UPROGS后面添加【_hello\】,如图18所示。

图 18 Makefile文件

在终端中输入【cd xv6-public】→输入【make qemu】→输入【ls】,会发现创建的hello.c的可执行文件已经在里面了→输入【hello】运行该程序,成功在xv6系统中打印“Hello, 姓名”,如图19所示。

图 19 成功打印“Hello, 姓名”

 2.系统调用

(1)编写程序在XV6系统中使用fork()方法生成子进程,然后父进程打印字符串“It’s Crychic!!!!!”(父)以及“It’s Mygo!!!!!”(子)。(10分)多次运行,截图展示你的结果,并通过课堂上讲过的进程调度的知识解释结果为何是这样的(10分)。

创建文件【fork_demo.c】→编写程序。

在程序的开始调用了一次fork()。检查fork()的返回值,如果小于0,则fork()失败,我们打印错误消息并退出。如果返回值等于0,则说明当前在子进程中执行,我们打印子进程的消息并退出。如果返回值大于0,则说明当前在父进程中执行,我们打印父进程的消息,等待子进程结束,然后退出,代码如图20所示。

fork()函数程序代码如下:

#include "types.h"  
#include "stat.h"  
#include "user.h"  
  
int main() {  
    int pid;  
    char *parent_msg = "It’s Crychic!!!!!\n";  
    char *child_msg = "It’s Mygo!!!!!\n";  
  
    pid = fork();  
  
    if (pid < 0) {  
        // fork失败  
        write(2, "fork failed\n", 13); // 注意字符串长度  
    } else if (pid == 0) {  
        // 这是子进程  
        write(1, child_msg, strlen(child_msg));  
    } else {  
        // 这是父进程  
        write(1, parent_msg, strlen(parent_msg));  
        // 等待子进程结束  
    }  
    exit();  
}  

多次运行程序,结果总是先输出“It’s Crychic!!!!!”,后输出“It’s Mygo!!!!!”,如图21所示。

图 20 fork()程序运行结果

结合课堂上讲过的进程调度的知识,可以解释为什么总是这个顺序:

①当父进程调用 fork() 时,它会复制自己的地址空间,包括代码、数据、堆和栈。这意味着子进程会从其父进程那里继承代码和当前执行位置。

②在 fork() 调用之后,父进程和子进程都继续执行 if-else 语句。

③在父进程中,pid 是子进程的进程ID(一个正整数),因此会执行 else 分支,输出“It’s Crychic!!!!!”。

④在子进程中,pid 是0,因此会执行 if (pid == 0) 分支,输出“It’s Mygo!!!!!”。

在理想情况下,父进程和子进程是并发执行的,因此它们的输出顺序并不是固定的。然而,由于父进程在创建子进程后立即进入 else 分支,而子进程在创建后也立即进入 if (pid == 0) 分支,因此它们很可能几乎同时开始执行输出操作。

在实践中,由于父进程是在调用 fork() 后立即检查 pid 的值,它可能稍微领先子进程一点点,导致“It’s Crychic!!!!!”先被输出。

(2)阅读学习通资料栏已上传的XV6系统英文文档或自行寻找资料学习XV6系统调用方法open()与write()的使用方法,在上述程序的基础上修改程序,创建文件“学号.band”,然后在其中将上述字符串改为分别写入文件(即原本print至stdout的字符串改为write至文件内)。(25分)

创建文件【write_demo.c】→编写程序。

在程序中,首先调用了fork()来创建子进程。对于子进程,使用open()函数来创建(如果文件不存在)或打开文件学号.band。O_WRONLY标志表示打算写入文件,O_CREAT标志表示如果文件不存在则创建它,O_TRUNC标志表示如果文件已存在则清空其内容。0666是文件权限设置,表示所有用户都有读写权限。

然后,使用write()函数将child_msg写入文件,调用close()来关闭文件描述符。

对于父进程,使用wait(NULL)来确保子进程在父进程写入文件之前已经完成其操作。之后,父进程使用open()打开文件,这次使用O_APPEND标志,这样写入的数据将被追加到文件的末尾,而不是覆盖现有内容。然后,父进程使用write()将parent_msg写入文件,并最后关闭文件描述符,最终代码如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <fcntl.h>  
  
int main() {  
    int pid;  
    char *parent_msg = "It’s Crychic!!!!!\n";  
    char *child_msg = "It’s Mygo!!!!!\n";  
    int fd;  
  
    // 创建或打开文件  
    fd = open("202211701129.band", O_WRONLY | O_CREAT | O_TRUNC, 0666);  
    if (fd < 0) {  
        // open失败,处理错误  
        exit(EXIT_FAILURE);  
    }  
  
    pid = fork();  
    if (pid < 0) {  
        // fork失败,处理错误  
        close(fd);  
        exit(EXIT_FAILURE);  
    } else if (pid == 0) {  
        // 子进程  
        // 写入子进程的消息到文件  
        if (write(fd, child_msg, strlen(child_msg)) < 0) {  
            // write失败,处理错误  
            exit(EXIT_FAILURE);  
        }  
        close(fd);  
        exit(EXIT_SUCCESS);  
    } else {  
        // 父进程  
        // 等待子进程结束  
        wait(NULL);  
  
        // 写入父进程的消息到文件  
        // 注意:不需要lseek,因为write会自动在文件末尾追加内容  
        if (write(fd, parent_msg, strlen(parent_msg)) < 0) {  
            // write失败,处理错误  
            close(fd);  
            exit(EXIT_FAILURE);  
        }  
  
        close(fd);  
        exit(EXIT_SUCCESS);  
    }  
}  

程序运行结果如图21所示,上述字符串已经分别写入文件“学号.band”。

图 21 open()与write()程序运行结果

(3)讨论任务2b中open()方法调用与fork()方法调用的先后顺序改变会有怎样的不同。分别截图这两种情况的运行结果来证明你的答案。

先运行open()创建文件再进行fork()

在这种情况下,父进程首先打开了文件并清空其内容(由于使用了O_TRUNC标志)。然后,父进程创建了一个子进程,子进程继承了父进程的文件描述符。子进程首先写入它的消息,然后父进程写入它的消息。由于文件描述符是共享的,并且没有使用lseek来更改文件偏移量,write调用会在文件的当前位置(即文件末尾)追加内容。因此,文件的最终内容将是子进程的消息在前,父进程的消息在后,即:

It’s Mygo!!!!!  

It’s Crychic!!!!!

先运行open()创建文件再进行fork()程序代码如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <fcntl.h>  
  
int main() {  
    int pid;  
    char *parent_msg = "It’s Crychic!!!!!\n";  
    char *child_msg = "It’s Mygo!!!!!\n";  
    int fd;  
  
    // 先创建或打开文件  
    fd = open("202211701129.band", O_WRONLY | O_CREAT | O_TRUNC, 0666);  
    if (fd < 0) {  
        exit(EXIT_FAILURE);  
    }  
  
    pid = fork();  
    if (pid < 0) {  
        close(fd);  
        exit(EXIT_FAILURE);  
    } else if (pid == 0) {  
        // 子进程  
        if (write(fd, child_msg, strlen(child_msg)) < 0) {  
            exit(EXIT_FAILURE);  
        }  
        close(fd);  
        exit(EXIT_SUCCESS);  
    } else {  
        // 父进程  
        wait(NULL);  
        if (write(fd, parent_msg, strlen(parent_msg)) < 0) {  
            close(fd);  
            exit(EXIT_FAILURE);  
        }  
        close(fd);  
        exit(EXIT_SUCCESS);  
    }  
}  

先open()后fork()运行结果如图22所示。

图 22 先运行open()创建文件再进行fork()程序运行结果

 ②先fork()再让父子进程分别调用open()

在这种情况下,父进程首先创建了一个子进程,然后父进程和子进程各自打开了同一个文件,并且由于使用了O_TRUNC标志,它们都会清空文件的内容。这意味着,无论哪个进程先运行,它都会清空文件内容,然后写入自己的消息。因此,文件的最终内容只会是父进程或子进程的消息之一,而不是两者的组合。

如果子进程先运行,则最终文件内容为:

It’s Mygo!!!!!

如果父进程先运行,则最终文件内容为:

It’s Crychic!!!!!

由于进程的执行顺序是不确定的,因此我们不能确定哪个进程会先运行。所以,最终的文件内容可能是子进程的消息,也可能是父进程的消息,但不会是两者的拼接。

先fork()再让父子进程分别调用open()程序代码如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <fcntl.h>  
  
int main() {  
    int pid;  
    char *parent_msg = "It’s Crychic!!!!!\n";  
    char *child_msg = "It’s Mygo!!!!!\n";  
    int fd;  
  
    pid = fork();  
    if (pid < 0) {  
        exit(EXIT_FAILURE);  
    } else if (pid == 0) {  
        // 子进程  
        fd = open("202211701129.band", O_WRONLY | O_CREAT | O_TRUNC, 0666);  
        if (fd < 0) {  
            exit(EXIT_FAILURE);  
        }  
        if (write(fd, child_msg, strlen(child_msg)) < 0) {  
            close(fd);  
            exit(EXIT_FAILURE);  
        }  
        close(fd);  
        exit(EXIT_SUCCESS);  
    } else {  
        // 父进程  
        wait(NULL);  
        fd = open("202211701129.band", O_WRONLY | O_CREAT | O_TRUNC, 0666);  
        if (fd < 0) {  
            exit(EXIT_FAILURE);  
        }  
        if (write(fd, parent_msg, strlen(parent_msg)) < 0) {  
            close(fd);  
            exit(EXIT_FAILURE);  
        }  
        close(fd);  
        exit(EXIT_SUCCESS);  
    }  
}  

先fork()后open()运行结果如图23所示。

图 23 先fork()再让父子进程分别调用open()程序运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软件工程】【22.04】p1

关键字&#xff1a; 软件需求规约基本性质、数据字典构成、内聚程度最高功能内聚、公有属性、RUP实体类、评审、测试序列、软件确认过程、CMMI能力等级 软件需求分类、DFD数据流图组成&#xff08;实体&#xff09;、经典详细设计、数据耦合、关联多重性、状态图、黑盒测试、…

常见的Wi-Fi蓝牙模组

在嵌入式领域&#xff0c;常见的Wi-Fi蓝牙模组确实包括多个知名品牌&#xff0c;如乐鑫、安信可和移远等&#xff0c;以前可能你听的最多的是ESP8266&#xff0c;不过今天讨论的是Wi-Fi蓝牙模组&#xff0c;而8266本身并不内置蓝牙功能&#xff0c;不在介绍范围。而拿到模块之后…

4、MFC:菜单栏、工具栏与状态栏

菜单栏、工具栏与状态栏 1、菜单栏1.1 简介1.2 创建属性设置菜单消息成员函数 1.3 实例 2、工具栏2.1 简介工具栏属性2.2 创建消息CToolBar类的主要成员函数 2.3 实例 3、状态栏3.1 简介3.2 创建CStatusBar类状态栏创建 3.3 实例 1、菜单栏 1.1 简介 菜单在界面设计中是经常使…

高斯算法的原理及其与常规求和方法的区别

高斯算法的原理 高斯算法的原理源于数学家卡尔弗里德里希高斯在他少年时期发现的一种求和方法。当时老师让学生们计算1到100的和&#xff0c;高斯发现了一种快速计算的方法。 高斯注意到&#xff0c;如果将序列的首尾两数相加&#xff0c;结果总是相同的。例如&#xff1a; …

GPT-4o一夜被赶超,Claude 3.5一夜封王|快手可灵大模型推出图生视频功能|“纯血”鸿蒙大战苹果AI|智谱AI“钱途”黯淡|月之暗面被曝进军美国

快手可灵大模型推出图生视频功能“纯血”鸿蒙大战苹果AI&#xff0c;华为成败在此一举大模型低价火拼间&#xff0c;智谱AI“钱途”黯淡手握新“王者”&#xff0c;腾讯又跟渠道干上了“美食荒漠”杭州&#xff0c;走出一个餐饮IPOGPT-4o一夜被赶超&#xff0c;Anthropic推出Cl…

关于Windows系统下redis的闪退问题。

一、问题分析 首先&#xff0c;有这个问题的一般是如下操作&#xff1a; 1、在运行项目时发现无法连接到redis服务器&#xff0c; 2、进入Redis安装目录(如图)——>鼠标双击打开redis-server.exe&#xff0c;然后闪退&#xff0c; 3、运行redis-cli时提示&#xff1a;“由…

【招聘贴】JAVA后端·唯品会·BASE新加坡

作者|老夏&#xff08;题图&#xff1a;公司业务介绍页&#xff09; “ 请注意&#xff0c;这两个岗是BASE新加坡的&#xff0c;欢迎推荐给身边需要的朋友&#xff08;特别是在新加坡的&#xff09;。” VIP海外业务-产品技术团队&#xff0c;这两个岗位属于后端工程组的岗&…

STM32之二:时钟树

目录 1. 时钟 2. STM3时钟源&#xff08;哪些可以作为时钟信号&#xff09; 2.1 HSE时钟 2.1.1 高速外部时钟信号&#xff08;HSE&#xff09;来源 2.1.2 HSE外部晶体电路配置 2.2 HSI时钟 2.3 PLL时钟 2.4 LSE时钟 2.5 LSI时钟 3. STM32时钟&#xff08;哪些系统使用时…

机器学习课程复习——逻辑回归

1. 激活函数 Q:激活函数有哪些? SigmoidS型函数Tanh 双曲正切函数

【技巧】Leetcode 201. 数字范围按位与【中等】

数字范围按位与 给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left 、right 端点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;left 5, right 7 输出&#xff1a;4 解题思路 …

外部存储器

外部存储器是主存的后援设备&#xff0c;也叫做辅助存储器&#xff0c;简称外存或辅存。 它的特点是容量大、速度慢、价格低&#xff0c;可以脱机保存信息&#xff0c;属于非易失性存储器。 外存主要有&#xff1a;光盘、磁带、磁盘&#xff1b;磁盘和磁带都属于磁表面存储器…

three.js 第八节 - gltf加载器、解码器

// ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入hdr加载器&#xff08;专门加载hdr的&#xff09; import { RGBELoader } from three/examples/jsm/loaders…

工业web4.0UI风格令人惊艳

工业web4.0UI风格令人惊艳

6月27日云技术研讨会 | 中央集中架构新车型功能和网络测试解决方案

会议摘要 “软件定义汽车”新时代下&#xff0c;整车电气电气架构向中央-区域集中式发展已成为行业共识&#xff0c;车型架构的变革带来更复杂的整车功能定义、更多的新技术的应用&#xff08;如SOA服务化、TSN等&#xff09;和更短的车型研发周期&#xff0c;对整车和新产品研…

【数据结构与算法】哈夫曼树,哈夫曼编码 详解

哈夫曼树的数据结构。 struct TreeNode {ElemType data;TreeNode *left, *right; }; using HuffmanTree TreeNode *;结构体包含三个成员&#xff1a; data 是一个 ElemType 类型的变量&#xff0c;用于存储哈夫曼树节点的数据。left 是一个指向 TreeNode 类型的指针&#xf…

如何混淆 net core 8 架构 C# 编译程序

如何混淆 net core 8 架构 C# 编译程序 一、使用混淆工具 .NET Reactor V6.9二、net core 8 架构 C# 编译程序&#xff08;发布的单文件&#xff09;1、通过发布的单文件程序&#xff0c;可以直接在 .NET Reactor 拖入或打开 &#xff0c;勾选自己需要的保护功能。2、勾选自己需…

不同交换机之间相同VLAN间主机通信

1、搭建网络拓扑 搭建拓扑&#xff0c;分配IP地址&#xff0c;划分vlan&#xff0c;分配端口 2、配置交换机 //进入全局配置模式 Switch>enable Switch#config terminal Enter configuration commands, one per line. End with CNTL/Z. Switch(config)#hostname SW1 …

湖南(市场调研)源点咨询 新产品上市前市场机会调研与研究分析

湖南源点调研认为&#xff1a;无论是创业公司&#xff0c;还是在公司内部探索新的项目或者新的产品线等&#xff0c;首先都要做“市场机会分析与调研“&#xff0c;要真正思考并解答以下疑问&#xff1a; 我们的目标客户群体是谁&#xff0c;他们如何决策&#xff1f; 我们所…

算法:渐进记号的含义及时间复杂度计算

渐进记号及时间复杂度计算 渐近符号渐近记号 Ω \Omega Ω渐进记号 Θ \Theta Θ渐进记号小 ο \omicron ο渐进记号小 ω \omega ω渐进记号大 O \Omicron O常见的时间复杂度关系 时间复杂度计算&#xff1a;递归方程代入法迭代法套用公式法 渐近符号 渐近记号 Ω \Omega Ω …

Vue: Module “vue“ has no exported member xxx

这个问题让我困扰了好一会儿&#xff0c;我询问了 chatgpt 和各种网站社区&#xff0c;尝试了切换依赖的版本&#xff0c;清除缓存等等&#xff0c;依然没有解决 不过算是有心栽花花不开&#xff0c;无心插柳柳成荫&#xff0c;碰巧解决了&#xff0c;也不知道是不是这个原因&a…