美文网首页
一文搞懂了进程与线程

一文搞懂了进程与线程

作者: 小马过河R | 来源:发表于2020-09-28 13:22 被阅读0次

    小马整理。

    进程和线程的区别:

    简而言之,一个程序至少有一个进程,一个进程至少有一个线程。

    非常直观的图

    进程:

    每个进程都有独立的内存地址空间;

    系统进行资源分配和调度的基本单位;

    进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享的,进程创建时分配,主要存放 new 创建的对象实例;

    进程里的方法区,是用来存放进程中的代码片段的,是线程共享的;

    在多线程 OS 中,进程不是一个可执行的实体,即一个进程至少创建一个线程去执行代码。

    对于PHP,所谓进程其实就是操作系统中一个正在运行的程序,在一个终端当中,通过 php 运行一个 php 文件,这个时候就相当于创建了一个进程,这个进程会在系统中贮存,申请属于它自己的内存空间,系统资源并且运行相应的程序。

    对于一个进程来说,它的核心内容分为两部分 :

    一个是它的内存,这个内存是这进程创建之初从系统分配的,它所有创建的变量都会储存在这一片内存环境当中;

    另一个是它的上下文环境,我们知道进程是运行在操作系统的,那么对于程序来说,它的运行依赖操作系统分配给它的资源,操作系统的一些状态。

    在操作系统中可以运行多个进程的,对于一个进程来说,他可以创建自己的子进程,当我们在一个进程创建出若干个子进程的时候,子进程和父进程一样,拥有自己的内存空间和上下文环境:

    如图:

    借用一下图片

    父子进程

    子进程会复制父进程的内存空间和上下文环境;

    子进程会复制父进程的IO句柄即fd描述符;

    子进程的内存空间与父进程的内存空间是独立,是互不影响的;

    修改子进程的内存空间并不会修改父进程或其他子进程的内存空间。

    例如:父进程通过fopen打开文件后得到一个IO句柄fd,子进程复制父进程后同样会得到这个fd。如果父进程和子进程同时对一个文件进行操作,会造成文件混乱,因此需要加互斥锁。

    例如:父进程中的变量x=1,父进程派生子进程后,子进程也会存在变量x=1,但是修改父进程中的变量x并不会影响子进程的变量x的值。

    总结:子进程只复制但并不会互相影响。

    为什么要有线程?

          每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大、请求响应效率低,因此操作系统引进线程。

    区别:

    本质:进程是操作系统资源分配的基本单位;线程是任务调度和执行的基本单位。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。

     内存分配:系统在运行的时候会为每个进程分配不同的内存空间,建立数据表来维护代码段、堆栈段和数据段;除了 CPU 外,系统不会为线程分配内存,线程所使用的资源来自其所属进程的资源。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。(如上图)

    资源拥有:进程之间的资源是独立的,无法共享同一进程的所有线程共享本进程的资源,如内存,CPU,IO 等。因进程间无法共享,所以会有解决进程间通信的问题,如下文。

     开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行程序计数器和栈,线程之间切换的开销小使得多线程程序的并发性高

    通信:进程间 以IPC(管道,信号量,共享内存,消息队列,文件,套接字等)方式通信 ;同一个进程下线程间可以共享全局变量、静态变量等数据进行通信,做到同步和互斥,以保证数据的一致性。这个概念的理解在很多多线程,协程等程序语言中非常重要。

    栈:为编译器自动分配和释放,如函数参数、局部变量、临时变量等等;

    堆:为成员分配和释放,由程序员自己申请、自己释放。否则发生内存泄露。典型为C使用new申请的堆内容;

    静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量

    执行过程:每个进程都有一个程序执行的入口,顺序执行序列;线程不能够独立执行,必须依存在应用程序中,由程序的多线程控制机制控制。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

    健壮性:每个进程之间的资源是独立的,当一个进程崩溃时,不会影响其他进程;同一进程的线程共享此线程的资源,当一个线程发生崩溃时,此进程也会发生崩溃,稳定性差,容易出现共享与资源竞争产生的各种问题,如死锁等。进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮(这也是为什么swoole和nginx都采用多进程模型的原因,一个master多个worker),但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    可维护性:线程的可维护性,代码也较难调试,bug 难排查。

    例子:

    swoole进程模型为例,我们来看swoole文档里对进程的描述。

    worker进程间资源不共享(全局变量)

    以上说到swoole是对进程模型而非多线程。

    协程可以简单理解为线程,只不过这个线程是用户态的,不需要操作系统参与,创建销毁和切换的成本非常低,和线程不同的是协程没法利用多核 cpu 的想利用多核 cpu 需要依赖 Swoole 的多进程模型(这么说来nginx也是多进程模型?是的。如下文。)。指定worker进程个数,进程起协程来处理请求,效率可观。在Server的set方法中增加了一个配置参数max_coroutine用于配置一个Worker进程最多同时处理的协程数目。因为随着Worker进程处理的协程数目的增加,其占用的内存也会增加,为了避免超出php的memory_limit限制,请根据实际业务的压测结果设置该值,默认为3000。

    Nginx 要保证它的高可用,高可靠性, 如果Nginx 使用了多线程的时候,由于线程之间是共享同一个地址空间的,当某一个第三方模块引发了一个地址空间导致的断错时 (eg: 地址越界), 会导致整个Nginx全部挂掉; 当采用多进程来实现时, 往往不会出现这个问题。

    必须通过提供的$this->request->get等来获取

    “全局变量:协程使得原有的异步逻辑同步化,但是在协程的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及static变量的一致性。”这句话是协程间资源不共享的意思么?像GO是用管道进行协程间的通信,达到并发执行处理(如起协程同时调用两个远程接口得到结果)。

    Java 编程语言中多线程是通过 java.lang.Thread 类实现的。

    Thread 类中包含 tid(线程id)、name(线程名称)、group(线程组)、daemon(是否守护线程)、priority(优先级) 等重要属性。



    彩蛋时间:

    PHP-FPM

    早期版本的 PHP 并没有内置的 WEB 服务器,而是提供了 SAPI(Server API)给第三方做对接。现在非常流行的 php-fpm 就是通过 FastCGI 协议来处理 PHP 与第三方 WEB 服务器之间的通信。

    比如 Nginx + php-fpm 的组合,这种方式运行的 fpm 是 Master/Worker 模式,启动一个 Master  进程监听来自 Nginx 的请求,再 fork 多个 Worker 进程处理请求。每个 Worker  进程只能处理一个请求,单一进程的生命周期大体如下:

    初始化模块。

    初始化请求。此处请求是请求 PHP 执行代码的意思,并非 HTTP 的请求。

    执行 PHP 脚本。

    结束请求。

    关闭模块。

    Swoole

    Swoole 采用的也是 Master/Worker 模式,不同的是 Master 进程有多个 Reactor 线程,Master 只是一个事件发生器,负责监听 Socket 句柄的事件变化。

    Worker 以多进程的方式运行,接收来自 Reactor 线程的请求,并执行回调函数(PHP 编写的)。启动 Master 进程的流程大致是:

    初始化模块。

    初始化请求。因为 swoole 需要通过 cli 的方式运行,所以初始化请求时,不会初始化 PHP 的全局变量,如 $_SERVER, $_POST, $_GET 等。

    执行 PHP 脚本。包括词法、语法分析,变量、函数、类的初始化等,Master 进入监听状态,并不会结束进程。

    Swoole 加速的原理

    由Reactor(epoll 的 IO 复用方式)负责监听Socket句柄的事件变化,解决高并发问题。

    通过内存常驻的方式节省 PHP 代码初始化的时间,在使用笨重的框架时,用 swoole 加速效果是非常明显的。


    参考文献:

    什么是线程?什么是进程?为什么要有线程?有什么关系与区别?

    相关阅读:

    php与swoole进程模型知多少

    编程语言中的堆栈和垃圾回收

    相关文章

      网友评论

          本文标题:一文搞懂了进程与线程

          本文链接:https://www.haomeiwen.com/subject/pwkduktx.html