作者:云聪京初瑞子_617 | 2021-08-11 01:38
linux可以借鉴的设计思想(笔记)1、C语言嵌入结构实现继承(典型的Kobject);封装:通过结构体对对一类信息包装,便于后续扩展、升级,即使信息只有一个简单数据,如kref;对结构体成员的访问要通过函数来实现
linux可以借鉴的设计思想(笔记)
1、C语言嵌入结构
实现继承(典型的Kobject);
封装:通过结构体对对一类信息包装,便于后续扩展、升级,即使信息只有一个简单数据,如kref;对结构体成员的访问要通过函数来实现成员封装,使得结构体面向功能服务,易于扩展
抽象:通过在结构体内嵌入特殊用途的结构体(可包含相关具体函数)实现抽象设计,可以将对象看作某类对象,看作实现某种服务抽象,类似C++的virtual。如cpu均衡部分,就绪队列中,嵌入与均衡相关的结构及处理,上层在得到均衡通知时,任务下放sched_class具体处理。另一个例子是调度,他处理的是可调度实体(sched_entity),可以是进程、也可以是进程组(不同用户)。 www.2cto.com
2、数据结构:
散列表在linux上的实现:定义散列区间(一般不能太大),确定数组,在每一个数组项上,通过双链表方式存储散列项相同的数据项,而散列函数的理想是等概率分布。
红黑树:通过深度优先搜索(DFS)生成有序队列,如调度的等待时间,通过深度优先,生成按照等待时间排序的队列,在调度时,只调度最左侧地下的一个就ok。(时间复杂度为对数)
双向循环链表:
位图辅助索引:
3、副本与写时复制技术:
通过复制副本获取父对象的所有资源,并使得修改副本是一个独立对象,从而不影响父对象。(进程结构:task_struct, 命名空间nsproxy);
fork复制了task_struct,实际上就有了2个完全相同的进程,并且都运行到了同一个地址上。在linux上进程与线程的不同仅体现在共享资源的不同上,每个线程都有唯一的task_struct,fork与clone只有细微的差别。
4、结构相关: www.2cto.com
宏定义:通过宏定义进行与结构相关操作,隐藏具体细节,便于移植;如结构体中,变量相对位置不能假定,应采用宏定义的方式计算得到(container_of),如一个数据不同位代表不同含义;
小的内联函数:提供封装性,且效率很快
5、合理的goto结构:
虽然混乱的使用goto容易打乱程序的结构,备受诟病,但是在初始化时按照以下匹配的方式(栈)使用goto,代码相当简洁:
if(A)
gotooutA;
if(B)
gotooutB;
if( C)
gotooutC;
...
outC:...
outB:...
outA:...
6、合理提高效率:
不能对正在运行的进程进行迁移
设备上下文恢复时考虑部分保存与部分恢复
块设备数据读取请求合并、预测读取策略
有以上足可见对性能追求之极致
www.2cto.com
7、设备控制的4种方式,在类似问题上综合考虑能否提供通用模版支持扩展:
通过通用文件操作,传输数据,根据数据的特殊性(控制指令),实现设备控制,少数设备,基本废弃
添加一系列特有控制函数(不同于常规文件系统):不利于扩展,未采用。
添加ioctl,就是命令函数,与invoke等类似:稍显过时,应用层和内核的命令要匹配修改,麻烦
sysfs:统一的属性修改方式,易于扩展。
8、中断处理机制:
修改全局状态的情况:先保存要修改的状态,修改为新状态,使用新状态处理,恢复原状态;(例如在绘图相关程序中,对绘图设备属性更改,如果没有采用这种方式处理,往往运行结果随机出现不正常,且不具有重现性)
将必要的内容放在中断处理程序中,将非必要的处理放在下半部分,提高性能;(比如在绘图的onpaint中,是不是里面只包含了必要的绘图,在达不到要求的帧率条件下,是否可以把不必要的内容放在外面,双缓冲绘图,实际可看做将绘图处理部分放在主绘图线程之外的一种做法,它的主线程只负责copy帧内容)
www.2cto.com
9、对象生存期管理:
引用计数方式
谁分配谁释放原则
10、设计模式理念:
块设备驱动中bio对象封装读写请求,ioctl:借鉴了命令模式的理念
VFS中的file_operations中的open:按照普通文件、特殊设备文件的顺序处理(如果不是该我处理的转下去),暗含了责任链的思想。
io读写请求调度(分离的功能和实现方式):可归类策略模式;
VFS:为不同的文件系统提供统一的外部接口,可作为不同类型文件系统之间的中介或者也可说是适配器。
11、通用处理默认、专用处理修改的技巧,简化开发:
各种标准接口中大部分都提供了通用的处理,如file_operation的接口基本都有默认处理函数
html中可以将主流风格定义样式,默认适用于整个html显示元素,同时支持对个别元素的外观特征的修改,一般通用部分可通过样式工厂设置、处理;
12、回调:
回调就是预先告诉第三方你可以接收消息或者事件的接口,典型的“有事callme”,与中断本质上是一会事。
回调用的场合:架构设计。在C中一般在相应数据结构中定义回调函数指针,初始化时赋值,如驱动中file_operation定义的架构。 www.2cto.com
广义的回调不限于函数本身:如
浏览器最初只定位为
html界面元素的显示,通过引入脚本语言来增强浏览器不支持的能力,可看做浏览器提供回调接口,扩充浏览器本身没有的算法处理能力。
13、进程/线程/用户进程/内核线程:
进程与线程:linux不提供单独的线程支持,线程也是一种进程,只不过与别的进程在clone时通过设置共享标志,与其他进程共享某些资源(包括地址空间),有独立的task_struct;
内核线程与普通线程:普通进程或者线程有独立的虚拟地址空间,而内核线程没有独立的虚拟地址空间,都在内核地址空间中运行。
14、字符设备驱动与块设备驱动:
区别:从架构上讲,两者区别仅限于在块设备驱动中增加了缓冲层(需要增加请求队列管理和调度的部分),提高性能;
联系:向上(VFS)提供统一的文件操作接口;
15、抢占与中断:
抢占:属于调度的范畴、可发生在用户空间或者内核空间,分属内核抢占和用户抢占,特点都是发生在某种事件发生或结束时,有更高优先级的进程需要优先运行;在时间上属于准实时;
中断:属于打断当前执行的情况,在时间上属于实时;
16、用户空间切换内核空间的两种方式:
硬件中断(不可睡眠、抢占); www.2cto.com
系统调用(是一种软中断,可睡眠、抢占,必须保证代码可重入)
17、中断:
申请中断号(有一部分中断号是固定的如时钟,其他的如PCI上的设备,可动态申请,共享使用(设备多,中断号少))
定义中断处理函数,即注册时的回调函数
中断注册函数可睡眠(具体为内核内存分配函数可睡眠),因此不能在不可睡眠的地方进行中断注册,一般在设备初始化或者设备与驱动建立连接时进行,确保设备已经初始化才能注册中断;
使用完后,应该释放中断处理程序、甚至禁用中断线
18、为什么在中断处理程序中不能阻塞或者睡眠?
因为中断处理程序不被进程调度程序管理,阻塞后,无法通过调度恢复。
19、中断处理程序/下半部处理程序
数据的收集/数据的处理使用不在同一线程:保证可靠的接收数据;如入库
计算/显示分属不同线程:确保显示不死机、响应慢;
目的是调和两种矛盾。 www.2cto.com
20、linux操作系统运行的两种时序:
内核作为一个独立运行体,按照顺序执行的方式执行,调度不过是执行过程中的一种执行路径的选取,进程是内核管理执行体的插件方式;
中断执行:由硬件触发,打破当前执行序列的执行;
21、系统调用
系统调用:是用户程序访问系统资源或者使用系统的唯一通道,安全检查,提高可靠性;
实现机制:软中断(int80);
实现方式:system_call(系统调用号,参数表)跟invoke类似,属于命令模式范畴
22、软中断、tasklet、工作队列比较
软中断、tasklet:是运行在中断上下文中的,都是软中断,tasklet是对软中断的一种同步化、序列化封装、使用简化封装;不可以调度、不可以睡眠;
工作队列:是运行在进程上下文的,是有进程控制块的内核线程;可以调度、可以睡眠;