PHP底层内核源码深入浅出
2021-04-21 13:18:01 0 举报
AI智能生成
说起来可能不信 这一切都要从PHP源码说起,,我倒要看看 最后能连线成啥样
作者其他创作
大纲/内容
从c语言到PHP
Linux
内核
boot loader
grup
uboot
namespaces
cgroup
内存屏障
lib.so
操作级别
编译安装PHP
分支主题
分支主题
怎么变成解释性语言
变量 z_val
变量类型
zend_string
引用 refercence
引用计数
记录多少个zval指向同一个zend_value,新的zval+1,销毁zval减1,=0可以销毁
不是所有的类型都用引用计数
string
array
object
resource
reference
数组 zend_array
内存管理
PHP中的内存
垃圾回收
自动内存回收的不是垃圾
垃圾是value不再使用,但是引用还>0,自动回收机制cover不住的
循环引用的场景
垃圾回收器会把可能是垃圾的value收集起来,等达到一定数量之后才会回收
垃圾存在的类型
array
object
其他情况不会出现循环引用
垃圾回收期的判断依据
IS_TYPE_COLLECTABLE
疑问:标志是怎么打上去的
回收算法
垃圾由于成员引用自身导致的,对value的所有成员减1引用计数
写时复制
发生写时才进行深拷贝
并不是所有的value都可以复制
string
array
内存池
malloc,频繁分配、释放内存,产生内存碎片,降低系统性能
zendMM
减少内存分配及释放的次数
控制内存碎片的产生
参考tcmalloc/jemalloc重写
trunk、page、slot结构
slots
提供8,16,24,...3072等size的内存
每种size在一个或者若干个连续的page上分配
释放的内存放入对应尺寸的链表
pages
在trunk上连续分配
用bitmap标记空闲块,加速空闲块的查找
trunks
大于2M直接分配一个trunks
直接向系统要,不走malloc
内存回收时机
zval断开value的指向时,发现refcount=0,直接释放value
修改变量与函数返回时,会断开指向
unset()函数
Golang中的内存
Linux 中的内存
VM 语法分析
VM 语法分析
VM 编译器
handle
VM 执行器
Fast-cgi
多进程
多线程
协程
Goroutine
cli下 编程
面向对象底层内核实现
文件
FD
FPM
TCP/IP
信号
pipe
中断
上下文切换
虚拟内存
Socket
select
poll
epoll
aio
libevent.so
Nginx源码派生
数据结构
进程
进程通信
进程模型
整体架构
核心进程模型
cache进程模型
共享内存
slab机制
信号处理
事件管理
i/o多路复用
事件处理
epoll
请求服务流程
监听套接口
创建连接套字节
请求处理
数据相应
子请求
关闭连接
keepalive
延迟关闭机制
负载均衡
Golang源码派生
变量
子主题
表达式
内存管理
内存组件
Golang内存分配架构
基础元件
mspan
type mspan struct {
next *mspan
prev *mspan
startAddr uintptr // 起始地址
npages uintptr // 页数
freeindex uintptr
allocBits *gcBits
gcmarkBits *gcBits
allocCache uint64
state mSpanStateBox
spanclass spanClass 实际为int8,其中前七位为sizeClass类型,后一位判断是否包含指针
...
}
next *mspan
prev *mspan
startAddr uintptr // 起始地址
npages uintptr // 页数
freeindex uintptr
allocBits *gcBits
gcmarkBits *gcBits
allocCache uint64
state mSpanStateBox
spanclass spanClass 实际为int8,其中前七位为sizeClass类型,后一位判断是否包含指针
...
}
list
pages
golang内存管理中的基本单位,也是由页组成的,每个页大小为8KB,
mspan里面按照8kb*2n大小(8kb,16kb,32kb .... ),每一个mspan又分为多个object
mspan里面按照8kb*2n大小(8kb,16kb,32kb .... ),每一个mspan又分为多个object
objects 内存分配
当用户程序或者线程向 runtime.mspan 申请内存时,该结构会使用 allocCache 字段以对象为单位在管理的内存中快速查找待分配的空间
如果我们能在内存中找到空闲的内存单元,就会直接返回,当内存中不包含空闲的内存时,上一级的组件 runtime.mcache 会为调用 runtime.mcache.refill 更新内存管理单元以满足为更多对象分配内存的需求
如果我们能在内存中找到空闲的内存单元,就会直接返回,当内存中不包含空闲的内存时,上一级的组件 runtime.mcache 会为调用 runtime.mcache.refill 更新内存管理单元以满足为更多对象分配内存的需求
objescts
67个跨度类
对象大小从 8B 到 32KB,总共 67 种跨度类的大小、存储的对象数以及浪费的内存空间,以表中的第四个跨度类为例,跨度类为 4 的 runtime.mspan 中对象的大小上限为 48 字节、管理 1 个页、最多可以存储 170 个对象。因为内存需要按照页进行管理,所以在尾部会浪费 32 字节的内存,当页中存储的对象都是 33 字节时,最多会浪费 31.52% 的资源
跨度类资源
runtime.spanClass 是 runtime.mspan 结构体的跨度类,决定内存管理单元中存储的对象大小和个数
跨度类对于内存布局
mcache
runtime.mcache 是 Go 语言中的线程缓存,它会与线程上的处理器一一绑定,主要用来缓存用户程序申请的微小对象。每一个线程缓存都持有 67 * 2 个 runtime.mspan,这些内存管理单元都存储在结构体的 alloc 字段中
mcentral-中心缓存
mheap-大内存
初始化
核心单元
内存扩容
内存分配
mallocgc
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
mp := acquirem()
mp.mallocing = 1
c := gomcache()
var x unsafe.Pointer
noscan := typ == nil || typ.ptrdata == 0
if size <= maxSmallSize {
if noscan && size < maxTinySize {
// 微对象分配
} else {
// 小对象分配
}
} else {
// 大对象分配
}
publicationBarrier()
mp.mallocing = 0
releasem(mp)
return x
}
堆上所有的对象都会通过调用 runtime.newobject 函数分配内存,该函数会调用 runtime.mallocgc 分配指定大小的内存空间,这也是用户程序向堆上申请内存空间的必经函数
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
mp := acquirem()
mp.mallocing = 1
c := gomcache()
var x unsafe.Pointer
noscan := typ == nil || typ.ptrdata == 0
if size <= maxSmallSize {
if noscan && size < maxTinySize {
// 微对象分配
} else {
// 小对象分配
}
} else {
// 大对象分配
}
publicationBarrier()
mp.mallocing = 0
releasem(mp)
return x
}
微对象
0b-16b
小对象
16b-32k
确定分配对象的大小以及跨度类 runtime.spanClass
从线程缓存、中心缓存或者堆中获取内存管理单元并从内存管理单元找到空闲的内存空间;
调用 runtime.memclrNoHeapPointers 清空空闲内存中的所有数据;
大对象
>32k
运行时对于大于 32KB 的大对象会单独处理,我们不会从线程缓存或者中心缓存中获取内存管理单元,而是直接在系统的栈中调用 runtime.largeAlloc 函数分配大片的内存
runtime.largeAlloc 函数会计算分配该对象所需要的页数,它会按照 8KB 的倍数为对象在堆上申请内存:
申请内存时会创建一个跨度类为 0 的 runtime.spanClass 并调用 runtime.mheap.alloc 分配一个管理对应内存的管理单元
缓存和上限
在启动时初始化后 每一个处理器都会被分配一个线程缓存 runtime.mcache 用于处理微对象和小对象的分配,它们会持有内存管理单元 runtime.mspan
每个类型的内存管理单元都会管理特定大小的对象,当内存管理单元中不存在空闲对象时,它们会从 runtime.mheap 持有的 134 个中心缓存 runtime.mcentral 中获取新的内存单元,中心缓存属于全局的堆结构体 runtime.mheap,它会从操作系统中申请内存。
在 amd64 的 Linux 操作系统上,runtime.mheap 会持有 4,194,304 runtime.heapArena,每一个 runtime.heapArena 都会管理 64MB 的内存,单个 Go 语言程序的内存上限也就是 256TB。
设计原理
子主题
gc 管理
三色标记
并发增量
分代处理
接口
并发编程
并发调度
通道
缓存池
反射
子主题
Docker内核源码派生
Docker Client
Docke Deamon
Docker 网络环境
Deamon网络
容器网络
bridge桥接
hostmoshi
other
none模式
创建容器网络流程
启动容器网络流程
子主题
libcontainer模块
namespace
cgroup
镜像
rootfs
union mount
image
layar
镜像下载
解析镜像参数
配置认证信息
发送api请求
解析请求参数
创建配置 执行job
解析job
创建session对象
镜像存储
容器
容器创建
生命周期
启动容器
setupContainerDns
Mount
InitlizeNetworking
verifyDeamonSetting
prepareVolumensForContainer
SetupLinkedcontainers
子主题
收藏
收藏
0 条评论
下一页