为什么要单独讲kernel的数据结构?

kernel是一个支持很多平台很多架构的OS,不同的架构上,很多的数据结构都是不同的,如何保证kernel能兼容多个架构,这就得益于kernel良好的数据结构设计。

kernel中各种各样的数据结构,主要三类:

1, int等标准的C类型

2, 表明大小或者长度的类型,如u32等

3, kernel自己的结构体,如pid_t等

后面会讲应该如何使用这样的数据结构。

11.1. Use of Standard C Types


尽管developer可以尽情使用像int,long这类标准的数据类型,但是要注意类型冲突或者潜在的bug。这类数据类型,在不同的架构上占用的memory是不一样的:

arch   Size:  char  short  int  long   ptr long-long  u8 u16 u32 u64
i386            1     2     4     4     4     8        1   2   4   8
alpha           1     2     4     8     8     8        1   2   4   8
armv4l          1     2     4     4     4     8        1   2   4   8
ia64            1     2     4     8     8     8        1   2   4   8
m68k            1     2     4     4     4     8        1   2   4   8
mips            1     2     4     4     4     8        1   2   4   8
ppc             1     2     4     4     4     8        1   2   4   8
sparc           1     2     4     4     4     8        1   2   4   8
sparc64         1     2     4     4     4     8        1   2   4   8
x86_64          1     2     4     8     8     8        1   2   4   8

可以看出long类型和指针占用的内存是一样大的,所以在kernel中大量使用unsigned long来表示地址。之所以不直接使用指针,是为了防止随意通过指针访问memory,因为kernel推荐使用各种各样的interface来访问。

11.2. Assigning an Explicit Size to Data Items


有些场景下,需要明确指定变量的size,比如:

#include <asm/types.h>
u8;   /* unsigned byte (8 bits) */
u16;  /* unsigned word (16 bits) */
u32;  /* unsigned 32-bit value */
u64;  /* unsigned 64-bit value */

如果user space需要使用这样的类型,需要使用带两个下划线的版本,如__u8,__u32等。

11.3. Interface-Specific Types


有些接口返回的数据类型,不是标准的C数据类型,而是kernel通过typedef自己定义的数据结构,比如进程ID pid_t。使用这种自定义的类型,好处在于可以隐藏架构不同导致的数据差异,对driver developer而言,无论什么架构,使用的数据类型都是pid_t,kernel则隐藏了每个架构的实现。

然而后面kernel的code使用interface specific 数据类型的越来越少了,许多code直接使用相应的数据类型,而不是类似于pid_t的数据类型。因为这种数据类型有其弊端,比如size_t可能对应unsigned int,或者unsigned long,那么你打印的时候按照什么数据类型打印。通常情况下,把它按照最大的数据类型转换,再打印就可以,比如size_t转换为unsigned long来打印。

11.4. Other Portability Issues


code中可能存在一些预定义的常量,导致存在某些移植性的问题,比如一些预定义的宏。

11.4.1. Time Intervals

计算时间间隔,不要假设当前的HZ是1000,就用1000直接计算,而应该使用HZ来计算。比如要计算0.5s,应该是HZ/2,而不是直接使用jiffies 500.

11.4.2. Page Size

虽然x86或者其他的架构上PAGE_SIZE是4KB,但是不能直接使用4KB,而应该使用PAGE_SIZE和PAGE_SHIFT。这里有一个例子,如果你需要分16KB的memory,如何分配呢?

#include <asm/page.h>
int order = get_order(16*1024);
buf = get_free_pages(GFP_KERNEL, order);

应该使用get_order来计算order,再使用order调用get_free_pages来分page。

11.4.3. Byte Order

不同架构上可能使用不同的字节序,但是device driver在实现时,尽量不对字节序做任何假设,也不应该依赖于字节序。但是在某些特定的情况下,可能需要依赖于字节序。

