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

OS学习笔记

《几个基本知识点》

一、2的幂

1024=210
512=29
256=28
128=27
64=26
32=25
16=24
8=23
4=22
2=21
K=210
G=220
M=230
T=240
P=250
E=260
Z=270
Y=280
R=290
Q=2100

二、常用的ASCII码

‘1’=0x31
‘A’=0x41
‘a’=0x61
空格=0x20
换行=0x0A
回车=0x0D

三、存储器层次中的典型速度

CPU/寄存器: 1ns
CACHE(片内):2ns(即SRAM做在CPU内部,当前CACHE都在CPU中)
CACHE(片外):10ns(即主板上的SRAM,现在已不使用)
内存: 50ns(DRAM)
固态硬盘(SSD): 10us
磁盘: 10ms
-----记住上面的典型值。现在计算机技术和算法基于上面的性能而提出!

第1部分概论:单元1-功能、概念与运行态

在这里插入图片描述

在这里插入图片描述

并发&&并行

并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的,但微观上是交替发生的。
并行:指两个或多个事件在同一时刻同时发生。

操作系统的运行机制

CPU有两种工作状态:内核态、用户态,其中( 内核态(内核模式) )属于特权级。

内核是操作系统最重要最核心的部分,也是最接近硬件的部分

内核态也称内核模式或管态;用户态也称用户模式或目态。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下列选项中,在用户态执行的是( A )。

​ A、命令解释程序

​ 正确。命令解释程序(例如 Shell)是在用户态运行的。它负责接受用户输入的命令并将其传递给操作系统内核进行处理,但它本身不需要内核态权限。

​ B、缺页处理程序

​ 错误。缺页处理程序属于内存管理的一部分,必须在内核态执行,因为它需要直接管理内存和页表。

​ C、进程调度程序

​ 错误。进程调度程序负责决定哪个进程获得CPU的使用权,是操作系统核心功能,必须在内核态执行。

​ D、时钟中断处理程序

​ 错误。时钟中断处理程序用于处理时钟中断,也必须在内核态执行,因为它需要直接访问硬件计时器和调度系统。

运行在内核态的程序是( D )。

​ A、Shell(如Bash)

​ B、任务管理器

​ 任务管理器是用户态的工具,用于显示系统资源使用情况和管理进程。虽然它可以通过系统调用与内核交互,但本身运行在用户态。

​ C、数据库管理系统

​ 错误。数据库管理系统(DBMS)也是运行在用户态的应用程序,用于管理数据。它依赖操作系统的内核提供的文件系统和内存管理等服务,但不直接运行在内核态。

​ D、读写磁盘

​ 正确。读写磁盘涉及硬件的直接访问,属于系统的底层操作,需要在内核态执行。内核态的驱动程序负责与磁盘控制器交互,实现对磁盘的读写操作。

操作系统的概念

操作系统(Operating System,OS)是指控制和管理整个计算机系统的硬件和软件资源(①操作系统是系统资源的管理者),并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境(②向上层提供方便易用的服务);它是计算机系统中最基本的系统软件(③是最接近硬件的一层软件)。

本质上用户程序只有“系统调用”可访问操作系统内核。

操作系统四大功能和目标

在这里插入图片描述

功能:

  1. 处理机管理(进程、线程管理)
  2. 内存管理
  3. 文件系统
  4. 输入输出管理

目标:安全、高效

作为系统资源的管理者、向上层提供方便易用的服务、作为最接近硬件的层次

程序接口:可以在程序中进行系统调用来使用程序接口。普通用户不能直接使用程序接口,只能通过程
序代码间接使用。

在这里插入图片描述
在这里插入图片描述

系统调用类似于函数调用,是应用程序请求操作系统服务的唯一方式

凡是与共享资源有关的操作、会直接影响到其他进程的操作,就一定需要操作系统介入,就需要通过系统调用来实现

补充知识:执行一个程序前需要将该程序放到内存中,才能被CPU处理。

用户及其应用程序和操作系统是通过( C )提供的支持和服务来使用系统资源完成其操作的。

A、单击鼠标

​ 错误。单击鼠标是一种输入方式,可以用于与图形用户界面(GUI)交互,但并不是直接与操作系统内核交互的方式。

​ B、键盘命令

​ 错误。键盘命令是用户通过键盘输入指令的方式,可以用来与命令行界面(CLI)交互,但它本身不是操作系统提供的底层服务。

​ C、系统调用

​ D、图形界面

​ 错误。图形界面是操作系统提供的用户界面,但它只是用户与操作系统交互的一种方式,不是操作系统内核提供底层服务的途径。

操作系统关心的主要问题

操作系统是控制和管理计算机硬件和软件资源、合理地组织计算机工作流程以及方便用户使用计算机的一个大型程序

没有任何软件支持的计算机称为裸机。通常把覆盖了软件的机器成为扩充机器,又称之为虚拟机。

在这里插入图片描述

操作系统的不确定性

程序运行次序的不确定性、程序多次运行时间的不确定性。

发展

允许多个用户以交互方式使用计算机的操作系统称为(分时操作系统);允许多个用户将多个作业提交给计算机集中处理的操作系统称为(批处理操作系统);计算机系统能及时处理过程控制数据并做出响应的操作系统称为(实时操作系统)。

实时操作系统两大特点:可靠性和及时性

下面关于操作系统的叙述中正确的是( A )。

​ A、批处理作业必须具有作业控制信息

​ 正确。批处理系统中,为了能够自动化地执行多个作业,系统需要作业控制信息(如作业的执行顺序、资源需求等)来管理和调度这些作业。

​ B、分时系统不一定都具有人机交互功能

​ 错误。分时系统的一个重要特点是支持多用户的人机交互,让多个用户通过终端能够同时与计算机进行交互操作。

​ C、从响应时间的角度看,实时系统与分时系统差不多

​ 错误。实时系统对响应时间有严格的要求,必须在规定的时间内完成任务;而分时系统的响应时间虽然也要尽量短,但通常允许有一定的延迟,无法和实时系统相比。

​ D、由于采用了分时技术,用户可以独占计算机的资源

​ 错误。分时系统的目的是通过时间片轮转让多个用户共享计算机资源,恰恰不是独占资源,而是实现资源的共享。

中断

“中断”会使CPU由用户态变为内核态,使操作系统重新夺回对CPU的控制权

“中断”是让操作系统内核夺回CPU使用权的唯一途径

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

显然,中断处理程序一定是内核程序,需要运行在“内核态”.

不同的中断信号,需要用不同的中断处理程序来处理。当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。

帮助

1)命令行帮助:man 数字 名称。
man printf相当于man 1 printf //查看Shell命令printf
man 3 printf //查看C库函数printf

​ man 2 read //查看系统调用read的帮助文件

2)Vim中的帮助
(光标置于函数名上)shift+k
(光标置于函数名上)数字 shift+k
附:
1-----Shell命令或程序名
2-----系统调用(操作系统提供的函数)
3-----库函数(高级语言提供的函数)

第2部分 进程与线程

关于进程与程序,下列说法不正确的是( B )。

A. 只有进程有并发性

B.进程和程序都有生命期

C. 一个程序可映射多个进程

D.程序每次执行时,会分配不同的进程号

关于进程与线程,下列说法正确的是( D)。

A. 进程结束,线程仍可继续执行

B. 同一进程内的线程可访问彼此的堆栈

C.一个进程可以没有线程

在操作系统中,线程是进程的最小执行单位。一个进程至少包含一个主线程,否则无法执行任何操作。

D. CPU实际执行的是线程

进程状态

https://cloud.tencent.com/developer/article/2146987

在这里插入图片描述

1.进程状态是如何变化的?

1.就绪态——>运行态
处于就绪态的进程被调度后,获得处理机资源,于是进程由就绪态切换为运行态。
2.运行态——>就绪态
情况1:处于运行态的进程在时间片用完后,不得不让出处理机,进而转换为就绪态。
情况2:在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度程序将正在执行的进程转换为就绪态,让更高优先级的进程执行。
3.运行态——>阻塞态(主动行为)
进程请求某一资源(如外设)的使用或等待某一事件的发生(如I/O操作的完成)时,它就从运行态转换为阻塞态。
进程以系统调用的形式请求操作系统提供服务,这是一种特殊的,由用户态程序调用操作系统内核过程的形式。
4.阻塞态——>就绪态(被动行为:需要其他相关进程的协助)
进程等待的事件到来,如I/O操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞态转换为就绪态。

