在 Rust 中使用 nix 这个库,在某些情况下可以简化 Unix 系统编程。本文主要包括以下内容:

  • 前言:什么是 Unix 系统编程?

  • nix 库介绍

  • nix 库使用示例

什么是 Unix 系统编程?

Unix 系统编程实际上是把底层编程和系统设计两个概念混在了一起,本文将其理解为“操作系统层级的编程”。在进行 Unix 系统编程时,关键要熟悉 POSIX 规范 中定义的接口函数,以及 Unix/Linux 的 man 手册,以下是一些示例:

  • 进程管理(例如,fork,kill)

  • 文件处理(例如,read,write)

  • 网络编程(例如,socket,listen)

  • 与硬件交互(例如,ioctl,mmap)

  • Linux容器(例如,clone,mount)

nix 库介绍

nix 库 旨在提供对各种类 Unix 平台(Linux,Darwin等)API 的友好绑定(bindings),其代码地址在:https://github.com/nix-rust/nix。在其 lib.rs 文件中有如下代码:

// Re-exported external cratespub extern crate libc;
它通过使用强制合法或安全的类型对 libc 库进行了一次封装,相对于 libc 库
暴露的 unsafe API,它具有两个特点:
  • 用户代码中尽量没有 unsafe

  • Rust 风格的错误处理

以系统调用 gethostname 为例,我们来看一下,libc 和 nix 之间的区别:

// libc api (unsafe, requires handling return code/errno)pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int;
// nix api (returns a nix::Result<CStr>)pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr>;

不过尽管 nix 库尝试支持 libc 库支持的所有平台,但由于技术或人力限制,仅支持其中的某些平台。可能这也是一些底层库(比如:tokio项目中的mio)在版本v0.6.3之后 移除 对 nix 库依赖的一个原因吧。

nix 库中的模块大致如下:

  • dir,相对标准库中的std::fs::ReadDir更底层的目录接口。

  • errno, nix 库中处理各种类 Unix 系统的错误类型,对于 FreeBSD,IOS,MacOS 系统直接封装的 libc 库中的。

  • fcntl, Unix 系统中文件 IO 的数据结构,以及对文件的各种操作接口。

  • features,用于操作系统级功能的测试。

  • ifaddrs,使用 Linux 或 BSD 中的函数getifaddrs获取网络接口及地址列表。

  • kmod,包含加载和卸载内核模块的功能。

  • mount,包含设备文件的挂载操作,mountumount

  • mqueue, 对应 POSIX 规范中消息队列 API 的功能。

  • net,涉及网络接口的功能。

  • poll,在特点文件描述符上触发 wait 事件。

  • pty,创建主从虚拟伪终端 PTYs。

  • sched,提供 Linux 系统的调度接口。

  • sys,这个模块包括各种系统相关的功能:POSIX 异步 I/O,文件系统事件的监控 API,Socket接口函数等。

  • ucontext,提供协程上下文相关的借接口。

  • unistd,在 libc 库unistd.h头文件中函数的 safe 封装。

nix 库使用示例

在项目的 Cargo.toml 中添加如下配置,就可以导入 nix 库了。

[dependencies]nix = "0.17.0"

用 nix 如何创建一个子进程

我们用 nix 库重写 libc 文章中创建一个子进程的示例,代码如下:

