linux内核设计与实现思想 – C风格的面向对象

2023-10-26

原文链接:linux内核学习 – C风格的面向对象

 

 

linux内核大量使用面向对象的编码风格。然而linux内核是完全使用C写就。学习他们如何使用C模拟面向对象机制很有意思。这种做法很可能被人贬为扯淡,但是的确使用C模拟面向对象机制,使得程序员对类型构造/析构,拷贝/赋值等操作有了绝对的控制权,可以提高对效率的嗅觉,减少错误,同时也避免了对C++编译器各种不同类/对象实现机制的依赖。

类的多态特征是linux内核经常用到的。例如在驱动代码中常常使用函数指针来定义一组设备操作函数,从而模拟了多态的特点。


struct file_operations scull_fops = {
    .owner = THIS_MODULE,
    .llseek = scull_llseek,
    .read = scull_read,
    .write = scull_write,
    .ioctl = scull_ioctl,
    .open = scull_open,
    .release = scull_release,
};

上面的例子是Linux Device Driver中抄来的示例代码。很好地展示了file operation结构体如何使用这种机制来定义一组文件操作的方式。用这种方式,Linux很好地贯彻了所有的设备都是文件这种概念。不同的设备可以有不同的处理函数,但使用相同的接口,这样就把底层设备的差异在文件系统这一层隔离开来了。

Linux内核中也经常用到类的继承关系。这种关系使用C也很容易模拟,就是使用结构体嵌套。例如


struct scull_dev {
     struct scull_qset *data;   
     int quantum;               
     int qset;                  
     unsigned long size;    
     unsigned int access_key;  
     struct semaphore sem;     
     struct cdev cdev;      //内嵌linux内核定义的cdev结构体
};

这个例子同样来自LDD。注意在自定义的cdev字符设备结构体中包含了struct cdev cdev成员。这个成员同样是一个结构体,由内核定义,是字符设备描述符。使用这种方式,可以一定程度模拟C++的继承机制,当然有他的局限,例如他不能如同在C++中一样直接引用cdev的成员,而必须通过scull_dev.cdev来引用。

另一方面,这种方式也无法通过“基类”,即cdev的指针,访问“子类”,即scull_dev的成员。精彩的部分来了,linux通过一组宏,巧妙的实现了这一点。在文件处理的函数中,入参会给入inode指针,从这个指针可获得其cdev成员。如何从这个cdev成员获取包含它的“子类”对象,scull_dev的指针呢?

container_of(ptr, type, member)

使用这个宏,container_of(inode->i_cdev, struct scull_dev, cdev)就可获得包含cdev的scull_dev的地址。这个巧妙的宏是如何实现的呢?

#define container_of(ptr, type, member) ({ \
                const typeof( ((type *)0)->member ) *__mptr = (ptr); 
                (type *)( (char *)__mptr - offsetof(type,member) );})

这个宏首先定义一个指向结构体成员的指针__mptr = (ptr),他的类型是const typeof(...)。这里用到了C语言一个较新的关键字typeof,可以在编译期获得变量的类型。而这个类型是((type*)0)->member,这里type和member分别是宏传入的参数。这一行代码就比较清晰了。得到这个__mptr之后,将他向回移动一个offset,(char*)__mprt - offsetof(...),而这个offset恰好为member相对于type的偏移量,offsetof(type,member),则移动完毕__mptr就指向type类型的起始地址了,只需将其转换为type*类型就可以了,(type*)(...)

好了,这个宏已经看懂,神奇的地方就出在这个offsetof宏了,他是如何计算成员相对于结构体的偏移量呢?这里linux内核hacker们用了一个小小trick。

#define offsetof(s, m)   (size_t)&(((s *)0)->m)

是的,代码非常简单。其思想是,假如结构体处于0地址,获取其成员的地址。这个地址就是成员相对于结构体初始地址的偏移量了。没错0地址是不能运行时访问的,但这句代码只在编译期使用了0地址,因此是合法的。当然其实使用成员指针和结构体指针相减也可做到,但用这种方式可以减少一次运算,确保了这个宏可以在编译期求出结果。可谓是精益求精。