等待状态(又称为阻塞状态)是进程的一种状态。

( B ) 必会引起进程切换。

​ A、一个进程创建后,进入就绪态

​ B、一个进程从运行态变为就绪态

​ C、一个进程从阻塞态变为就绪态

​ D、都不对

进程自身决定( A )。

​ A、从执行状态到阻塞状态

​ B、从执行状态到就绪状态

​ C、从就绪状态到执行状态

​ D、都不对

下列选项中,导致创建新进程的操作是( C )。【***,10考研】

Ⅰ. 用户登录成功 Ⅱ. 设备分配 Ⅲ. 启动程序执行

​ A、仅Ⅰ和Ⅱ

​ B、仅Ⅱ和Ⅲ

​ C、仅Ⅰ和Ⅲ

​ D、Ⅰ、Ⅱ、Ⅲ

​ 用户登陆后,系统提供GUI 界面等需要创建新进程; 设备分配由操作系统完成,无需创建新进程,只需改变某些数据结构,如信号量; 启动程序执行需要创建新进程,创建新的PCB.

设系统中有n(n>2)个进程,且当前不执行进程调度程序,试考虑下述4种情况,不可能发生的情况是哪些?【**】 A

​ A、没有运行进程,有2个就绪进程,n-2个进程处于等待状态

在一个多进程系统中,如果有就绪进程并且没有运行进程,调度程序会将其中一个就绪进程分配到CPU上运行。因此,不可能出现有就绪进程但没有运行进程的情况。

​ B、有1个运行进程,没有就绪进程,n-1个进程处于等待状态

​ C、有1个运行进程,有1个就绪进程,n-2个进程处于等待状态

​ D、有1个运行进程,n-1个就绪进程,没有进程处于等待状态

在这里插入图片描述

程序&进程

程序在磁盘上,进程
在这里插入图片描述

在这里插入图片描述

父进程没了就挂到一号进程下。

现有程序并没有用6核(只用了1核),除非创建线程。

多道程序设计与CPU的利用率

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

线程

进程:process

线程:thread 轻量级的进程,创建代价低。

gcc -o -pthread

线程是CPU使用的基本单元,独有同一进程中的所有线程,共享LWP轻量级进程 类似于函数

把p强行转成整型,把整型给她(4).

pthread_creat(1,2,3,4) 内存地址用int不够(危险)

在这里插入图片描述
在这里插入图片描述

在下面的叙述中,正确的是( D )。

​ A.引入线程后,处理机只在线程间切换

​ B. 引入线程后,处理机仍在进程间切换

​ C. 线程的切换,不会引起进程的切换

​ D. 线程的切换,可能引起进程的切换

在下面的叙述中,正确的是( D )。

​ A、同一进程内的线程可并发执行,不同进程的线程只能串行执行

​ B、同一进程内的线程只能串行执行,不同进程的线程可并发执行

​ C、同一进程或不同进程内的线程都只能串行执行

​ D、同一进程或不同进程内的线程都可以并发执行

在下面的叙述中,正确的是( B )。

​ A、线程是比进程更小的能独立运行的基本单位

​ B、引入线程可提高程序并发执行的程度,可进一步提高系统效率

​ C、线程的引入增加了程序执行时的时空开销

​ 线程的引入通常减少了时空开销,因为线程共享进程的资源,创建和上下文切换的开销比进程小。

​ D、一个进程一定包含多个线程

以下关于线程的叙述中,正确的是( A )。

​ A、内核支持线程的切换都需要内核的支持

​ 正确。内核支持线程的切换确实需要内核的参与。在内核线程模型中,线程切换由内核管理和调度,这意味着线程的上下文切换需要进入内核态,由内核来完成调度工作。特指内核线程

​ B、线程是资源的分配单位,线程都是拥有资源的独立单位

​ 错误。进程才是资源的分配单位,而线程是执行的基本单位。线程共享进程的资源,并不独立拥有进程级别的资源(如内存空间、文件描述符等),而是和同一进程中的其他线程共享这些资源。

​ C、不管系统中是否有线程,线程都是拥有资源的独立单位

​ 错误。线程并不是资源分配的独立单位。资源(如内存、文件句柄等)是分配给进程的,而线程共享所在进程的资源

​ D、在引入线程的系统中,进程仍是资源分配和调度分派的基本单位

​ 错误。引入线程后,进程依然是资源的分配单位,但调度分派的基本单位已经变成了线程,因为调度器可以直接调度线程,使得线程可以独立运行。

当进程采用操作fork()创建新的进程时,父进程和子进程之间共享了以下哪些状态?©

​ A、堆栈

​ B、堆

​ C、共享内存段

​ 共享内存段是可以被多个进程共享的区域。如果父进程和子进程使用了共享内存(如通过 shmget()mmap() 创建的共享内存),它们会共享这些内存区域。在 fork() 调用后,父子进程都会有对共享内存的访问权限,因此共享内存段是父子进程之间共享的资源。

在用户空间实现线程,其最大的优点是什么?最大的缺点是什么?

​ 最大优点:效率。因为线程切换时,无须陷入内核。

​ 最大缺点:一个线程被阻塞,整个进程被阻塞。

进程或线程的某些操作(如I/O、内存管理、进程管理等)需要系统资源,因此需要由操作系统内核来处理。这种情况下,程序必须通过系统调用进入内核态,这个过程称为陷入内核

  1. 内核线程
    • 内核线程由操作系统内核管理,它们直接运行在内核态,或者可以通过系统调用由用户态进程触发。内核负责这些线程的创建、调度和切换。
    • 因此,对于内核线程的切换,操作系统的内核必须介入,进行上下文切换等调度操作,需要内核的支持
  2. 用户线程
    • 用户线程由用户级线程库管理,操作系统内核并不直接感知这些线程的存在。
    • 在用户空间,线程的创建和切换可以在用户态完成,不需要内核支持。用户线程切换由线程库自行管理,切换的效率更高,因为不需要从用户态切换到内核态。

两者的区别

  • 内核线程切换:需要内核支持,因为操作系统负责管理内核线程的调度。
  • 用户线程切换:不需要内核支持,用户线程的切换可以在用户态完成。

在这里插入图片描述

输出结果:PARENT:value=5

本注:父子进程有各自独立的内存段空间。全局变量在数据段。虽然子进程将value的值增加了15,但这只影响了子进程的副本,父进程的value仍然保持原来的值5。

如图所示的程序,创建了多少进程(包括初始父进程)?

#include <stdio.h>

#include <unistd.h>

int main(){

​ /* fork a child process */

​ fork();

​ /* fork another child process */

​ fork();

​ /* and fork another */

​ fork();

​ return 0;

}

8个进程。

本注:将三个fork分别对应A、B、C。

17baea6c46bfaa8e735a413ab38a1b27.png

如下图(原图3-32)所示的程序创建了多少个进程(包括初始的父进程)?

#include <stdio.h>

#include <unistd.h>

int main(){

​ int i;

​ for(i=0;i<4;i++)

​ fork();

​ return 0;

}

16个进程。

捕获.PNG

对下图(原图3-33)所示的标记为printf(“LINE J”)的行所能执行的环境,请解释一下。

img

exec()系列函数(包括 execlp())的设计逻辑:它们成功执行后会将当前进程的地址空间替换为指定的程序,即完全接管了当前进程的执行。

1)因执行函数exec(),将可能替代当前程序的地址空间的代码,替代者由参数指令(即ls程序)。

2)如果exec()成功执行,则新的代码将执行,不会返回执行printf(“LINE J”)语句。

3)如果exec()调用出错(如未找到参数指令的程序),则会执行printf(“LINE J”)语句。

采用下图(原图3-34)所示的程序,确定行A、B、C、D中的pid的值。(假定父进程和子进程的pid分别为2600和2603)。

img

​ 当fork()函数被调用时,它会创建一个子进程:在子进程中,fork()返回0。在父进程中,fork()返回新创建的子进程的PID,这个PID是一个正数。getpid则是获取当前进程的pid号。

​ A:pid=0; //fork后子进程返回0

​ B:pid1=2603; //getpid()获取当前进程的ID

​ C:pid=2603; //fork后返回子进程的ID

​ D:pid1=2600; //getpid()获取当前进程的ID

使用下图(原图3-35)所示的程序,请解释一下行X和Y的输出是什么?(教材练习3.16)

img

子进程对 nums[]的修改不会影响父进程的 nums[]。因为由于 fork()会复制父进程的地址空间,而子进程的修改是在自己独立的地址空间中进行的。

X行输出:0,-1,-4,-9,-16

Y行输出:0,1,2,3,4

在支持多线程的系统中,进程P创建的若干线程不能共享的是( D)。

A、进程P的代码段

B、进程P中打开的文件

C、进程P的全局变量

D、进程P中某线程的栈指针

在同一进程的多线程之间,下列哪些程序状态部分会被共享( BC ) (多选题)

A、寄存器 B、堆 C、全局变量 D、堆栈

命令行参数

​ 1.使用C语言主函数中的参数
​ main(int argc; char** argv){…}
​ 1)argc:命令行参数的个数
​ 2)argv:参数的内容(字符串数组)
​ 2.所有高级语言均有引用命令行参数的功能

void main(int argc,char* argv[]){}

在这里插入图片描述

main函数中argc,argv分别是什么?(./test.out hello world)

argc是统计你运行程序时送给main函数的命令行参数的个数 3
argv字符串数组,用来存放指向你的字符串参数的指针数组,每一个元素指向一个参数. 3个元素
argv[0] 指向程序运行的全路径名
argv[1] 指向在DOS命令行中执行程序名后的第一个字符串
argv[2] 指向执行程序名后的第二个字符串

\12. 在Linux中的命令行环境下执行C程序test.out: ./test.outab//./test.out a b //./test.outab//为命令提示符

若源代码中main函数语句是:int main(int argc,char** argv)。则argv[1]的值是

( C )。

A. a B. b C. “a” D. “b”

A、B是字符,C、D是字符串

进程&kill&ps&sleep&write

1.操作系统功能:处理机管理、内存管理、文件系统、输入输出管理
2.进程状态:运行、就绪、阻塞
3.杀死进程
kill -9 pid
kill -l //列出所有的信号(singal)(包括9-杀死,2-Ctrl+c)
4.ps选项

ps程序,查看进程信息(注:ps命令执行时刻机器状态的快照)
​ ps 列出当前终端进程
​ ps -H 显示进程的继承关系
​ ps -e 系统中的所有进程,entire //查看系统所有进程,包括不在本终端的进程
​ ps -l、-f 进程详细信息
​ ps -t 终端名 显示指定终端中的进程
​ ps -C 程序名 显示指定程序名的进程
​ ps -S 显示进程状态
​ ps -p 进程号 显示指定进程号的进程
​ ps -o 属性1,属性2,… 显示指定的进程属性列 //典型:ps -o pid,ppid,cmd,stat

tty命令:显示当前正在使用的终端的名称

​ 示例:ps -Ht 终端名 -o ppid,pid,cmd,stat (终端名类似:pts/0, pts/1, tty4)

5.sleep(秒)库函数:等待指定的秒数。

6.write(文字描述符,输出字符串指针,长度)

7.syscall(系统功能调用号,其他参数)

​ http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

/路径分割
Linux所有都是文件
0键盘 1显示器

4.就绪、运行(单 CPU 单核)、阻塞状态中,进程数最多、最少有多少个?
就绪:最少0个,最多受系统允许的进程数。

ctrl+z:让这个运行挂起来

ps -e

fork创建进程(bash创建的) 代码一样,但是是独立的进程(此时有两个进程)

fork前用getchar停一下看程序

在父进程中folk创建的 按键的时候不知道是父进程还是子进程按的 但是可以通过程序测得

0号进程是唯一没有父进程的进程

在这里插入图片描述

这里wait(0)的作用是等待创建的进程结束。

wait会一直停下来,直到任何一个进程退出。父进程等待创建的进程结束。

exec

https://www.cnblogs.com/mickole/p/3187409.html

https://www.cnblogs.com/erictanghu/p/3761011.html

./test.out hello world

execlp

execlp(“/home/gym/test.out”,“./test.out”,“hello”,“world”,NULL)

execlp(“绝对路径”, “执行的程序”,命令行参数):用在当前进程中,找到程序,加载进来执行,替换代码(把空间全部替换)。执行磁盘中指定的程序.

①创建进程 ②填充掉进程

execvp

execvp(“/bin/ls”,v); char *v={“ls”,NULL};

跟上面的一样,只是变成了数组

execxy

​ X:

​ Y:怎么找到要执行的文=文件 跟上P:利用系统环境变量(echo $PATH)

​ V:

wait&waitpid

https://www.cnblogs.com/LUO77/p/5804436.html

C语言中return,告诉操作系统拿到了某个值。(echo $?)

int s;

wait(&s);放上子进程退出的那个值

fork printf有\n 无\n

https://blog.csdn.net/zy1049677338/article/details/115544198

任何一个程序都会构造虚拟内存空间,结构一样(内存中的进程)。

在这里插入图片描述

text代码段 data放数据段 heap动态内存分配(内存泄漏,程序员手动垃圾清理) stack堆栈放局部变量

heap、data可读可写

只调用必要的进去,

进程的代码段二者共用;堆栈和堆段复制一份,各自拥有一份。(本注)二者有各自的虚拟内存空间,代码段指向同一份物理内存(共享);堆栈和堆段在物理内存中不共享,虽然开始时的内容是一样的。

因为text都是一样的,所以fork里的text都会指向同一块物理内存。

pid<0的情况:PID的值不是无限的,不能被不断地fork。

fork

在这里插入图片描述

  1. 执行到第 9 行时
    • fork() 已经执行,创建了一个子进程。
    • 这时共有两个进程:一个是父进程,一个是子进程。
    • 子进程在第 9 行的 getchar(); 处等待用户输入,父进程在 wait(NULL); 处等待子进程结束。
    • 使用 ps 命令查看时,应该看到两个进程,一个是父进程,一个是子进程,并且子进程的父进程 ID(PPID)应指向父进程。
  2. 执行到第 14 行时
    • 子进程已经执行完毕并退出。
    • 父进程从 wait(NULL); 返回,并在第 14 行的 getchar(); 处等待用户输入。
    • 此时系统中只剩下父进程一个。
    • 使用 ps 命令查看时,应该只看到父进程。

在这里插入图片描述

1)六个

​ i=0时,创建了一个进程,总共两个进程,随后各自输出“-“,该轮次输出总计两个。

​ i=1时,每个进程在都再创建一个进程,总共四个进程,随后各自输出“-“,该轮次输出总计四个。

​ 加起来总共输出六个“-“。(遇到\n,数据刷出缓冲区)

2)八个

​ 原理:硬盘、内存都是块设备,一般都有缓存。在C语言中,printf等标准输出函数在大多数情况下是缓冲的。当调用printf(“-”);时,输出并不会立即显示到屏幕上,而是首先存储在一个内存缓冲区中,等待适当的时机(比如换行符、缓冲区满了、程序结束或手动刷新)才会将缓冲区的内容真正输出到终端。而在fork()的调用处,整个父进程空间会原模原样地复制到子进程中。

​ 过程:当循环中i=1时,printf(“-“);把“-”复制到了缓存中,并没有真正的输出。父进程和子进程在fork()后分别拥有自己的缓冲区,但这两个缓冲区都包含了同一个-符号。每个fork()之后,父进程和子进程都持有了同样的缓冲区内容,导致多次打印相同的-(也就是开始缓存的-)。

在这里插入图片描述

printf(”-\n“)直接扔到缓冲区了,并没有直接送到屏幕上。

printf(”-“)

红色的-为缓冲复制的

两种情况把缓冲区送到屏幕上:①\n;②

i=1时整个的内存(包括缓冲区、程序计数器等,因为没有清空)都会被复制过去

进程号保存在操作系统内核空间里(-放在里面,等操作系统闲或…)

printf(”-\n“)直接扔到缓冲区了,并没有直接送到屏幕上。

printf(”-“)

红色的-为缓冲复制的

两种情况把缓冲区送到屏幕上:①\n;②

i=1时整个的内存(包括缓冲区、程序计数器等,因为没有清空)都会被复制过去

进程号保存在操作系统内核空间里(-放在里面,等操作系统闲或…)

以下关于父进程和子进程的叙述中,正确的是( B )。

​ A、父进程创建了子进程,因此父进程运行完了,子进程才能运行

​ 错误。父进程和子进程创建后可以独立执行,并不要求父进程运行完后子进程才能运行。实际上,父进程和子进程可以同时运行,甚至子进程可能先结束。

​ B、父进程和子进程可以并发执行

​ 正确。父进程调用 fork() 创建子进程后,父进程和子进程会各自独立执行自己的代码,它们可以并发执行,即同时运行。

​ C、撤销子进程时,应该同时撤销父进程

​ 错误。子进程的终止不会影响父进程,父进程仍然可以继续执行。父进程和子进程的生命周期是独立的。

​ D、撤销父进程时,应该同时撤销子进程

​ 错误。父进程终止时,子进程不会被自动撤销。通常情况下,如果父进程终止,子进程会被孤立,并由 init 进程(在 Unix/Linux 中)接管,成为孤儿进程继续运行。

父进程和子进程在创建后可以并发执行,它们的生命周期是相对独立的

进程 P1 通过 fork()函数产生一个子进程 P2。假设执行 fork()函数之前,进程 P1 占用了

64 个(用户态的)物理块内存(4MB/块),则 fork()函数之后,进程 P1 和进程 P2 共占用

( A )个(用户态的)物理页。

A.64 B. 128 C. 64 或 128 D. 不确定

fork() 函数的作用是创建一个子进程,子进程会复制父进程的地址空间。

然而,为了提高效率,现代操作系统中通常采用 写时复制(Copy-On-Write, COW) 技术。

fork() 调用之后,如果没有写操作发生,父子进程共享相同的 64 个物理块。因此,fork() 调用之后,父进程和子进程共占用 64 个物理块

fork&&pthread

fork()pthread_create() 都可以用于创建新的执行单元,但它们在操作系统级别的实现和应用场景上有显著的不同。以下是二者的区别:

  1. 创建的执行单元类型
  • fork():创建一个新的进程。父进程和子进程拥有各自的地址空间,资源彼此隔离。

  • pthread_create():创建一个新的线程。新线程和创建它的线程共享同一个进程的地址空间和资源。

  1. 资源共享
  • fork()
    • 子进程继承父进程的资源(如文件描述符、环境变量等),但有自己独立的内存空间(通过写时复制机制)。
    • 父进程和子进程的数据是独立的,一个进程中的修改不会直接影响另一个进程。
    • 通信复杂,通常需要使用进程间通信(IPC)方式,如管道、共享内存等。
  • pthread_create()
    • 新线程与原线程共享同一个进程的地址空间,因此可以直接访问和修改进程中的全局变量和资源。

    • 由于共享地址空间,线程间的数据修改是可见的,通信较为方便,但需要使用同步机制(如互斥锁)来避免数据竞争。

  1. 开销
  • fork()
    • 创建进程的开销较大,因为操作系统需要分配新的进程控制块(PCB)、独立的内存空间、文件描述符表等。
    • 进程切换的开销也较大,因为涉及到上下文切换,包括内存页表的切换。
  • pthread_create()
    • 创建线程的开销较小,因为线程共享进程的地址空间,不需要分配新的地址空间或文件描述符表。

    • 线程切换的开销较小,因为切换发生在同一进程内,省去了内存页表切换等操作。

  1. 生命周期和控制
  • fork()
    • 父进程和子进程是相对独立的,子进程可以独立于父进程存在。父进程退出时,子进程可以继续运行。
    • 子进程的终止不会直接影响父进程的执行。
    • 需要使用 wait()waitpid() 等函数来回收子进程的资源,以防止出现僵尸进程。
  • pthread_create()
    • 线程是进程的一部分,进程退出时,所有线程都会被强制终止。
    • 通常使用 pthread_join() 等方式等待线程完成,并进行资源清理。
    • 如果主线程结束,进程和其所有线程都会一起结束。

考虑下面的代码段:【《操作系统概念精要》2th,P132,题4.15】

1.创建了多少个单独进程?

2.创建了多少个单独线程?

pid_t pid;

​ pid=fork();

​ if(pid==0){ /*child process */

​ fork();

​ thread_create(…);

​ }

​ fork();

  1. 6

  2. 2(本注:每个进程自创一个主控线程,也可认为有8个线程创建)

    本注:

    3.png

如图所示的程序采用Pthreads API。该程序的LINE C和LINE P的输出分别是什么?【《操作系统概念精要》2th,P132,题4.17】

#include <pthread.h>

#include <stdio.h>

int value=0;

void *runner(void param); / the thread */

int main(int argc,char *argv[]){

​ pid_t pdi;

​ pthread_t tid;

​ pthread_attr_t attr;

​ pid=fork();

​ if(pid==0){ /* child process */

​ pthread_attr_init(&attr);

​ pthread_create(&tid,&attr,runner,NULL);

​ pthread_join(tid,NULL);

​ printf(“CHILD:value=%d”,value); /* LINE C */

​ }

​ else if(pid>0){ /* parent process */

​ wait(NULL);

​ printf(“PARENT:value=%d”,value); /* LINE P */

​ }

}

解答:

​ LINE C:5

​ LINE P:0

​ 本注:

  1. fork创建一个原进程的内存副本,只是内容一样。一旦,对应功能区信息改变(如数据段、代码段),则各自有私有的物理内存,不会共享。
  2. pthread_create在进程内部创建线程,将除堆栈段外均共享。对数据段的改变(题中的value)将为所有线程所见。

Pthread函数

https://blog.csdn.net/weixin_46151178/article/details/138531636

在这里插入图片描述

pthread_create函数

1、简介:pthread_create是UNIX环境创建线程的函数

2、头文件:#include <pthread.h>

3、函数声明:

int pthread_create(pthread_t* restrict tidp, const pthread_attr_t* restrict_attr, void* (start_rtn)(void), void *restrict arg);

4、输入参数:

(1)tidp:事先创建好的pthread_t类型的参数。成功时tidp指向的内存单元被设置为新创建线程的线程ID。

(2)attr:用于定制各种不同的线程属性。通常直接设为NULL。

(3)start_rtn:新创建线程从此函数开始运行。无参数是arg设为NULL即可。

(4)arg:start_rtn函数的参数。无参数时设为NULL即可。有参数时输入参数的地址。当多于一个参数时应当使用结构体传入。(以下举例)

5、返回值:成功返回0,否则返回错误码。

6、说明。

传递参数的时候传地址: pthread_create(&tid, NULL, thr_fn, &param1);

线程函数的第一句通常是获取传入参数:Param tmp = *(Param *)arg;

pthread_join()

(1)pthread_join()即是子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

(2)函数说明

​ 1)头文件 : #include <pthread.h>

​ 2)函数定义: int pthread_join(pthread_t thread, void **retval);

​ 3)描述 :pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

​ 4)参数 :thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。

​ 5)返回值 : 0代表成功。 失败,返回的则是错误号。

竞争条件

并发会引起竞争,导致输出结果出错。

在这里插入图片描述

参数在???传算的数 使用结构体

在这里插入图片描述

在这里插入图片描述

有两个并发执行的进程P1和P2,共享初值为1的变量x。P1对x加1,P2对x减1。加1和减1操作的指令序列分别如下所示:【***,11考研】

​ //加1操作 //减1操作

​ Load R1,x //取x到寄存器R1中 Load R2,x

​ inc R 1 dec R2

​ store x,R1 //将R1的内容存入x store x,R2

两个操作完成后,x的值( C )。

​ A、可能为-1或3

​ B、只能为1

​ C、可能为0、1或2