use nix::unistd::*;
fn main() {    match fork() {        Ok(ForkResult::Parent { child }) => {            // 在父进程中            println!("Hello, I am parent thread: {}", getpid());        }        Ok(ForkResult::Child) => {            // 在子进程中            println!("Hello, I am child thread: {}", getpid());            println!("My parent thread: {}", getppid());        }        Err(errno) => {            // fork 创建子进程失败            println!("Fork creation failed!");        }    }}

fork/kill示例

熟悉 POSIX 规范的话,其中的fork()函数可以用来创建一个新的进程(子进程),而kill()函数可以用来向一个或一组进程发送信号。我们来看如下的一段 C 语言代码:

#include <signal.h>#include <unistd.h>
int main(void){    pid_t child = fork();    if (child)    {        sleep(5);        kill(child, SIGKILL);    }    else    {        for (;;)        // 循环直到被 kill 掉        ;    }return 0;}

这段代码有问题吗?

我们知道fork()函数如果执行成功,则向子进程返回 0,并将子进程的进程 ID 返回给父进程。否则,将向父进程返回 -1,不创建子进程,并设置errno来标识错误。

上述代码中没有处理fork()函数失败时的逻辑,这样则可能将 -1(fork的错误结果)视为子进程的进程 ID。这时在随后的程序中关闭子进程kill(child, SIGKILL);,你知道进程 ID 为 -1 时会发生什么吗?

If pid is -1, sig shall be sent to all processes (excluding an unspecified set of system processes) for which the process has permission to send that signal.

如果进程 ID 等于 -1,则将信号发送到调用进程有权发送信号的每个进程,一些系统进程(如init)除外。

kill(-1, SIGKILL);等效于 kill 你有权发送信号的所有其他进程。

我们来看 nix 库中的fork()函数,其返回值为Result<ForkResult, Errno>类型,相比 C 语言中的fork()函数,它有两个优点:

  • Rust的错误处理风格,使用类型Result区分成功和失败的情况

  • 使用枚举类型ForkResult区分返回父/子进程

这时使用 nix 库来重写上述逻辑,代码如下:

use nix::sys::signal::*;use nix::unistd::*;
fn main() {    match fork().expect("fork failed") {        ForkResult::Parent{ child } => {            sleep(5);            kill(child, SIGKILL).expect("kill failed");        }        ForkResult::Child => {            // 直到被 kill 掉            loop {}        }    }}

以上代码示例地址:https://github.com/lesterli/rust-practice/tree/master/ffi/nix

总结

nix 库通过对 libc 库暴露的 unsafe API 进行封装,为 libc 库支持的某些平台提供了一种 safe 的替代方案。

Rust FFI 编程 - nix crate相关推荐

  1. Rust FFI 编程 - Bindgen 工具介绍

    前面我们经历了<Rust FFI 编程 - 基础知识>.<Rust FFI 编程 - 手动绑定 C 库>和<Rust FFI 编程 - Rust 导出共享库>三个大 ...

  2. Rust FFI 编程--理解不同语言的数据类型转换

    1. 简介 "FFI"是" Foreign Function Interface"的缩写,大意为不同编程语言所写程序间的相互调用.鉴于C语言事实上是编程语言界的 ...

  3. Rust FFI 编程 - bindgen 使用示例

    当我们拥有一组具有良好声明的头文件时,自己定义 C 库的 Rust FFI 绑定函数是毫无意义的.我们可以使用 bindgen 这种工具从 C 库的头文件生成 Rust FFI 绑定函数.然后,我们运 ...

  4. Rust中FFI编程知识点整理总结

    Rust语言对FFI的支持 Rust 语言主要在关键字和标准库两个方面对 FFI 提供了支持,具体如下: 关键字 extern 属性 #[no_mangle] 外部块 ExternBlock 及其属性 ...

  5. Rust嵌入式编程---动态内存分配器(Vec,String等)

    本教程不是0基础的Rust嵌入式编程,需要有一定的Rust裸机编程的基础知识. 作为一个比较接近C的例子,适合入门,代码比较容易理解.本次例子使用的是target = "thumbv8m.m ...

  6. Rust FFI 与C语言互相调用

    Rust FFI 与C语言互相调用 参考 cbindgen 简介 二进制方式构建 脚本构建 Demo程序说明 示例工程: makefile test脚本 基本数据类型 Rust侧 C侧 对象 Rust ...

  7. rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio

    1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...

  8. 为什么说 Rust 是编程的未来?

    作者 | Scalac 译者 | 弯月 出品 | CSDN(ID:CSDNnews) 2020年 Stack Overflow 的调查报告显示,Rust 名列最受欢迎编程语言的榜首,86% 的开发人员 ...

  9. Rust语言编程实例

    001: 有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数? fn main() {let mut a = 0;for i in 1..5{for j in 1..5{for k in ...

  10. Rust 高级编程 变性的直观解释

    Rust 高级编程 变性的直观解释 协变在Rust中是一个今人迷惑的话题.对于新手可能感知不到这些概念.本篇内容将给出一个更通俗的理解. 首先, 有两个重要的点我们要关注一下. 变性是一个与通用参数T ...

最新文章

  1. php把单词切割成数组,PHP – 将单词分解为数组
  2. [LeetCode] Find All Numbers Disappeared in an Array 找出数组中所有消失的数字
  3. R开发(part8)--应用R语言中的函数环境空间
  4. php如何使用遮罩,CSS绝对定位实现窗口遮罩功能:2019年1月15日作业
  5. Java千万数据导入mysql_java之5分钟插入千万条数据
  6. merge into ORA-30926
  7. 11 MM配置-主数据-定义物料类型的编码范围
  8. Scrapy Crawl 运行出错 AttributeError: 'xxxSpider' object has no attribute '_rules' 的问题解决...
  9. jsf el 表达式_JSF表达式语言– JSF EL
  10. java 上溯造型与下塑造型
  11. php 正则忽略空白,(PHP)正则表达式-忽略空白
  12. 在VirtualBox中体验Fedora15与GNOME3
  13. 下载pyboard的flash中的驱动程序_教你如何安装打印机驱动程序
  14. 软件测试面试题(等待解答)
  15. 进程隐藏博文 hook
  16. 关于抢购秒杀的实现思路与事例代码
  17. 使用spyder查询帮助文档
  18. 列车信息系统Java+MySQL(没完全实现)
  19. 个人电脑应用常识记录
  20. linux系统可以显示中文输入法,2 Linux支持中文显示和中文拼音输入法(番外篇)...

热门文章

  1. 通过Python爬虫技术获取小说信息
  2. 解决混淆报错问题-打包签名出现问题的解决方法
  3. 已解决Python爬虫网页中文乱码问题
  4. hdu2639(01背包变形-第k大背包)
  5. 5月18日第壹简报,星期三,农历四月十八
  6. 【历史上的今天】4 月 18 日:第一款交互式电子游戏;IBM 率先研发兆位芯片;硬件公司 Roland 成立
  7. 世界上都有哪些常用的聊天软件?
  8. 关于“明显没有bug的代码”的一些拙见
  9. 分门别类刷leetcode——链表(C++实现)
  10. 2021-2025年中国智能储藏加热器行业市场供需与战略研究报告