Go最全操作系统总结_临界资源的三个特点(2),2024年最新Golang初级面试题2024

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

经典同步问题:生产者和消费者

2.进程控制

在进程生存的全部期间,对其全部行为的控制,分为四个典型的控制行为

  • 创建进程
  • 阻塞进程
  • 撤销进程
  • 唤醒进程
2.1 进程创建:
  • 创建一个空白PCB
  • 获得并赋予进程标识符ID
  • 为进程分配空间
  • 初始化PCB
  • 插入相应的进程队列(新进程一般插入就绪队列)
    在这里插入图片描述
2.1.1 Linux系统创建进程–fork()
pid_t pid = fork()
// fork()函数返回值:
// 			在子进程中:pid = 0
// 			在父进程中:pid > 0 (子进程ID)
// 			出错: pid = -1

  1. 新进程是当前进程的子进程
  2. 父进程是fork() 的调用者,新建的进程的子进程
  3. 子进程是父进程的复制(相同的代码、数据、堆栈。不同的ID、时间参数)
  4. 父进程和子进程并发运行

fork() 执行流程:

// 父进程
int main(void){
	pid_t pid;
	pid = fork();
	if(pid == 0)
		printf("hello word\n");
	else if(pid > 0)
		printf("how are you\n")
}

// 子进程:和父进程有相同的代码,从fork()之后开始运行
int main(void){
	pid_t pid;
	pid = fork();
    // 从这里开始运行
	if(pid == 0)
		printf("hello word\n");
	else if(pid > 0)
		printf("how are you\n")
}

在linux中,除了 init 进程不是通过 fork() 创建,其他所有进程都是通过 fork() 函数创建。

那么子进程如何执行和父进程不同的功能呢?

exec函数簇:

  • 功能
    • 装入一个指定的可执行程序运行
    • 使子进程具有和父进程完全不同的功能
  • 步骤
    • 根据文件名找到响应的可执行程序
    • 将可执行程序的内容填入子进程的地址空间
    • 进入新进程执行且不再返回
2.2 进程的阻塞与唤醒

正在执行的进程由于等待的事件未发生,由系统执行阻塞原语,由运行态变为阻塞态。

阻塞过程:

  • 找到将要被阻塞进程的 PCB。
  • 如果进程为运行态,保护现场并转为阻塞态,停止运行。
  • 把 PCB 插入相应事件的等待队列,当被阻塞进程期待的事件发生时,由相关进程调用唤醒原语,将进程唤醒。

唤醒过程:

  • 在该事件的等待队列中找到进程对应的 PCB。
  • 将其从等待队列中移除,设置状态为就绪态。
  • 将 PCB 插入就绪队列,等待调度程序调度。
2.3 进程切换

进程切换是指处理机从一个进程的运行转到另一个进程上运行。

进程切换过程:

  • 保存处理机上下文,包括程序计数器和其他寄存器。
  • 更新 PCB 信息,并把 PCB 移入相应的阻塞队列。
  • 选择另一个进程执行并更新其 PCB。
  • 更新内存管理的数据结构,恢复处理机上下文。
2.4 进程终止

调用exit( )函数终结进程。exit(int status)

进程终结时,要释放资源并报告父进程

  • 利用status传递进程结束时的状态
  • 变为僵尸状态,保留部分PCB信息供wait( )收集,包括:
    • 正常结束还是异常结束
    • 占用系统cpu时间
    • 缺页中断次数
  • 调用 schedule( ) 函数,让操作系统选择新进程运行

3.进程通信

管道、消息队列、共享内存,信号量,socket,信号,文件锁

3.1 管道

管道是进程间的一种通信机制,一个进程(A)可以通过管道把数据传输给另外一个进程(B)。

特点:

  • 面向字节流
  • 生命周期随内核
  • 自带同步互斥机制
  • 半双工,单向通信,两个管道实现双向通信。
3.2 信号
  • 信号是Linux进程间一种重要的通信机制
  • 信号是向进程发送的一个通知,通知某个事件已发生
  • 收到信号的进程可以立即执行指定的操作
  • 信号的发出者可以是进程,也可以是系统(含硬件)
    在这里插入图片描述
3.3 信号量

线程:

信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

资源:

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有

信号量的值为正的时候,说明它空闲。

所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

对信号量的操作

  • P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  • V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

PV操作用于同一进程,实现互斥。

PV操作用于不同进程,实现同步。

3.4 消息队列

消息队列是保存在内核中的消息链表,消息的发送方和接收方要约定好消息体的数据类型,每个消息体都是固定大小的存储块,不像管道是无格式的字节流。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。消息队列的通信效率高于管道,进程发送消息时,把数据放在消息队列后就可以正常返回。

消息队列不适合较大数据的传输,因为内核中每个消息体都有最大长度限制。此外,消息队列通信存在数据拷贝开销,进程写数据到消息队列时,会发生从用户态拷贝数据到内核态的过程,读取数据时,会发生从内核态拷贝数据到用户态的过程。

3.5 共享内存

共享内存解决了消息队列中用户态与内核态间的数据拷贝问题,将虚拟地址空间映射到相同的物理内存,当某个进程写数据时,另一个进程马上就能看到,不需要拷贝,提高通信效率。

特点:

  • 不用从用户态到内核态的频繁切换和拷贝数据,直接从内存中读取就可以。
  • 共享内存是临界资源,所以需要操作时必须要保证原子性。使用信号量或者互斥锁都可以。
  • 生命周期随内核。

4.进程调度

在合适的时间以一定策略选择一个就绪进程运行。

1.调度算法
  • 先来先服务:先进入系统的作业优先被运行
  • 短作业优先调度:优先运行时间短的作业
  • 响应比高者优先调度:响应比(作业的响应(等待+运行)时间和运行时间的比值)
  • 优先数调度
  • 循环轮转调度:把所有进程按先进先出原则排成队列
2.Linux进程调度

普通进程:

  • 采用动态优先级调度
  • 调度程序周期性的修改优先级(避免饥饿)

实时进程:

  • 采用静态优先级调度
  • 由用户预先指定,以后不会改变
3.Linux进程的优先级

动态优先级:

  • 在进程运行期间可以按调度策略改变
  • 非实时进程采用动态优先级,由调度程序计算
  • 只要进程占用CPU,优先级就随时间流失而不断减小
  • task_struct的counter表示动态优先级

静态优先级:

  • 进程创建时指定,或由用户修改
4.进程切换
  • 内核挂起当前CPU上的进程并回复之前挂起的某个进程
  • 任务切换,上下文切换

进程切换与中断上下文切换有差别:中断前后在同一进程上下文中,只是用户态向内核态执行

进程上下文包含了进程执行需要的所有信息:

  • 用户地址空间:包括程序代码,数据,用户堆栈等
  • 控制信息:进程描述符,内核堆栈等
  • 硬件上下文(注意中断也要保存硬件上下文,只是保存方法不同)

两个进程A,B切换的基本过程:

  1. 正在运行用户态进程A
  2. 发生中断(例如时钟中断)
    • 保存当前进程的cs:eip/esp/eflags到内核堆栈
    • 从内核堆栈装入ISR中断服务例程的cs:eip和ss:esp
  3. SAVE_ALL //保存现场,已进入内核中断处理过程
  4. 中断处理过程中或者中断返回前调用schedule()
    • 其中的switch_to做了进程的上下文切换
  5. 运行用户态进程B,(B曾经通过以上步骤被切换出去过)
  6. RESTORE_ALL // 恢复现场
  7. iret // 中断返回
  8. 继续运行用户态B

二.线程

线程是进程中的一个实体,是操作系统独立调度和分配的基本单位,由线程 ID、程序计数器、寄存器集合和堆栈组成。引入线程是为了减少程序并发执行的开销,进一步提高操作系统的并发性能。

2.1 线程和进程的区别

调度: 进程是分配资源的基本单位,而线程是独立调度的基本单位。

资源: 进程拥有系统资源,而线程只有一点运行必需的资源。如果线程也是分配资源的单位,切换就需要较大开销,引入没有意义。

开销: 进程切换涉及当前 CPU 环境的保存和设置,但线程切换只需要保存和设置少量的寄存器容量

地址空间: 进程的地址空间互相独立,同一进程的线程共享进程资源,进程内的线程对其他进程不可见。

通信: 进程通信需要同步和互斥手段的辅助,保证数据一致性。线程可以直接读写进程数据段(全局变量)来进行通信。

2.2 线程实现

内核级线程 1:1 实现

内核通过操纵调度器对线程进行调度,并将线程的任务映射到处理器上。程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口,轻量级进程,即通常意义上的线程。

优点:当一个线程被阻塞时,允许其他线程继续执行。

缺点:代价相对较高,需要在用户态和内核态来回切换。


用户级线程 1:N 实现

从广义上讲,一个线程只要不是内核线程,就可以认为是用户线程。狭义上的用户线程指的完全建立在用户空间的线程库上,系统内核不能感知到用户线程的存在及其是如何实现的。

优点:由于线程管理在用户空间进行,不需要切换到内核态,开销小,支持大规模并发。

缺点:一个线程在使用内核服务时被阻塞,整个进程都会被阻塞。


混合方式 N:M 实现

混合模式下既存在用户线程,也存在轻量级进程。用户线程完全建立在用户空间中,因此开销依然很小,可以支持大规模并发。轻量级进程作为用户线程和内核线程之间的桥梁,使用内核提供的线程调度功能及处理器映射,降低整个进程阻塞的风险。

2.2.1 linux创建线程 CreateThread( )

// 用线程实现并发画圆和画方
void DrawCircleAndRect(){
    CreateThread(0, 0, DrawCircle, 0);
    CreateThread(0, 0, DrawRect, 0);
}

// 创建线程的关键:把 DrawCircle() 和 DrawRect() 作为参数传给 CreateThread( )

2.3 临界资源 和 临界区

临界资源(Critical Resource):一次只允许一个进程独占访问(使用)的资源

比如:图中的 共享变量i

临界区(Critical Section):进程中访问临界资源的程序段

比如:图中标红区域
在这里插入图片描述

临界区和临界资源的访问特点:

  • 排他性
  • 并发进程不能同时进入临界区

临界区访问机制的四个原则:

  • 忙则等待:当临界区忙时,其他进程必须在临界区外等待
  • 空闲让进:当没有进程处于临界区时,任何有权进程可进入临界区
  • 有限等待:进程进入临界区的请求应在有限时间内得到满足
  • 让权等待:进入等待状态的进程放弃CPU,让其他进程有机会得到CPU

三.死锁

1.死锁的定义

两个或多个进程无限期的等待永远不会发生的条件的一种状态。(结果:相关的每个进程永远阻塞)

2.产生原因

  • 互斥
  • 不可剥夺
  • 请求并保持
  • 循环等待

3.预防死锁

  • 破坏互斥条件

允许系统资源共享,但有的资源不可能同时访问,如打印机等临界资源。

  • 破坏不可剥夺条件

允许剥夺其他进程已占有的资源,但释放已获得的资源可能会造成前一段工作的失效。

  • 破坏部分分配条件

采用预先资源分配法,在进程运行前一次性分配它需要的所有资源,缺点是有些资源可能仅在运行初期或快结束时才使用。

  • 破坏循环等待条件

采用顺序资源分配法, 给系统资源编号,规定每个进程必须按编号递增的顺序请求资源。

避免死锁:不破坏产生死锁的条件,而是在资源分配过程中,用某种方法去评估若分配资源是否会让系统进入死锁状态,若是,则拒绝此次分配资源。从而避免死锁产生。

**检测和恢复死锁:**允许死锁发生,但可以通过检测机制及时检测出死锁状态,并精确确定与死锁有关的进程和资源,然后采取措施将死锁清除,将进程从死锁状态解脱出来。

4. 解除死锁

  • 资源剥夺法

挂起某些死锁进程,抢占其资源,分配给其它死锁进程。

  • 撤销进程法

强制撤销部分甚至全部死锁进程,可以按进程优先级和撤销代价进行。

  • 进程回退法

让一个或多个进程回退到足以避免死锁的地步,要求系统保持进程的历史信息,设置还原点。

5. Windows和Linux采用了哪种死锁解决方案?

鸵鸟策略:忽略死锁,当死锁发生时假装没有看见。

四.内存

实际存储体系:

CPU + Cache、内存、辅存。

存储管理的功能:

  • 地址映射
  • 虚拟存储
  • 内存分配
  • 存储保护

地址映射

  • 把程序中的地址(虚拟地址/逻辑地址) 转换成内存的真实地址(物理地址)的过程
  • 地址重定位,地址重映射

虚拟内存

  • 虚拟内存是面向用户的虚拟封闭存储空间

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

时假装没有看见。

四.内存

实际存储体系:

CPU + Cache、内存、辅存。

存储管理的功能:

  • 地址映射
  • 虚拟存储
  • 内存分配
  • 存储保护

地址映射

  • 把程序中的地址(虚拟地址/逻辑地址) 转换成内存的真实地址(物理地址)的过程
  • 地址重定位,地址重映射

虚拟内存

  • 虚拟内存是面向用户的虚拟封闭存储空间

[外链图片转存中…(img-UuaxsPDt-1715510833286)]
[外链图片转存中…(img-QX5XrJVd-1715510833286)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值