首页 > 系统相关 >终于有人把进程与线程讲清楚了

终于有人把进程与线程讲清楚了

时间:2023-11-02 16:15:38浏览次数:32  
标签:讲清楚 调度 线程 内存 进程 OS CPU

前言

很多人对进程、线程没有什么概念,面试的时候也说不出其中的核心内涵。

所以,今天我打算花点篇幅把进程和线程讲清楚。

01 CPU与内存

**CPU **大家都知道是计算机的中央运算单元,用来计算的。

CPU从内存里面读取一条一条的代码指令,然后根据指令来执行运算(加,减,乘,除,复制数据等)。

CPU在运算的过程中一些数据存放在CPU的寄存器和内存里面。

CPU里面有各种寄存器,各司其职。指令指针寄存器存放的是当前执行到那条代码指令了。代码指令是写完程序后被编译器编译成二进制指令代码

 

 

02 内核与虚拟内存

电脑或手机开机以后,上电跑启动代码,运行OS内核,内核里也有线程,这个我们把它叫做内核态。

内核启动以后, 内核将物理内存管理起来。内核提供虚拟内存管理机制给每个进程(应用程序App)内存服务。

它的思路是什么呢?每个进程(应用App) 都有自己的虚拟内存空间,注意这里的空间只是一个数字空间,没有划分实际的物理内存。

这样做的好处是多个进程(应用App)内存都是独立的相互不影响,物理内存只有一个,多个进程(应用App)不会因为直接使用物理内存而冲突。

 

 

那么OS是如何管理物理内存的呢?进程(应用App)需要内存的时候,OS分配一块虚拟内存(起点---终点),然后OS在从自己管理的物理内存里面分配出来物理内存页,然后通过一个MMU的单元,将分配的虚拟内存与物理内存页映射起来,这样,读写虚拟内存地址最终通过映射来使用物理内存地址,这样每个进程之间的内存是独立的,安全的。每个进程会把虚拟内存空间分成4个段(代码段, 数据端,堆,栈)

代码段:用来存放进程(应用App)的代码指令。

数据端:用来存放全局变量的内存。

堆:调用os的malloc/free 来动态分配的内存。

栈:用来存放局部变量,函数参数,函数调用与跳转。

每个进程(应用App)相当于一个容器,所有应用App里面需要的资源和机制都在进程里面。

线程是OS独立调度执行的单元,OS调度执行的单位就是线程,线程需要以进程作为容器和使用进程相关的环境。

应用态没有进程就不会有线程。

03 进程与线程

上面说过进程是容器,应用态的线程必须要基于进程来创建出来。

那么进程与线程他们之间到底是一个什么样的关系,接下来我们来分析一下。

 

 

例如"在桌面上双击打开一个App", 桌面App程序会调用OS的系统调用接口fork,让OS 创建一个进程出来,OS为你准备好进程的结构体对象,将这个App的文件(xxx.exe, 存放编译好的代码指令)加载到进程的代码段,同时OS会为你创建一个线程(main thread), 在代码里面,还可以调用OS的接口,来创建多个线程,这样OS就可以调度这些线程执行了。

虚拟内存空间是进程的概念,那么线程如何使用的呢?各线程使用共享进程的代码段,数据段,堆,每个线程在进程的栈空间创建一个属于自己的栈空间。

所以这样就得到一些结论如下:

每个线程共享进程的代码段内存空间,所以我们编写多线程代码的时候,可以在任何线程调用任何函数。

每个线程共享进程的数据段内存空间,所以我们编写多线程代码的时候,可以在任何线程访问全局变量。

每个线程共享进程的堆,所以我们编写多线程代码的时候,可以在一个线程访问另外一个线程new/malloc出来的内存对象。

每个线程都有自己的栈的空间,所以可以独立调用执行函数(参数,局部变量,函数跳转)相互之间不受影响。

 

 

04 OS如何调度线程的

CPU一般会有多个核心,每个核心都调度一个线程执行。