我说错了。即使使用减法也可以做到编译期求值,因为结构体和成员指针地址都是可以编译期得到的,常量数值计算应该可以做到编译期优化,计算完成。这种做法应该是
&((type*)0)->member - ((type*)0)
这样的代码的一个直觉性的优化,减0的话,何必还要减呢。事实上两句代码的运行时间是一样的,但这样做可以减轻编译时间。
在container_of宏中,也有一句减法计算。这个计算引用了运行时求值的__mptr,所以无法做到编译期求值。

类似这种用法,在linux内核中经常出现。深深佩服大牛们的创造力,并且深深的意识到了即使是C语言也是学无止境的。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内核设计与实现思想 – C风格的面向对象 的相关文章

  • 如何使用 Windows 粘贴命令将文本粘贴到 C# 中的其他应用程序?

    如何调用互操作来使用 Windows Pastse 命令将文本粘贴到 C 中的其他应用程序 调用互操作 我的意思是如何对 C 进行编程相同的右键单击粘贴文本 在某些情况下这可能有点棘手 但实际上非常简单且容易做到 下面的示例介绍了如何使用文
  • jni.h:没有这样的文件或目录

    我一直在关注本教程 http www java tips org other api tips jni simple example of using the java native interface html 在第 5 步 我从 GCC
  • 在 MVC 5 中,如何在单个 Ajax POST 请求中发送 ViewModel 和文件?

    我有一个 ASP NET MVC 5 应用程序 我正在尝试发送带有模型数据的 POST 请求 并且还包括用户选择的文件 这是我的 ViewModel 为了清晰起见进行了简化 public class Model public string
  • 如何在 CUDA 中执行多个矩阵乘法?

    我有一个方阵数组int M 10 以便M i 定位第一个元素i th 矩阵 我想将所有矩阵相乘M i 通过另一个矩阵N 这样我就收到了方阵数组int P 10 作为输出 我看到有不同的可能性 分配不同元素的计算M i 到不同的线程 例如 我
  • Polly 策略记录异常并重新抛出

    我考虑使用Polly https github com App vNext Polly handing return values and policytresult创建策略来记录异常并重新抛出 我没有找到允许它开箱即用的现有方法 但我看到
  • 指向基类的基本多态指针

    虽然我已经在 C 领域工作了一段时间 但直到现在我才需要使用多态特性 而且我对它们非常感兴趣 如果我有一个基类ClassA和另一个ClassB从中衍生出来 我明白我可以拥有virtual中的成员函数ClassA即 当实施于ClassB 将被
  • QSpinBox 具有用于十六进制输入的 Unsigned Int

    这里写了很多关于 QSpinBox 使用 int 作为其数据类型的限制的问题 人们通常希望显示更大的数字 就我而言 我希望能够以十六进制显示无符号 32 位整数 这意味着我希望我的范围为 0x0 0xFFFFFFFF 正常的 QSpinBo
  • 在 Silverlight 中编辑并继续?

    Edit And Continue 是我最喜欢的调试工具之一 我之前曾在基于 C 的 Winforms 和 ASP NET 项目中使用过它 但是 我在 VS 2008 上运行 Silverlight 3 0 应用程序 每当我尝试进行更改 中
  • 从该共享库中查找加载的共享库的位置?

    从共享库中的函数 在正在运行的进程 用 C 编写 内 我如何发现该共享库是从哪里加载的 我找到的所有答案都涉及使用诸如ldd在命令行中 或者通过查看 proc self maps 在 Win32 上 我只需使用GetModuleFileNa
  • 在 C 中从数组创建子数组的最佳方法

    我有一个数组说a 3 1 2 5 我必须创建另一个数组a2 2 2 5 我尝试过的是创建一个新数组a2 只需复制所需位置范围内的所有元素即可 在C语言中还有其他方法可以实现这一点吗 memcpy a2 a 1 2 sizeof a
  • 无法打开作为链接添加的 configSource 文件

    在我的 MVC 应用程序中 我使用外部配置文件来保持干净的 web config 有些文件很常见 我将它们作为链接从一个位置添加到项目中 对于这些文件 我将 复制 选项设置为 始终复制 这些文件将被复制到目标文件夹 我会看到它们 但是当我尝
  • 线程忙等待

    基本上 我需要忙着等待一些 html 出现在网页上 我创建了以下代码来忙等我 public void ExecuteBusyWaitThreads foreach Canidate canidate in allCanidates Thre
  • std::async 可以调用 std::function 对象吗?

    是否可以使用 std async 调用使用 std bind 创建的函数对象 以下代码无法编译 include
  • Haskell FFI - 你能从 Haskell 数据结构中获取 C 指针吗?

    我有很多 C 结构体 结构如下 typedef struct unsigned int a unsigned int b StructA 还有很多功能 比如 void doSomethingWith StructA StructB Stru
  • 如何在 servicestack.net 中实现身份验证

    我正在调查 servicestack net 但它的示例和文章似乎没有涵盖身份验证 这是由 servicestack net 处理的东西 如果是的话如何处理 我特别有兴趣实现对以下方面的支持 OAuth 因此能够检查原始请求并验证它 检索关
  • 提升精神带走关键字并忽略船长

    这是使用表达式的语法的一小部分 prefix lit L not gt gt prefix lit gt gt prefix postfix 我在 postfix 内部有某种方式纯名称获取标识符 name pure lexeme boost
  • 如何从函数调用事件处理程序?

    我有一个类 我从中调用一个函数ABC string st 带字符串参数 该函数定义在一个Form class Form1 我有一个列表视图 想要从函数中自动调用列表视图 mouse click 事件 我该如何做到这一点 您不能调用另一个类的
  • 有条件地使用按位运算符

    条件运算符如何使用按位运算符表示 这是一个家庭作业问题 我必须仅使用按位运算来实现条件运算符 那就很简单了 如果if允许使用语句 但它必须是严格的按位运算符 仅运营商 gt gt and lt lt 可以使用 不if可以使用语句或循环 该函
  • 以编程方式更改 Windows 服务用户

    我需要以编程方式更改 Windows 服务的登录用户 我使用以下代码来做到这一点 string objPath string Format Win32 Service Name 0 ServiceName using ManagementO
  • Eigen 如何沿特定维度连接矩阵?

    我有两个特征矩阵 我想将它们连接起来 就像在 matlab 中一样cat 0 A B eigen 有等价物吗 Thanks 您可以使用逗号初始值设定项语法 水平方向 MatrixXd C A rows A cols B cols C lt

