使用Linux中的“直接渲染管理器”在dumbbuffer上调用mmap在使用C
有一段时间,我一直在使用
Linux‘
Direct Rendering Manager,这允许我们进行一些非常低级别的图形管理.这通常在C中完成,在
libdrm的帮助下,或直接使用
DRM headers.
我正在尝试在Rust中创建一个与libdrm等效的东西,它不仅仅是对C库的绑定,而是直接使用系统调用.这不是一项容易的任务,因为那里几乎没有DRM的文档,但我正在关注this example in C以获取从哪里开始的提示. 我现在到了我应该创建一个哑缓冲区并将其映射到内存中的点,所以我可以修改屏幕上显示的每像素像素数.为此,我必须使用mmap,但我得到一个非常奇怪的错误. 这是C中的最小工作代码: #include <errno.h> #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <drm/drm.h> #include <drm/drm_mode.h> #include <sys/ioctl.h> #include <sys/mman.h> int main() { // STEP 1: GET ACCESS TO?DRM int fd = open("/dev/dri/card0",O_RDWR | O_CLOEXEC); if (fd < 0) { printf("Error in function open(): %sn",strerror(errno)); return 1; } // STEP 2: CREATE DUMBBUFFER struct drm_mode_create_dumb dreq; dreq.height = 1080,dreq.width = 1920,dreq.bpp = 32,dreq.flags = 0,dreq.handle = 0,dreq.pitch = 0,dreq.size = 0; int ret = ioctl(fd,DRM_IOCTL_MODE_CREATE_DUMB,&dreq); if (ret == -1) { printf("Call to DRM_IOCTL_MODE_CREATE_DUMB failed: %sn",strerror(errno)); return 1; } // STEP 3: ADD FRAMEBUFFER struct drm_mode_fb_cmd creq; creq.fb_id = 0; creq.width = dreq.width; creq.height = dreq.height; creq.pitch = dreq.pitch; creq.bpp = dreq.bpp; creq.depth = 24; creq.handle = dreq.handle; ret = ioctl(fd,DRM_IOCTL_MODE_ADDFB,&creq); if (ret == -1) { printf("Call to DRM_IOCTL_MODE_ADDFB failed: %sn",strerror(errno)); return 1; } // STEP 4: PREPARE FOR?MAPPING struct drm_mode_map_dumb mreq; mreq.handle = dreq.handle; mreq.pad = 0; mreq.offset = 0; ret = ioctl(fd,DRM_IOCTL_MODE_MAP_DUMB,&mreq); if (ret == -1) { printf("Call to DRM_IOCTL_MODE_MAP_DUMB failed: %sn",strerror(errno)); return 1; } // STEP 5: MAPPING?PROPER void *map = mmap(0,dreq.size,PROT_READ | PROT_WRITE,MAP_SHARED,fd,mreq.offset); if (map == MAP_FAILED) { printf("Error in function mmap(): %sn",strerror(errno)); return 1; } else { printf("Address of mapped data: 0x%xn",map); } return 0; } 这是Rust中完全相同的代码.当然,我的真实代码中有更多的东西,但这个最小的代码足以得到错误: #![feature(libc)] extern crate libc; use self::libc::{c_char,c_int,c_ulong,c_void,off_t,size_t}; extern { pub fn ioctl(fd : c_int,request : c_ulong,arg : *mut c_void) -> c_int; } fn errno() -> c_int { unsafe { *libc::__errno_location() } } fn get_c_error() -> String { unsafe { let strerr = libc::strerror(errno()) as *mut u8; let length = libc::strlen(strerr as *const c_char) as usize; let mut string = String::with_capacity(length); for i in 0..length { let car = *strerr.offset(i as isize) as char; if car == (0 as char) { break; } string.push(car); } string } } #[repr(C)] struct CCreateDumb { height : u32,width : u32,bpp : u32,_flags : u32,handle : u32,pitch : u32,size : u64,} #[repr(C)] struct CFrameBuffer { _fb_id : u32,_width : u32,_height : u32,_pitch : u32,_bpp : u32,_depth : u32,_handle : u32,} #[repr(C)] struct CMapDumb { _handle : u32,_pad : u32,offset : u32,} fn main() { // STEP 1: GET ACCESS TO?DRM let pathname = "/dev/dri/card0".to_string(); let fd : c_int = unsafe { libc::open(pathname.as_ptr() as *const c_char,libc::O_RDWR | libc::O_CLOEXEC) }; if fd < 0 { panic!("Error in call of C function open(): {}",get_c_error()); } // STEP 2: CREATE DUMBBUFFER let mut dreq = CCreateDumb { height : 1080,width : 1920,bpp : 32,_flags : 0,handle : 0,pitch : 0,size : 0,}; // NB?: 0xc02064b2 = DRM_IOCTL_MODE_CREATE_DUMB let mut ret = unsafe { ioctl(fd,0xc02064b2 as c_ulong,&mut dreq as *mut _ as *mut c_void) }; if ret == -1 { panic!("Call to DRM_IOCTL_MODE_CREATE_DUMB failed: {}",get_c_error()); } // STEP 3: ADD FRAMEBUFFER let mut creq = CFrameBuffer { _fb_id : 0,_width : dreq.width,_height : dreq.height,_pitch : dreq.pitch,_bpp : dreq.bpp,_depth : 24,_handle : dreq.handle,}; // NB?: 0xc01c64ae = DRM_IOCTL_MODE_ADDFB ret = unsafe { ioctl(fd,0xc01c64ae as c_ulong,&mut creq as *mut _ as *mut c_void) }; if ret == -1 { panic!("Call to DRM_IOCTL_MODE_ADDFB failed: {}",get_c_error()); } // STEP 4: PREPARE FOR?MAPPING let mut mreq = CMapDumb { _handle : dreq.handle,_pad : 0,offset : 0,}; // NB?: 0xc01064b3 = DRM_IOCTL_MODE_MAP_DUMB ret = unsafe { ioctl(fd,0xc01064b3 as c_ulong,&mut mreq as *mut _ as *mut c_void) }; if ret == -1 { panic!("Call to DRM_IOCTL_MODE_MAP_DUMB failed: {}",get_c_error()); } // STEP 5: MAPPING?PROPER let map = unsafe { libc::mmap( 0 as *mut c_void,dreq.size as size_t,libc::PROT_READ | libc::PROT_WRITE,libc::MAP_SHARED,mreq.offset as off_t ) }; if map == libc::MAP_FAILED { panic!("Error in call of C function mmap(): {}",get_c_error()); } else { println!("Address of mapped data: 0x{:p}",map); } } 它编译得很好,但是当我执行它时,我得到了这个错误.
使用extern块直接链接到原始的C mmap函数而不是Rust的crate libc并不会改变任何东西. 我看了this project如何调用mmap,并尝试做同样的事情,以确保大小和偏移量是页面对齐的,但它没有改变任何东西,因为它们已经是页面对齐的. This SO question使用名为std :: os :: MemoryMap的stdlib工具,但它不再存在. 解决方法
所以我使用了Shepmaster推荐的strace,并发现了问题:CMapDumb结构的偏移字段应该是u64而不是u32(我从C原版中复制过快). DRM_IOCTL_MODE_MAP_DUMB IOCTL返回的实际偏移量为33位,我丢失了最重要的偏移量.它现在工作正常.
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |