2024年Go最新操作系统总结_临界资源的三个特点,2024年最新Golang程序员面试必备的知识点

img
img
img

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

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

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

// 在父进程中: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进程间一种重要的通信机制
* 信号是向进程发送的一个通知,通知某个事件已发生
* 收到信号的进程可以立即执行指定的操作
* 信号的发出者可以是进程,也可以是系统(含硬件)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701154639236.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1bWF0Y2g=,size_16,color_FFFFFF,t_70#pic_center)


#### 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):进程中访问临界资源的程序段**


比如:图中标红区域  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210701154730437.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N1bWF0Y2g=,size_16,color_FFFFFF,t_70#pic_center)


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


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


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


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


## 三.死锁


### 1.死锁的定义


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


### 2.产生原因


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


### 3.预防死锁


* 破坏互斥条件

 允许系统资源共享,但有的资源不可能同时访问,如打印机等临界资源。
* 破坏不可剥夺条件

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

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

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


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


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


### 4. 解除死锁


* 资源剥夺法

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

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

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


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


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


## 四.内存


实际存储体系:


CPU + Cache、内存、辅存。


存储管理的功能:


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


地址映射


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


虚拟内存


* 虚拟内存是面向用户的虚拟封闭存储空间
* 程序员编程时使用线性虚拟地址


### 4.1内存管理


#### 4.1.1 物理内存


物理内存管理技术:


* 内存拼接
* 对换技术
* 覆盖技术


交换技术:


原理:


* 内存不够时把进程写到磁盘(换出/ swap out)
* 当进程要运行时重新写回内存(换出/ swap in)


#### 4.1.2 虚拟内存


实现思路:在程序运行时,只把当前必要的很小一部分代码和数据装入内存,其余代码和数据需要时再装入。不再运行的代码和数据及时从内存中删除。


典型的虚拟内存管理方式:




![img](https://img-blog.csdnimg.cn/img_convert/538f04aafd4a2784ce59591f27d26f9b.png)
![img](https://img-blog.csdnimg.cn/img_convert/32cc71815a945fe2625e26f0b49f6765.png)

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**


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

* 对换技术
* 覆盖技术


交换技术:


原理:


* 内存不够时把进程写到磁盘(换出/ swap out)
* 当进程要运行时重新写回内存(换出/ swap in)


#### 4.1.2 虚拟内存


实现思路:在程序运行时,只把当前必要的很小一部分代码和数据装入内存,其余代码和数据需要时再装入。不再运行的代码和数据及时从内存中删除。


典型的虚拟内存管理方式:




[外链图片转存中...(img-ZNhe9hK0-1715649000619)]
[外链图片转存中...(img-DdwwGjAm-1715649000619)]

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**


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

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值