随机推荐

  • 有关树莓派+arduino构建小车

    这里写自定义目录标题 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题 有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一个表格 设定内容居中 居左 居右 Sma
  • eclipse中没有runtime environments_Go语言中的panic和recover

    初识别panic和recover 本节将分析两个经常成对出现的关键字 panic 和 recover 这两个关键字都与 defer 有千丝万缕的联系 也都是 Go 语言中的内置函数 但是提供的功能却是互补的 panic 能够改变程序的控制流
  • opencv 图像雾检测_雾的检测算法

    雾的检测算法相对来说文献不是很多 这次和大家介绍两篇相对来说比较容易实现的两篇文章 其中一篇是基于灰度直方图的方式进行分析检测 另一篇是将rgb图像空间转化为hsv空间进行分析检测 1 灰度图检测 首先来说第一片 Fog Detection
  • 如何用ai写文章?这三个软件可以自动生成文章

    随着人工智能技术的不断发展 ai写作已经成为了当今的热门话题 它是指利用机器学习 自然语言处理等技术 让机器能够像人类一样写作 相较于传统写作方式 ai写作大大提高了写作的效率和质量 可以让我们的创意和技术相融合 其应用范围也非常广泛 无论
  • 探索编程世界的宝藏:程序员必掌握的20大算法

    程序员必须掌握哪些算法 文章目录 1 引言 2 冒泡排序算法 编程世界的排序魔法 3 选择排序算法 排序世界的精确挑选器 4 插入排序算法 排序世界的巧妙插珠者 5 快速排序算法 排序世界的分而治之大师 6 归并排序算法 排序世界的合而为一
  • 统计并输出该字符串中26个英文字母

    字符串 第3题 描述 输入一个字符串 统计并输出该字符串中26个英文字母 不区分大小写 出现的次数 输入 输入一个字符串 输出 分行输出26个英文字母 不区分大小写 出现的次数 输入示例 I am a student 输出示例 a 2 d
  • Python:蒙特卡罗方法模拟解决三门问题

    蒙特卡罗方法与三门问题 蒙特 卡罗方法 Monte Carlo method 也称统计模拟方法 是一种统计学的方法 模拟方法 通过大量随机样本模拟问题 从而获得所要计算的值 三门问题 三门问题 Monty Hall problem 亦称为蒙
  • STM32:TIM_OCxInit函数用法解释

    TIM OC3Init函数是用于初始化定时器TIM的PWM输出通道3的函数 可以使用以下步骤进行使用 创建一个TIM OC InitTypeDef类型的结构体对象 例如命名为TIM OCInitStruct 使用TIM OCStructIn
  • 网络基础知识总结

    一 广域网和局域网 局域网 Local Area Network 简称LAN 也就是内网 局域网就是在固定的一个地理区域内由2台以上的电脑用网线和其他网络设备搭建而成的一个封闭的计算机组 它可以是邻居之间的2台电脑 也可以是一幢100层大楼
  • [深入研究4G/5G/6G专题-41]: URLLC-12-《3GPP URLLC相关协议、规范、技术原理深度解读》-6-MAC层调度器的总体架构、调度器的原理和三种URLLC调度器的增强方案

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文主页 https blog csdn net HiWangWenBing article details 125942872 目录 前言
  • 杭电ACM 1000题

    import java util Scanner public class Main public static void main String args Scanner cin new Scanner System in while c
  • ajax 跨域 session 丢失问题

    ajax 跨域时session丢失了 解决方法 首先我 Google 了一下这个问题的原因 我找到了这个 1 Access Control Allow Origin该字段是必须的 它的值要么是请求时Origin字段的值 要么是一个 表示接受
  • Cookie与前端安全

    目录 一 Cookie的作用 二 cookie与http协议 1 http响应 cookie的生成 2 http请求 cookie的发送 三 Cookie的属性 1 Expires Max Age 2 Domain Path 3 Secur
  • 30分钟速成TypeScript

    一 TypeScript 是什么 TypeScript 是 JavaScript 的超集 在 JavaScript 的基础上增加了类 模块 接口 泛型和静态类型 可选 等常见的概念 从编程语言的动 静来区分 TypeScript属于静态类型
  • JS代码风格利器——Eslint

    ESLint 是一个开源的 JavaScript 代码检查工具 由 Nicholas C Zakas 于 2013 年 6 月 创建 代码检查是一种静态的分析 常用于寻找有问题的模式或者代码 并且不依赖于具体 的编码风格 对大多数编程语言来
  • 网易云音乐首页导航样式的实现

    实现效果图 背景图片
  • xml sax localName和qName的区别

    对于DefaultHandler类中方法的成员 String uri String localName String qName Attributes attributes 弄的不是很清楚 于是得到下面这片文章感觉讲的蛮清楚的 无节操的贴过
  • CSS动画实现的三种方式

    CSS动画 CSS动画就是元素从一种样式过渡到另一种样式的过程 常见的动画效果很多 比如 平移 缩放 旋转等 CSS实现动画的方式有以下几种 transition 实现渐变动画 transform 实现缩放 平移等效果 animation
  • 金币收集问题

    金币被放在1到n编号的格子中 一个人从第一个格子出发 有m张卡片 共4种 卡片上的数字1 4 表示可以走的步数 求到达终点获得的最大金币数 import java util public class Main public static v
  • linux内核设计与实现思想 – C风格的面向对象

    原文链接 linux内核学习 C风格的面向对象 linux内核大量使用面向对象的编码风格 然而linux内核是完全使用C写就 学习他们如何使用C模拟面向对象机制很有意思 这种做法很可能被人贬为扯淡 但是的确使用C模拟面向对象机制 使得程序员