​ D、可能为-1、1或2

在这里插入图片描述

不能正确工作,运行结果具有不确定性。因为公共变量x没有互斥使用。

情形一:先运行P1,再运行P2

y=1,z=1,t=2,u=2。

情形二:1)运行P1中的前两句,结果(x=1, y=0)

​ 2)运行P2中的前两句,结果(x=0, t=0)

​ 3)运行P1中的后两句,结果(z=0)

4)运行P2中的后两句,结果(t=2,u=2)

最终结果:y=0,z=0,t=2,u=2。

解决办法:将两个进程中的前三句定为“临界区”,使用信号量、自旋锁、Peterson解法均可。

在这里插入图片描述

计时函数(实操9)

在这里插入图片描述

在这里插入图片描述

while();忙等待 占着CPU的资源

临界区

临界区是指程序中那些访问公共资源的指令代码,即临界区是指令,并不是受访的静态公共资源.

在操作系统中,临界区是( C )。【*】

A、一个缓冲区

B、一段共享数据区

C、一段程序

D、一个互斥资源

一个正在访问临界资源的进程(线程)由于申请I/O操作而被阻塞时,( B )。【**】

A、可以允许其他进程(线程)进入该进程(线程)的临界区

B、不可以允许其他进程(线程)进入临界区和抢占处理机执行

C、可以允许其他就绪进程抢占处理机,继续执行

D、不允许其他进程抢占处理机执行

​ 即便一个进程在临界区内被阻塞,系统不会允许其他进程进入同一临界区,确保资源的安全和数据的一致性。同时,系统仍然可以调度其他就绪进程或线程使用处理机,但这种调度是正常的处理机调度过程,并非特别的“抢占”行为。因为处理机的调度是独立于临界区控制的。

临界区问题的解答须满足三项要求

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关于临界区正确的说法是( A )。【*】

​ A、访问不同临界资源的两个进程不要求必须互斥地进入临界区

​ B、临界区是包含临界资源的一段数据区

​ 错误。临界区并不是数据区,而是一段代码区域,它包含了访问临界资源的操作。临界区是为了保护对临界资源的访问而设计的代码块,而不是临界资源本身。

​ C、临界区一种用于进程同步的机制

​ 错误。临界区本身不是一种同步机制,而是用于保护共享资源的代码段。进程同步的机制是通过锁、信号量、互斥量等手段来实现的,它们用于确保进程或线程进入临界区时的互斥性。

​ D、临界区是访问临界资源的一个进程或线程

​ 错误。临界区是一段代码,它允许一个进程或线程在执行该代码时独占对共享资源的访问权限,而不是某个进程或线程本身。

在这里插入图片描述

× 进程处于临界区时不能进行处理机调度。

忙等待的互斥

“互斥”想解决什么问题?

​ 互斥(Mutual Exclusion)是解决并发编程中临界区访问冲突的问题。通过互斥机制,可以确保多个线程或进程在访问共享资源时不会相互干扰,避免竞争条件和数据不一致。例如,使用互斥锁(mutex)来保证在一个线程进入临界区时,其他线程必须等待,直到该线程退出临界区,从而实现资源的安全访问.

假定进程A和B共享一台机器的时间,并且每个进程都需要短时间使用同一个不可共享的资源。(例如,每个进程可能都打印一系列独立的短报告。)每个进程可能都重复地获得这个资源,释放它,稍后又再次请求它。按照下面的方法控制对该资源的访问存在什么缺点?【**,概论】

开始时,给一个标志赋予值0。如果进程A请求这个资源并且该标志为0,那么就批准这个请求;否则使进程A处于等待状态。如果进程B请求这个资源并且该标志为1,那么就批准这个请求;否则使进程B处于等待状态。每当进程A完成对这个资源的访问,就把标志变为1。每当进程B完成对这个资源的访问,就把标志变为0。

​ 这个系统保证,该资源一次不会被多个进程使用。然而,它表明了该资源是严格按照交替方式分配的。一个进程用完并释放了这个资源以后,如果该进程要再次访问这个资源,它就必须得等待其他进程用完这个资源。即使是第一个进程马上需要这个资源,而其他进程在一段时间内不需要这个资源,情况依然如此。(本注:参见AST的“严格轮转法”)

屏蔽中断法

使用“锁变量”实现互斥(有竞争条件)

在这里插入图片描述

“锁变量”存在什么问题?

锁变量法存在竞争条件,可能会导致互斥失败。两个线程可能在几乎相同的时间检查和修改 turn 的值,导致双方都认为可以进入临界区,进而破坏互斥。

使用“严格轮转”实现互斥

在这里插入图片描述

在这里插入图片描述

在锁变量的基础上加入了等待和让权的策略,保证只有一个线程可以进入临界区。

只能按P0→P1→P0→P1→……这样轮流访问。这种必须“轮流访问”带来的问题是,如果此时允许进入临界区的进程是P0,而P0一直不访问临界区,那么虽然此时临界区空闲,但是并不允许P1访问。
因此,单标志法存在的主要问题是:违背“空闲让进”原则。

使用 Peterson 解法实现互斥

在这里插入图片描述

​ 算法中利用flag[]解决临界资源的互斥访问,利用turn解决“饥饿“现象,所以能保证进程互斥进入临界区,不会出现“饥饿”现象。

饥饿(Starvation):某个进程或线程在需要资源时,始终无法获得所需资源,导致它无法执行或无法完成任务。饥饿通常是由于资源分配不公平造成的,比如某些进程不断获取资源,而其他进程被长期忽视。

在这里插入图片描述

在这里插入图片描述

flagturn 的赋值语句互换,是否仍可实现互斥?为什么?

不可以实现互斥。

如果互换,可能造成2个都进入临界区。
线程0: 线程1:
turn=1; turn=0;
flag[0]=TRUE; flag[1]=TRUE
若按下列顺序,会出问题:
1)线程1:turn=0
2)线程0:turn=1;flag[0]=TRUE==>线程0进入临界区
3)线程1:flag[1]=TRUE ==>线程1进入临界区

如果做如下修改,是否可行?若不可行,例举出至少一个执行序列。

线程0的语句:

while(flag[1]&& turn1)改为 while(flag[1]&& turn0)

线程1的语句:

while(flag[0]&& turn0)改为 while(flag[0]&& turn1)

在这里插入图片描述

结合双标志法、单标志法的思想。Peterson算法用软件方法解决了进程互斥问题,遵循了空闲让进、忙则等待、有限等待三个原则,但是依然未遵循让权等待的原则。

让权等待的解释

在操作系统的进程同步中,有时需要等待某个条件(如其他进程退出临界区)才能进入临界区。对于这种等待,有两种常见的处理方式:

  1. 忙等待(Busy Waiting)
    • 进程在等待时会不断地轮询检查条件(例如标志变量、锁状态等)是否满足。
    • 这种方式会导致 CPU 资源的浪费,因为等待的进程在 CPU 上不停地运行,却没有任何实际进展。
    • Peterson 算法使用了忙等待:当一个进程等待进入临界区时,它会反复检查条件变量(如 flagturn),直到条件满足。
  2. 让权等待(Yielding Wait)
    • 进程在等待时不进行忙等待,而是主动放弃 CPU 使用权,进入阻塞状态,等待条件满足后再被唤醒。
    • 这通常由操作系统来实现,例如使用信号量条件变量。当等待的条件满足时,操作系统会唤醒阻塞的进程,使其重新获得 CPU。
    • 让权等待有效减少了 CPU 资源的浪费,因为等待的进程不消耗 CPU。

硬件同步

睡眠与唤醒

若信号量S的初值为2,当前值为-1,则表示有( 1 )等待进程。【*】

设与某资源相关联的信号量初值为3,当前值为1,若M表示该资源的可用个数,N表示等待该资源的进程数,则M、N分别是( 1,0 )。【**,10考研】

互斥量

全局变量 定义 互斥量的数据类型:pthread_mutex_t m

main函数里 创建互斥量 需要初值来激活 pthread_mutex_init(&m,0)

线程函数 进入前 判断(是不是绿灯)pthread_mutex_lock(&m) 原子操作(原语) 唤醒 //P 操作,申请互斥量

