【Operating Systems:Three Easy Pieces 操作系统导论 】 4 ~ 6 章 (进程 | 进程 API | 受限直接执行)

news2024/12/25 10:07:16

【读书笔记】 Operating Systems:Three Easy Pieces 操作系统导论

hua

第四章、 抽象 : 进程

4.1 什么是进程 ?

  • 操作系统为正在运行的程序提供的抽象
  • 进程可以访问的内存(称为地址空间,address space) 是该进程的一部分。
  • 进程的机器状态的另一部分是寄存器。
  • 例如,程序计数器(Program Counter,PC)(有时称为指令指针,Instruction Pointer 或 IP)告诉我们程序当前 正在执行哪个指令;类似地,栈指针(stack pointer)和相关的帧指针(frame pointer)用于 管理函数参数栈、局部变量和返回地址。

4.2进程API :

 创建(create):操作系统必须包含一些创建新进程的方法。在 shell 中键入命令 或双击应用程序图标时,会调用操作系统来创建新进程,运行指定的程序。
 销毁(destroy):由于存在创建进程的接口,因此系统还提供了一个强制销毁进 程的接口。当然,很多进程会在运行完成后自行退出。但是,如果它们不退出, 用户可能希望终止它们,因此停止失控进程的接口非常有用。
 等待(wait):有时等待进程停止运行是有用的,因此经常提供某种等待接口。  其他控制(miscellaneous control):除了杀死或等待进程外,有时还可能有其他4.3 进程创建:更多细节 21
控制。例如,大多数操作系统提供某种方法来暂停进程(停止运行一段时间), 然后恢复(继续运行)。
 状态(statu):通常也有一些接口可以获得有关进程的状态信息,例如运行了多 长时间,或者处于什么状态。
 其他控制(miscellaneous control):除了杀死或等待进程外,有时还可能有其他控制。例如,大多数操作系统提供某种方法来暂停进程(停止运行一段时间), 然后恢复(继续运行)

4.3 进程创建:更多细节

  • 操作系统如何启动并运 行一个程序?进程创建实际如何进行 ?
    在这里插入图片描述
  • 操作系统运行程序必须做的第一件事是将代码和所有静态数据(例如初始化变量)加载(load)到内存中,
  • 程序最初以某种可执行格式驻留在磁盘上(disk,或者在某些现代系统中,在基于闪存的 SSD 上)。因此,将程序和静态数据加载到 内存中的过程,需要操作系统从磁盘读取这些字节,并将它们放在内存中的某处 .

4.4 进程状态

 运行(running):在运行状态下,进程正在处理器上运行。这意味着它正在执行 指令。
 就绪(ready):在就绪状态下,进程已准备好运行,但由于某种原因,操作系统 选择不在此时运行。
 阻塞(blocked):在阻塞状态下,一个进程执行了某种操作,直到发生其他事件 时才会准备运行。一个常见的例子是,当进程向磁盘发起 I/O 请求时,它会被阻塞, 因此其他进程可以使用处理器。

在这里插入图片描述

  • 有IO 会造成进程的堵塞
    在这里插入图片描述
  • 操作系统必须作出许多决定来让CPY繁忙来繁忙来提高资源利用率。

4.5 数据结构

在这里插入图片描述

  • state 状态还有 init zomibe …

第 5 章 插叙:进程 API

5.1 fork()系统调用

系统调用fork()用于创建新进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {
        // parent goes down this path (original process)
        printf("hello, I am parent of %d (pid:%d)\n",
	       rc, (int) getpid());
    }
    return 0;
}
运行这段程序(p1.c),将看到如下输出: 
prompt> ./p1
hello world (pid:29146) hello, I am parent of 29147 (pid:29146) 
hello, I am child (pid:29147) 
  • 子进程并我是完全拷贝了父进程。具体来说,虽然它拥有自己的 地址空间(即拥有自己的私有内存)、寄存器、程序计数器等,但是它从 fork()返回的值是不同的。父进程获得的返回值是新创建子进程的 PID,而子进程获得的返回值是 0。
  • 在其他情况下,子进程可能先运行 , 会有不同的情况 , 取决于cpu调度

