进程与线程
- 进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程。
- 线程是进程的一部分,是程序执行中一个单一的顺序控制流程。
进程是资源分配的最小单位,线程是CPU调度的最小单位。
进程和线程的关系
- 进程中任意一线程崩溃都会导致整个进程崩溃
- 线程之间可以共享进程中的数据
- 当一个进程被关闭后,操作系统会回收进程占用的资源
- 进程之间的内容相互隔离,使OS中的进程互不干扰
Chrome浏览器架构图
Chrome浏览器包括:浏览器主进程、GOU进程、网络进程、多个渲染进程、多个插件进程。
- 浏览器主进程负责界面显示、用户交互、子进程管理、存储
- 渲染进程:将HTML、CSS、JS转换为用户可以与之交互的网页
- GPU进程:绘制UI界面
- 网络进程:负责网络资源的加载
- 插件进程:负责插件的运行。因为插件容易奔溃,所以将插件 独立出来,防止插件崩溃对浏览器和页面造成影响
打开一个网页时,至少需要4个进程。
虽然多线程造就了浏览器的稳定性、安全性,但也带来了一些问题,例如:
- 更多的资源占用:每个进程都会包含公共基础结构的副本,意味着浏览器会消耗更多的资源
- 更复杂的体系结构:耦合性高、扩展性差
进程和线程的区别
- 进程可以看作独立的应用,线程不能
- 进程是cpu资源分配的最小单位,是能拥有资源和独立运行的最小单位;线程是cpu调度的最小单位,线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程。
- 线程可以共享同一进程中的资源;进程必须通过协助进行通信
- 进程切换比线程切换开销大。线程切换不会引起进程的切换,但某个进程中的线程切换到另一个进程中的线程时,会引起进程切换
- 创建和撤销进程时,系统都要为其分配或回收资源
浏览器渲染进程的线程有哪些
20
(1) GUI渲染进程:
负责渲染页面,解析HTML、CSS,构建DOM树。当页面发生重绘或回流时,该线程就会执行。
(2) JS引擎线程:
负责解析、执行JS脚本,一个页面无论什么时候都只有一个JS引擎在运行JS脚本。
GUI渲染进程和JS引擎进程是互斥的,当JS进程执行时GUI引擎会被挂起,等待JS进程空闲再执行;但如果JS进程执行的时间过长,会造成页面渲染不连贯,导致页面渲染加载阻塞。
(3) 事件触发线程:
用于控制事件循环,当JS引擎进程执行到如setTimeout、鼠标事件、AJAX异步请求等操作时,会把对应的任务添加到事件触发线程中,事件触发线程会把这些事件添加到一个事件队列里,等待JS引擎进程处理。
(4) 定时器触发线程:
因为JS引擎是单线程的,如果遇到线程阻塞可能会影响定时器的准确性,因此使用单独的定时器线程,计时完成后加入任务队列等待JS引擎空闲后执行。
(5)异步请求线程:
进行异步请求连接后浏览器会开一个线程将请求后的回调函数加入事件队列中,等待JS引擎空闲后执行。
进程之间的通信方式
- 管道通信
管道就是操作系统开辟的一段缓冲区,进程A可以将需要交互的数据拷贝到这段缓冲区中,进程B就可以读取了。
管道通信的特点:
- 面向字节流的
- 只能单向通信
- 只能适用于父子进程
- 管道内部提供了同步机制
-
消息队列通信
消息队列就是一个消息的列表,用户可以在消息队列中添加、读取消息。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法,但是数据块的最大长度受到了限制,如果进程需要频繁地读取队列中的数据到内存中,就相当于将一个进程拷贝到另一个进程中,这比较耗费时间。 -
信号量通信
信号量的本质就是一个计数器,用来实现进程之间的互斥与同步。例如信号量的初始值是 1,然后 a 进程来访问内存1的时候,我们就把信号量的值设为 0,然后进程b 也要来访问内存1的时候,看到信号量的值为 0 就知道已经有进程在访问内存1了,这个时候进程 b 就会访问不了内存1。所以说,信号量也是进程之间的一种通信方式。 -
共享内存
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问(使多个进程可以访问同一块内存空间)。 -
套接字通信
例如我们平时通过浏览器发起一个 http 请求,然后服务器给你返回对应的数据,这种就是采用 Socket 的通信方式了。
僵尸进程和孤儿进程
- 孤儿进程:父进程退出了,而它的一个或多个子进程还在运行,那么这鞋子进程就是孤儿进程,他们将被init进程收养,并由init进程对它们完成状态收集工作。
- 僵尸进程:子进程先比父进程结束,而父进程没有释放子进程占用的资源,此时仍然认为子进程还在系统中,这种进程称为僵尸进程。
死锁产生的原因?如何解决
死锁就是多个进程在运行过程因为争夺某一资源而造成的一种循环等待。
系统中的资源可以分为可剥夺资源和不可剥夺资源。
产生死锁的原因:
- 资源竞争:
- 竞争不可剥夺资源。例如,系统中只有一台打印机,进程P1正在使用,P2进程若请求打印机资源将会被阻塞。
- 竞争临时资源。临时资源包括信号、消息、硬件中断等。通常消息通信顺序不当会产生死锁。
- 进程的推进顺序非法:
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如:当P1需要R2资源而P2又需要R1资源时,就会发生相互等待的情况,这时就会发生死锁。
产生死锁的必要条件
- 互斥条件:某段时间内某资源仅为一进程所占用。
- 请求和保持条件:进程因请求资源时发生阻塞而对已获得资源保持不放时。
- 不剥夺条件:进程获得资源的未使用完成前不能剥夺,只能在使用完成时由自身释放。
- 环路等待条件:在发生死锁时,必然存在一个进程——资源的环形链。
预防死锁的方法
- 资源一次性分配:一次性分配所有资源,这样就不会再有请求了(破坏请求条件)。
- 资源有序分配:每个进程按照编号递增的顺序进行请求资源,释放则相反(破坏环路等待条件)
- 可剥夺资源:当某进程获得了部分资源,若得不到其他资源,则释放当前拥有的资源(破坏不可剥夺条件)
- 如果只有一个资源的到不到分配,那么就不给这个进程分配其它资源(破坏请求保持条件)
如何实现浏览器内多个标签页之间的通信
多个标签页之间的通信,本质上都是通过中介者模式来实现的,因为标签页之间无法进行通信,所以应该找一个中介,进行数据通信。
- localStorage:我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
- postMessage:用于将一条消息放入到消息队列中。消息队列里的消息通过调用GetMessage和PeekMessage取得。使用postMessage必须要获得对应标签页的引用。
- websocket:因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
- shareWorker:shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。