线程函数 离开前 又要把灯改回来 pthread_mutex_unlock(&m) 唤醒 //V 操作,释放互斥量

创建互斥量 pthread_mutex_destroy(互斥量);

在这里插入图片描述

17行资源永远是一个 7行会拿走这个资源,如果发现没资源就睡觉

12行把资源还回去,并观察哪些需要这个资源

互斥量是资源数量唯一得到东西

比较方法

while();忙等待 占着CPU的资源 容易搞错 执行效率高

此方法虽然简单,但是需要调动操作系统,切换到操作系统内核,

信号量

信号量代表资源的个数,用来做互斥用。

sem_t s;

sem_init(&s,0,1) 1的位置代表资源的个数

sem_wait(&s); 申请信号量(看资源数是什么前一行最后一个数,也就是1)用资源,如果有资源的话,就会消耗掉一个消耗量,没有的话就睡眠

离开 sem_post(&s); 还回资源

在这里插入图片描述

抢的/申请资源行为叫做P操作 将信号量的数目-1 阻挡

离开的行为叫做V操作 将信号量的数目+1 唤醒

都只还回1个或消耗1个

称为信号上的PV操作算法

说明:

1.P和V操作必须是“原子操作”,不能被中断。

2.由操作系统的基本系统调用提供

sem_init(&s,0,0) 信号量为0 谁都别想进来 两个线程发现都没有资源

但是可以这样设计

在这里插入图片描述

先实现
在这里插入图片描述

如果倒过来,改s1、s2或者改init上的0和1
在这里插入图片描述

s1=1 s2=0 s1方先进

s1=0 s2=1 s2方先进

在这里插入图片描述
在这里插入图片描述

三个进程P1、P2、P3互斥使用一个包含N(N>0)个单元的缓冲区。P1每次用Produce()生成一个正整数并用put()送入缓冲区的某一个空单元中;P2每次用getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数;P3每次用geteven()从该缓冲区中取出一个偶数并用counteven()统计偶数个数。请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义信号量的含义。要求用伪代码描述。【****,09考研】

//首先定义信号量
//因为缓冲区是共享的,所以需要一个信号量
semaphore  mutex=1;
//进程P1和P2需要一个信号量
semaphore odd=0;
//进程P1和P3需要一个信号量
semaphore even=0;
//P1、P2和P3共享的缓冲区的空位需要一个
semaphore empty=N;
然后开始实现每一个进程
P1(){while(true){p(empty);num = produce();p(mutex);put();v(mutex);if(num%2 == 0)v(even);elsev(odd);}
}
P2(){while(1){p(odd);p(mutex);getodd();countodd();v(mutex);v(empty);}
}
P3(){while(1){p(even);p(mutex);geteven();counteven();v(mutex);v(empty);}
}
保证信号进程输出交替

0线程:输出1 1线程:输出1……

两个信号量 一个信号量为1 一个信号量为0

在这里插入图片描述

水果

在这里插入图片描述
在这里插入图片描述

若要求第一次爸爸先放,随后爸妈随便

在这里插入图片描述

若要求第一次爸爸先放,随后爸妈交替

在这里插入图片描述

爸妈交替放

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

至少5个,看有向线段有多少个.

在这里插入图片描述

经典同步问题

有界缓冲问题(生产者-消费者问题)

对数据的操作只能有一个

保证生产者和消费者补同时放跟取,所以再加一个信号量

也防备两个生产者或两个消费者进缓冲区

放数据:取当前队头指针- -队尾指针+1- -

在这里插入图片描述

对于生产者-消费者问题,若缓冲区中缓冲区单元只有一个,生产者和消费者各只有一人。用P、V原语实现生产者和消费者的同步操作。【**】

对于生产者-消费者问题,若缓冲区中缓冲区的单元有n个,生产者和消费者各只有一个。用P、V原语实现生产者和消费者同步操作。【***】

在这里插入图片描述

读者-写者问题(1)

读者和写者之间互斥

Semaphore S1=1;S=1

S1:用于保护写操作的互斥信号量。

S:用于保护对共享变量 count 的访问互斥信号量。

int count=0; //记录当前读者数量

读者 写者

P(S)

if(count==0)) //如果第一个读者抢到,后面读者都可以P(S1)								P(S1)	count++;    //保证这三行 

V(S)

​ 查成绩 改成绩

P(S) V(S1)

​ count- -;

if(count==0) //如果是最后一个就负责把信号量打开

​ V(S1)

V(S)

缺点:

①很有可能读者一直占据着,以至于写者进不来(饥饿)。

读者-写者问题(2)

改进方案:

只允许读者在里面工作的最长时间,如果到达这个事件,只出不进.

即限制查询事件

但还是会出现饥饿.

哲学家就餐问题

不必深究怎么解决

归纳出产生死锁的必要条件

规定按顺序拿设备,编号控制

考虑一下:当哲学家每次拿一根筷子时,就餐哲学家问题可能出现死锁。讨论在这种情况下死锁的四个必要条件如何成立。讨论如何通过消除四个必要条件中的任何一个来避免死锁。

1)死锁的四个必要条件:

​ 互斥条件:(本注)筷子不可共用

​ 占有与等待条件:哲学家都需要两个筷子,若拿到一根,还需等待另一根

​ 不可抢夺条件:当哲学家拿到一根筷子时,无规则剥夺拥有权

​ 循环等待条件:五个哲学家彼此等待

2)(本注)预防死锁

​ 破坏互斥条件:增加五根筷子,每个哲学家均拥有各自的筷子,不共用

​ 破坏占有与等待条件:除非两个筷子都可用,否则不去拿起筷子

​ 破坏不可抢夺条件:允许强制哲学家放弃筷子

​ 破坏循环等待:为筷子编号,要求先拿小号,再拿大号。

死锁

死锁定义:如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么,该进程的集合就是死锁.

在这里插入图片描述

某系统中有11台打印机,N个进程共享打印机资源,每个进程要求3台。当N的取值不超过( 5 )时,系统不会发生死锁。【**】

某计算机系统中有8台打印机,由K个进程竞争使用,每个进程最多需要3台打印机,该系统可能会发生死锁的K的最小值是( 4 )。【**,09考研】

银行家算法在解决死锁问题中是用于( B )的。【*】

​ A、预防死锁

​ B、避免死锁

​ C、检测死锁

​ D、解除死锁

采用资源剥夺法可以解除死锁,还可以采用( B )方法解除死锁。【*】

​ A、执行并行操作

​ B、撤销进程

​ 强制终止死锁中的某些进程。被撤销的进程所占用的资源会被释放,从而解除死锁。

​ C、拒绝分配新资源

​ 这是避免死锁的一种方法,而非解除死锁的方法。拒绝分配新资源(如银行家算法)可以在死锁发生前防止其出现,但无法解除已经发生的死锁。

​ D、修改信号量

​ 修改信号量可能改变进程的执行逻辑,但这通常用于进程同步问题,而非专门用于解除死锁。

在这里插入图片描述

1)死锁的四个必要条件:

​ 互斥条件:十字路口只能被一辆车占有

​ 占有与等待条件:若要通过,需占有两个十字路口

​ 不可抢夺条件:占有十字路口的车辆不能移走

​ 循环等待条件:四辆车相互等待其它车从十字路口移走

2)预防死锁

​ 破坏互斥条件:在十字路口,设置高架桥,使得上下两个方向可同时通行

​ 破坏占有与等待条件:除非前方两个路口都无车,否则不进入十字路口。设置红绿灯。

​ 破坏不可抢夺条件:强行将其中一辆车拉出十字路口

​ 破坏循环等待:红绿灯

死锁的四个必要条件

1.互斥条件:资源具有每次只能被一个进程使用的属性

2.占有和等待条件(部分已分配条件、请求和保持条件)

3.不可抢占条件(非剥夺条件)

4.循环等待条件

说明:

1.死锁出现时,四个条件一定同时成立

2.循环等待条件意味着占有和等待条件,所以四个条件并非完全独立

处理死锁的策略:破坏四个必要条件之一,预防之

死锁的预防是根据( C )而采取措施实现的。【*】

