进程与线程概述

版权声明:ssupdding https://blog.csdn.net/sspudding/article/details/88921032
什么是进程???

进程就是正在运行的程序。
是系统进行资源分配和调度的基本单位。例:I/O资源,内存资源…
每一个进程都有它自己的内存空间和系统资源

那么多进程的意义是什么呢?
可以在一个时间段内执行多个任务,可以提高CPU的使用率

什么是线程???

在同一个进程内可以执行多个任务,而这每一个任务都可以看成是一个线程。
所以线程就是程序的执行单元,执行路径。是程序使用CPU的最基本单位
线程是独立调度和分派的基本单位

单线程:程序只有一条执行路径
多线程:程序有多条执行路径

多线程的意义又是什么呢??
是为了提高应用程序的使用率,而不是提高程序的执行速度。

进程与线程的联系:一个程序至少有一个进程,一个进程至少有一个线程。

那么Java中启动main方法是进程还是线程呢???

java中main方法启动的是一个进程也是一个主线程,main方法里面的其他线程均为子线程。

为什么说是一个线程也是一个进程呢??

  • 一个java程序启动后它就是一个进程,进程相当于一个空盒(分配了内存空间、JVM虚拟机实例),它只提供资源装载的空间,具体的调度并不是由进程来完成的,而是由线程来完成的。
  • 一个java程序从main开始之后,进程启动,为整个程序提供各种资源,而此时将启动一个线程,这个线程就是主线程,它将调度资源,进行具体的操作。
  • Thread、Runnable的开启的线程是主线程下的子线程,是父子关系,此时该java程序即为多线程的,这些线程共同进行资源的调度和执行。

进程和线程的关系:

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,
所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

进程和线程的区别

  • 一个程序至少有一个进程,一个进程至少有一个线程。
  • 线程的划分尺度小于进程,使得多线程程序的并发性高。
  • 创建进程会给分配相应的内存空间,线程创建则不会给分配内存空间
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,
    由应用程序提供多个线程执行控制。(执行过程中的区别)
  • 进程与进程之间是相互隔离的,若一个进程崩溃,不会影响其他进程的正常执行
    线程是隶属于进程的,线程在进程间可以共享空间,一个线程的崩溃可能会影响到其他线程的正常执行
  • 从逻辑上看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。
    但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。(进程和线程的重要区别)

多线程编程的好处

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。
多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好

Java进程和本地操作系统的关系

Java中创建的进程和线程最终会映射到本地操作系统的进程、线程模型上

进程 线程
Java Runtime.exec thread
Linux fork / vfork pthread_create

注意: windows的进程线程模型不等于Linux进程线程模型

java跑在Linux上,跟踪下java进程,线程具体的系统调用,linux系统本身的fork和pthread_created调用对比,一模一样;
所以可以知道java进程/线程映射到了linux系统上。

在Linux系统利用strace跟踪进程的启动过程,并打印

  • java的Thread线程
clone(child_stack=0x7fc26391dff0, flags=CLONE_VM|CLONE_FS|
CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fc26391e9d0, tls=0x7fc26391e700, child_tidp
  • Linux的fork进程
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|
CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f15f4f809d0) = 4195
  • Linux的pthread_create线程
clone(child_stack=0x7f15f478bff0, flags=CLONE_VM|CLONE_FS|
CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|
 CLONE_SYSVSEM|CLONE_SETTLS|CLONE_P

Linux上 fork/pthread_create => clone(标志不一样)
进程和线程最终都会映射到内核、内核都会创建PCB(内核控制块)

小贴士

strace在linux下用来跟踪某个进程的系统调用
在solaris下,对应的是dtrace
在mac下,对应的命令是dtruss

dtruss:使用DTrace来监视系统调用。
dtruss 在进程进行系统调用的时候,进行输出。它就像DTrace版的truss,和truss相比,它插入一些信息。
值得注意的是只有在有root权限的终端下,可以使用DTrace。

  • 执行并监控"df -h"命令的系统调用
# dtruss df -h
  • 监控pid为1871的进程此时之后的系统调用
# dtruss -p 1871
  • 监控所有名为 "tar"的进程
# dtruss -n tar
# dtruss -f test.sh
  • 执行"date" 命令并输出cpu消耗和剩余时间
# dtruss -eo date

进程间通信——共享内存原理

任何语言写的代码都会产生两个东西:指令和数据

Java/C++/PHP/Python(高级语言) ===》 编译过程 = =》 JVM虚拟机/操作系统所能识别的汇编级指令 =》运行(操作系统/虚拟机/模拟器/解释器)

举例:

int a =data;
data(地址)找到地址对应的寄存器,将寄存器值在返回给 a(寻址)

这块就需要了解一下虚拟地址、物理地址、虚拟地址空间的基本概念简单来说,

物理地址就是真正存在的地址
编译阶段,上面的数据和指令所需要的地址 =》 虚拟地址 =》 虚拟地址空间(区分虚拟内存)

虚拟地址空间见下述详细解释:

以Linux 32位操作系统为例,平台上去运行一个进程的时候,Linux系统创建线程会分配内存空间大小为4G (2^32 = 4G)(虚拟地址空间)

进程的虚拟地址空间:
----------------------- 0x00000000
预留区
----------------------- 0x08048000
.text(指令) .rodata(常量区)
-----------------------
.data .bss (数据段)
-----------------------
.heap(堆)
-----------------------
.stack(栈)
----------------------- 0xC0000000
上面部分(用户空间user space)默认3G,下面部分(内核空间, kernel space)默认1G
内核
----------------------- 0xFFFFFFFF

用户空间与内核空间

  • 所有的进程,都有自己的用户空间(用户空间是隔离的),内核空间是共享的,栈线程私有的, 堆和数据段是线程共享的
  • Linux的进程和线程一样,进程和进程之间的用户空间是隔离的,线程在内核空间和进程是一样的,但是线程没有自己的
    用户空间,必须共享某个进程的用户空间
  • 从系统调用上,也可以清晰的看到,进程创建的clone没有CLONE_VM,
    线程创建的clone调用有CLONE_VM标志,代表共享父进程的虚拟地址空间
  • 程序运行的时候,执行指令和读写数据, 用的都是虚拟地址,指令和真正的数据是要往物理内存上放的

下面是运行磁盘上的程序
DP(disk page) =》VP(虚拟地址空间上的地址) =》物理内存上放的
虚拟地址 =》 映射到 =》 物理地址(一块物理内存)

进程间通信

  • 管道通信、消息队列、信号量
  • 共享内存
    进程1 =》 自己的虚拟地址空间 =》(多级页表映射) 映射的是物理内存块3
    进程2 =》 自己的虚拟地址空间 =》(多级页表映射) 映射的是物理内存块3
    最终多个进程映射带同一块物理内存上去了

猜你喜欢

转载自blog.csdn.net/sspudding/article/details/88921032