Userfaultfd 不会收到分叉事件,除非之前完成注册

问题描述 投票:0回答:0

我正在创建一个程序,它使用

userfaultfd
来操作子进程的某些页面。我需要这个程序来监控所有的子程序,所以我正在用
EVENT_FORK
收听叉子。但是,除非我先
register
一个虚拟页面,否则这不起作用:

Cargo.toml:

[package]
name = "..."
version = "0.1.0"
edition = "2021"

[dependencies]
nix = "0.26.2"
rustix = { version = "0.37.3", features = ["mm"] }
userfaultfd = "0.5.1"

main.rs:

use {nix::unistd, rustix::mm, std::ptr};

fn main() {
    // Allocate a "dummy" page for uffd to register
    println!("Allocating dummy page");
    let dummy_page_size = 4096;
    let dummy_page = unsafe {
        mm::mmap_anonymous(
            ptr::null_mut(),
            dummy_page_size,
            mm::ProtFlags::READ | mm::ProtFlags::WRITE,
            mm::MapFlags::PRIVATE,
        )
        .expect("Unable to allocate memory")
    };

    // Initialize uffd
    println!("Building uffd");
    let uffd = userfaultfd::UffdBuilder::new()
        .require_features(userfaultfd::FeatureFlags::EVENT_FORK)
        .require_ioctls(userfaultfd::IoctlFlags::empty())
        .close_on_exec(false)
        .user_mode_only(true)
        .non_blocking(false)
        .create()
        .expect("Unable to create uffd");

    // Perform a dummy register to fully initialize the uffd.
    println!("Registering dummy page");
    uffd.register(dummy_page, dummy_page_size)
        .expect("Unable to perform dummy register");

    // Fork on a separate thread
    let _ = std::thread::spawn(move || {
        println!("Forking");
        match unsafe { unistd::fork().expect("Unable to fork") } {
            // On the parent, wait for the child
            unistd::ForkResult::Parent { child } => {
                println!("Waiting for child {child}");
                let _ = nix::sys::wait::wait().expect("Unable to wait for child");
                println!("Child {child} exited");
            },

            // On the child, execute the command
            unistd::ForkResult::Child => {
                println!("Executing child");
                std::thread::sleep(std::time::Duration::from_secs(1));
            },
        }
    });

    println!("Entering uffd event loop");
    while let Some(event) = uffd.read_event().expect("Unable to read event") {
        println!("Event: {event:?}");
    }
    println!("Exiting uffd event loop");
}

输出:

Allocating dummy page
Building uffd
Registering dummy page
Entering uffd event loop
Forking
Event: Fork { uffd: Uffd { fd: 4 } }
Waiting for child 228151
Executing child
Child 228151 exited

重要的是收到的

Event: Fork { uffd: Uffd { fd: 4 } }
事件。

去掉

uffd.register(dummy_page, dummy_page_size)
部分,输出如下(不管
mmap
分配是否发生)

Allocating dummy page
Building uffd
Registering dummy page
Entering uffd event loop
Forking
Waiting for child 228349
Executing child
Child 228349 exited

其中不包含 fork 事件。这是为什么?

编辑:

即使使用虚拟寄存器,如果子进程本身进行分叉,也不会收到分叉事件。 我还没有测试过在孩子身上写寄存器是否有效,但我怀疑这会让我看到孩子的叉子。

运行时,如果相关的话,我使用

sudo setcap cap_sys_ptrace=ep ./target/debug/...
来运行程序。

执行像

uffd.register(std::ptr::null_mut(), 0).unwrap_err()
这样的无效注册不会导致分叉事件。

在做虚拟寄存器时,

fork
调用停止,直到
read_event
在主线程中发生。 当不做虚拟寄存器时,
fork
根本不等待。

linux rust fork
© www.soinside.com 2019 - 2024. All rights reserved.