• 操作系统问题解答


    1.进程线程,什么是线程和进程?区别是啥?

      程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

      进程是操作系统资源分配的基本单位(cpu、内存、文件、网络端口),进程是应用程序的执行实例,比如电脑里运行着浏览器进程、QQ客户端进程、邮件进程还有其他操作系统的一些基础进程;线程是cpu调度和程序执行的基本单位为什么是调度的基本单位

      并发 != 并行

      总线程数<= CPU数量:并行运行

      总线程数> CPU数量:并发运行

      进程:多个程序的并发执行;线程:程序中多个任务的并发执行。

      进程和线程都是并行执行程序,但是只有多处理器下的多线程才可以真正实现并行(多个线程在同一个时间片同时运行),其他的实际上并不是真正的并行,都是交替在cpu上运行,只是每个程序运行的时间很短(时间片),快速的交替,所以看上去就是同时在运行,实际上只属于并发。 

      进程是一个实体,每一个进程都有它自己的地址空间。一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。线程共享进程的地址空间,这意味着线程之间共享全局变量,但线程在进程的内存空间中也有自己的内存空间用来临时保存自己的堆栈或寄存器内容或程序计数器,用来恢复继续运行。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,更进一步说同一个进程内的线程共享进程的资源,但每个线程有自己的堆栈和局部变量

      进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。

      为什么使用进程?

      由于CPU与其他PC资源之间速度的不协调(cpu的运算速度比其他资源的速度快很多),人们想提高资源利用率,所以人们提出了多任务系统。得益于CPU的计算速度,我们可以“同时”运行多个任务,实质上是多个任务之间轮流使用CPU资源,由于速度超快,给用户的感觉就是连续的。任务的执行需要依赖各个PC资源,我们可以称为计算机执行的上下文环境。要实现“同时执行”,就需要不断轮换,为了后来继续从当前状态执行下去,计算机需要保存切换前的程序上下文。所以有了进程:用进程去描述程序当前上下文的状态信息----内存位置、变量值、任务ID……所以,进程是资源分配的单位。一般来说宏观上可以看做是一个软件的运行,例如一个word文档的打开。

       

      为什么使用线程?线程与进程的区别?

      1.在进程中运行多种活动时,当进行某一项活动时比如读取磁盘数据,此时进程就处于阻塞状态,整个事情的进展就处于暂时的停止状态,如果我们将进程中的多种工作分派给不同的线程去处理,比如一个线程在向内存中写入数据,一个线程在分析数据,这样就不会在读取数据的时候无法分析数据了。多个任务之间切换因为要保存上下文、调入上下文,一旦多了的时候,还是有一定的时间消耗的。为了进一步提高资源利用率,人们在进程中,引入了线程,线程只是CPU轮流调度的单位,其他上下文信息用所在进程中的。这样上下文切换的耗时就降了下来。同样的,宏观上来可以看做是一个软件中的多个处理功能,例如上述打开word中拼写检查功能、字体加粗……

       2.因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高。

      进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响.,不同的进程之间存在着很大的独立性,使得进程安全性比较高。线程有自己的堆栈和局部变量,但线程地址空间共享,这意味着线程之间共享全局变量。由于各个线程都可以访问进程地址空间中的任意内存地址,所以一个线程可以修改读取甚至删除另一线程的堆栈,线程之间是没有保护的。一个线程死掉就等于整个进程死掉

      所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

       3.体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。     

      具体实例: 

        线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。

      操作系统按照一定的规则来分配什么时候由谁来获得CPU的计算资源,比如分时间片

      进程调度算法

    https://blog.csdn.net/kuangsonghan/article/details/80674777

    https://blog.csdn.net/zsl091125/article/details/52540348

    https://www.cnblogs.com/azheng007/p/3581696.html

    3.涉及到了锁机制,所以问问并行处理图片时候哪些地方要加锁; 死锁(python gil)、锁机制

    临界区:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用,一次仅允许一个进程使用的资源称为临界资源,使用临界资源的代码段称为临界区

    锁机制:保证在多核多线程环境中,在某一个时间点上,只能有一个线程进入临界区代码,从而保证临界区中操作数据的一致性

    死锁: 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象

        若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程(线程)

    死锁的原因:

    主要原因(1) 因为系统资源不足。(2) 进程运行推进的顺序不合适,保证有先后顺序。(3) 资源分配不当等。

    死锁的必要原因:

    产生死锁的四个必要条件:
    (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    (3) 不剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
    (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。存在一个进程等待序列{P1,P2,…,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一 源,……,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

    解决死锁的方式:

    1、撤消陷于死锁的全部进程
    2、逐个撤消陷于死锁的进程,直到死锁不存在
    3、从陷于死锁的进程中逐个强迫放弃所占用的资源,直至死锁消失。
    4、从另外一些进程那里强行剥夺足够数量的资源分配给死锁进程,以解除死锁状态

    C++多线程开发中,容易出现死锁导致程序挂起的现象。
    解决步骤分为三步:
    1、检测死锁线程。
    2、打印线程信息。
    3、修改死锁程序。

     https://blog.csdn.net/lz20120808/article/details/51707247

    死锁避免:

    死锁预防是设法至少破坏产生死锁的四个必要条件之一

    1.设置加锁顺序:谁先加锁,谁后加锁

    2.设置加锁时限:在获取锁的时候尝试加一个获取锁的时限,超过时限不需要再获取锁,放弃操作

    https://blog.csdn.net/qq_38663729/article/details/80058980

    4.进程间通信、线程间通信?进程间通信,说一下如何实现管道?

      进程是相互独立的,一般而言是不能相互访问的。但很多情况下进程间需要互相通信,来完成系统的某项功能,进程通过与内核及其它进程之间的互相通信来协调它们的行为。

      进程通信(IPC)应用场景:

        数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。

        共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。

        通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。  

        资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。

        进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

      管道通信类型:管道、消息队列、共享内存、socket

      管道:

        管道分为无名管道和命名管道。

        无名管道:1.只能用于具有亲缘关系的进程(父子进程或者兄弟进程)之间的通信,通常一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可以通过管道通信

             2.传输方向只能是一个方向(单向),具有固定的读端和写端,一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据

             3.每次使用都需要创建管道对象

        命名管道(有名管道):1.可以在任意进程间实现通信。提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信,改管道是通过路径名来指出,在文件系统中是可以看到的,在建立管道后可以当做普通文件来使用读写操作。

                   2.FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。

             无名管道:半双工

             有名管道:双工 (不确定)

        管道如何通信: 

        管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
        管道如何建立:
        从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。最开始的时候,上面的两个箭头都连接在同一个进程Process 1上(连接在Process 1上的两个箭头)。
        当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。随后,每个进程关闭自己不需要的一个连接 (两个黑色的箭头被关闭; Process 1关闭从PIPE来的输入连接,
        Process 2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了如上图的PIPE。

          https://blog.csdn.net/c15522627353/article/details/52972941#t31

        https://www.imooc.com/article/11177

      线程间通信:

        https://www.cnblogs.com/xh0102/p/5710074.html

        https://www.cnblogs.com/Wenxu/p/7979023.html

        https://blog.csdn.net/qq_26399665/article/details/54982102

  • 相关阅读:
    Zookeeper 基础知识【1】
    Spark 基础复习【1】
    ZooKeeper 入门 一致性
    Hive 视图 索引
    Yarn调度 历史与基础
    mysql 优化【1】
    TCP IP知识梳理
    Java 基础 锁
    Spark 累加器使用
    RATE-MAX----beta答辩博客
  • 原文地址:https://www.cnblogs.com/ymjyqsx/p/9745020.html
Copyright © 2020-2023  润新知