往期文章:
linux 相关知识
Linux 内核空间与用户空间
- 为什么要区分内核空间与用户空间
-
- 用户空间:操作系统划分出的程序空间,内核空间:操作系统管理空间(进程)
-
- 代码处于用户空间与内核空间,并且两种模式在不停切换,如果调用操作系统内核的功能(如读写文件、进程管理、网络通信等),则在内核空间,否则不需要系统内核参与,只需CPU计算或内存消耗的代码处于用户空间(比如算法等)
程序是不能越过操作系统来操作硬件的,无法跨层调用
-
用户态 vs 内核态 指程序执行时的状态,当没有用到系统内核时,为用户态,反之,为内核态
-
如何从用户空间进入内核空间
-
处理器在任何制定时间点上的活动:
-
- 运行于用户空间,执行用户进程
-
- 运行于内核空间,处于进程上下文,代表某个特定的进行执行
-
- 运行与内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断(执行)
- 运行与内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断(执行)
Linux 的信号机制
- 软中断信号(signal,简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据
- 收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
-
- 1.对于需要处理的信号,进程可以指定处理函数,由该函数来处理(进程收到了某一个信号(编号) -> 找到对应的函数处理)
-
- 2.忽略某个信号,对该信号不做任何处理,就象未发生过一样。其中,有两个信号不能忽略:SIGKILL(kill信号)和SIGSTOP(终止进程)。kill并没有做杀死进程的操作,而是向操作系统发送“停止进程”的信号,kill pid 向操作系统发送 要kill 某个 pid的信号,操作系统会找到该pid所在的进程,并通知改进程终止,操作系统本身不直接回收进程,而是让进程自己结束,最后操作系统回收该进程所占资源
-
- 3.对该信号的处理保留系统的默认值,对大部分的信号的缺省操作是使得进程终止。此方法为缺省操作。进程通过系统调用signal来指定进程对某个信号的处理行为。
- 内核处理一个进程收到的信号时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号
用人话说:异步操作离不开信号通信,操作系统做完事情,需要通过信号机制通知进程
- 使用 kill -l 查看所有信号标志
Linux 中的 I/O 模型
同步阻塞I/O模型
- 用户空间的应用程序在执行系统调用之后会被阻塞,直到系统调用完成(数据传输完成或者出现错误)。此时应用程序只是处于简单的等待响应状态,不会消耗CPU
- 从CPU角度看,它是高效的
同步非阻塞I/O模型
- 非阻塞意味着如果I/O操作不能立即完成,需要应用层序多次调用直到任务完成
- 这可能非常低效,因为大多数程序必须忙等待或者尝试做其他事情直到数据可用
异步阻塞I/O模型
- 设备以非阻塞方式打开,然后应用程序阻塞在select系统调用中,用它来监听可用的I/O操作
- select系统调用最大的好处就是可以监听多个描述符,而且可以指定每个描述符要监听的事件(可读事件、可写事件、发生错误事件)
- select系统调用的主要问题是效率不高。虽然他是一个异步通知模型,但不见将其用于高性能I/O中
- 高性能场景一般使用 epoll 系统调用
异步非阻塞I/O模型
- 可以并行处理I/O操作
- 读请求会立即返回,表明读操作成功启动。然后应用程序就可以在读操作完成之前做其他的事情。当读操作完成时,内核可以通过信号或者基于线程回调函数来通知应用程序读取数据
- 单个进程可以并行执行多I/O请求是因为CPU处理数独要远大于I/O的处理速度。当一个或多个I/O请求在等待处理时,CPU可以处理其他任务或者处理其他已完成的I/O请求
linux select 模型
- select模型的关键是使用一种有序方式,对多个套接字进行统一管理和调度
- select模型缺点
-
- 单个进程能够监听的文件描述符的数量存在最大限制,通常是1024,虽然可以改变数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差(linux内核头文件定义 #define__FD_SETSIZE 1024)
-
- 内核/用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销
-
- select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能知道哪些句柄发生了事件,效率较低
-
- select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行I/O操作,那么之后每次select调用还是会将这些文件描符通知进程
linux epoll 模型
- epoll 模型优点:
-
- 支持一个进程打开大数目的socket描述符
-
- I/O效率不随FD(文件描述符)数目增加而线性下降
-
- 使用mmap加速内核与用户空间的消息传递
- epoll 的两种工作模式:
-
- LT(level triggered):水平触发模式,是默认的工作方式,并且同时支持 block 和 non-block socket。在这种方式下,内核会告诉程序一个文件描述符是否就绪,然后程序可以对这个就绪的FD进行I/O操作。如果程序不做任何操作,内核还是会继续通知。所以,这种模式编程出错可能性要小一些。
-
- ET(edge-triggered):边缘触发模式,是高效工作方式,支持 no-block socket。在该模式下,当描述符从未就绪变为就绪时,内核通过epoll通知程序,然后它会假设你已经知道了FD已经就绪,并且不会再为该FD发送更多更多的就绪通知,等到下次有新的数据进来时才会再次发出就绪事件。说白了,就是内核通知过的事情不会再通知,数据错过没读,程序自己负责,这种机制确实速度提高了,但风险较大
windows IOCP模型
Node.js 体系结构
Nodejs 结构
libuv
主要做三件事情:事件循环、I/O,进程
- libuv 是用C语言编写的,跨平台,非常适用嵌入到 js 和 python 这样的高级语言中
- libuv 使用异步事件驱动的方式,核心提供I/O的事件循环和异步回调
- libuv 的API包含时间、非阻塞网络、异步文件操作、子进程等
- 当程序在等待I/O完成时,CPU不会被等待中的程序阻塞,libuv提供的编程方式使得开发异步程序变得简单
- libuv文档
- libuv源码
-
- include/uv.h:C语言头文件,它库入口,不是可执行程序的入口,这里面定义的函数的索引
-
- src目录下为源码
V8引擎源码分析
渲染引擎及webkit体系结构
- 渲染引擎:能够将HTML/CSS/JS文本及相应资源文件转换成图像结构
- 渲染引擎种类:
-
- Tridend(IE)
-
- Gecko(FF)
-
- Webkit(Sarari,Chrome,Andriod浏览器等),处于独立的进程中
- Webkit(Sarari,Chrome,Andriod浏览器等),处于独立的进程中
js 引擎与渲染引擎
- 渲染引擎使用JS引擎的接口来处理逻辑代码并获取结果
- JS引擎通过桥接接口访问渲染引擎中的DOM和CSSOM
js引擎工作流程
V8 与 Javascript Core
- Javascript Core 引擎是Webkit中默认的js引擎,也是apple的开源项目,最初,性能不是很好,从2008年开始了一系列优化,重新实现了编译器和字节码解释器,使得引擎的性能有较大的提升。随后引入内嵌缓存、基于正则表达式的JIT、简单的JIT及字节码解释器等技术,Javascript Core引擎也在不断的迭代和发展
- Javascript Core与V8有一些不同之处,其中最大的不同就是V8新增了字节码的中间表示,并加入了多层JIT编译器(如简单JIT编译器、DFG JIT编译器、LLVM等)来优化性能
V8 源码分析
❤️ 加入我们
字节跳动 · 幸福里团队
Nice Leader:高级技术专家、掘金知名专栏作者、Flutter中文网社区创办者、Flutter中文社区开源项目发起人、Github社区知名开发者,是dio、fly、dsBridge等多个知名开源项目作者
期待您的加入,一起用技术改变生活!!!