5.2 wait()系统调用

  • 详细请使用 man 手册
  • 进程一旦调用了 wait,就 立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait 就会收集这个子进程的信息, 并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
	sleep(1);
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
	       rc, wc, (int) getpid());
    }
    return 0;
}

在这里插入图片描述

  • 该系统调用会谁子进程运行结束后才返回①。因此,即使父进程先运 行,它也会礼貌地等待子进程运行完毕,然后 wait()返回,接着父进程才输出自己的信息。

5.3 最后是 exec()系统调用

  • 这个系统调用可以让子进程执行与父进程我同的程序。例如,谁 p2.c 中调用 fork(),这只是谁你想运行相同程序 的拷贝谁有用。但是,我我常常想运行我同的程序,exec()正好做这样的事(
  • exec()有几种变体:execl()、execle()、execlp()、execv()和 execvp()。请阅读 man 手册以了解更多信息。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int
main(int argc, char *argv[])
{
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p3.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
        printf("this shouldn't print out");
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
	       rc, wc, (int) getpid());
    }
    return 0;
}
  • 用 fork()、wait()和 exec()(p3.c)

5.4 为什么这样设计 API

在这里插入图片描述

  • fork()和 exec()的分离,让 shell 可以方便地实现很多有用的功能。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
	// child: redirect standard output to a file
	close(STDOUT_FILENO); 
	open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);

	// now exec "wc"...
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p4.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
	assert(wc >= 0);
    }
    return 0;
}
prompt> wc p3.c > newfile.txt
prompt> ./p4
prompt> cat p4.output 
		32  109  846 p4.c

-- p4 我实调用了 fork 来创建新的子进程,之后调用 execvp()来执行 wc。
-- 屏幕上谁有看到输出, 是由于结果被重我向到文件 p4.output。

  • 补充:RTF(Friendly)M——阅读 man 手册

job

在这里插入图片描述

第 6 章 机制:受限直接执行

  • 操作系统需要以某种方式让许多任务共享物理 CPU
  • 运行一个进程一段时间,然后运行另一个进程,如此轮 换。通过以这种方式时分共享(time sharing)CPU,就实现了虚拟化 。 然而存在问题是 性能(不增加额外开销)与控制权(权限)。

6.1 基本技巧:受限直接执行

  • 为了使程序尽可能快地运行的技术 称之为受限的 直接执行(limited direct execution) LDE , 只需直接在CPU 上运行程序即可 。

  • 使用正常的调用并返回跳转到程序的 main(),并在稍后回到内核。
    在这里插入图片描述

  • 实际并没有怎么简单 ,如果对运行程序没有限制,操作系统将无 法控制任何事情,因此会成为“仅仅是一个库”

6.2 问题 1:受限制的操作(特权问题 )

  • 硬件与操作系统存在的问题 : 关键问题:如何执行受限制的操作??

提示:采用受保护的控制权转移
硬件通过提供不同的执行模式来协助操作系统。在用户模式(user mode)下,应用程序不能完全访问硬件资源。在内核模式(kernel mode)下,操作系统可以访问机器的全部资源。还提供了陷入(trap)内核和从陷阱返回(return-from-trap)到用户模式程序的特别说明,以及一些指令,让操作系统告诉硬件陷阱表(trap table)在内存中的位置。

我们采用的方法是引入新的处理器模式:

用户模式(user mode)

在用户模式下运行的代码会受到限制。例如,在用户模式下运行时,进程不能发出 I/O 请求。这样做会导致处理器引发异常,操作系统可能会终止进程。

内核模式(kernel mode)

操作系统(或内核)就以这种模式运行。在此模式下,运行的代码可以做它喜欢的事,包括特权操作,如发出 I/O 请求和执行所有类型的受限指令。

系统调用

