介绍
我听说过一些关于用 Java 编写设备驱动程序的事情(听到的是“用我的耳朵”,而不是来自互联网)并且想知道......我一直认为设备驱动程序在操作系统级别上运行,因此必须用相同的语言编写作为操作系统(因此主要是 CI 假设)
问题
- 我通常对这个假设有误吗?(好像是这样)
- 如何在操作系统中使用“外星人”语言的驱动程序?
- 无论如何,设备驱动程序有什么要求(从编程语言的角度来看)?
谢谢阅读
我听说过一些关于用 Java 编写设备驱动程序的事情(听到的是“用我的耳朵”,而不是来自互联网)并且想知道......我一直认为设备驱动程序在操作系统级别上运行,因此必须用相同的语言编写作为操作系统(因此主要是 CI 假设)
谢谢阅读
有几种方法可以做到这一点。
首先,在“操作系统级别”运行的代码不需要用与操作系统相同的语言编写。它只需要能够与操作系统代码链接在一起。几乎所有语言都可以与 C 互操作,而这正是我们所需要的。
所以语言方面,技术上没有问题。Java函数可以调用C函数,C函数可以调用Java函数。如果操作系统不是用 C 编写的(比如说,为了论证它是用 C++ 编写的),那么操作系统 C++ 代码可以调用一些中间 C 代码,这些代码会转发给你的 Java,反之亦然。C 几乎是一种编程语言。
一旦程序被编译(到本地代码),它的源语言就不再相关了。无论源代码在编译之前是用哪种语言编写的,汇编程序看起来都差不多。只要您使用与操作系统相同的调用约定,就没有问题。
一个更大的问题是运行时支持。操作系统中没有很多软件服务可用。例如,通常没有 Java 虚拟机。(从技术上讲,没有理由不存在,但通常,但通常,可以安全地假设它不存在)。
不幸的是,在其“默认”表示中,作为 Java 字节码,Java 程序需要大量的基础设施。它需要 Java VM 来解释和 JIT 字节码,它需要类库等等。
但是有两种方法可以解决这个问题:
所以是的,这是可以做到的。但这并不简单,也不清楚你会得到什么。
当然,另一个问题可能是 Java 不允许您访问任意内存位置,这会使很多硬件通信变得非常棘手。但这也可以解决,也许可以通过调用非常简单的 C 函数来简单地将相关的内存区域作为数组返回,供 Java 处理。
用 Java 编写 Solaris 设备驱动程序涵盖了一个用 Java 编写的 RAM 磁盘设备。
另一个适用于 Linux 的。更深入地了解为什么您可能还需要 Java 中的 DD(因为有些人对其他帖子和评论的外观感到疑惑)
设备驱动程序可能是很多东西
我实际上用java编写设备驱动程序为生:工业设备的驱动程序,例如秤或称重设备、包装机、条形码扫描仪、称重桥、袋子和盒子打印机…… Java在这里是一个非常好的选择。
工业设备与您的家庭/办公设备(例如扫描仪、打印机)非常不同。特别是在制造业(例如食品)中,公司越来越多地选择运行MES应用程序(例如用Java开发)的集中式服务器。MES服务器需要与生产线的设备接口,但也包含业务逻辑。Java 是一种可以两者兼得的语言。
您的家庭/办公设备通常内置于您的计算机或通过 USB 电缆连接,这些工业设备通常使用以太网或 RS232 连接器。因此,从本质上讲,几乎每种语言都可以完成这项工作。
这方面还没有太多的标准化。大多数供应商更喜欢为他们的设备创建自己的协议。毕竟他们是硬件制造商,而不是软件天才。结果是协议的高度多样化。一些供应商更喜欢简单的纯文本协议,但其他供应商更喜欢带有 CRC 码、帧的复杂二进制协议……有时他们喜欢堆叠多个协议(例如,在 OPC 层之上的供应商特定握手算法)。强大的 OOP 语言在这里有很多优势。
例如,我看到 java 以 100 毫秒/周期的连续速度打印。这包括生成一个独特的标签,将其发送到打印机,接收确认,将其打印在纸上并使用气压将其粘贴到产品上。
总结一下,java的强大之处:
这并非不可能,但可能很难,而且可能没有多大意义。
有可能,因为Java是一门普通的编程语言,只要你有办法访问数据就没有问题。通常在现代操作系统中,内核有一个层允许以某种方式对硬件进行原始访问。用户空间中也已经存在驱动程序,至少用户空间部分在Java中实现应该没有问题。
这可能没有太大意义,因为内核必须启动一个 JVM 来执行驱动程序。此外,JVM 实现通常会占用大量内存。
您还可以使用编译为在平台上本地执行的 Java 代码(而不是借助 JVM)。这通常效率不高,但它可能适用于设备驱动程序。
问题是,用Java实现驱动有意义吗?或者换一种说法:如果您使用 Java 来实现驱动程序而不是其他替代方案,您希望得到什么好处?如果你能回答这个问题,你应该想办法让它成为可能。
最后是对JNode的提示,该项目试图实现一个完全基于 Java 的完整操作系统。
您可能听说过对 JDK 的引用吗?
如果没有本机代码来提供 (1) 特定于操作系统的驱动程序入口点和约定,以及 (2) JVM 实例之间的交互,则 100% 用 Java 编写设备驱动程序是不可能的。JVM 实例可以“进程内”启动(并且“进程内”可能具有不同的含义,具体取决于操作系统以及驱动程序是内核模式还是用户模式驱动程序),也可以作为单独的用户区启动一个瘦的本机驱动适配层可以与之通信的进程,并且所述驱动适配层可以在其上卸载实际的用户空间工作。
您对设备驱动程序的看法过于狭隘。
我已经在汽车应用程序的 MOST 之上编写了此类设备驱动程序。如果 Java 有一个像样的 USB 库,那么更广泛的用途可能是 USB 设备的驱动程序。
在这些情况下,有一个在本机代码中处理的通用低级协议,Java 驱动程序处理设备细节(数据格式、状态机等)。
可以将java 代码编译为硬件本机(即不是 JVM 字节码)指令。参见例如GCJ。有了这些,您就比以前更接近于编译设备驱动程序了。
不过,我不知道它有多实用。
可能的?
可以,但仅限于特殊情况。因为你可以用 Java 和 C# 编写操作系统,然后,应该能够为它编写设备驱动程序。这些驱动程序和操作系统的内存损失将是巨大的。
可能?
不见得。至少不在 Windows 或 MacOS 甚至 Linux 的世界里……至少不会很快。因为像 C# 和 Java 这样的语言依赖于 CLR 和 JVM。这些语言的工作方式意味着它们不能有效地加载到 ring0 中。
此外,如果在设备驱动程序中使用托管语言,性能损失会相当大。
出于动机,请记住有很多比 C 更好的快速编程语言;它们可能不如 C 快,但它们是安全的语言:如果你犯了错误,你不会得到未定义的行为。“未定义的行为”包括执行某些攻击者提供的任意代码,这些代码会格式化您的 HD。许多函数式语言通常被编译为本机代码。
设备驱动程序包含操作系统内核中最多的错误——我知道对于 Linux(Linus Torvalds 和其他人一直这么说),我听说对于 Windows。对于磁盘或以太网驱动程序,您需要一流的性能,而 Linux 驱动程序是当今10G以太网或 SSD 磁盘的瓶颈,大多数驱动程序不需要那么高的速度 - 所有计算机都以相同的速度等待。
这就是为什么有各种项目允许编写在内核之外运行的驱动程序,即使这会导致速度变慢;当你能做到这一点时,你可以使用任何你想要的语言;您将需要为您使用的硬件控制库进行 Java 绑定 - 如果您使用 C 编写驱动程序,您仍然会有一个带有 C 绑定的库。
对于内核模式下的驱动程序,我还没有提到两个问题:
垃圾收集,这是一个严格的要求。你需要编写一个内核垃圾收集器;一些 GC 算法依赖于虚拟内存,你不能使用它们。此外,您可能需要扫描整个操作系统内存以找到 GC 的根。最后,我只相信保证(软)实时 GC 的算法,这会使开销更大。阅读了有关 Linux 之上的 Java 设备驱动程序的论文,他们只是放弃了,并要求程序员手动释放内存。他们试图争辩说这不会损害安全性,但我认为他们的论点没有说服力——甚至不清楚他们是否理解垃圾收集是安全语言所必需的。
反射和类加载。一个完整的 Java 实现,即使在运行本机代码时,也需要能够加载新代码。这是一个您可以避免使用的库,但如果您在内核中有解释器或 JIT 编译器(并且没有真正的原因使其在技术上不可能)。
表现。关于 Linux 上的 JVM 的论文非常糟糕,而且它们的性能数字也没有令人信服——事实上,他们测试了一个 USB 1.1 网络驱动程序,然后证明性能还不错!但是,只要付出足够的努力,肯定可以做得更好。
最后两件事:
设备驱动程序必须用可以在内核中执行的语言编写,或者编译到内核中,或者在运行时作为模块加载。这通常排除了用 Java 编写设备驱动程序,但我想您理论上可以在设备驱动程序中实现 JVM 并让它执行 Java 代码。并不是任何理智的人都愿意这样做。
在 Linux 上,有几个文件系统的用户级(即非内核)实现,它们使用一个称为 (fuse) 的通用抽象层,它允许用户级程序实现通常在内核中完成的事情。
Windows Driver Foundation (WDF) 是一个 Microsoft API,它允许编写用户模式和内核模式设备驱动程序。这是今天完成的,它现在与 w2k 及更高版本兼容(过去没有 w2k 作为支持的目标)。没有理由不能进行 JNI 调用以在 JRE 中执行某些工作。. . (假设 JNI 仍然是从 C/C++ 调用 Java 的方式......我的知识在那个领域已经过时了)。这可能是一种有趣的方式,可以让高级算法直接从 USB 管道中获取数据以达到某种效果。. . 酷的东西!
PCIe 用户空间设备驱动程序可以用纯 Java 编写。有关 OFED 上下文中基于内存的直接硬件访问的详细信息,请参阅JVerbs。这是一种可用于创建高性能系统的技术。
您可以检查 PCI 总线以确定给定设备的内存区域、它具有哪些端口等。内存区域可以映射到 JVM 的进程中。
当然,您有责任自己实施一切。
我没说容易。我说可能。;)
另请参阅用户空间中的设备驱动程序,其中讨论了使用 UIO 框架构建用户空间驱动程序。
首先,请注意,我不是设备驱动程序方面的专家(尽管我以前自己写过一些),更不用说 Java 方面的专家了。
让我们暂时搁置用高级语言编写设备驱动程序不是一个好主意(出于性能和可能的许多其他原因)这一事实,并回答您的问题。
至少在理论上,您几乎可以用任何语言编写设备驱动程序。
然而,大多数设备驱动程序需要做很多低级的事情,比如处理中断和使用操作系统 API 和系统调用与操作系统通信,我相信你在 Java 中做不到。
但是,如果您的设备使用串行端口或 USB 进行通信,并且操作系统不一定需要知道该设备(只有您的应用程序会访问该设备*),那么您可以在任何提供访问设备的必要手段的语言。
例如,您可能无法用 Java 编写 SCSI 卡驱动程序,但您可以为专有控制设备、USB 熔岩灯、许可证加密狗等编写驱动程序。
* 这里明显的问题是,当然,这算作司机吗?