如果你第一次看 Linux 内核、Android HAL、Binder 驱动、FFmpeg 源码,很容易有一个强烈感受:

👉 怎么全是 struct + 函数指针?几乎看不到“类”。

struct file_operations {
    int (*open)(struct inode*, struct file*);
    ssize_t (*read)(struct file*, char*, size_t, loff_t*);
    ssize_t (*write)(struct file*, const char*, size_t, loff_t*);
};
struct hw_module_methods_t {
    int (*open)(const struct hw_module_t*, const char*, struct hw_device_t**);
};
binder_device->ops->ioctl(...);

看起来像“面向过程”,但实际上:

👉 这是操作系统世界的“对象模型”

一、先给结论:系统层的“对象”,本质是 struct + 函数表

在系统层:

  • struct → 对象的“内存实体”
  • 函数指针表 → 对象的“行为接口”
  • 通过函数表调用 → 运行时多态

也就是说:

👉 struct + 函数指针 = 对象 + 接口 + 多态。

二、为什么系统层不用 C++ / Java 的类?

不是“不会”,是不能随便用

系统层面对的不是“开发效率”,而是:

  • ABI 稳定性
  • 内存布局可控
  • 启动阶段可用
  • 无运行时依赖
  • 可跨语言
  • 可被内核使用
  • 可被驱动加载

而 C 风格结构刚好满足:

要求 struct + 函数表
ABI 稳定
内存可控
无运行时
跨编译器
内核可用
插件友好

👉 这是“操作系统级接口”,不是“应用层接口”。

三、系统层对象模型的真实结构

典型模型:

struct DeviceOps {
    int (*open)(void*);
    int (*close)(void*);
};

struct Device {
    struct DeviceOps* ops;
    int fd;
};

调用:

dev->ops->open(dev);

这在机制上,等价于:

dev->open();   // C++ 虚函数

或:

dev.open();    // Java 接口调用
 

区别只是:

  • C:你手写函数表
  • C++:编译器生成虚表
  • Java:虚拟机管理分发

👉 抽象模型完全一致。

四、Linux 为什么大量使用这种模型?

1️⃣ 驱动是“可插拔模块”

Linux 设备模型:

struct file_operations;
struct net_device_ops;
struct bus_type;

驱动注册时:

device->ops = &usb_ops;

运行时:

device->ops->read(...);

👉 这本质就是:

加载一个实现 → 挂一张函数表 → 内核统一调度。

这就是“插件架构”。

2️⃣ 内核不能依赖 C++ 运行时

内核里不能:

  • new/delete
  • RTTI
  • 异常
  • STL

所以它必须:

👉 用最小语义单位搭最大抽象系统。

3️⃣ 内核要控制每一个字节

struct 模型可以:

  • 精确控制内存布局
  • 嵌入对象
  • 做 container_of
  • 和硬件寄存器对齐

这是面向硬件的能力。

五、Android 为什么也是这一套?

你会在 Android 各层看到同构设计:

1️⃣ HAL

struct hw_module_methods_t {
    int (*open)(...);
};

厂商只要:

  • 实现函数
  • 填函数表
  • register

👉 系统通过接口调用,不关心厂商实现。

2️⃣ Binder 驱动

Binder 驱动是纯 C:

  • struct binder_proc
  • struct binder_node
  • struct binder_driver_return

而 libbinder / system_server 是 C++:

👉 C 提供稳定模型
👉 C++ 提供工程封装

3️⃣ Android framework

Camera / Audio / Sensor:

HAL 是 struct + ops

Framework 是 C++ 虚类

App 层是 Java 接口

👉 同一模型,三种实现。

六、这套模型解决了系统层三大核心问题

✅ 1. 稳定接口(ABI)

函数表 = 固定协议
可升级、可替换、可回滚

✅ 2. 模块解耦

内核 / framework 不依赖具体设备
设备只实现接口

✅ 3. 运行时多态

同一个调用点
不同设备、不同实现

七、为什么说“回调 / 接口 / 虚函数 / 驱动模型”是同一件事?

普通回调:

register(on_event);

系统对象模型:

ops->on_event(obj);

区别只是:

👉 对象模型 = 一组“语义相关的回调”。

所以:

  • Java 接口 = 高级函数表
  • C++ 虚类 = 自动函数表
  • Linux ops = 手写函数表

八、一句话总结(系统工程师版)

👉 系统层不是“有没有对象”,而是“怎么实现对象”。
👉 struct + 函数表,是操作系统世界最稳定的对象模型。

九、你再看系统源码,会发生什么变化?

你会从:

❌ “好多结构体,看不懂”

变成:

✅ “这是接口”
✅ “这是实现”
✅ “这是设备实例”
✅ “这是多态调度点”

👉 系统源码可读性瞬间上一个层级。

十、终极总结

硬件世界  → struct
行为抽象  → 函数表
系统架构  → 对象模型

Linux / Android 不是“面向过程”,
而是**“用 C 实现的面向对象系统”。**

当你真正理解 struct + 函数表,

你就已经站在:

👉 系统接口设计层。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