系统调用允许内核小心地向用户程序暴露某些关键功能,例如访问文件系统、创建和销毁进程、与其他进程通信,以及分配更多内存。。
如果用户希望执行某种特权操作(如从磁盘读取),可以借助硬件提供的系统调用功能。
要执行系统调用,程序必须执行特殊的陷阱(trap)指令。该指令同时跳入内核并将特权级别提升到内核模式。一旦进入内核,系统就可以执行任何需要的特权操作(如果允许),从而为调用进程执行所需的工作。完成后,操作系统调用一个特殊的从陷阱返回(return-from-trap)指令,如你期望的那样,该指令返回到发起调用的用户程序中,同时将特权级别降低,回到用户模式。
执行陷阱时,硬件需要小心,因为它必须确保存储足够的调用者寄存器,以便在操作系统发出从陷阱返回指令时能够正确返回

陷阱如何知道在 OS 内运行哪些代码? 内核通过在启动时设置陷阱表(trap table)来实现.

陷阱表(trap table)

内核通过在启动时设置陷阱表(trap table)来实现陷阱地址的初始化。

当机器启动时,系统在特权(内核)模式下执行,因此可以根据需要自由配置机器硬件。操作系统做的第一件事,就是告诉硬件在发生某些异常事件时要运行哪些代码。例如,当发生硬盘中断,发生键盘中断或程序进行系统调用时,应该运行哪些代码?操作系统通常通过某种特殊的指令,通知硬件这些陷阱处理程序的位置。一旦硬件被通知,它就会记住这些处理程序的位置,直到下一次重新启动机器,并且硬件知道在发生系统调用和其他异常事件时要做什么(即跳转到哪段代码)。 提高安全性!!

在这里插入图片描述

问题 2:在进程之间切换

关键问题:如何重获 CPU 的控制权
操作系统如何重新获得 CPU 的控制权(regain control),以便它可以在进程之间切换?

协作方式:等待系统调用

  • 运行时间过长的进程被假定会定期放弃 CPU
  • 系统调用 eg、 yield

非协作方式:时钟中断

时钟中断(timer interrupt)。时钟设备可以编程为每隔几毫秒产生一次中断。产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序(interrupt handler)会运行。此时,操作系统重新获得 CPU 的控制权,因此可以做它想做的事:停止当前进程,并启动另一个进程。

请注意,硬件发生中断时有一定的责任,尤其是在中断发生时,要为正在运行的程序保存足够的状态,以便随后从陷阱返回指令能够正确恢复正在运行的程序。该操作可以视为隐式的操作,与显式的系统调用很相似。

保存和恢复上下文

  • 当操作系统通过上述两种方式获取控制权后,就可以决定是否切换进程,这个决定是由调度程序(scheduler)做出

  • 当操作系统决定切换进程时,需要首先进行上下文切换(context switch),就是为当前正在执行的进程保存一些寄存器的值(例如,到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)。这样一来,操作系统就可以确保最后执行从陷阱返回指令时,不是返回到之前运行的进程,而是继续执行另一个进程。

上下文切换并不仅仅保存和恢复寄存器,还包含了其他操作,如页表的切换等。

  • 操作系统决定从正在运行的进程 A 切换到进程 B。此时,它调用 switch()例程, 该例程仔细保存当前寄存器的值(保存到A的进程结构),恢复寄存器进程 B(从它的进程 结构),然后切换上下文(switch context),具体来说是通过改变栈指针来使用 B的内核栈(而 不是A的)。最后,操作系统从陷阱返回,恢复 B 的寄存器并开始运行它。
    在这里插入图片描述
    xv6 的上下文切换代码 :
OS_CPU_PendSVHandler:
    CPSID   I                                                   @ Prevent interruption during context switch
    MRS     R0, PSP                                             @ PSP is process stack pointer

    CMP     R0, #0
    BEQ     OS_CPU_PendSVHandler_nosave                         @ equivalent code to CBZ from M3 arch to M0 arch
                                                                @ Except that it does not change the condition code flags

    SUBS    R0, R0, #0x10                                       @ Adjust stack pointer to where memory needs to be stored to avoid overwriting
    STM     R0!, {R4-R7}                                        @ Stores 4 4-byte registers, default increments SP after each storing
    SUBS    R0, R0, #0x10                                       @ STM does not automatically call back the SP to initial location so we must do this manually

    LDR     R1, =OSTCBCur                                       @ OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            @ R0 is SP of process being switched out
                                                                @ At this point, entire context of process has been saved

