前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Rust FFI 编程 - nix crate

Rust FFI 编程 - nix crate

作者头像
MikeLoveRust
发布2020-05-14 16:44:05
1.5K0
发布2020-05-14 16:44:05
举报

在 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 文件中有如下代码:

代码语言:javascript
复制
// Re-exported external crates
pub extern crate libc;
它通过使用强制合法或安全的类型对 libc 库进行了一次封装,相对于 libc 库
暴露的 unsafe API,它具有两个特点:
  • 用户代码中尽量没有 unsafe
  • Rust 风格的错误处理

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

代码语言:javascript
复制
// 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 库了。

代码语言:javascript
复制
[dependencies]
nix = "0.17.0"
用 nix 如何创建一个子进程

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

代码语言:javascript
复制
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 语言代码:

代码语言:javascript
复制
#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 库来重写上述逻辑,代码如下:

代码语言:javascript
复制
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 的替代方案。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust语言学习交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 Unix 系统编程?
  • nix 库介绍
  • nix 库使用示例
    • 用 nix 如何创建一个子进程
      • fork/kill示例
      • 总结
      相关产品与服务
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档