Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Rust to Read a Shared Memory Mapped file created and maintained by another process

Goal

I'm just beginning in Rust, and wanted to try a small project where I collect some data from the Iracing Simulator. It makes its data and telemetry available via a memory mapped file.

Working example in another language

In Python I can read the file and show that it is accessible using this code:

import mmap

MEMMAPFILE = 'Local\\IRSDKMemMapFileName'
MEMMAPFILESIZE = 1164 * 1024

iracing_data = mmap.mmap(0, MEMMAPFILESIZE, MEMMAPFILE, access=mmap.ACCESS_READ)
print(iracing_data.readline())

And the output looks something like this, which is expected

b'\x02\x00\x00\x00\x01\x00\x00\x00<\x00\x00\x00"\x00\x00\x00\x00\x00\x08\x00p\x00\x00\x005\x01\x00\x00p\x00\x08\x00\x03\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x\x00\x00@\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xc2\x01\x00p\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xc2\x01\x00p`\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xc2\x01\x00p\xc0\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00---\n'

As a starting point I wanted to reproduce this in Rust and proceed from there, but I haven't figured out how to even read the file.

This is in a **Windows **environment (not WSL).

What did I try?

I found the memmap2 crate, but as far as I can tell, it will only produce memory mapped files that can be accessed in the same process. I couldn't find anything in the docs about accessing shared memory and reading existing memory mapped files.

I also found the shared_memory crate, and I tried tweaking an example to read a file, with no success.

fn main() {

    let iracing_file_location = "Local\\IRSDKMemMapFileName";
    
    let iracing_memory = match ShmemConf::new().size(1164 * 1024).flink(iracing_file_location).open() {
        Ok(m) => m,
        Err(ShmemError::LinkExists) => ShmemConf::new().flink(iracing_file_location).open().unwrap(),
        Err(e) => {
            eprintln!("Unable to create or open shmem flink {iracing_file_location} : {e}");
            return;
        }
    };

    let iracing_pointer = iracing_memory.as_ptr();

    unsafe {
        std::ptr::read_volatile(iracing_pointer);
    }
}

Produces:

Unable to create or open shmem flink Local\\\\IRSDKMemMapFileName : Opening the link file failed, The system cannot find the path specified. (os error 3)

I have no doubt I am fundamentally misunderstanding some concepts here, so any help would be appreciated.

I recognise that there is lots of discussion about the value and safety of using memory mapped files within Rust and that Rust may not be the tool for the job here.

like image 743
Shaun H Avatar asked Dec 10 '25 11:12

Shaun H


1 Answers

I have not accessed the IRacing shared memory file, but I have done the same for Automobilista 2, which uses the same shared memory API to publish telemetry. I'm using the windows API crate to open and access the data.

To test, I copied some parts of my AMS2 code and hastily wrote the following test. It opens the header of the IRacing shared memory and then maps a struct to that memory area. The contents of the struct are derived from the information in this IRacing forum post.

Disclaimer: I'm a rust novice, so the following code is probably horrendous from a rust and safety point.

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct IRacingHeader {
    pub ver: i32,
    pub status: i32,
    pub tick_rate: i32,
    pub session_info_update: i32,
    pub session_info_len: i32,
    pub session_info_offset: i32,
    pub num_vars: i32,
    pub var_header_offset: i32,
    pub num_buf: i32,
    pub buf_len: i32,
    pub padding: [u32; 2],
}

#[test]
fn test_open_mem() {
    let file_name = r"Local\IRSDKMemMapFileName";

    let file_name_pcstr = PCSTR::from_raw((file_name.to_string() + "\0").as_str().as_ptr());
    let file_handle = unsafe { OpenFileMappingA(PAGE_READONLY.0, FALSE, file_name_pcstr) }
        .expect("Failed to open shared memory");
    let view_handle = unsafe {
        MapViewOfFile(
            file_handle,
            FILE_MAP_WRITE,
            0,
            0,
            size_of::<IRacingHeader>(),
        )
    };
    let header: IRacingHeader = unsafe {
        let mem_ptr = view_handle.Value as *const IRacingHeader;
        core::ptr::read_volatile(mem_ptr)
    };

    println!("{:?}", header);

    unsafe {
        UnmapViewOfFile(view_handle).expect("Unmap failed");
        CloseHandle(file_handle).expect("Close failed");
    };
}

The output from the code above when IRacing is running is the following (I added LF for clarity):

IRacingHeader { 
  ver: 2, 
  status: 1, 
  tick_rate: 60,
  session_info_update: 4,
  session_info_len: 524288,
  session_info_offset: 112,
  num_vars: 314,
  var_header_offset: 524400,
  num_buf: 3,
  buf_len: 7764,
  padding: [0, 0] 
}

I also found another project on Git Hub that does what you want. But using that will take away your goal of learning Rust :)

like image 100
Fredrik Jambrén Avatar answered Dec 13 '25 10:12

Fredrik Jambrén