zoukankan      html  css  js  c++  java
  • 面试笔记:操作系统相关——进程、线程

    一、线程与进程的区别

    • 进程是系统分配资源的最小单位
    • 线程是CPU调度的最小单位
    • 由于默认进程内只有一个线程,所以多核CPU处理多进程就像是一个进程一个核心

    进程

    进程是系统资源分配的最小单位, 系统由一个个进程(程序)组成。
    一般情况下,包括文本区域(text region)数据区域(data region)堆栈(stack region)

    文本区域存储处理器执行的代码
    数据区域存储变量和进程执行期间使用的动态分配的内存;
    堆栈区域存储着活动过程调用的指令和本地变量。

    因此进程的创建和销毁都是相对于系统资源,所以是一种比较昂贵的操作。
    进程有三个状态:

    等待态: 等待某个事件的完成;
    就绪态: 等待系统分配处理器以便运行;
    运行态: 占有处理器正在运行。

    进程是抢占式的争夺CPU运行自身,而CPU单核的情况下同一时间只能执行一个进程的代码,但是多进程的实现则是通过CPU飞快的切换不同进程,因此使得看上去就像是多个进程在同时进行.

    通信问题: 由于进程间是隔离的,各自拥有自己的内存内存资源, 因此相对于线程比较安全, 所以不同进程之间的数据只能通过 IPC(Inter-Process Communication) 进行通信共享.

    线程

    • 线程属于进程
    • 线程共享进程的内存地址空间
    • 线程几乎不占有系统资源

    通信问题: 进程相当于一个容器,而线程而是运行在容器里面的,因此对于容器内的东西,线程是共同享有的,因此线程间的通信可以直接通过全局变量进行通信,但是由此带来的例如多个线程读写同一个地址变量的时候则将带来不可预期的后果,因此这时候引入了各种锁的作用,例如互斥锁等。

    同时多线程是不安全的,当一个线程崩溃了,会导致整个进程也崩溃了,即其他线程也挂了,
    但多进程而不会,一个进程挂了,另一个进程依然照样运行。

    • 进程是系统分配资源的最小单位
    • 线程是CPU调度的最小单位
    • 由于默认进程内只有一个线程,所以多核CPU处理多进程就像是一个进程一个核心

    线程和进程的上下文切换

    进程切换分3步:

    1. 切换页目录以使用新的地址空间
    2. 切换内核栈
    3. 切换硬件上下文

    而线程切换只需要第2、3步,因此进程的切换代价比较大
    协程

    协程是属于线程的。 协程程序是在线程里面跑的,因此协程又称微线程和纤程等
    协没有线程的上下文切换消耗。协程的调度切换是用户(程序员)手动切换的,因此更加灵活,因此又叫用户空间线程.
    原子操作性。由于协程是用户调度的,所以不会出现执行一半的代码片段被强制中断了,因此无需原子操作锁。

    协程的实现:迭代器和生成器

    迭代器: 实现了迭代接口的类,接口函数例如:current,key,next,rewind,valid。迭代器最基本的规定了对象可以通过next返回下一个值,而不是像数组,列表一样一次性返回。语言实现:在Java的foreach遍历迭代器对(数组),Python的for遍历迭代器对象(tuple,list,dist)。
    生成器: 使用 yield 关键字的函数,可以多次返回值,生成器实际上也算是实现了迭代器接口(协议)。即生成器也可通过next返回下一个值。

    协程举例:在Python中,使用了yield的函数为生成器函数,即可以多次返回值。则生成器可以暂停一下,转而执行其他代码,再回来继续执行函数往下的代码。

    作者:feng409
    链接:https://juejin.im/post/5b0014b7518825426e023666
    来源:掘金


    由该问题引出有一个有趣的东西。曾经被同学问过:在一个大型多人连线游戏平台中,每个玩家有一个人物与一定信息需要在大厅中显示和交互,此时如果让你设计这个平台,多个玩家控制多个人物时是使用多线程还是多进程来跑?

    答案显而易见的是:进程

    原因很简单,不同进程间的资源是相互独立互不影响的。如果使用多线程,当大厅中某个玩家出现出现问题,那么该大厅中控制所有玩家的进程内所有线程共用的资源将被影响,导致一个玩家出问题,整个平台崩溃。 而用多进程来跑时,一个玩家出现问题,退出该进程即可,该玩家可以重新登陆,而不影响平台中其他玩家的使用。

    这个问题还有一个例子就是浏览器,市面上大多数浏览器如IE、Firefox等都是使用多线程来显示多个网页,一个浏览器即一个进程,一个浏览器多个网页即多线程。原因很简单,进程的开销很昂贵,一个网页一个进程过于奢侈,用多线程是比较合适的方法。

    而Google的Chrome浏览器则不同,采用一个网页一个进程的方式,当你使用Chrome大概四五十个网页时,任务管理器里就真的多出了四五十个进程。相当耗费内存,你甚至可以在Chrome中找到一个自带的任务管理器,用于管理控制Chrome上开启的多个进程,包括,浏览器本体的功能,插件进程,网页进程,GPU进程等,信息包括CPU使用情况,内存所占空间以及进程ID。

    这样看似非常耗费内存资源的方式却大大提高了Chrome的使用体验,现代浏览器因为网页的请求资源增加,插件增加,js以及其他功能需要大量资源,单进程运行一个网页已经比较吃力,让单进程运行多个网页更是容易出现崩溃。 如果你使用firefox浏览器打开数十个标签,你会发现在标签之间切换开始变得吃力,一旦某个网页出现崩溃,数十个网页将同样崩溃,这就形如上述游戏平台中出现的情况,同一进程中的某个线程崩溃,将连带影响该进程中所有线程。

    而Chrome就不会。一个网页崩了,其他标签仍能正常浏览。因此现在越来越多的人喜欢同一浏览器上开数十个网页,这在以前IE普遍使用的年代是不可想象的。多开几个网页,你就有全线崩溃的危险。

    关于Chrome是如何协调多线程,多进程,CPU,GPU和内存使用的细节具体可以参考:
    [译]官方图解:Chrome 快是有原因的,现代浏览器的多进程架构!

    令人兴奋的是,在此基础上,Google可以像开发操作系统一样开发Chrome的功能,更多方便的小插件,浏览器的新性能,可以以应用程序的形式在Chrome上实现。
    在这里插入图片描述

    线程生命周期
    二、线程共享的环境包括
    进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。

    进程拥有这许多共性的同时,还拥有自己的个性。有了这些个性,线程才能实现并发性。这些个性包括:

    1.线程ID
    每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。

    2.寄存器组的值
    由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。

    3.线程的堆栈
    堆栈是保证线程独立运行所必须的。
    线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈, 使得函数调用可以正常执行,不受其他线程的影响。

    4.错误返回码
    由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用
    后设置了errno值,而在该 线程还没有处理这个错误,另外一个线程就在此时
    被调度器投入运行,这样错误值就有可能被修改。
    所以,不同的线程应该拥有自己的错误返回码变量。

    5.线程的信号屏蔽码
    由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样的信号处理器。

    6.线程的优先级
    由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。

    涉及多线程程序涉及的时候经常会出现一些令人难以思议的事情,用堆和栈分配一个变量可能在以后的执行中产生意想不到的结果,而这个结果的表现就是内存的非法被访问,导致内存的内容被更改。

    理解这个现象的两个基本概念是:在一个进程的线程共享堆区,而进程中的线程各自维持自己堆栈。

  • 相关阅读:
    旁友数独会伐啦?python秒解数独了解下伐啦?
    趁老王不在,和隔壁邻居斗斗地主,比比大小
    ll字段 详解 文件权限
    etc/pass命令列表
    maven配置
    linux常用汇总
    Tomcat学习笔记
    JavaEE高级-Hibernate学习笔记
    JavaEE高级-通用Mapper学习笔记
    jQueryrocket
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11794305.html
Copyright © 2011-2022 走看看