CPU有几个核心,最多同时可调度几个线程(多核能让电脑更快就是这个原理)。

OS的功能就是要在合适的时候分配CPU核心来调度合适的线程。

为了能实现多任务并发,OS不允许一个OS核心长期固定调度一个线程。

OS是如何调度CPU核心来执行各个线程呢?

 

动图封面  

 

OS会根据线程的优先级分配每次调度最多执行的时间片,这个时间一到,无论如何都要重新调度一次线程(也许还是调度到这个线程,这个不重要)。

除了时间片以外,线程会等待某些条件(磁盘读取文件,网卡发送完数据,线程休眠, 等待用户操作)这样也会把这个线程挂起,OS会重新找一个新的线程继续执行,只到挂起的这个线程的条件满足了,重新把这个线程放到可调度队列里面,这个线程又有机会被OS调度CPU核心来执行。

当我们打开电脑的任务管理器,你会发现很多线程的CPU占有率为0%, 说明这些线程都由于某些条件而挂起了,没有被OS调度。

每个线程“随时随地”都可能被OS中断执行,并调度到其它的线程执行。

OS是如何保证一个线程在调度出去后,再重新调度回来能继续之前的数据状态来执行呢?

OS是这么做的:每个线程都会有一个运行时的环境(运行时CPU的每个寄存器的值、栈独立。栈的内存数据不会变。数据段、堆共用,可能调度回来会变)。

当OS要把某个CPU核心调度出去给其它线程的时候,首先会把当前线程的运行环境(寄存器的值等)保存到内存,然后调度到其它线程,等再次调度回来的时候,再把原来保存到内存的寄存器的值,再设置会CPU核心的寄存器里面,这样就回到了调度出去之前的进度。

因为多线程之间共用了代码段(代码段只读,不会改),数据段(全局变量调度回来后,可能被其它线程篡改,不是调度之前的那个值了),堆(调度回来后,动态内存分配的对象内存数据可能被其它线程出篡改),调度回来后,栈上的数据是不变的,因为每个线程都有自己的栈空间。线程调度前后哪些会变,哪些不变你要清楚。这样你写多线程代码的时候才能清晰。

线程调度的开销就是:保存上下文执行环境,内核态运行算法决定接下来调度那个线程,切换这个线程的上下文环境。

05 线程锁的核心原理是什么?

多线程切换的时候,栈、代码段的数据不会变,数据段与堆的数据切换前后可能会发生改变,这个就造成了"竞争", 如果某些关键数据,在执行代码的时候,不允许这种竞争性的改变,怎么办呢?这个时候多线程就给了一个机制,这个机制就是锁,那么锁的原理是什么?接下来我来这你详细讲解。

动图封面

 

例如: 我编写一个函数

funcA() {

    lock(锁)   // 要保护的数据的逻辑部分。

    …

    unlock(锁)

}

当线程A调用FuncA(),线程B也调用FUNCA(),OS如何设计锁能保证他们竞争的唯一性的呢?我们把具体过程来分析一下。

假设线程A调用funcA();它获取了锁,执行到中间某个代码的时候,时间片用完了,被OS调度出去,OS调度线程B来执行funcA(), 当线程B跑到lock(锁),发现这个锁已经被线程A拿了,此时,线程B会主动把自己挂起到锁这个“事件”上(等着锁释放)。

OS从新调度线程执行,当重新调度到线程A的时候,线程A执行,执行完成以后,释放掉这个锁,那么线程B又从等待这个锁的队列,到线程调度的就绪队列,又可被OS调度到,等线程A调度出去后,线程B去lock这个锁,就占用了这个锁,然后继续执行。这样就保证了lock/unlock之间的代码永远只有一个线程跑进去了。这样保护了这段代码里面相关的数据和逻辑。

 

进程与线程各位老铁一定要掌握好,这样写程序才能做到心中用代码。

好了,今天的分享就到这里,谢谢大家收看,我们下期再见。

