精华内容
下载资源
问答
  • 进程的同步(上)

    2020-04-11 18:53:23
    引入进程之后,程序实现了并发执行,但是也带来了程序的运行结果有不可再现性的隐患,因此期望通过进程的同步来避免程序运行结果的不可再现性 二.进程同步的基本概念 没有实行进程同步之前: 1.两种形式的制约...

    阻塞状态:一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)正在运行的进程由于提出系统服务请求(如I/O操作),但因为某种原因未得到操作系统的立即响应,或者需要从其他合作进程获得的数据尚未到达等原因,该进程只能调用阻塞原语把自己阻塞,等待相应的事件出现后才被唤醒。
    一.进程同步的目的
    在引入进程之后,程序实现了并发执行,但是也带来了程序的运行结果有不可再现性的隐患,因此期望通过进程的同步来避免程序运行结果的不可再现性

    二.进程同步的基本概念

    没有实行进程同步之前:

    1.两种形式的制约关系
    (1)相交进程(相互之间有关系的进程,进程之间多为该关系)
    1)相互合作的进程
    *直接制约关系
    *为完成某一任务而相互合作
    *举例:生产者消费者问题,读者写者问题。
    *没有获得消息的进程阻塞
    获得消息后的进程被唤醒,被调度执行
    有因为各种各样原因而产生的阻塞队列,例如提出“打印请求”后进入了等待打印完成的阻塞队列的进程所在的阻塞队列,打印完成了的时候这个进程才会被唤醒进入了就绪队列(等待处理机cpu调度)。
    *cpu空闲表示就绪队列是空的,因为没有需要被调度的进程
    *例如司机进程与售货员进程的关系
    2)相互矛盾的进程
    *间接制约关系
    *多进程共享系统中某一资源
    *进程的互斥
    *用户进程使用资源时必须要向系统申请,系统统一分配这个资源
    *两个进程和一个打印机(进程同步包含进程互斥
    (2)无关进程(进程之间不互抢任何资源)

    总之:未引入进程同步之前:进程之间两种形式的制约关系使得进程走走停停(异步性),访问的资源每一次的状态可能不同,使得结果的不可再现性——与时间、速度有关的错误,进程执行的相对速度不能由进程自己控制,因此必须有一个机制来控制进程执行的次序

    进程并发执行导致的与时间有关的错误的举例
    几乎都是因为中断位置的任意性导致的
    :(甲、乙凳子)(一家民航公司的两个民航系统,)(计算一分钟的车流量)(资源使用和回收:)(共享资源开始为1,R1执行-1,R2执行+1)

    对最后一个例子的分析:共有三种情况。一种是先对x+1,再对x-1;第二种赋给R1R2之后只对R1,R2进行操作之后或者把R1的值给x,或者把R2的值给x。

    2.临界资源(对上述问题进行改进,切记切记这个资源是提供给进程的共享资源,每次只可以被一个进程使用(公用队列),不可以一会被这个进程使用一会又被另外一个进程使用(磁盘存储介质,重用代码))
    *原理:一个进程在访问一个资源时其他的进程不可以访问,这个资源叫做临界资源,进程中临界资源所在的代码叫做临界区。一定是共享资源
    (1)*共享资源
    (打印机)(公用队列:取里面的元素要互斥使用)(磁盘存储介质宏观上(即一段时间内)可以A写一会,B写一会)(可重入代码:在一段时间内A可以走一遍B也可以走一遍的代码)
    *非共享资源
    (私有数据)
    (2)临界区:
    1)进入区:判断邻接资源可用,以及给邻接资源上锁
    2)临界区:一次只允许1进程,有限时间退出,进程无法获得临界资源就让出处理机
    *有限时间内进入自己临界区就是有限等待
    *有进程在用临界资源就忙则等待
    *无法进入自己临界区就让权等待——让出处理机
    *没有进程在用临界资源就空闲让进
    3)退出区:告诉别人邻接资源空闲了

    (3)解决互斥问题的几个方法/解决临界区问题的几个方法
    1)硬件同步机制(少)
    2)信号量机制P-V(大小题)
    3)管程机制(小题)

    展开全文
  • 线程与进程的区别

    2019-06-19 10:08:49
    线程与进程的区别 进程与线程的区别是很重要的一个知识点,也...引入进程之前–多道程序设计 概述 多道程序设计技术是操作系统最早引入的技术,它的设计思想是允多个程序同时进入内存并运行,其目的是为了CPU...

    线程与进程的区别

     

    进程与线程的区别是很重要的一个知识点,也是面试中经常问到的。网上转载博客痕迹明显,且千篇一律。我简单提取下,记录下来,希望能帮到你。另外在 LeetCode 上也有关于此问题的讨论,可以直接浏览“Read more” 部分。

    引入进程之前–多道程序设计

    概述

    多道程序设计技术是操作系统最早引入的技术,它的设计思想是允多个程序同时进入内存并运行,其目的是为了CPU的利用率,进而提高系统效率。

    特点

    多道程序设计技术引入之前,多个程序串行执行。只存在一个程序计数器(PC, program counter),一个程序执行完毕之后,才会执行下一个程序。而多道程序设计技术允许多个程序同时进入内存并运行,那就要每个程序分配程序计数器。如果内存中有四个程序在并发执行,那就需要四个程序计数器。

    新技术带来的问题

    一个技术、一个机制的引入,一方面解决了以前棘手的问题,但同时,往往带来新的问题。多道程序设计技术也是如此。


    多道程序设计技术允许多个程序同时进入内存并运行,在这样的并发环境下,如何描述、刻画这样执行的程序呢?因此引入了“进程”。


    进程(Process)

    定义

    进程是具有独立功能的程序关于某个数据集合上一次运行活动,是系统进行资源分配和调度(若不支持线程机制,进程的系统调度的单位。否则,线程是系统调度的单位)的独立单位。

     特点

    • 进程是程序的一次执行过程。若程序执行两次甚至多次,则需要两个甚至多个进程。
    • 进程是是正在运行程序的抽象。它代表运行的CPU,也称进程是对CPU的抽象。(虚拟技术的支持,将一个CPU变幻为多个虚拟的CPU)
    • 系统资源(如内存、文件)以进程为单位分配。
    • 操作系统为每个进程分配了独立的地址空间
    • 操作系统通过“调度”把控制权交给进程。

    为什么引入线程 有了进程这概念之后 应用程序可以并发地去执行了 那么为什么要在进程当中再派生出线程呢?

    为什么引入线程?

    首先我们引入了进程这个概念,虽然进程有利于资源的管理和保护。然而在实际应用中,进程有这样的问题: 
    1. 进程切换的代价、开销比较大; 
    2. 在一个进程内也需要并行执行多个程序,实现不同的功能。 
    3. 进程有时候性能比较低。

    引入线程有以下三个方面的考虑

    1. 应用的需要。比如打开一个浏览器,我想一边浏览网页,一边下载文件,一边播放音乐。如果一个浏览器是一个进程,那么这样的需求需要线程机制。
    2. 开销的考虑。在进程内创建、终止线程比创建、终止进程要快。同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换。线程之间相互通信无须通过内核(同一进程内的线程共享内存和文件)
    3. 性能的考虑。多个线程中,任务功能不同(有的负责计算,有的负责I/O),如果有多个处理器,一个进程就可以有很多的任务同时在执行。

    线程的属性

    线程

    • 有标识符ID
    • 有状态及状态转换,所以需要提供一些状态转换操作
    • 不运行时需要保存上下文环境,所以需要程序计数器等寄存器
    • 有自己的栈和栈指针
    • 共享所在进程的地址空间和其它资源
    • -

    进程与线程区别

    1. 定义方面:进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。(进程可以创建多个线程)
    2. 角色方面:在支持线程机制的系统中,进程是系统资源分配的单位,线程是CPU调度的单位。
    3. 资源共享方面:进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
    4. 独立性方面:进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。
    5. 开销方面。进程切换的开销较大。线程相对较小。(前面也提到过,引入线程也出于了开销的考虑。)

    Read more

    Process Vs Thread

    Difference between Process and Thread 
    what is different between thread and process

    理解Android进程创建流程(转)

    Android中的进程和线程

    关于这方面内容,笔者参考了 android中的线程与进程 
    以及《Android技术内幕.系统卷》 
    在此感谢。


    Android 系统在启动时首先会启动 Linux 基础系统,然后引导加载 Linux kernel 并启动初始化进程(Init),如图。 
    这里写图片描述

    接着,启动 Linux 守护进程(daemons)。这个过程需要启动以下内容。 
    - 启动USB守护进程(usbd)来管理USB连接。 
    - 启动Android Debug Bridge 守护进程(adbd)来管理ADB连接。 
    - 启动Debug 守护进程(debuggerd)来管理调试进程的请求(包括内存转换等)。 
    - 启动无限接口守护进程(rild)来管理无线通信。

    这里写图片描述

    在启动 Linux 守护进程的同时还需要启动 Zygote 进程。他主要包括以下需要启动和注册的内容。 
    - 初始化一个Dalvik虚拟机实例。 
    - 装载 Socket 请求所需的类和监听。 
    - 创建虚拟机实例来管理应用程序的进程。

    这里写图片描述

    再接着,需要初始化 runtime 进程,这个过程需要处理的操作:

    • 初始化服务管理器
    • 注册服务管理器,以它作为默认 Binder 服务的 Context 管理器。

    这里写图片描述

    runtime 进程初始化之后, runtime 进程将发送一个请求到 Zygote ,开始启动系统服务,这时 Zygote 将为系统服务进程建立一个虚拟机实例,并启动系统服务,如图。

    这里写图片描述

    紧接者,系统服务将启动原生系统服务,主要包括 Surface Flinger 和 Audio Flinger。这些本地系统服务将注册到服务管理器 (Service Manager) 作为 IPC 服务的目标。

    这里写图片描述

    系统服务将启动 Android 管理服务, Android 管理服务将都被注册到服务管理器上。

    这里写图片描述

    最后,当系统加载完所有的服务之后会处于等待状态,等待程序运行。但是,每一个程序都将启动一个单独的进程。如图。系统启动一个 Home 进程 和一个 Contracts 进程。

    这里写图片描述 
    在Android系统中,每一个App都是一个Linux用户。一般情况下,每个App都是运行在一个进程的一个线程中,这个线程习惯称为主线程(Main Thread). 
    Zygote是一个虚拟机进程,同时也是一个虚拟机实例的孵化器,每当系统要求执行一个 Android应用程序,Zygote就会FORK出一个子进程来执行该应用程序。 
    这样做的好处显而易见:Zygote进程是在系统启动时产生的,它会完成虚拟机的初始化,库的加载,预置类库的加载和初始化等等操作,而在系统需要一个新的虚拟机实例时,Zygote通过复制自身,最快速的提供个系统。 
    另外,对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域,大大节省了内存开销。

    Android 进程模型

    Linux通过调用start_kernel函数来启动内核,当内核启动模块启动完成后,将启动用户空间的第一个进程——Init进程,下图为Android系统的进程模型图:

    Android 进程模型

    从上图可以看出,Linux内核在启动过程中,创建一个名为Kthreadd的内核进程,PID=2,用于创建内核空间的其他进程;同时创建第一个用户空间Init进程,该进程PID = 1,用于启动一些本地进程,比如Zygote进程,而Zygote进程也是一个专门用于孵化Java进程的本地进程,上图清晰地描述了整个Android系统的进程模型

    Zygote进程孵化新进程

    下面来对Zygote进程孵化新进程的过程做进一步了解:

    1. Zygote进程调用fork()函数创建出Zygote’ 子进程
    2. 子进程Zygote’ 共享父进程Zygote的代码区与连接信息

    这里写图片描述

    Fork()橙色箭头左边是Zygote进程,右边是创建出的Zygote‘子进程;然后Zygote’ 子进程将执行流程交给应用程序A,Android程序开始运行。

    新生成的应用程序A会使用已有Zygote父进程的库与资源的连接信息,所以运行速度很快。

    另外,对于上图,Zygote启动后,初始并运行DVM,而后将需要的类与资源加载到内存中。随后调用fork()创建出Zygote’ 子进程,接着子进程动态加载并运行应用程序A。

    运行的应用程序A会使用Zygote已经初始化并启动运行的DVM代码,通过使用已加载至内存中的类与资源来加快运行速度。

    展开全文
  • 首先需要回答一个问题,为什么要引入进程的概念? 前面在物理内存管理部分,我有提到物理内存管理的一切目的,都是为了多任务的系统,否则对于单道程序的操作系统,直接让那单道程序独占内存和CPU就可以了,自然不...

    进程产生的背景

    首先需要回答一个问题,为什么要引入进程的概念?

    前面在物理内存管理部分,我有提到物理内存管理的一切目的,都是为了多任务的系统,否则对于单道程序的操作系统,直接让那单道程序独占内存和CPU就可以了,自然不需要内存管理,显然也不需要进程的管理。所以,进程概念的产生,其实还是处在多道程序的背景下的。

    正是因为操作系统希望同时运行多道程序,而程序运行需要资源,因此操作系统需要合理地把自己的资源组织管理起来,在程序运行的时候分配相应的资源,在程序退出的时候回收那部分资源。那么问题来了?操作系统怎么知道把资源分配给了谁?怎么知道资源的占有者什么时候退出?又怎么知道应该从哪里回收分配出去的资源?因此,需要对每一个运行的任务进行抽象。对每一个运行的程序的抽象,就是这里进程的概念。这样,进程就成了操作系统资源分配的基本单位。

    进程与程序的区别

    程序是编译后的代码,机器码,驻留在外存或者内存当中。那么进程呢?其实上面已经提出了,进程本质上就是运行的程序。这句话涵盖了它们之间的全部区别。

    一方面,进程必然是包含程序的,毕竟它需要运行这个程序,但是程序只是进程的一部分。【进程是运行的程序】,这里的【运行】,就体现了它们之间的区别——既然程序要运行,就必然在代码以外还需要一定的数据,就必然需要占用一定的资源,比如代码和数据驻留的内存资源,就会涉及到程序当前的执行状态,比如一些上下文的信息,还有寄存器的信息。因此,可以概括出,进程是由代码,数据,各个寄存器以及占用的资源所组成的。

    从上面的讨论中,已经可以总结出进程和程序的区别了:

    • 首先,程序是静态的,进程是动态的。程序其实是静态的可执行文件,是可以永久保存的;但是进程由于其运行的性质,不可避免地具有各个生命周期,比如创建,运行,等待,退出等,它是动态变化的,有始有终的。由此也可以得出结论,同一个程序的多次执行过程是对应了不同的进程。
    • 其次就是两者的组成成分不同,进程是包含了程序的,除此以外进程还含有数据,状态信息等。

    现在,我们可以给出一个更加严谨的进程的定义了:

    进程是指一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。

    它具有以下特点:

    • 动态性
    • 并发性:进程就是为了多任务并发运行而产生的
    • 独立性:指不同的进程,它们的工作互不影响
    • 制约性:操作系统的资源是有限的,所有进程共享这些有限的资源,会因为资源的分配或者进程间同步而产生制约

    进程控制块

    进程管理是进行什么管理呢?

    这就好比你在烟花巷开了一家青楼,对于每一个客人,有的要先喝个酒,你得给他分配一瓶酒;有的客人直接带姑娘上楼了,那你得知道它带的是哪个姑娘,别的客人请求这个姑娘的时候他们就只能等着;然后这个客人带着这个姑娘,你得知道他什么时候完事儿了,你总不能让他就把姑娘给你带走了,你得把姑娘进行回收,然后再把姑娘分配给等着的那些客人。

    进程管理也是出于同样的目的。进程管理其实只在意资源的分配和回收。但是为了管理资源,还需要同时管理进程的状态,这样操作系统才知道什么时候应该分配资源,什么时候又应该回收资源。

    进程控制块(PCB, Process Control Block)就是操作系统用于完成进程管理的数据结构。操作系统用PCB来描述进程的基本情况以及运行变化的过程,每加入一个进程,操作系统就为它管理一个PCB,一旦进程退出运行,就随即销毁它的PCB,因此,PCB是进程存在的唯一标志。

    进程控制块的具体结构是什么?

    进程控制块的功能其实就是完成对一个进程的管理工作,它的结构必然是与它要完成的功能相适应的。为了完成这样的管理工作,进程控制块至少要保存:

    • 进程的状态信息:比如进程如果刚被创建,就需要为它分配必要的资源。如果它执行完成退出了,那么需要去回收分配给它的资源。
    • 进程的资源占用信息。进程当前占用了哪些资源,比如占用了哪些内存资源,方便在退出的时候回收到操作系统。

    当然,这里的状态信息和资源占用信息都只是从一个相当概括的角度来谈的。

    具体说来,状态信息不仅包含进程当前所处的是就绪态,创建态还是运行态,还包括进程切换时保存的上下文信息,进程的中断帧信息,还有用于调度算法一些信息,比如当前运行的时间,或者所处调度队列的一个链接等。此外,资源占用信息也可以更加细化,比方说,内存资源占用包括用户内存空间和内核堆栈,除此以外,还有打开的文件,等等。

    除此以外,还需要进程的标志信息,即pid(Process id),来唯一地标识一个进程。在pid以外,进程还有一个名字,它们共同构成进程的身份信息。

    进程控制块的组织

    为了管理所有进程,操作系统需要将代表所有进程的进程控制块组织起来。具体组织的方式有链表和索引表。

    这里的链表其实就是把所有的进程的PCB都添加到一个链表上。此外,根据进程状态的不同,多个状态可以对应多个链表,这样就可以形成就绪链表,阻塞链表等。索引表的思路和链表其实也差不多,就是数据结构的实现不同而已,同样将同一状态的进程归入一个索引表,多个状态可以对应多个索引表,这样形成就绪索引表,阻塞索引表等。PCB链表和索引表的组织,可以看一下下图:

    organize_PCB

    进程的状态

    前面已经提到,进程是动态执行的,在进程的执行过程中涉及进程状态的切换。目前我们已经看到进程具有创建态,运行态,就绪态,此外还有等待态和退出态。

    进程各个状态的切换以及相关的事件都如下面这张图所示:

    proc_state

    • 进程的创建。在系统刚启动时,或者一个应用进程要求创建另一个子进程时,都会产生创建态的进程。进程的创建态实际上标志了进程还没有完全准备就绪,操作系统在进行进程的初始化工作。
    • 进程的就绪。进程创建完成之后,就进入了就绪状态。此外,正在运行的进程也可能因为时间片用完或者被更高优先级的进程抢占而进入就绪态。处于等待状态的进程可以因为等待的事件发生而进入就绪态。
    • 进程的运行。处理机每次都会从就绪进程中挑选一个进程,让它实际上得到CPU资源,使该进程实际上投入运行。
    • 进程的等待。正在运行的进程,可能因为等待某个被占用的资源,或者等待某个时间的发生而不能继续运行,此时应该让该进程进入等待状态,从而让出CPU资源让其它进程运行。
    • 进程的退出。进程执行完毕,或者出现错误,会进入退出状态。退出状态是指该进程还没有完全消失,此时操作系统正在回收它的资源,并且等待该进程的父进程的处理结果。

    可以看到,在进程的整个生命周期内,主要都是在就绪,运行,等待三个状态中来回进行切换,在另外两个状态创建和退出的时间很少。因此,将这里的就绪,运行和等待称为三状态进程模型。

    挂起进程模型

    在出现虚拟内存管理后,进程还会出现一个新的状态,挂起。相应的,这种进程模型也称为挂起进程模型。挂起,是指在虚拟内存管理中,一个进程从内存中被换出到外存中的状态。会发生挂起的现象是因为物理内存资源是有限的,当内存空间不足时,操作系统需要对内存中的进程进行合理的安排,有些进程会被暂时调出内存,当条件允许时,再将这些进程调回到内存中。

    处于挂起状态的进程映像在磁盘上。将挂起状态与三状态进程模型综合一下,就产生了等待挂起状态和就绪挂起状态,因为显然在运行态的进程必须驻留在内存中,不可能换出到外存。

    等待挂起状态是指进程在外存中,并且等待某个事件出现的情况;就绪挂起状态是指进程在外存中,但是一旦进入内存,就可以运行。挂起进程模型的切换和相关的时间如下图所示:

    suspend_proc

    引入了挂起状态后,状态的切换也相应的增加了,下面分别做具体的说明。

    • 等待到等待挂起。如果当前运行的进程需要更多内存空间时,或者没有进程处于就绪状态时,会发生这种切换。
    • 就绪到就绪挂起。当有高优先级进程处于等待状态,并且系统认为很快就绪时,会将低优先级的就绪进程换出到外存中,以获得更多内存资源。
    • 运行到就绪挂起。对抢占式系统,高优先级的等待挂起进程因为事件发生到进入就绪挂起时,会抢占低优先级的运行进程。
    • 等待挂起到就绪挂起。等待事件发生时产生。
    • 就绪挂起到就绪。没有就绪进程或挂起就绪进程优先级高于就绪进程时,产生这种切换。
    • 等待挂起到等待。当一个进程释放了足够多的内存资源时,有可能将高优先级的等待挂起进程移入内存中。

    状态队列的概念

    前面已经提过,操作系统会为不同状态的进程分别维护一个单独的队列,即状态队列。现在我们知道,随着进程状态的切换,状态队列也会发生相应的变化,进程会从从一个队列加入到另一个队列。在进程状态模型下,状态队列的切换如图所示:

    status_queue

    线程的概念

    为什么需要引入线程的概念?

    设想一种场景。现在我们有一个MP3播放器的应用程序,该应用程序主要有三个核心功能模块,即从MP3音频文件中读取数据,将读取的数据解压缩,将解压缩后的音频数据播放出来。应该如何实现这个应用程序呢?

    这里我们主要考虑的指标是音频文件播放是否流畅、连贯。为此,就需要设计的应用程序可以及时将刚读出来的压缩数据解压缩,并且播放出来。一种简明的设计思路如下图所示:

    mp3_version1

    在该设计思路中,我们将读取数据,解压缩,播放文件依次执行。但是不难看出,这种实现存在很致命的缺陷:各个模块之间不能并行进行。可以看到,这里读取压缩音频文件涉及到I/O操作,解压缩是CPU密集型操作,因此这两个模块在理论上是可以并发执行的,即在将压缩文件读取出来的同时,CPU可以对这些数据进行解压缩,这样可以大幅度提高资源的利用效率。此外,按照我们的设想,在播放时应该也可以同时进行读文件和解压的操作,在这种实现方式都却不能做到。在这种实现下,为了播放音频文件连贯,必须一次性将全部音频文件读出并且解压缩,最后才能播放,漫长的等待时间非常影响用户体验。

    为了将各个操作并行地推进,我们很自然地联想到将各个核心模块全都加载到一个进程中,即为了运行这个MP3播放器软件,我们同时需要三个进程,这种设计思路如下图所示:

    mp3_version2

    这种方法的确解决并行的问题,但是却引入了新的问题——可以看到,这里的三个进程之间需要共享数据,前一个进程的输出,是下一个进程的输入,因此需要解决在进程之间通信以及共享数据的问题。此外,进程的创建以及进程之间的切换是需要相当的开销的,这种实现方式无疑增加了开销。

    我们的梦想呢,是有这样一种结构,它可以像进程一样进行切换,从而实现并行性;另一方面,数个这样的结构又可以共享数据,并且它们之间的切换的开销要足够的小。这样的梦想中的结构,就是线程。

    线程的概念

    为了解决上面的问题,可以在进程内部增加一种实体,这些实体之间都共享进程的资源,而CPU又可以在这些实体之间切换,这种实体就是线程。

    线程是进程的一部分,描述指令流执行状态。它是进程中的指令执行流的最小单元,是CPU调度的基本单位。引入了线程以后,CPU的调度就不再以进程为单位了,从而进程就退化成为了资源分配的单位;而进程中的线程共享分配给进程的资源,并且都可以独立地描述其中指令流的执行状态。

    那么,线程之间切换的开销的确小于进程切换吗?我们可以做一个考虑。首先由于线程本身不占有资源,在创建线程时不需要像进程一样分配资源,因此时间开销要小于进程的创建;也由于同样的原因,线程的退出需要的时间也比进程短,因为没有资源的回收;此外,同一进程的不同线程之间的切换,由于它们是共享数据,不需要进行用户空间、内核堆栈等的切换,只需要保存和恢复上下文信息就足够了,需要的时间也的确比进程切换要短。因此可以看到,线程相比于进程,能减少并发执行的时间和空间开销。

    关于进程和线程的关系,可以用一句话总结,即进程拥有一个完整的资源平台,而线程只独享指令流执行的必要资源,如寄存器和栈。进程是资源分配单位,线程是CPU调度单位。

    为了在实际系统中实现线程,可以有几种方案,即用户线程,内核线程以及轻量级进程。

    用户线程

    用户线程的思路其实非常简明:只是在用户空间实现线程,进程的线程对于操作系统是不可见的。这样,进程仍然是操作系统资源分配和调度的基本单位。用户线程的示意图如下图所示:

    user_thread

    仍然拿上面的MP3播放器举个例子,我可以在编写程序的时候,手动为它划分三个核心功能模块。在操作系统调度这个播放器进程运行的时候,在进程内部也运行一个调度程序,来选择某一个核心模块实际运行。

    通过上面的实例可以看到用户线程的特征:

    • 首先是非常灵活,因为是在进程的内部提供线程的调度算法,因此调度算法的设计可以更有针对性,不同的进程可以拥有不同的调度算法。
    • 线程对操作系统是不可见的,线程控制块TCB需要进程本身来维护。
    • 线程之间的切换很快,直接在进程运行中就可以切换,不需要用户态/内核态的切换。

    但是用户线程还是存在不足。编程困难都不算什么不足,毕竟程序员是干什么吃的?!最大的不足是一个线程阻塞的时候,其他线程也都阻塞了。因为操作系统看不到用户线程,一旦发生阻塞,操作系统就立即将CPU的控制权转交给其他进程了,对于MP3播放器这种应用,还是不能实现I/O和解压缩的CPU操作并行。

    除此以外,操作系统调度进程是以进程为单位分配CPU时间,一个进程的所有线程共享这部分CPU时间,每个线程的时间片较小。

    内核线程

    内核线程可以解决上面提到的用户线程的不足。内核线程就是指,线程对于操作系统是可见的,由操作系统来管理线程。内核线程的示意图所下图所示:

    kern_thread

    由于是由内核来同时维护PCB和TCB,一个进程的线程被阻塞并不会影响该进程的其他线程,这样音频文件的读取和解压缩就可以并行进行了,因此在实现了内核线程的操作系统上,我们的MP3播放器终于可以正常地并行地工作了。此外,由于操作系统调度的基本单位成了线程,不会出现上面提到的线程的时间片太短的情况。

    但是相对于用户线程,内核线程也具有一定的缺陷。一方面,内核线程的创建、切换和退出的开销比较大,因为这些操作是通过系统调用实现的,需要进行用户态/内核态的切换。此外,由操作系统统一给出线程的调度算法,不如用户线程那么灵活。

    为了综合用户线程和内核线程的优点,提出了轻量级进程的概念。

    轻量级进程(LightWeight Process)

    轻量级进程是内核支持的用户线程。一个进程可有一个或多个轻量级进程,每个轻权进程由一个单独的内核线程来支持。

    轻量级进程的实现综合了内核线程和用户线程。具体说来,对于每一个进程,内核中都管理了一定量的它的内核线程。但是在用户进程的内部,同时还存在着一定的用户线程。操作系统调度的基本单位仍然是内核线程,但是可以将用户线程和内核线程进行绑定,以在一定程度上改变这种调度关系,实现用户线程那种灵活的线程调度。轻量级进程的示意图如下图所示:

    lightweight_proc

    轻量级进程的思路是很好的,但是在实际系统中的运行不太理想,因为轻量级进程实现的开销太高了,在一定程度上抵消了它的优越性。

    可以看到,在轻量级进程中,用户线程和内核线程是多对多的关系。除此以外,还有用户线程对内核线程是一对一或者多对一的关系的情况,分别对应了内核线程和用户线程。

    下一篇,将继续讨论进程调度策略相关的问题。

    展开全文
  • 引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。 线程最直接的理解就是“轻量级进程”,它是一个...

    本博客前面部分转自:http://c.biancheng.net/cpp/html/2593.html


    线程的基本概念


    引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并行执行时所付出的时空开销,提高操作系统的并发性能。

    线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。

    引入线程后,进程的内涵发生了改变,进程只作为除CPU以外系统资源的分配单元,线程则作为处理机的分配单元。


    线程与进程的比较


    1) 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源拥有的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。

    2) 拥有资源。不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点必不可少的资源),但线程可以访问其隶属进程的系统资源。

    3) 并发性。在引入线程的操作系统中,不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。

    4) 系统开销。由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、 I/O设备等,因此操作系统所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程CPU环境的保存及新调度到进程CPU环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此,这些线程之间的同步与通信非常容易实现,甚至无需操作系统的干预。

    5) 地址空间和其他资源(如打开的文件):进程的地址空间之间互相独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。

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


    线程的属性



    在多线程操作系统中,把线程作为独立运行(或调度)的基本单位,此时的进程,已不再是一个基本的可执行实体。但进程仍具有与执行相关的状态,所谓进程处于“执行”状态,实际上是指该进程中某线程正在执行。线程的主要属性如下:
    1. 线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
    2. 不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统为它们创建成不同的线程。
    3. 同一进程中的各个线程共享该进程所拥有的资源。
    4. 线程是处理机的独立调度单位,多个线程是可以并发执行的。在单CPU的计算机系统中,各线程可交替地占用CPU;在多CPU的计算机系统中,各线程可同时占用不同的CPU,若各个CPU同时为一个进程内的各线程服务则可缩短进程的处理时间。
    5. —个线程被创建后便开始了它的生命周期,直至终止,线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。

    线程的实现方式



    线程的实现可以分为两类:用户级线程(User-LevelThread, ULT)和内核级线程(Kemel-LevelThread,  KLT)。内核级线程又称为内核支持的线程。

    在用户级线程中,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程起始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。图2-2(a)说明了用户级线程的实现方式。

    在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程架构的基础上完成。图2-2(b)说明了内核级线程的实现方式。

    在一些系统中,使用组合方式的多线程实现。线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被映射到一些(小于或等于用户级线程的数目)内核级线程上。图2-2(c)说明了用户级与内核级的组合实现方式。

    多线程模型



    有些系统同时支持用户线程和内核线程由此产生了不同的多线程模型,即实现用户级线程和内核级线程的连接方式。

    1) 多对一模型(many2one)

    将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。
    此模式中,用户级线程对操作系统不可见(即透明)。

    优点:线程管理是在用户空间进行的,因而效率比较高。
    缺点:当一个线程在使用内核服务时被阻塞,那么整个进程都会被阻塞;多个线程不能并行地运行在多处理机上。

    该模型完全在核外实现多线程,调度也在用户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式,显然,这种核外的线程调度器实际上只需要完成线程运行栈的切换,调度开销非常小,但同时因为核心信号(无论是同步的还是异步的)都是以进程为单位的,因而无法定位到线程,所以这种实现方式不能用于多处理器系统,而这个需求正变得越来越大,因此,在现实中,纯用户级线程的实现,除算法研究目的以外,几乎已经消失了


    2) 一对一模型(one2one)


    将每个用户级线程映射到一个内核级线程。

    优点:当一个线程被阻塞后,允许另一个线程继续执行,所以并发能力较强。
    缺点:每创建一个用户级线程都需要创建一个内核级线程与其对应,这样创建线程的开销比较大,会影响到应用程序的性能。

    3) 多对多模型(many2many)


    将 n 个用户级线程映射到 m 个内核级线程上,要求 m <= n。

    特点:在多对一模型和一对一模型中取了个折中,克服了多对一模型的并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程,开销太大的缺点。又拥有多对一模型和一对一模型各自的优点,可谓集两者之所长。


    Linux线程的发展历史


    本文只简略说明一下Linux发展历史,更详细的请参考linux的线程模型:2.6是个分界点,以及Linux 线程实现机制分析,感谢两位作者。第一个主要分析Linux线程发展的历史,第二个主要分析线程内核实现的机制和原理,都很有参考价值。

    我们知道线程是实现模型真正应用上的只有one2one模型和many2many模型。

    目前使用one2one模型的我所知道的有Windows,用户创建的线程真真正正对应一个内核线程,这种方式并发能力比较强,可以高效利用处理器多核特性,缺点是每个用户线程对应一个内核线程,线程创建开销比较大。

    目前使用many2many模型的有Solaris操作系统等,既可以一个用户线程对应一个内核线程,也可以多个用户对应一个内核线程,既可满足多处理机系统的需要,也可以最大限度的减小调度开销。

    那么Linux使用的是那种模型呢?之前我一直有疑问,现在才明白了,Linux的pthread库实际上使用的是one2one模型,属于KLT实现方式。但是Linux实际上本身内核是不支持多线程的,所谓的one2one实际上是一个用户线程对应一个LWP,这就不难理解为什么我在用GDB调试的时候,线程出现,会有****[LWP]的提示了。

    下面有一些解释是我转载的,来自于参考的两个博客:

    轻量级进程LWP


        既然称作轻量级进程,可见其本质仍然是进程,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符,并和其他进程有着父子关系;这是和类Unix操作系统的系统调用vfork()生成的进程一样的。LWP由内核管理并像普通进程一样被调度。Linux内核是支持LWP的典型例子。Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程,通过参数决定子进程和父进程共享的资源种类和数量,这样就有了轻重之分。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。

    在大多数系统中,LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息,而这也是它之所以被称为轻量级的原因。
    因为LWP之间共享它们的大部分资源,所以它在某些应用程序就不适用了;这个时候就要使用多个普通的进程了。例如,为了避免内存泄漏(a process can be replaced by another one)和实现特权分隔(processes can run under other credentials and have other permissions)。

    由于ULT的线程是在用户态,对应的内核部分还是一个进程,因此ULT就没有办法利用多处理器的优势,而KLT就可以通过调度将线程分布在多个处理上运行,这样KLT的性能高得多;另外,一个ULT的线程阻塞,所有的线程都阻塞,而KLT一个线程阻塞不会影响其它线程。

    其实最初根本没有线程的概念,只有进程,一个任务一个进程一个执行流,多任务处理机就是多进程。后来提出线程的概念,但是要如何去实现,这里就有很多种实现方法了,文章看到这里,可以想到两种实现方法,一种就是上面所说的用户线程的方法,其优缺点上文以简述;再有就是用轻量级进程去模拟,即我们可以把LWP看成是一个线程。就应为这个使得线程和进程的概念混淆了,至少我觉得很多人其实根本就不知道,至少我以前不知道,有人说系统调度单位是进程,又有人说是线程,其实系统调度的单位一直就没有改变,只是后来部分线程和进程的界限模糊了,至少上文中的用户线程绝对不是调度对象,LWP模拟的线程却是调度对象。

    Posix标准对线程提出以下要求:
    1. 查看进程列表的时候, 相关的一组task_struct应当被展现为列表中的一个节点;
    2. 发送给这个"进程"的信号(对应kill系统调用), 将被对应的这一组task_struct所共享, 并且被其中的任意一个"线程"处理;
    3. 发送给某个"线程"的信号(对应pthread_kill), 将只被对应的一个task_struct接收, 并且由它自己来处理;
    4. 当"进程"被停止或继续时(对应SIGSTOP/SIGCONT信号), 对应的这一组task_struct状态将改变;
    5. 当"进程"收到一个致命信号(比如由于段错误收到SIGSEGV信号), 对应的这一组task_struct将全部退出;
    6. 等等(以上可能不够全);

    模型一 :LinuxThreads(已淘汰,了解即可)


    linux 2.6以前, pthread线程库对应的实现是一个名叫linuxthreads的lib.这种实现本质上是一种LWP的实现方式,即通过轻量级进程来模拟线程,内核并不知道有线程这个概念,在内核看来,都是进程。

    LinuxThreads是LWP的实现,不过模型一 在目前linux版本中已经被干掉了。简单说一下LinuxThreads的特点。

    在LinuxThreads中,专门为每一个进程构造了一个管理进程,负责处理线程相关的工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建并启动管理线程。然后管理线程再来创建用户请求的线程。也就是说,用户在调用pthread_create后,先是创建了管理线程,再由管理线程创建了用户的线程。

    linuxthreads值实现了第5点, 我认为这一点是最重要的. 如果某个线程"挂"了, 整个进程还在若无其事地运行着, 可能会出现很多的不一致状态. 进程将不是一个整体, 而线程也不能称为线程. 或许这也是为什么linuxthreads虽然与POSIX的要求差距甚远, 却能够存在, 并且还被使用了好几年的原因吧~

    但是, linuxthreads为了实现这个"第5点", 还是付出了很多代价, 并且创造了linuxthreads本身的一大性能瓶颈.
    接下来要说说, 为什么A程序创建了10个线程, 但是ps时却会出现11个A进程了. 因为linuxthreads自动创建了一个管理线程. 上面提到的"第5点"就是靠管理线程来实现的.

    当程序开始运行时, 并没有管理线程存在(因为尽管程序已经链接了pthread库, 但是未必会使用多线程).

    程序第一次调用pthread_create时, linuxthreads发现管理线程不存在, 于是创建这个管理线程. 这个管理线程是进程中的第一个线程(主线程)的儿子.然后在pthread_create中, 会通过pipe向管理线程发送一个命令, 告诉它创建线程. 即是说, 除主线程外, 所有的线程都是由管理线程来创建的, 管理线程是它们的父亲.于是, 当任何一个子线程退出时, 管理线程将收到SIGUSER1信号(这是在通过clone创建子线程时指定的). 管理线程在对应的sig_handler中会判断子线程是否正常退出, 如果不是, 则杀死所有线程, 然后自杀.

    那么, 主线程怎么办呢? 主线程是管理线程的父亲, 其退出时并不会给管理线程发信号. 于是, 在管理线程的主循环中通过getppid检查父进程的ID号, 如果ID号是1, 说明父亲已经退出, 并把自己托管给了init进程(1号进程). 这时候, 管理线程也会杀掉所有子线程, 然后自杀.
    可见, 线程的创建与销毁都是通过管理线程来完成的, 于是管理线程就成了linuxthreads的一个性能瓶颈.
    创建与销毁需要一次进程间通信, 一次上下文切换之后才能被管理线程执行, 并且多个请求会被管理线程串行地执行.

    这种通过LWP的方式来模拟线程的实现看起来还是比较巧妙的,但也存在一些比较严重的问题:

    1)线程ID和进程ID的问题
    按照POSIX的定义,同一进程的所有的线程应该共享同一个进程和父进程ID,而Linux的这种LWP方式显然不能满足这一点。

    2)信号处理问题
    异步信号是以进程为单位分发的,而Linux的线程本质上每个都是一个进程,且没有进程组的概念,所以某些缺省信号难以做到对所有线程有效,例如SIGSTOP和SIGCONT,就无法将整个进程挂起,而只能将某个线程挂起。

    3)线程总数问题
    LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。

    4)管理线程问题
    管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。

    5)同步问题
    LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。

    6)其他POSIX兼容性问题
    Linux中很多系统调用,按照语义都是与进程相关的,比如nice、setuid、setrlimit等,在目前的LinuxThreads中,这些调用都仅仅影响调用者线程。

    7)实时性问题
    线程的引入有一定的实时性考虑,但LinuxThreads暂时不支持,比如调度选项,目前还没有实现。不仅LinuxThreads如此,标准的Linux在实时性上考虑都很少

    模型二:NPTL(当前使用标准)


    到了linux 2.6, glibc中有了一种新的pthread线程库--NPTL(Native POSIX Threading Library).

    本质上来说,NPTL还是一个LWP的实现机制,但相对原有LinuxThreads来说,做了很多的改进。下面我们看一下NPTL如何解决原有LinuxThreads实现机制的缺陷

    NPTL实现了前面提到的POSIX的全部5点要求. 但是, 实际上, 与其说是NPTL实现了, 不如说是linux内核实现了.

    在linux 2.6中, 内核有了线程组的概念, task_struct结构中增加了一个tgid(thread group id)字段.
    如果这个task是一个"主线程", 则它的tgid等于pid, 否则tgid等于进程的pid(即主线程的pid).
    在clone系统调用中, 传递CLONE_THREAD参数就可以把新进程的tgid设置为父进程的tgid(否则新进程的tgid会设为其自身的pid).

    类似的XXid在task_struct中还有两 个:task->signal->pgid保存进程组的打头进程的pid、task->signal->session保存会话 打头进程的pid。通过这两个id来关联进程组和会话。
    有了tgid, 内核或相关的shell程序就知道某个tast_struct是代表一个进程还是代表一个线程, 也就知道在什么时候该展现它们, 什么时候不该展现(比如在ps的时候, 线程就不要展现了).

    而getpid(获取进程ID)系统调用返回的也是tast_struct中的tgid, 而tast_struct中的pid则由gettid系统调用来返回.
    在执行ps命令的时候不展现子线程,也是有一些问题的。比如程序a.out运行时,创建 了一个线程。假设主线程的pid是10001、子线程是10002(它们的tgid都是10001)。这时如果你kill 10002,是可以把10001和10002这两个线程一起杀死的,尽管执行ps命令的时候根本看不到10002这个进程。如果你不知道linux线程背 后的故事,肯定会觉得遇到灵异事件了。

    为了应付"发送给进程的信号"和"发送给线程的信号", task_struct里面维护了两套signal_pending, 一套是线程组共享的, 一套是线程独有的.
    通过kill发送的信号被放在线程组共享的signal_pending中, 可以由任意一个线程来处理; 通过pthread_kill发送的信号(pthread_kill是pthread库的接口, 对应的系统调用中tkill)被放在线程独有的signal_pending中, 只能由本线程来处理.

    当线程停止/继续, 或者是收到一个致命信号时, 内核会将处理动作施加到整个线程组中.

    当前Linux使用的就是NPTL版本。

    下面还有一个关于LWP的理解,有代码实现,我暂时没有时间敲,先记在这:http://www.iqiyi.com/v_19rr9txm6c.html




    展开全文
  • 线程的基本概念 引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。 线程最直接的理解就是“轻量...
  • 进程与线程

    2016-03-18 13:56:37
    进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。 进程的特征:动态性、...引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量。而引入线程,则是为了减小程序在并发执行
  • 线程 vs 进程

    万次阅读 多人点赞 2017-01-22 19:51:54
    进程与线程区别是很重要...引入进程之前–多道程序设计概述多道程序设计技术是操作系统最早引入技术,它设计思想是允多个程序同时进入内存并运行,其目的是为了CPU利用率,进而提高系统效率。特点多道程序设
  • 随着计算机系统结构发展,有些计算机还不断引入新...其目的是避免用程序直接修改程序中指令。 这对程序检查和流水线等技术应用均有好处。 有计算机采用堆栈实现程序调用指令和返回指令。 调用时将返回地
  • 进程是多处理程序中作为资源分配和独立运行的基本单位,进程实体由程序段、数据段、PCB三个部分组成,引入进程的目的是为了使进程实体能和其他进程实体并发执行 进程的三种基本状态:就绪状态、执行状态、阻塞状态 ...
  • 作业调度和进程调度区别

    千次阅读 2011-01-02 16:06:00
    处理机调度是操作系统主要功能之一,它实现策略决定了操作系统类型,其调度算法优劣直接影响整个系统性能。处理机调度任务是选出待分派作业或进程,为之...引入中级调度主要目的是为了提高内存利用率
  • 操作系统-进程和线程管理

    千次阅读 2017-08-18 22:24:21
    操作系统-进程和线程管理线程的基本概念引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。线程最...
  • 引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。 线程最直接的理解就是“轻量级进程”,它是一个...
  • Python3 多线程编程

    千次阅读 2019-09-05 18:34:02
    引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。 线程最直接的理解就是“轻量级进程”,它是一个...
  • 线程总结

    2016-08-24 18:15:01
    基本概念:引入进程的目的,是为了使多道程序并发执行,以提高资源利用率和系统吞吐量;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。线程最直接的理解就是“轻量级进程”,...
  • Android AIDL入门篇

    2017-01-06 17:09:09
    由于Android中两个进程间无法直接进行通信,但是有时候又会有这样需求,所以在此,我们引入了AIDL(Android接口定义语言),目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。这样我们就可以...
  • 内存处理是编程人员容易出现问题地方,忘记或者错误内存回收会导致程序或系统不稳定甚至崩溃,Java提供GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存...
  • 通过process对象获取和控制Node自身进程的各种信息。另外process是一个全局对象,在任何地方都可以直接使用。 部分属性 - process.pid 进程pid - process.versions node、v8、ssl、zlib等组件的版本 - process...
  • Oracle共享池

    2018-07-08 08:35:47
    库高速缓存Oracle引入库高速缓存的目的是共享SQL和PL/SQL代码。 服务器进程执行SQL和PL/SQL时,首先会进入库高速缓存查找是否有相同SQL,如果有,就不再进行后续编译处理,直接使用已经编译SQL...
  • 共享池

    2017-02-10 11:37:04
    库高速缓存Oracle引入库高速缓存的目的是共享SQL和PL/SQL代码。服务器进程执行SQL和PL/SQL时,首先会进入库高速缓存查找是否有相同SQL,如果有,就不再进行后续编译处理,直接使用已经编译SQL和
  • Oracle共享池 Oracle共享池(Share ...Oracle引入库高速缓存的目的是共享SQL和PL/SQL代码。 服务器进程执行SQL和PL/SQL时,首先会进入库高速缓存查找是否有相同SQL,如果有,就不再进行后续编译处理,直接...
  • (3)引入进程的意义是描述多道程序设计系统中程序的动态执行过程。 2、进程的定义及特征 (1)程序和进程的区别 (2)进程的五个基本特征:动态性、并发性、独立性、制约性、结构性 3、进程...
  • 1.引入多道程序的目的在于( A )。 A.充分利用CPU,减少CPU等待时间 B.提高实时响应速度 C.有利于代码共享,减少主、辅存信息交换量 D.充分利用存储器 2. 一个进程当前处于等待状态,则( D )。 A. 它可以被...
  • 4.3.6 在jdk1.5中,引入了泛型,泛型存在是用来解决什么问题。 4.3.7 这样a.hashcode() 有什么用,与a.equals(b)有什么关系。 4.3.8 有没有可能2个不相等对象有相同hashcode。 4.3.9 Java中HashSet内部...
  • 缓冲区的目的,是为了减少频繁系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前信息,为了减少这种损耗时间、也损耗性能系统调用,于是出现了缓冲区。...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    C#--微软.NET第一语言 本书着重介绍语言本身,比较少涉及应用,不错入门书,从头讲起,不怕不明白。 <<page 1>> page begin==================== 目 目目 目 录 录录 录 第一部分 C#语言概述.4 ...

空空如也

空空如也

1 2 3
收藏数 52
精华内容 20
关键字:

引入进程的直接目的