在需要依赖于字节序的地方,可以通过宏来check:__BIG_ENDIAN和__LITTLE_ENDIAN。但是如果这样的宏太多,会比较麻烦,所以kernel提供了这样的函数直接来实现:

u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);

使用上面的两个宏,会把数据转换为小尾端的u32出来,无论你是大尾端还是小尾端。

11.4.4. Data Alignment

为了提高performance,编译器可能会对数据结构进行padding,而不同的架构上,可能对其的结构并不一样。为了减少这种问题,driver可以自己对数据结构进行padding。比如kernel的一个例子:

struct {u16 id;u64 lun;u16 reserved1;u32 reserved2;
} _ _attribute_ _ ((packed)) scsi;

11.4.5. Pointers and Error Values

很多interface如果fail了,会直接返回NULL;但是有时候caller需要直到发生了哪种错误,这就需要interface返回一些error code,而不是NULL,caller通过一些方法检查这些error code,就能知道发生了什么事。

//返回error ptr (通过error生成)
void *ERR_PTR(long error);//判断是否返回了error ptr
long IS_ERR(const void *ptr);//如果需要从error ptr解析error code
long PTR_ERR(const void *ptr);

11.5. Linked Lists


kernel实现了一套自己的双向链表,通过这个双向链表可以让device driver很方便的实现自己的双向链表结构,在使用的时候,如果需要互斥的访问链表资源,那driver就得自己做保护。

#include <linux/list.h>
struct list_head {struct list_head *next, *prev;
};struct todo_struct {struct list_head list;int priority; /* driver specific *//* ... add other driver-specific fields */
};

需要使用双向链表的数据结构来说,只需要把struct list_head包含进自己的数据结构即可。

上图就是双向链表的组织形式。使用之前要初始化:

//运行时初始化
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);//编译时初始化
LIST_HEAD(todo_list);//把new添加到head后面,head不一定是链表头,可以是链表中的任何节点
list_add(struct list_head *new, struct list_head *head);//把new添加到head前面,也就是链表的尾巴。相当于把双向链表作为FIFO使用
list_add_tail(struct list_head *new, struct list_head *head);//将entry从双向链表中删除,如果还会放入别的list,就使用list_del_init。
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);//把entry从当前链表移动到head链表头部,如果放入尾部,用list_move_tail。
list_move(struct list_head *entry, struct list_head *head);
list_move_tail(struct list_head *entry, struct list_head *head);//判断链表是否为空
list_empty(struct list_head *head);//把list这个链表,插入到head后面
list_splice(struct list_head *list, struct list_head *head);//通过ptr获取包含struct list_head的大的数据结构地址
list_entry(struct list_head *ptr, type_of_struct, field_name);
//sample code:
struct todo_struct *todo_ptr =list_entry(listptr, struct todo_struct, list);

通常需要loop这个双向链表,kernel已经提供了函数,这里有个例子:

void todo_add_entry(struct todo_struct *new)
{struct list_head *ptr;struct todo_struct *entry;list_for_each(ptr, &todo_list) {entry = list_entry(ptr, struct todo_struct, list);if (entry->priority < new->priority) {list_add_tail(&new->list, ptr);return;}}list_add_tail(&new->list, &todo_struct)
}

除了list_for_each,kernel还提供了别的loop用的函数:

//普通的loop,需要注意loop的过程中对链表的修改
list_for_each(struct list_head *cursor, struct list_head *list)//逆向loop
list_for_each_prev(struct list_head *cursor, struct list_head *list)//如果loop的过程中,需要从链表中删除node,就使用这个函数
list_for_each_safe(struct list_head *cursor, struct list_head *next, struct list_head *list)//获取大结构体的指针
list_for_each_entry(type *cursor, struct list_head *list, member)
list_for_each_entry_safe(type *cursor, type *next, struct list_head *list, member)