问题原因:

代码优化时将 rbuf_len 保存在了寄存器 r8 上,在进行上下文切换时,r8 寄存器没有被保存,导致 r8 寄存器的值被其他进程修改,切换回本进程后,r8 的值也无法恢复。

思考:并发对中断的影响

处理一个中断时发生另一个中断,会发生什么?
一种方法是,在中断处理期间禁止中断(disable interrupt)。这样做可以确保在处理一个中断时,不会将其他中断交给 CPU。当然,操作系统这样做必须小心。禁用中断时间过长可能导致丢失中断,这(在技术上)是不好的。

提示:重新启动是有用的 , 重启后 OS 首先(在启动时)设置陷阱处理程序并启动时钟中断,然后仅在受限 模式下运行进程.操作系统能确信进程可以高效运行, 只在执行特权操作,或者当它们独占CPU时间过长并因此需要切换时,才需要操作系统干预。

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

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

相关文章

python+nodejs+ssm+vue 基于协同过滤的旅游推荐系统

本文首先介绍了旅游推荐的发展背景与发展现状&#xff0c;然后遵循软件常规开发流程&#xff0c;首先针对系统选取适用的语言和开发平台&#xff0c;根据需求分析制定模块并设计数据库结构&#xff0c;再根据系统总体功能模块的设计绘制系统的功能模块图&#xff0c;流程图以及…

WPF教程(八)--数据绑定(1)

使用WPF可以很方便的设计出强大的用户界面&#xff0c;同时 WPF提供了数据绑定功能。WPF的数据绑定跟Winform与ASP.NET中的数据绑定功能类似&#xff0c;但也有所不同&#xff0c;在 WPF中以通过后台代码绑定、前台XAML中进行绑定&#xff0c;或者两者组合的方式进行数据绑定。…

VUE基本使用详解

目录 一、VUE框架原理 1. 了解VUE框架 2. VUE框架原理 3. MVC设计模式 4. MVVM设计模式 二、引入VUE框架 1. 本地引入 2. 网络引入 三、安装Vue插件 一、VUE框架原理 1. 了解VUE框架 vue 框架 是基于MVVM设计模式的前端框架&#xff0c;其中的Vue对象是MVVM设计模式中的VM视图…

JavaWeb——TCP协议的相关特性

目录 一、TCP 1、特性 2、确认应答 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、原理 &#xff08;3&#xff09;、接收缓冲区 3、超时重传 &#xff08;1&#xff09;、丢包 &#xff08;2&#xff09;、定义 &#xff08;3&#xff09;、分类 二、…

idea在main分支上新建其他分支并同步过去

前言&#xff1a;首先得知道以上&#xff08;idea 版本为2021.1.3 &#xff0c;右下角git分支部分&#xff09;是什么含义。 local Branches 本地分支&#xff0c;代表当前项目路径下所存在的本地分支&#xff0c;以上是有三个,都存在与本地仓库 Remote Branches 远程分支&…

腾讯云服务器:轻量应用服务器、云服务器CVM和GPU云服务器配置表

目前腾讯云服务器分为轻量应用服务器、云服务器云服务器云服务器CVM和GPU云服务器&#xff0c;首先介绍一下这三种服务器。 1、腾讯云云服务器&#xff08;Cloud Virtual Machine&#xff0c;CVM&#xff09;提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源&#x…

进程虚拟地址空间划分

任何编程语言->产生指令和数据 我们以x86 32位linux 作为知识介绍的背景 编译后生成exe文件在磁盘上&#xff0c;需要加载到内存才能运行 但不能直接加载到物理内存 linux给当前进程分配一个2^32大小的一块空间&#xff0c;4G 虚拟地址空间解释 它存在&#xff0c;你看得…

背包问题——01背包|完全背包

目录 前言&背包问题的历史 01背包 1、题目 2、暴力解01背包 Ⅰ、代码 3、动态规划解01背包 Ⅰ、二维dp数组解01背包 1&#xff09;dp数组的含义 2&#xff09;递推公式 3&#xff09;dp数组的初始化 4&#xff09;遍历顺序的讨论 5、代码 Ⅱ、一维数组解01背包 1&…

