CS_OS
0.1 · Scheduler#
待调度任务:线程,处于执行/未执行状态,有不同优先级
资源:CPU时间,同一时间只能执行一个任务
调度开销:上下文切换
0.1.1 · 调度方式
- 抢占式Preemptive:允许任务在执行过程中被调度器挂起。
- 协作式Cooperative:也叫非抢占式调度,是指当前运行的任务通过自身代码逻辑__主动通知调度器__让出资源。
任务执行时间长,协作式会使得部分任务饿死;任务执行短,协作式可以带来较少任务切换而降低开销,提高调度性能。
一般以抢占式为主,同时支持协作式。
0.1.2 · 任务再分配调度
工作分享:调度器创建任务时,将一部分任务分给其他调度器; 工作窃取:当前调度器的资源未充分利用时,去其他调度器窃取一些待执行的任务。
工作窃取只有在当前调取器的资源未充分利用时,才窃取,开销更小。
0.1.3 · 调度器设计
单调度器:串行调度 多调度器:并行调度
状态模块:收集信息,为调度提供上下文,包含资源状态,任务状态,任务队列等… 决策模块:根据状态模块提供的上下文进行调度决策;1)确定任务调度顺序;2)确定调度资源;3)无资源时,选择牺牲的抢占对象。
0.1.4 · Linux Process Scheduler#
task_struct可以代表进程,也可以代表线程。
0.1.4.1 · 类型
长期调度:任务调度,平衡I/O密集型和CPU密集型任务,最大化利用CPU与I/O设备; 中期调度:移除不活跃的进程,释放资源; 短期调度:从就绪队列中选择进程,进行执行。
0.1.4.2 · 短期调度
- 初始调度器:简陋,同时最多处理64个任务
- O(n)调度器:最坏情况下会遍历所有任务
- O(1)调度器:通过运行队列、优先数组实现常数时间内完成调度
- CFS完全公平调度器:为不同的进程提供完全公平性 https://draveness.me/system-design-memory-management/
0.2 · 内存管理
内存分配器:处理用户程序内存分配需求; 垃圾收集器:标记内存中的内存且回收不需要内存; 用户程序:通过分配器创建内存对象、更新对象持有指针;
0.2.1 · 内存布局
虚拟内存:栈内存、堆内存、未初始化变量区、数据区、代码区;
静态内存:编译期确认、固定、无运行时、不支持递归;使用绝对地址寻址,不需要管理; 栈内存:支持递归,固定,栈指针,栈溢出;后进先出,调用函数而加入栈,函数完成而销毁栈,不需要管理; 堆内存:按需分配,可变大小,内存泄漏(未释放),悬挂指针(提前释放),需要管理;
0.2.2 · 堆内存管理
手动管理:调用内存管理API 自动管理:垃圾回收器
对象头: 头,对象,对齐; 对象头与对象放一起,或者因为影响局部性而单独一片区域放对象头;
0.2.3 · 内存分配
线性分配器: 回收的内存无法利用,需要搭配合适垃圾回收算法:标记压缩、复制回收、分代回收;
空闲链表分配器: 遍历空闲内存块,找到足够大的内存快,分配给用户程序; 首次适应,循环首次适应,最优适应,隔离适应;
0.2.4 · 垃圾回收
https://draveness.me/system-design-memory-management/
https://en.wikipedia.org/wiki/Garbage_(computer_science) 垃圾包括对象,数据,在未来的计算中不会被使用,需要交回堆在未来复用。
语法垃圾、语义垃圾: 语法垃圾,程序内部从根对象无法达到的对象或数据;使用GC回收; 语义垃圾,永远不会被程序访问到的对象或数据;使用编译器识别部分语义垃圾,不使用的变量等。
收集器性能:吞吐量,最大暂停时间; 吞吐量:垃圾收集器在执行阶段的速度,单位时间的标记与清理内存的能力;HEAP_SIZE / TOTAL_GC_TIME; 或者程序运行总时间除以GC运行总时间,GC是额外的开销; 最大暂停时间:某些阶段触发STW,用户程序不能执行,影响程序处理请求; 垃圾回收阶段用户程序不能执行,但是垃圾标记可以和用户程序并发执行;
对象头、对齐出现的缝隙,都是开销;
收集器类型: 直接垃圾回收:引用计数; 跟踪垃圾回收:标记清理、标记压缩、复制垃圾回收;
引用计数: 改变对象之间的引用关系时,会修改对象之间的引用计数; 每个对象都记录了当前有多少个对象指向了该对象; 当该对象的计数为0时,释放该对象空间。 对象头中添加引用计数。 问题:循环引用、递归回收(对象被释放,递归该对象的引用将被引用对象的计算器减去1);
标记清除:标记、清除两个阶段; 标记,使用DFS或BFS算法扫描堆中的存活对象;从根节点出发,沿着引用遍历堆中的全部对象,能访问到即存活对象,不能访问到即垃圾; 清除,在标记后,回收内存中的垃圾;遍历堆中所有对象,释放垃圾,以链表串联起来; 对象头中添加标记头,但是会与操作系统的写时复制不兼容,虽然对象没有修改,但是对象头会被修改;可以使用位图,对象存活标记与对象分别存活; 空闲链表分配器,容易有内存碎片。
标记压缩:标记、压缩两个阶段; 标记同上; 压缩,将所有存活对象紧密排列;挤出存活对象之间的空隙;
复制垃圾回收: 堆分两个大小相等的区域;左侧分配,右侧垃圾回收; 左侧内存不足时, 1)复制存活对象到右侧, 2)设置对象转发地址(左侧->右侧),3)修复指针(右侧->左侧),4)释放左侧内存
0.2.5 · 高级垃圾回收算法
0.2.5.1 · 分代垃圾回收:
大多数对象会在生成后,立马变成垃圾,只有少数对象可以存活很久; 分为青年代、老年代:对象初始化后进入青年代,经过多轮GC后,未被回收进入老年代; 青年代回收只会扫描部分堆,减少GC时扫描的堆大小与STW时间; 问题:跨代引用:写屏障、卡表?
0.2.5.2 · 标记区域收集:
结合标记清除、复制垃圾回收;使用前者追踪堆中的存活对象,后者减少碎片; 拆分堆未特定大小的块,根据用户申请内存大小去找内存块;
0.2.5.3 · 增量并发收集:三色抽象,垃圾回收屏障(插入/删除写屏障)
增量:增量的标记、清除垃圾 并发:利用多核,在用户程序执行时,并发的标记、清除垃圾 使用屏障技术保证并发垃圾回收的正确性; 增量,将较长的暂停时间切分未多个更小的GC时间片; 与三色标记一起使用,在垃圾收集开始前,打开写屏障,用户对内存的修改都会经过写屏障,保证对象关系中的三色不变性; 并发收集使得收集工作尽量的并发执行; 问题:标记未垃圾的对象,重新被其他对象引用;
0.2.5.3.1 · 三色抽象:
白色:垃圾,可能被回收 灰色:活跃对象,存在指向白色对象的指针,收集器会扫描子对象 黑色:活跃对象,不指向任何对象,从跟对象可达
- 根对象标记为灰色
- 扫描灰色指向的白色,白色变灰色,原灰色变黑色
- 重复2至无灰色对象,结束 需要使用STW
强三色不变性:黑色不指向白色,黑色只指向灰色或黑色; 弱三色不变性:黑色可以指向白色,但是一定有一条路径,经灰色对象可达此白色对象;
0.2.5.3.2 · 屏障技术:
https://en.wikipedia.org/wiki/Memory_barrier
在内存屏障前的操作,一定先于内存屏障后的操作;
垃圾收集中的屏障技术,实在用户读取/创建/更新对象时执行的一段代码; 根据操作分为读/写屏障,读屏障对性能影响很大,一般使用写屏障;
插入写屏障:
writePointer(slot, ptr):
shade(ptr)
*field = ptr
新增对一个对象的指向:在 *slot = ptr 操作时,如果ptr是白色的,使用shade(ptr)把ptr变为灰色; 相对保守,是可能存活的对象都标记为灰色,满足强三色不变性;
删除写屏障:
writePointer(slot, ptr)
shade(*slot)
*slot = ptr
改变对一个对象的指向时(删除原指向),这个对象变灰色;
1 · 内存
物理内存、虚拟内存、局部性原理(时间/空间)
链接器为变量和函数分配地址
操作系统管理虚拟内存到物理内存的映射
分配到虚拟内存后,并未映射到对应的物理内存;只有在对该虚拟内存读写时,操作系统才会真正分配物理内存 内存以页为单位,在虚拟内存中连续的内存,在物理内存不一定连续
缺页中断发生在映射过程中
映射过程由CPU的内存管理单元(MMU)自动完成,但是依赖操作系统设置的页表
虚拟空间的每一个页在页表中都有一个页表项(PTE)对应 PTE记录了虚拟内存页对应的物理内存页起始地址
页大小为4KB,每个页表项4B,一个页可以存1024个页表项,1个页表项对应4KB,那么1024个对应4MB;
页目录表,管理页表,每一个页目录表项(PDE)对应一个页目录表,构成多级页表结构
CPU如何通过虚拟地址,找到物理地址:
- 确定页目录基址,每一个CPU都有一个页目录基质寄存器,存着最高级页表基地址
- 定位页目录项,地址的高10位确定页目录项,因为2^10=1024,一个页表刚好存1024个页表项
- 定位页表项,中间10位定位页表项,定位后就能知道物理内存起始位置
- 确定物理地址,使用剩余的位数表示页内偏移
页面换入换出:局部性原理,在物理内存足够时,尽可能让多的页驻留物理内存中
实模式:直接访问物理内存 保护模式:使用虚拟内存,将每个进程的地址空间隔离开,同时页表项中有多种权限保护标志
段寄存器:cs、ds、es、gs、ss等
实模式,记录段基地址:物理地址 = 段寄存器: 段内偏移 = 段寄存器 << 4 + 段内偏移
ds 数据段起始位置; cs 代码段起始位置
保护模式,段选择子,记录GDTR下标:虚拟地址 = 段寄存器: 段内偏移 = GDTR[段寄存器] + 段内偏移,由MMU映射为物理地址
GDT结构:段基址、段长度、属性P—是否在内存中存在、DPL—描述符特权级、
S—1为数据/代码段 0为系统段/门描述符(门是提供切换特权级的机制,有调用、陷阱、中断、任务等)、
G—段颗粒度 0单位字节 1单位4KB也就是1页,段界限20位,段最大长度2^20*4KB=4GB、
Type—描述符类型
段管理: 按功能分割内存空间,不同段有不同读写权限与特权级,提供安全性; 但段长度不能固定,以段为单位进行内存分配/回收,难于设计数据结构,会造成内存空间浪费; 页管理:不按功能分割,按固定大小分割,易于分配回收;段式管理所能提供的安全性,在现代 CPU 上也可以被页表项中的属性替代; 现代的操作系统都是采用段式管理来做基本的权限管理,而对于内存的分配、回收、调度都是依赖页式管理。
中断描述符表IDT: CPU 与外设之间的协同工作是以中断机制来进行的:
- 外设控制器向CPU发起中断请求
- CPU接受到请求后,把当前的寄存器状态全部保存好
- 调用中断服务程序(由操作系统定义) IDT基地址存储在idtr寄存器中,IDT本质是一个数据,每一个中断有一个编号对应,称为中断向量号,由CPU提前分配; https://www.csie.ntu.edu.tw/~wcchen/asm98/asm/proj/b85506061/chap4/overview.html
内存布局: 代码段、数据段(已经初始化且不为0的全局变量与静态变量)、BSS段(未初始化全局变量与静态变量,只记录大小)、堆/栈空间(程序运行中的临时数据,不是磁盘中加载进内存; 加载的共享库内存空间、共享内存段(IPC)、内存映射文件
磁盘程序,每一个单元称作section,readelf -S;内存镜像,每一个单元称作segment,readelf -l;
linux进程内存布局: 32位机器,每个进程都有4GB寻址能力,默认高1GB给内核,剩余为用户空间;
64位机器寻址空间为2^48,即256TB,低128T为空户空间,高128T为内核空间;64位->2^64为16EB,中间的空间的不可访问由CPU来保证;
申请堆空间:内核维护一个brk变量,指向堆的顶部,brk的实际大小决定了堆的大小; sbrk和mmap系统调用可以修改堆的大小;
栈管理: 调用函数,分配一部分空间,称作栈帧,本质上是一个函数的活动记录。 先创建的最后才销毁,后创建的最先被销毁,先入后出。
进程是分配资源的单位,线程是执行的单位,两者由操作系统调度; 协程是比线程更轻量的执行单位,由用户程序调度; 切换:将上下文保存在特定位置,切换到待执行的执行单位;
fork时,进程栈的写时复制与切换?? 用户态和内核态切换,是两个栈的切换,发生中断,CPU去特定结构中取得stack段选择子和栈顶指针,送入ss寄存器和rsp寄存器,就完成了一次栈切换;
每个变量和函数都有自己名称,称作符号,链接器将符号转换成地址:
- 静态链接,生成binary时
- 动态链接,加载进内存时,或运行期间解析符号 链接器两步链接: 1.合并section 2.重定位
局部变量的内存分配与释放,在运行时通过 %rbp 的改变来进行的,内存地址不需要链接器来操心; 静态函数与同一个编译单元内函数相对位置保持不变,所以静态函数的调用地址在编译阶段可以确定; 外部变量/外部函数/全局变量/全局函数/静态变量(与静态函数分属不同段),由链接器确定地址;
07 动态链接的重定位发生在加载期间或运行期间,实现依赖于地址无关代码; 常用公共函数放于一个文件,只被加载到内存一次;linux中的so,windows中的ddl; 通过虚拟内存映射到物理内存,使得共享库可以在不同进程中的虚拟内存中的地址不同; 地址无关代码(Position Independent Code, PIC):全局偏移表 (Global Offset Table, GOT):每个进程私有,一个中间层,间接调用真实地址;
08 延迟绑定:在函数第一次被调用的时候再和函数地址绑定; patch code:在生成call指令的时候,目标地址为内部用于解析符号的函数,在CPU调用此函数时候,则调用符号解析函数,加载所需的目标函数。 动态库的延迟绑定:patch 的对象不是指令,而是 GOT 中的一项。
使用系统调用分配大块内存,在用户态分割成更小块以便使用; malloc/free、 分桶式内存管理:多个链表,对于单个链表,内存大小相同; 伙伴系统:某大小内存块数量不够时,向上查找,找到后可能执行分割内存;
页中断:
- 页面未映射 -> 分配物理页面,在页表中设置好映射
- 页面内容在磁盘中 -> 从磁盘中将数据按页读入内存
- 写只读页面 -> 如果写时复制,则复制一页,标记可写;否则写异常,触发SIGSEGV
- 没有访问权限 -> 给进程发送SIGSEGV fork:共享映射,但只读,子或父修改时,复制一份,改为读写,原一份在被访问时,改回读写;
13 存储器主要分为寄存器、缓存和主存: RS锁存器/D锁存器/D触发器 SRAM快贵/DRAM慢廉
缓存:程序的空间局部性与时间局部性原理 处理得当,命中率达到70~90%,使得整个存储系统的性能接近寄存器,成本接近内存; 多核芯片上:集中式缓存/分布式缓存/混合式缓存 cache line: 缓存进行管理的最小存储单元,由内存按cacheline加载进缓存 直接相连映射/全相连映射/组组相连映射 LRU替换策略 缓存缺失: false sharing
15 多核系统下,缓存一致性问题: 缓存写策略:写回(当缓存被替换时,若被修改,才写回)、写直达(修改立刻写回); 某个CPU修改缓存值,其他CPU对缓存副本更新策略:写更新(前者写,立马请求后者更新)、写无效(前者写,后者将副本设置为无效); 要写的数据不在缓存,是否加载进缓存,分为两种写策略:写分配、写不分配; 缓存一致性:同一个数据,在每个CPU的私有缓存中副本是相同的。 MESI协议
16 cache: 储存的信息是副本,加速查找 buffer:蓄水池,缓冲 内存屏障:前边读写操作未完成,后面的读写操作不能开始 写屏障:屏障前后的写操作都不能翻过屏障 读屏障:屏障前后的读操作都不能翻过屏障 单向屏障:
非一致性访存:不同的 CPU 访问不同地址主存的速度各不相同
18 java volatile
GC: mutator, collector 根引用:不在堆内,却指向堆里;根引用指向的对象,为活跃对象; GC的分配/回收效率,是否产生内存碎片,空间利用率,是否停顿,实现复杂度; GC分类:引用计数/基于可达性分析;
scavenge: 基于copy 活跃对象从 From空间 -> To空间 根引用:栈上引用、全局变量等 遍历引用图:,深度优先的非递归写法需占用额外空间,但有利于提高业务线程运行期的缓存命中率。而广度优先相反,不利于运行期的缓存命中,但算法的执行效率更高。 Scavenge 算法
分代算法:年轻代copyGC,老年代使用Mark-Sweep Mark: 遍历对象,标记活跃对象;Sweep: 遍历整个堆,将未标记区域回收; CMS:并发标记清除:引入三色标记:写屏障,写入引用时,直接标记引用对象为灰色,可能导致应回收垃圾未被回收;或,被引用对象由黑变灰色;
分区回收算法: 删除屏障:删除有引用白色对象的对象,将白色对象标记成灰色
23 无暂停GC 以ZGC为例,最大停顿时间不超过 10ms; 对象转移最耗时,在应用线程运行时,GC线程并发进行对象转移; wirte barrier 主要是通过拦截写动作,在对象赋值时加入额外操作。read barrier ,就是在对象读取时加入额外操作。
24 python,go GC
1.1 · I/O#
unix网络编程 https://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/toc.html https://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.html
I/O Models: https://rickhw.github.io/2019/02/27/ComputerScience/IO-Models/
阻塞/非阻塞:请求在等待结果的状态 同步/异步:用户程序与内核的通信模式
syn: 同步,主动等待或主动轮询。include: block nonclock IO-multiplexing asyn : 异步,等待对方消息通知你,回调函数。 A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes. An asynchronous I/O operation does not cause the requesting process to be blocked.
block & non-block & IO-multi: step1: Waiting for the data to be ready; 内核等待数据,区别阻塞/非阻塞; step2: Copying the data from the kernel to the process; 线程从内核复制数据,区别同步/异步;
socket:1st step, waiting for data to arrive on the network, waiting for data to arrive on the network. 2nd step, copying this data from the kernel’s buffer into our application buffer.
‘block’ block from step1 to step2; ‘non-block’ check in step1 and block in step2; ‘IO-multiplexing’ call to select then block, waiting for one of many socks to become readable, then copy the data to the app buffer;
有A,B,C,D四个人在钓鱼,钓鱼分为1)知道鱼上钩了,2)拉杆两个动作: block: A用的是最老式的鱼竿,一直守着(阻塞),等到鱼上钩了,再拉杆; non-block: B的鱼竿有个功能,能够显示是否有鱼上钩,所以B就和旁边的MM聊天,隔会再看看(非阻塞)有没有鱼上钩,有的话就迅速拉杆; IO-multiplexing: C用的鱼竿和B差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,他就将对应的鱼竿拉起来; asyn: D是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了(异步),就给D发个短信。
1.1.1 · 同步
阻塞Blocking: 线程在访问文件或网络资源时,会因发起了内核的系统调用被挂起,内核会检查文件描述符是否可读,当文件描述符中存在数据时,内核会将数据复制给线程并交回控制权,线程从挂起状态切换成可运行状态等到内核调度运行。
非阻塞Non-blocking: 线程在访问文件或网络资源时,因文件描述符是非阻塞的,线程在检查数据是否可读的阶段是非阻塞的。此模型需要线程不停的通过轮询(polling)的方式检查文件描述符是否可读。 但之所以属于同步I/O,是因为在最终读取数据(recvfrom)时需要从内核态中拷贝数据(recvfrom)到用户态中,这个阶段线程依旧被阻塞住无法处理其他指令。
多路复用Multiplexing: 同时访问多个文件描述符,这很适合构建高并发的Web服务器或中间件。但此模型会在检查文件描述符时会被阻塞(select),并且在读取数据(recvfrom)时也会被阻塞。和多路复用模型相似的是使用多线程和阻塞I/O,但当线程产生很多时会消耗大量的内存资源以及线程调度产生的上下文切换开销,所以多路复用模型一般只使用单线程模型。
信号驱动Signal Driven: 和上面的模型区别在于,之前的模型都需要线程主动轮询,信号驱动模型需要监听内核的SIGIO事件,通过注册事件处理函数,之后线程可以继续执行其他任务。当数据可读时,线程处理函数会以阻塞的方式从内核态复制数据到用户态。
1.1.2 · 异步
此模型和同步模型最大的区别在于,不仅在获取文件操作符时不会被阻塞,数据从内核态复制到用户态也不会被阻塞,因为内核会去做这个复制数据的工作,线程只需要在回调函数中使用数据即可。
file api:https://programmer.group/file-io-programming-under-linux.html
fd: https://en.wikipedia.org/wiki/File_descriptor
https://hechao.li/2022/01/04/select-vs-poll-vs-epoll/
https://jvns.ca/blog/2017/06/03/async-io-on-linux—select—poll—and-epoll/
1.2 · r#
通用寄存器(数据寄存器、指针寄存器、索引寄存器), 控制寄存器, 段寄存器 数据寄存器:算数、逻辑、其他操作; AX BX CX DX 指针寄存器:指令指针IP(待执行指令位置偏移,与段寄存器一起确定完整指令位置)、栈指SP(程序栈偏移,与段寄存器一起确定当前数据或地址的位置)、基指针BP(主要引用传递给函数的参数变量) 索引寄存器:源寄存器SI、目的寄存器DI 控制寄存器:指令寄存器加标志寄存器一起可以看作控制寄存器 段寄存器:代码段CS、数据段DS、栈段SS、ES、FS、GS
1.3 · sys call#
- 系统调用数字 —>> EAX
- 把系统调用参数放入 EBX ECX EDX ESI EDI EBP,如果超过六个参数,第一个参数的内存地址储存在EBX
- 调用相关中断(80h)
- 结果通常返回在 EAX
1.4 · 寻址模式
寄存器寻址: 立即数寻址: 内存寻址:直接内存,偏移,间接偏移
1.5 · 变量
汇编定义命令,分配内存空间;
[variable-name] define-directive initial-value [,initial-value]...
DB/DW/DD/DQ/DT(1/2/4/8/10 bytes)
分配未初始化变量: RESB/RESW/RESD/RESQ/REST(1/2/4/8/10 bytes)
TIMES命令,多次初始化相同的值,比如初始化数组
1.6 · 常量
EQT %define %assign
CONSTANT_NAME EQU expression
%assign TOTAL 10(define numeric constants, allows redefinition)
%define PTR [EBP+4](defining both numeric and string constants)
1.7 · 算术、逻辑
1.8 · 条件
无条件跳转:jmp指令 JMP label
有条件跳转:j
判断条件,cmp指令,两操作数之间比较但不修改原有的操作数: CMP destination, source
1.9 · 循环
可以使用jmp指令实现循环(配合一个变量的递减)
loop指令:LOOP label ecx寄存器存放循环的次数
mov ECX,10 l1: <loop body> loop l1
1.10 · 数字、字符串、数组
https://stackoverflow.com/questions/15191178/how-do-ax-ah-al-map-onto-eax In x86 assembly, AL is the least significant byte(8 bits) of eax register.
https://www.felixcloutier.com/x86/movups movups: Move Unaligned Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/movaps movaps: Move Aligned Packed Single-Precision Floating-Point Values
[ATT汇编]后缀字母:
b字节=1字节;
w字=2字节;
l双字=4字节;
q四字=8字节
*[s:0-59:, - * /] *[m:0-59:, - * /] *[h:0-23:, - * /] *[day of month:1-31:, - * ? / L W C] *[m:1-12:, - * /] *[day of week:1-7:, - * ? / L C #] *[y:可空:, - * /] cmd
2 · virtual memory#
虚拟内存:中间转一层访问物理空间 indirection: 任何计算机问题都可以通过引入新的一层被解决
3 个问题:
- 没有足够的空间
- 多个程序,导致空间不连续
- 多个程序互相写内存
映射虚拟内存到真实内存: 页表 地址翻译
映射部分程序地址空间到硬盘,当需要的时候加载到内存。 性能相比直接访问内存,慢了一些。硬盘比内存慢了 1000X 倍。
隔离进程。
3 · ┌───────────── minute (0 - 59)#
4 · │ ┌───────────── hour (0 - 23)#
5 · │ │ ┌───────────── day of the month (1 - 31)#
6 · │ │ │ ┌───────────── month (1 - 12)#
7 · │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;#
8 · │ │ │ │ │ 7 is also Sunday on some systems)#
9 · │ │ │ │ │
10 · │ │ │ │ │
11 · * * * * *
- 重复对应位置上时间,每s 每m 每h … , 列表,重复对应位置上多个指定时间 ? 无意义,占位符,日 周 位上
- 表示范围 时20-22,表示20 21 22点 / 起点/步长 日10/10,表示10 20 30日 L last,仅支持日月 日周位 ,月份最后一天或星期六 W weekday,仅支持日位,后边最近的工作日
12 · 仅支持周位,a#b 表示当月第 b 个星期 a#
C 关联日历
注意: 周位置 (0 - 6) (Sunday to Saturday; 7 is also Sunday on some systems) 周位上给定值后,在日位上要用 ? “L” 和 “W” 可在日位中联合使用,LW 表示这个月最后一周的工作日 “W” 字符指定的最近工作日是不能跨月 W 字符串只能指定单一日期,而不能指定日期范围 日位建议最大值为 28 ,因为 2 月有时候是 28 天
12.1 · debugging, profiling, tracing#
debugging:
- Interactive debugging: GDB
- Postmortem analysis: coredump
- Control flow analysis: tracing tools
- Testing
profiling:
- Analysis at program runtime
- Often achieved by sampling counters
- Uses specific tools: perf
- Gathering data from program execution: Function call count, memory usage, CPU load, cache miss, etc
- Extracting meaningful information from these data and modify the program to optimize it
tracing:
- Following the execution flow of an application
- Achieved by instrumenting code
- Often works by recording traces during runtime
12.1.1 · linux application stack#
user/kernel mode:
process/thread:
memory management:
memory layout: heap, stack, data, text
process context: register, task scheduling: Context switching: system calls; receiving exceptions Interrupts:
An exception is an unexpected event from within the processor. Interrupt is an unexpected event from outside the process. System Calls:
ptrace:
12.2 · linux#
12.2.1 · 接口
用户接口(程序) —> 标准库接口(标准库) —> 系统调用接口(操作系统) —> 硬件 用户模式-----------------------------><-----------------------内核模式
应用程序发起系统调用,把参数放在寄存器或栈中,然后发出trap系统陷入指令,切换用户态至内核态。 C提供了库,库函数对应系统调用,有些函数使用汇编语言编写的。 POSIX 是一组标准的系统调用接口。
三种接口:系统调用接口、库函数接口和应用程序接口。
12.2.2 · 组成
引导程序bootloader,内核kernel,初始化系统initsystem,后台进程daemon,图形服务器graphicalserver,桌面环境desktopenvironment,应用程序application
12.2.3 · shell#
12.2.4 · 应用程序
12.2.5 · 内核结构
系统调用 I/O 内存 进程 中断 调度器dispatcher
I/O:虚拟文件系统
内存:虚拟内存,页面替换,页面缓存
进程:信号,调度,创建/终止
12.2.6 · 进程/线程
每个进程运行一段独立的程序,有自己的程序计数器,允许进程创建额外的线程。
fork系统调用,创建一个源进程的拷贝(副本),调用fork为父进程,创建出来的为子进程,fork后,父子进程相互独立。 fork前,父进程已经打开文件,子进程可以共享这个文件,对文件的修改,父子同时可见。
pid = fork(); // 调用 fork 函数创建进程
if(pid < 0){
error() // pid < 0,创建失败
}
else if(pid > 0){
parent_handle() // 父进程代码
}
else { // pid == 0,子进程代码
child_handle() // 子进程代码
}
task结构体,包括 调度参数、打开文件描述符等等。
进程: 调度参数(scheduling parameters) 内存映像(memory image) 信号(signals) 寄存器 系统调用状态(system call state) 文件描述符表(file descriptor table) 统计数据(accounting) 内核堆栈(kernel stack)
12.2.7 · 调度
基于线程
实时先入先出 实时轮询 分时
12.2.8 · 进程间通信 IPC#
https://zhuanlan.zhihu.com/p/37872762
信号signals: 向一个或多个进程发送异步事件信号。 操作系统会中断目标程序,向其发送信号,如果进程注册的信号处理程序,则执行,否则采用默认处理。 如果同时产生多个信号,以任意顺序进行处理。
https://zhuanlan.zhihu.com/p/37889820
管道pipe:
两个进程之间,一个写字节流,一个读字节流。 管道符号|。
pipe
共享内存sharedmemory: 创建共享内存或使用已经创建的内存段: shmget 将进程附近到已经创建的内存段: shmat 从已经连接的共享内存的分离进程: shmdt 对共享内存进行控制操作: shmctl
先进先出队列FIFO: 通常称命名管道。 命名管道是以一个普通文件的形式出现的,包括三个文件操作:创建命名管道、写管道、读管道。
消息队列messagequeue: 独立于发送进程、接受进程而存在。 提供了一种从一个进程向另一个进程发送一个数据块的方法。 msgctl msgget msgrcv msgsnd
套接字socket: 套接字一般用于两个进程之间的网络通信。 socket
12.2.9 · 系统调用
内核态/用户态
系统调用 指的就是引起内核态和用户态切换的一种方式。
fork exec waitpid exit
12.2.10 · 启动
开机自检(Power-On-Self-Test, POST) MBR(Master Boot Record) 主引导记录 boot 程序读取启动设备的根目录 创建 init 进程(进程 1 ) 和 守护进程(进程 2)
12.2.11 · 内存
每个进程都有地址空间,text段,data段,stack段
brk mmap unmap
12.2.12 · 内存管理
虚拟内存(virtual memory)
缓存
页表
页分配和取消分配
内存映射
12.2.13 · IO#
文件描述符(file descriptor)
12.2.14 · fs#
create open close read write lseek fstat pip fcntl