最后,给大家分享一下咱们涵盖cocos、unity、laya...一系列的游戏实战大礼包,今天全部免费提供给大家,点击下方即可获取:

标签:讲清楚,调度,线程,内存,进程,OS,CPU
From: https://www.cnblogs.com/liuwenyi/p/17805625.html

相关文章

  • 终于有人把VMware虚拟机三种网络模式讲清楚了!
    你们好,我的网工朋友。前段时间VMware更新了,你用上最新版了吗?有几个网工朋友留言说,在操作中遇到过各种各样的问题。比如说由于公司服务器重启导致出现下面的问题:在Xshell里连接虚拟机映射时连接失败;能够连接上虚拟机的映射地址,但gitpull时报错无法解析hostname……其实这些都是ip问......
  • Docker开启远程守护进程访问
    默认情况下,Docker守护进程监听Unix套接字上的连接,以接受来自本地客户端的请求。通过将Docker配置为侦听IP地址和端口以及Unix套接字,可以允许Docker接受来自远程主机的请求。有关此配置选项的更详细信息,请参阅dockerdCLI参考。[1]配置远程访问使用systemctleditdocker.servi......
  • celery 进程如何做健康检查,判断 amqp 连接是不是断了?
    要检查Celery进程的健康状况并判断AMQP连接是否断开,可以参考以下方法:使用Heartbeat(心跳)机制:Celery提供了Heartbeat机制来确保AMQP连接的稳定性。你可以在Celery的配置文件中设置BROKER_HEARTBEAT参数来启用心跳检测。心跳检测会定期发送一条特殊的消息给AMQP服务器,如果服务器没有收......
  • Android进程冻结机制
    奇怪的ANR今天遇到了个很有意思的anr问题,应用出现了anr:7696:08-2914:12:59.56489879048341IWindowManager:ANRinWindow{3b0709u0me.linjw.demo.anr}.Reason:3b0709me.linjw.demo.anr(server)isnotresponding.Waited5001msforFocusEvent(hasFocus=false)8......
  • linux进程管理
    初识进程进程状态:进程介绍:进程唯一标识符:PID三种状态:就绪状态:已经具备运行条件,但CPU还没分配运行状态:占用并在cpu中运行阻塞状态:进程因等待某些事发生而暂停不能运行查看进程静态查看:   使用ps命......
  • Linux进程管理01
    查看进程静态查看进程:“psaux"查看当前目录的进程查看cpu占用率时,一般会希望进程按照cpu占用百分比的降序排列,使用“psaux--sort-%cpu”使用“psaxo”命令自定义显示的字段如:psaxopid,ppid,user,%cpu,command动态查看进程使用top查看命令动态查看进程进程优先级使......
  • C 语言多线程基础
    ......
  • 线程池如何实现参数的动态修改?线程池如何调优?
    线程池如何实现参数的动态修改线程池提供了几个setter方法来设置线程池的参数。这里主要有两个思路:1、在微服务架构下,可以利用配置中心,如Nacos、Apollo等等,也可以自己开发配置中心。业务服务读取线程池配置,获取相应的线程池实例来修改线程池的参数。2、如果限制了配置中心的使用,也......
  • 11月1日守护线程
    目录守护线程第一种情况只有主线程和守护线程的情况第二种情况有主线程、子线程以及守护线程的情况总结守护线程守护线程核心就是守护到整个进程运行周期结束什么是进程的运行周期呢?进程的运行周期通常以循环的方式重复进行,从就绪状态到运行状态再到阻塞状态,然后再次切换到就......
  • linux学习记录:进程管理
    1.进程:正在运行的程序,包括这个程序所占用的系统资源。每个进程都有唯一的进程标识pid,一个pid只能识别一个进程,ppid是父进程id。进程状态:就绪、运行、阻塞。2.查看进程静态查看进程:psaux(捕捉某一瞬间某一个进程的状态)-a:显示所有用户的进程,包括完整路径-u:显示使用者的名......