Data Types in the Kernel [LDD3 11]相关推荐

  1. mysql timestamp 插入null报错_读MySQL 5.7文档11.2 Date and Time Data Types总结

    作者:魏新平,知数堂第5期MySQL实战班学员,第10期MySQL优化班学员,现任职助教. 读MySQL5.7文档11.2 Date and Time Data Types MySQL的时间类型分为D ...

  2. OpenCL Data Types (数据类型)

    OpenCL Data Types (数据类型) 1. Built-in Scalar Data Types - 内置标量数据类型 The following table describes the ...

  3. date类型_Chapter 01. 常见数据类型概述 Overview of Common Data Types

    课程:DataCamp_Skill Track_SQL fundamentals[笔记] Chapter 01. 常见数据类型概述 Overview of Common Data Types 了解常见 ...

  4. 【12c】扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE

    [12c]扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE [12c]扩展数据类型(Extended Data Types)-- MAX_STRING_SIZ ...

  5. Redis Essentials 读书笔记 - 第二章: Advanced Data Types (Earning a Black Belt)

    Chapter 2. Advanced Data Types (Earning a Black Belt) 本章介绍数据类型:Set, Sorted Set, Bitmap, HyperLogLog. ...

  6. 成功解决ValueError: DataFrame.dtypes for data must be int, float or bool.Did not expect the data types

    成功解决ValueError: DataFrame.dtypes for data must be int, float or bool. Did not expect the data types ...

  7. Supported Data Types(flink支持的数据类型)

    文章目录 1.Tuples and Case Classes 2.POJOs 3.Primitive Types 4.General Class Types 5.Values 6.Hadoop Wri ...

  8. golang中的variable和data types

    https://golangbyexample.com/variables-in-golang-complete-guide/#What_is_variable 问题 go里面的string为什么是不 ...

  9. keil问题:报 ***WARNING L25: DATA TYPES DIFFERENT 警告,是什么问题?

    在A.c文件中定义了一个函数fun(),在外面其他X.c文件中使用到了,但是在A.h文件中没有声明fun(),会出现 "***WARNING L25: DATA TYPES DIFFEREN ...

最新文章

  1. Python开发【第十篇】:CSS (二)
  2. document 文挡对象解析
  3. android时间显示中文版,系统运行时间显示工具(Vov System Uptime)
  4. 解决Visual Studio (VS) 插件下载缓慢
  5. Arts 第十九周(7/22 ~ 7/28)
  6. .NET Worker Service 作为 Windows 服务运行及优雅退出改进
  7. 【OpenCV 例程200篇】46. 直方图均衡化
  8. redis同步到磁盘
  9. 如何解决VC 应用程序无法启动,因为应用程序的并行配置不正确 sxstrace.exe问题...
  10. 三阶魔方大中小魔公式_三阶魔方花样大汇总 ,带公式带图
  11. 2022.3.19-2022.3.27每周刷题
  12. 求有10个整型元素的数组中最大元素及其下标。
  13. java手机解锁_Android手机屏幕敲击解锁功能代码
  14. 种植牙术后的注意事项
  15. 专访Cisco 梁永健:网络会议撬动SaaS市场
  16. fseek函数的用法(用于设定指针位置)
  17. Flink实操 : DataSource操作
  18. 真正厉害的人,都在延迟满足
  19. windows10中创建Direct3D11设备出现0x887a002d错误的解决方案
  20. 10. 从0学ARM-基于Exynos4412的pwm详解

热门文章

  1. iec104点号_IEC104规约流程 | 学步园
  2. 使用pdfobject预览pdf
  3. 便携一体机设计资料机构图:定制化仪器户外便携式手提触摸一体机
  4. JSON版七彩影视双端百果炫彩UI前端主题
  5. 【持续更新中】C#常见问题及其解决(VS2019)
  6. H3C路由器基本配置命令
  7. AWVS 网页漏洞扫描工具安装使用(Linux)
  8. Ubuntu配置socks5转http
  9. 为什么学习帕拉卡3D动画编程要从搭建开始?
  10. PDManer安装教程