当前位置:网站首页>Rust FFI 编程 - libc crate
Rust FFI 编程 - libc crate
2022-06-28 06:34:00 【51CTO】
前文警示:如果对 Unix 环境系统编程没有基础知识的话,本文会看得云里雾里。
我们在做 Rust 开发编译的时候,常常能在依赖列表中,看到 libc 这个 crate 的身影。我们一般不会直接依赖这个 crate,但是依赖的依赖(的依赖的依赖……)可能就会用到这个 crate。总的来说,它是 Rust 生态中非常基础非常底层的一个 crate 了。
libc 是什么
libc 是对各平台的系统库的原始 FFI 绑定。其代码地址在:https://github.com/rust-lang/libc。可以看到,这是 Rust 官方维护的一个库。
libc 提供了与 Rust 支持的各平台上的最基础系统 C 库打交道的所有必要设施。它导出了底层平台的类型、函数和常量。
所有内容都直接放置在 libc 这个命名空间下,没有再分模块了。因此,你可以使用 libc::foo
这种形式访问这个库中的任何导出内容。
它可以与 std 配合使用,也可以在 no_std 环境下使用。
libc 的导入
在项目的 Cargo.toml
中添加如下配置,就可以导入 libc 了。
libc 的内容分类
libc 会导出底层 C 库的这些东西:
- C 类型,比如 typedefs, 原生类型,枚举,结构体等等
- C 常量,比如使用
#define
指令定义的那些常量 - C 静态变量
- C 函数(按它们的头文件中定义的函数签名来导出)
- C 宏,在 Rust 中会实现为
#[inline]
函数
另外,libc 中导出的所有 C struct 都已经实现了 Copy
和 Clone
trait.
好吧,熟悉 C 的同学,应该已经知道了,C 的接口,无非也就这些东西了。现在 libc 全给导出来了。
导出的结果是什么呢?直接打开 https://docs.rs/libc/0.2.69/libc/index.html 查看,在你面前将会出现一个长长的网页。有
- Structs 对应 C 中的符号
- Enums 对应 C 中的枚举
- Constants 对应 C 中的常量
- Functions 对应 C 中的函数接口
- Type Definitions 对应 C 中的 typedef 定义的符号
这些符号,可能 99% 的人都不敢打包票说用过 20% 以上,甚至很多专注于上层开发的同学从没见过这些命名。
这一套东西可不得了,它是计算机工程历史这么多年积累下来的成体系的精华之作。这套精华的体系就叫作Unix环境编程。这套体系在《UNIX环境高级编程(第3版)》这本书中做了权威讲解。
这套东西的精华核心在于,它不仅仅是一套符号的简单罗列,其内在包含有一套精巧的机制来驱动。对,是一套机制。这套机制又是由若干个不同的部分组成,这些部分之间区分得非常清晰(Unix 的 KISS 原则),但是在设计理念上,又保持了同一种味道。因此,这套东西,我们称其为工程、技术、哲学、甚至艺术。
这套东西是现代IT工业,互联网的基石。
libc 的界限
熟悉 linux 系统开发的同学都知道,linux 系统本身有个 libc 库,是几乎一切应用的基础库。基本上 linux 下 C 语言写的代码都要链接这个库才能运行。
而 Rust 的 libc crate,不完全等价于 C 的 libc 库的封装。具体区别如下:
- Linux (以及其它 unix-like 平台)下,导出的是 libc, libm, librt, libdl, libutil 和 libpthread 这几个库的符号。
- OSX 下,导出的是 libsystem_c, libsystem_m, libsystem_pthread, libsystem_malloc 和 libdyld 这几个库的符号。
- Windows 下,导出的是 VS CRT(VS C RunTime VS C 运行时库)中的符号。但是这些符号,比前两个平台的符号,数量上要少得多。因此,可以直接这样说,Rust libc crate 在 Windows 平台上的功能有限。在 Windows 平台上,建议使用
winapi
这个 crate 进行开发。
举例:使用 libc 创建子进程
说得那么神乎其神,还是让我们见见 libc 的庐山真面目吧。下面,我们就用一个示例——创建一个子进程——来展示 libc 的用法,以及与 Rust 标准库中线程操作的不同。
Rust 标准库中没有提供创建子进程的设施,不过可以创建一个子线程。作为对比演示,我们就创建一个新线程吧:
以上代码会输出:
下面我们来看看用 libc 如何创建一个子进程:
fn main() {
unsafe {
let pid = libc::fork();
if pid > 0 {
println!("Hello, I am parent thread: {}", libc::getpid());
}
else if pid == 0 {
println!("Hello, I am child thread: {}", libc::getpid());
println!("My parent thread: {}", libc::getppid());
}
else {
println!("Fork creation failed!");
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
这段代码会有类似下面的输出:
具体的进程 id 数字,每次运行都可能会变化。
从两个程序的简单对比,可以发现:
- libc 的所有函数调用,都必须放进
unsafe
块中。因为它的所有调用都是 unsafe 的; - std 的线程操作封装,好用,形象。libc 的进程操作,与 C 语言系统编程一样,完全是另外一套思路和编程风格;
- std 的线程操作虽然简洁,但是也缺少更细颗粒度的控制。而 libc 可以对进程的操作(及后面对子进程的功能扩充,父进程中的信号管理等),做到完全的控制,更加灵活,功能强大;
- std 本身无法实现进程 fork 的功能。
以上代码示例地址: https://github.com/daogangtang/learn-rust/tree/master/07libctest
哪些事情是 Rust std 不能做而 libc 能做的?
几乎所有底层编程的事情(当然这句话并不严谨)。
随便举几个例子:dup2
标准库有吗?openpty
标准库有吗?ioctl
标准库有吗?
ioctl 没有,那就是跟底层 say byebye 啦(进而跟严肃的嵌入式开发绝缘)。当然,你可以说,那我拿 Rust 自己写操作系统呗。对嘛,你用 Rust 写操作系统,也用不上 std 啊。
应该说,使用 libc,类 Unix 平台上的所有系统编程,之前只能由 C 完成的工作,现在都能用 Rust 来做了。在这一层面上,C 能做到的事情,Rust 都能做到。
通过 libc 这一层,Rust 闯入了系统编程领域。
可能,有的同学又要辩解了,不就是一个库嘛,这没什么大不了的。Python 也有对操作系统基础库的封装,Python 一样的可以做系统开发。这点不足以证明 Rust 是一门系统编程语言,Rust 在这一点上没有什么不同。
其实只需要用一句话就能回击这种质疑:因为我 Rust 的封装是 zero cost (零成本)的。
Yes,就这么简单。零成本抽象赋予了 Rust 系统编程的能力。
libc 与 std::os::*::raw 的关系?
细心的同学会发现,在标准库的 os 模块下面,有一些东西与 libc 的重复。
页面 https://doc.rust-lang.org/std/os/raw/index.html 包含了 c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort
。
而 libc 中,对这些内容,也重新定义了一份(比如:https://docs.rs/libc/0.2.69/libc/type.c_char.html)。为什么呢?
std::os::raw 中这些定义,可以用于与一些简单的 C 代码进行交互,比如说不存在系统调用的 C 代码。这个时候,就不需要再引入 libc 库了。
而一旦产生了系统调用或者 Unix 环境编程,那么就得引入 libc 库来操作。
std 下面还有一些 std::os::*::raw 的模块,这些模块现在已经被 Deprecated 了(比如:https://doc.rust-lang.org/std/os/unix/raw/index.html)。文档中明确注释了:
也就是说,这些东西,去 libc 中找吧,用 libc 来实现这些功能。
总结
我们应该庆幸,Rust 标准库为我们提供的人性化的便捷的编程方式。
同时,我们又应该庆幸,Rust 与 C 的亲密血缘关系,让我们 Rustaceans 可以轻松的几乎没有性能损失的用 C 的方式和思维进行最底层的系统编程。
这种小幸运(可能性),不是谁都能拥有的。
我为能掌握 Rust 而感到幸福。
边栏推荐
- FPGA - 7 Series FPGA selectio -08- oserdese2 of advanced logic resources
- Batch import of pictures into WPS table by date
- Configure redis from 0
- ROS rviz_satellite功能包可视化GNSS轨迹,卫星地图的使用
- 【Paper Reading-3D Detection】Fully Convolutional One-Stage 3D Object Detection on LiDAR Range Images
- freeswitch设置最大呼叫时长
- ThreadLocal
- 助力涨点 | YOLOv5结合Alpha-IoU
- sql及list去重操作
- High quality domestic stereo codec cjc8988, pin to pin replaces wm8988
猜你喜欢
Promotion intégrale et ordre des octets de fin de taille
YYGH-BUG-02
The code is correct, and the rendering page does not display the reason
Yolov5 adds a small target detection layer
Working principle of es9023 audio decoding chip
Install and manage multiple versions of PHP under mac
Paper recommendation: efficientnetv2 - get smaller models and faster training speed through NAS, scaling and fused mbconv
整型提昇和大小端字節序
RN7302三相电量检测(基于STM32单片机)
YOLOv5增加小目标检测层
随机推荐
链表(一)——移除链表元素
基本类型和包装类的区别
4. use MySQL shell to install and deploy Mgr clusters | explain Mgr in simple terms
FPGA - 7系列 FPGA SelectIO -07- 高级逻辑资源之ISERDESE2
Yolact++ Pytorch环境
Development trend of mobile advertising: Leveraging stock and fine marketing
调接口事件API常用事件方法
YYGH-BUG-02
职场IT老鸟的几点小习惯
搭建你jmeter+jenkins+ant
Introduction to openscap
socke.io长连接实现推送、版本控制、实时活跃用户量统计
AttributeError: 'callable_ iterator' object has no attribute 'next'
CAD secondary development +nettopologysuite+pgis reference multi version DLL
Linked list (I) - remove linked list elements
Is it safe to open a stock account? How to open a stock account?
MySQL (II) - basic operation
The code is correct, and the rendering page does not display the reason
Xcode13.3.1 error reported after pod install
Tryout title code