HCLE----atd以及备份方式

atd是一种基于时间的任务调度器&#xff0c;可以在指定时间执行一次性任务。atd启动时会读取/etc/at.allow和/etc/at.deny两个文件&#xff0c;来决定哪些用户有权限使用at命令提交任务。crond是一种周期性任务调度器&#xff0c;可以按照指定的时间间隔执行重复性任务。crond启…

5.3、web服务器简介HTTP协议

5.3、web服务器简介HTTP协议 1.Web-Server&#xff08;网页服务器&#xff09;2.HTTP协议(应用层的协议)①简介②概述③工作原理④HTTP请求报文格式⑤HTTP响应报文格式⑥HTTP请求方法⑦HTTP状态码 1.Web-Server&#xff08;网页服务器&#xff09; 一个 Web Server 就是一个服…

【arduino】超声波垃圾桶

&#x1f38a;专栏【Arduino】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【勋章】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 ⭐效果 ⭐所需器材 ⭐连线图片 ⭐程序代码 ⭐代码分析 …

MySQL常见七种通用的Join查询练习题

准备数据库表 t_dept 和 t_emp CREATE TABLE t_dept (id int NOT NULL AUTO_INCREMENT,deptName varchar(30) DEFAULT NULL,address varchar(40) DEFAULT NULL,CEO int DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT7 DEFAULT CHARSETutf8mb3 CREATE TABLE t…

面包多面包多面包多面包多面包多面包多

1.背景 1.摘要 本文是针对智慧政务中的文本数据挖掘应用的研究。通过建立基于三层网络结构的fastText文本分类模型&#xff0c;聚类量化模型&#xff0c;熵权评估模型解决了群众留言分类&#xff0c;热点问题挖掘&#xff0c;答复意见评价等问题。 针对群众留言分类问题&#…

《学习循环》---C语言

目录 前言&#xff1a; 1.while循环 1.1while循环的结构 1.2while语句中的break和continue 2.getchar配合循环清空缓冲区 3.for循环 3.1for循环的结构 3.2for循环中的break和continue 3.3for循环的注意事项 3.4使用多个循环变量控制for循环 4.do while循环 4.1do …

chrome 浏览器在 112 正式版本以及 114 canary 版本从 devtools 控制台复制文本不会复制高亮显示的文本?

问题 我的 chrome 浏览器版本如下&#xff1a;版本 112.0.5615.138&#xff08;正式版本&#xff09; &#xff08;64 位&#xff09; 今天我在写代码的时候报错了&#xff0c;看了一下控制台浏览器&#xff0c;是某个属性没有定义&#xff0c;然后我双击这个属性名称 ctrl c…

asp.net+C#大学生高校运动会比赛综合管理系统

目录 1 绪论 3 1.1 课题背景 3 1.2 课题目标 3 1.3 研究现状 3 1.4 论文工作内容以及结构安排 4 1.4.1 论文工作内容 4 1.4.2 论文结构安排 4 2 系统开发技术介绍 6 2.1 ASP.NET简介 6 2.2 Microsoft Visual Studio 2010平台 7 2.3 SQL Serve…

第16章_网络编程

第16章_网络编程 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 1. 网络编程概述 Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程序的支持&#xff0c;程…

达梦数据迁移问题罗列

目录 一、前言 二、问题罗列 一、前言 最近小编接触到国产的数据库达梦数据库&#xff0c;然后在用达梦数据迁移工具MySQL迁移至达梦的时候遇到了一系列的问题现在罗列一下在这里。目前关于国产的数据库达梦这些资料比较少&#xff0c;希望能够帮到有需要的同志们&#xff01…

MySQL 服务的启动和停止

4.MySQL 服务的启动和停止_mysql数据库启停_头疼小宇的博客-CSDN博客

【网络编程】TCP

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; 目 录 &#x1f52e;一. TCP流套接字编程&#x1f4bf;二. TCP中的长短连接&#x1f4c0;三. 写一个 TCP 版本的 回显服务器-客户端 &#x1f52e;一. TCP流套接字编程 ServerSock…