​ A、配置足够的系统资源

​ B、使进程的推进顺序合理

​ C、破坏死锁的四个必要条件之一

​ D、防止系统进入不安全状态

​ 预防死锁的核心思想是通过主动采取措施破坏死锁的四个必要条件之一,从而防止死锁发生。

资源的有序分配策略可以破坏死锁的( D )条件。【**】

​ A、互斥

​ B、请求和保持

​ C、不剥夺

​ D、循环等待

随机数

srand(time(0)); 用于设置随机数种子,确保每次运行生成不同的随机数。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>  // sleep 函数需要这个头文件// 生成并返回 0 到 max-1 的随机数
int getRandomNumber(int max) {return rand() % max;
}int main() {srand(time(0)); // 设置随机数种子int max = 10;while (1) {int randomNumber = getRandomNumber(max);printf("随机数: %d\n", randomNumber);sleep(rand() % 10); // 随机等待 0 到 9 秒}return 0;
}

生产者消费者问题

在这里插入图片描述

限制

CPU调度

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在进程调度算法中,对短进程不利的是( B )。【**】

(12.0分)

​ A、短进程优先调度算法

​ B、先来先服务算法

​ C、高响应比优先算法 。

​ (本注:

  \1.  响应比=(等待时间+执行时间)/执行时间\2.  高响应比优先算法:响应比高者优先。

​ D、多级反馈队列调度算法

下列进程调度算法中,综合考虑进程等待时间和执行时间是( D )。【**,09考研】

​ A、时间片轮转调度算法

​ B、短进程优先调度算法

​ C、先来先服务调度算法

​ D、高响应比优先调度算法

下面哪种调度算法可能导致饥饿?(多选题)BD(《精要》2th,P214,题6.19)

​ A、先来先服务

不会导致饥饿,因为作业一旦进入队列,最终一定会被执行,只是等待时间可能较长。

​ B、最短作业优先

可能导致饥饿,因为长时间作业可能会一直被短作业插队,导致长期得不到调度。

​ C、轮转

不会导致饥饿,因为每个作业都会轮到执行的机会,虽然响应时间可能较长,但最终一定会被执行。

​ D、优先级

可能导致饥饿,如果优先级低的作业始终被优先级高的作业抢占,低优先级作业可能一直得不到调度,导致饥饿。

ps -LmC

L显示进程 m表明进程里面的线程有几个

m代表表明进程属于哪个线程 C哪个线程

LmC 注意大小写

ps: 显示当前系统中的进程。

-L: 显示进程的所有线程信息。

-m: 显示线程模式。

-C mutex.out: 仅列出与指定可执行文件 mutex.out 相关的进程。

-e:显示所有进程。但这个选项只显示进程级别的信息,不会显示线程。

-f:以完整格式显示进程信息,包括 PPID、UID 等。但仍然只是在进程级别,不涉及线程

第3部分 内存管理

https://www.nowcoder.com/discuss/353159399634051072

内存1-综述

在这里插入图片描述

上面的是下面的子集

在这里插入图片描述

重定位

程序中有大量的寻址方式

记住起始地址(16384),用跳转的地址再加上起始地址(相对偏移)

在这里插入图片描述

0x1000B 3*4=12位 4KB 4096B 一页

按一个页一个页的切,顺序大小是一页或它的倍数

右移两位相当于除以4(页大小),整数部分是00(页号),余数部分01(偏移)

00查找页表发现是001

页号+块内偏移

在这里插入图片描述

上半部分给内核用,下半部分给用户用

虚拟内存跟内存条映射

每个进程都有

全局变量放在数据段(在整个周期里面大小不变,值会变)

放在堆里面:垃圾回收、内存泄露

在这里插入图片描述

存放进程号

在这里插入图片描述

堆栈heap不是在内存中来的

https://www.nowcoder.com/questionTerminal/befa4f14d42642d6b7a658c105a44069

把作业地址空间中使用的逻辑地址变成内存中物理地址称为(B)。
A.加载
B.重定位
C.物理化
D.逻辑化

为了保证一个程序在主存中改变了存放位置之后仍能正确执行,则对主存空间应采用( 动态重定向 )技术。【*】

交换技术

在这里插入图片描述

在这里插入图片描述

为了保证一个程序在主存中改变了存放位置之后仍能正确执行,则对主存空间应采用( 交换技术 )技术。【*】

交换技术的局限

在这里插入图片描述

在这里插入图片描述

内存管理技术概述

开机的时候已经分为碎片 内部碎片(问题) 划分大小不好定

固定分区:开机已经划分好内存条空间

可变分区

在这里插入图片描述

固定分区

在这里插入图片描述

可变分区

在这里插入图片描述

内部碎片和外部碎片

外部碎片(External Fragmentation)

定义:内存中存在许多分散的小块空闲空间,这些空间总和可能足够满足一个进程的内存请求,但由于它们不连续,无法分配给该进程,这些零散的小块空闲空间就是外部碎片。

内部碎片(Internal Fragmentation)

定义:当内存分配给某个进程时,实际分配的内存空间可能比进程实际需要的空间要大,这种多余的部分就被称为内部碎片。

特性内部碎片外部碎片
位置已分配的内存块中未分配的内存块之间
成因分区分配时,分配空间大于实际需求动态分配与释放导致内存碎片化
特点碎片位于已分配内存中,是分区分配后的剩余空间。碎片位于未分配的内存中,是分区间的剩余空间。
解决方法减小分区大小或使用分页/分段管理内存紧缩(Memory Compaction)

分段

在这里插入图片描述

C编译器创建的段

在这里插入图片描述

分段硬件

在这里插入图片描述

分区分配内存管理方式的主要保护措施是:A

A.界地址保护 B.程序代码保护 C.数据保护 D.栈保护

​ 内存保护是操作系统中的一个机制,对内存的存取权限进行管理。内存保护的主要目的是防止某个行程去存取不是操作系统配置给它的寻址空间。这个机制可以防止某个进程因为某些错误而影响到其他行程或是操作系统本身的运行。分区分配内存管理方式,通过向进程划分专属的存储空间,实现边界保护。

段表

在这里插入图片描述

在这里插入图片描述

分段的必要性示例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

分区管理要求对每个作业都分配( C )的内存单元。
A、若干地址不连续
B、若干连续的帧
C、地址连续
D、若干不连续的帧

https://blog.csdn.net/qq_50680576/article/details/129966624

在可变式分区存储管理中的拼接技术可以( A )。(注:拼接是指通过移动将多个分散的小分区合并成一个大分区。)【*】
A
集中空闲分区

B
增加内存容量

C
缩短访问周期

D
加速地址转换

在分段存储管理方式中,( A )。【*】

A.以段为单位分配,每段是一个连续存储区

分段存储管理 是一种将程序划分为多个段(如代码段、数据段、堆栈段等)进行管理的方式。每个段可以是一个逻辑上的独立单位。每段的大小可以不同,并且每个段内的存储是连续的。

B.段与段之间必定不连续

段与段之间不一定不连续,实际上,段与段之间可以是连续的,也可以是非连续的。分段存储管理允许段在物理内存中不连续,只要每个段内部是连续的。

C.段与段之间必定连续

D.每段是等长的

每段不必是等长的,段的大小可以根据需要动态分配,分段存储管理并不要求每段的大小相同。

分页

在这里插入图片描述

分页工作原理

页号作为数组下标,以确定起始地址,然后用块内偏移来确定物理地址位置

在这里插入图片描述

地址转换与分页硬件

在与不在位:缺页 1放了物理内存,f就是有效的

访问位

在这里插入图片描述

虚拟内存放在磁盘上

在这里插入图片描述

转成同一单位 2的幂

16MB=224 主存地址至少需要24位

8K=213 块大小为

访问这个值,很大概率会访问旁边的值,干脆把整一块塞到cache中.

硬盘大概10ms 内存速度50ns cache10ns 寄存器1ns

在这里插入图片描述

Cache共有多少组?

  • Cache容量为8K字节,即8192字节。
  • 每个块的大小为4个32位字,即4×4字节=16字节。
  • 每组包含4个块,因此每组的容量为16字节×4=64字节。
  • 总组数为8192字节 ÷ 64字节 = 128组。

因此,Cache共有128组

写出主存的字节地址的格式,并说明地址格式中的不同字段的作用和位数。

  • 假设主存容量为16MB,即2^24字节。
  • 每个块的大小为16字节,因此块偏移需要4位(2^4 = 16)。
  • Cache共有128组(2^7 = 128),因此组索引需要7位。
  • 剩余的位数为24 - 4 - 7 = 13位,用作标记位(Tag)。

地址格式为:[Tag(13位)] [Index(7位)] [Block Offset(4位)]

  • Tag(标记位,13位):用于在Cache中唯一标识主存块。
  • Index(组索引,7位):用于定位Cache中的具体组。
  • Block Offset(块偏移,4位):用于定位块内的具体字节。

一路组相连,全相联

在这里插入图片描述

字与字节

在计算机术语中,“字节”(Byte)和“字”(Word)是两个不同的概念:

  1. 字节(Byte)
    • 字节是最基本的数据存储单位之一,通常由 8位(bits) 构成。
    • 一个字节可以表示256种不同的值(2^8)。
    • 字节是内存中地址寻址的基本单位,常用于表示字符、较小的数据等。
  2. 字(Word)
    • “字”指的是一种数据长度单位,但它的具体位数取决于计算机系统的架构。
    • 在32位系统中,一个“字”通常是 32位(即4字节)。
    • 在16位系统中,一个“字”通常是 16位(即2字节)。
    • 在64位系统中,一个“字”通常是 64位(即8字节)。
    • 因此,“字”的长度是与计算机的架构有关的,而字节始终是8位。
      在这里插入图片描述

Cache块有一部分会放内存中的数据,往外扩了一些Tag,再往外扩一些有效位,再扩一些修改位

回写方式:只改cache,不改主存,但对应cache修改位要标记,要记得回写。

直写方式:两个位置同时改

044bd1f56b924057804673a278e03211.jpg

主存块大小计算: 每个字是32位,即4字节(因为1字节=8位)。

  • 主存块的大小为4个字,所以每个块的大小是4 × 4字节 = 16字节。

Cache的容量

  • Cache需要存放4K字的数据。
  • 4K字等于4 × 1024 = 4096字。
  • 每个字是4字节,因此总的数据容量为212 × 4字节 = 214字节(16KB)。

直接映射方式中的块数量

  • 每个块的大小为16字节。
  • 需要的总容量为214字节。
  • 所以Cache中块的数量为 214 字节÷ 16字节 = 210块。

地址位的划分

  • 主存地址为32位,按字节编址。
  • 每个块大小为16字节,需要4位来表示块内偏移(24 = 16)。
  • Cache有210块,因此需要10位来表示索引(210 = 1024)。
  • 剩下的部分用于标记位:32 - 10 - 4 = 18位。

总位数计算

  • Cache的总数据容量为214字节。
  • 每个块的标记位为18位,共有210 块,因此标记位总共需要 210 × 18 = 18432位。

总容量

  • Cache的总容量为数据位加标记位。
  • 总容量 = 214字节 + 210字节 × 18 = 149504位。

有效率=128/148

在这里插入图片描述

0开头说明是八进制

在这里插入图片描述

捕捉进程的

在这里插入图片描述
在这里插入图片描述

hd检验有没有写进去 小端

两个独立的程序,每隔一秒中往共享写东西(1、2、3……)。另一个发现里面有整数就读出来显示。两个程序同时进行,看它是否完成了通讯

保护位指明rwx
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5130/2048≈2.50400

在这里插入图片描述

逻辑地址 = 页号*页大小+页内地址;

物理地址 = 块号*块大小+页内地址;

在虚拟内存系统中,虚拟地址被划分为多个页面,每个页面在物理内存中对应一个或多个块,这些块的大小通常与页面大小一致。这种设计有助于优化内存访问和数据交换。

在这里插入图片描述

计算机开机的时候,并不知道有文件系统。

MBR主引导 512字节 三个分区

虚拟内存

在这里插入图片描述

缺页中断

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

页面置换算法

在这里插入图片描述

谁待着最久谁走

缺页次数:物理内存没有,要从磁盘中调用。缺页次数越多,效率越低

在这里插入图片描述

从5开始看,后面还需要访问的页面不进行替换。

OPT最佳,但是实际没办法实行,没办法之后后面几个进程是什么。

在这里插入图片描述

在这里插入图片描述

访问位

时钟页面替换算法

在这里插入图片描述

2进来,指针+1,标注上访问位。

发现2在,指针不动,没*就标注。其他进来,看有没有

没*才替换

文件系统

设备分两类 块设备 字符设备

磁盘的工作原理

固态硬盘缺陷:写入次数有限

机械硬盘:便宜,读写次数比固态硬盘好得多

RAID

寻道:寻找磁道

CHS定位

H磁头

一次至少读取一个扇区

多个扇区组成一个块,叫簇

进程分一半,一部分给操作系统,一部分给用户

空间局部性

当访问一个指令时,附近的指令执行的概率非常大

文件系统实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FAT32 32指大小,最多产生2的32的块 一块1KB

232=4GB 4GB*1KB=

在这里插入图片描述

先从根目录下开始找,里面放着一项一项的目录项(directory entry),然后在FAT表中找。217➡618➡339

但根目录区固定在某一个地方,很容易被黑客攻击。

在这里插入图片描述

数组实现链表 FAT FAT32 32位=4字节 结束标志FFFFF

小端

epoc

inode表 用一颗树

真正的数据在根目录、文件和目录中

df 查超级快

每一个文件有唯一的节点编号/索引节点,反之不行

每个索引节点可能会有多个文件节点

2硬链接 一个是公共的 第二个是

如果一级索引中放不下,启用二级索引,再放不下,三级索引启用。

在这里插入图片描述

2 12

磁头臂调度算法

buffer cache

在这里插入图片描述

三个阶段:
单缓冲
内核里面有两个缓冲区

http://www.dtcms.com/a/269671.html

相关文章:

  • B站视频下载器 Bili23-Downloader v1.63.1 绿色版
  • LLMs之DeepSeek:AI模型市场深度分析:DeepSeek的挑战与机遇,模型市场份额、Token经济学与未来发展
  • 力扣 239 题:滑动窗口最大值的两种高效解法
  • 【python】 time_str = time_str.strip() 与 time_str = str(time_str).strip() 的区别
  • 基于物联网的智能交通灯控制系统设计
  • 使用 Docker 搭建 Java(SpringBoot)开发环境——AI教你学Docker
  • 零基础|宝塔面板|frp内网穿透|esp32cam远程访问|微信小程序
  • 电商业务是如何防护DDoS攻击的?
  • 2563、统计公平数对的数目
  • ElasticSearch集群状态查询及_cat 命令详解
  • JDBC 获取新增行主键值详解
  • 向量与向量组的线性相关性 线性代数
  • 【Android】搭配安卓环境及设备连接
  • 17-C#的socket通信TCP-1
  • 静态路由实验以及核心原理
  • 计算机网络第九章——数据链路层《局域网》
  • 裂变时刻:全球关税重构下的券商交易系统跃迁路线图(2025-2027)
  • 3.springboot2使用selenium(java8)
  • 多模态大语言模型arxiv论文略读(152)
  • 【LeetCode 热题 100】142. 环形链表 II——快慢指针
  • X00193-MASAC强化学习算法的多无人机协同路径规划
  • 一文讲清楚React中的key值作用与原理
  • PostgreSQL复制技术全解析:从物理复制到逻辑复制的实践指南
  • Ollama+OpenWebUI 0.42+0.3.35 最新版一键安装教程,解决手动更新失败问题
  • Swift 解 LeetCode 321:拼接两个数组中的最大数,贪心 + 合并全解析
  • WebSocket实战:打造实时在线聊天室
  • NealFun安卓版:创意无限,娱乐至上
  • 学习设计模式《十七》——状态模式
  • 干货分享 | TSMaster DBC编辑器操作指南:功能详解+实战示例
  • Spring Boot 事务失效问题详解:原因、场景与解决方案