문제

임베디드 Linux 프로젝트를 위해 물리적 메모리에 직접 액세스하려고 하는데, 사용하기에 가장 적합한 메모리를 지정하는 방법을 잘 모르겠습니다.

장치를 정기적으로 부팅하고 /dev/mem에 액세스하면 원하는 곳 어디에서나 쉽게 읽고 쓸 수 있습니다.그러나 여기서는 모든 프로세스에 쉽게 할당할 수 있는 메모리에 액세스하고 있습니다.내가 하고 싶지 않은 일

/dev/mem에 대한 내 코드는 (모든 오류 검사 등입니다.제거됨):

mem_fd = open("/dev/mem", O_RDWR));
mem_p = malloc(SIZE + (PAGE_SIZE - 1));
if ((unsigned long) mem_p % PAGE_SIZE) {
    mem_p += PAGE_SIZE - ((unsigned long) mem_p % PAGE_SIZE);
}
mem_p = (unsigned char *) mmap(mem_p, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, mem_fd, BASE_ADDRESS);

그리고 이것은 작동합니다.하지만 나는 누구도 손댈 수 없는 기억을 사용하고 싶다.mem=XXXm으로 부팅한 다음 BASE_ADDRESS를 그 이상(그러나 실제 메모리 아래)으로 설정하여 커널이 보는 메모리 양을 제한하려고 시도했지만 동일한 메모리에 지속적으로 액세스하지 않는 것 같습니다.

온라인에서 본 내용에 따르면 ioremap() 또는 remap_pfn_range()(또는 둘 다???)를 사용하는 커널 모듈(괜찮습니다)이 필요할 수도 있지만 어떻게 해야 할지 전혀 모르겠습니다.누구든지 도와줄 수 있나요?

편집하다:내가 원하는 것은 항상 동일한 물리적 메모리(예: 1.5MB 상당)에 액세스하고 해당 메모리를 커널이 다른 프로세스에 할당하지 않도록 따로 설정하는 방법입니다.

나는 링커를 통해 메모리에 공간을 할당하고 다음과 같은 것을 사용하여 액세스할 수 있는 다른 OS(메모리 관리 없음)에 있던 시스템을 재현하려고 합니다.

*(unsigned char *)0x12345678

편집2:좀 더 자세한 내용을 제공해야 할 것 같습니다.이 메모리 공간은 임베디드 애플리케이션을 위한 고성능 로깅 솔루션을 위한 RAM 버퍼로 사용됩니다.우리가 보유한 시스템에는 소프트 재부팅 중에 물리적 메모리를 지우거나 스크램블하는 기능이 없습니다.따라서 물리적 주소 X에 비트를 쓰고 시스템을 재부팅하면 재부팅 후에도 동일한 비트가 계속 설정됩니다.이는 VxWorks를 실행하는 동일한 하드웨어에서 테스트되었습니다(이 논리는 다른 플랫폼인 FWIW의 Nucleus RTOS 및 OS20에서도 잘 작동합니다).내 생각은 물리적 메모리에 직접 주소를 지정하여 Linux에서 동일한 작업을 시도하는 것이었습니다.그러므로 부팅할 때마다 동일한 주소를 얻는 것이 중요합니다.

아마도 이것이 커널 2.6.12 이상에 대한 것임을 분명히 해야 할 것입니다.

편집3:여기 제 코드가 있습니다. 먼저 커널 모듈에 대한 코드이고 그 다음에는 사용자 공간 애플리케이션에 대한 코드입니다.

이를 사용하려면 mem=95m으로 부팅한 다음 insmod foo-module.ko, 그런 다음 mknod mknod /dev/foo c 32 0을 실행하고 foo-user를 실행하면 종료됩니다.gdb에서 실행하면 할당 시 종료되는 것으로 표시되지만 gdb 내에서는 mmap에서 얻은 주소를 역참조할 수 없습니다(printf는 가능하지만).

foo-module.c

#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/io.h>

#define VERSION_STR "1.0.0"
#define FOO_BUFFER_SIZE (1u*1024u*1024u)
#define FOO_BUFFER_OFFSET (95u*1024u*1024u)
#define FOO_MAJOR 32
#define FOO_NAME "foo"

static const char *foo_version = "@(#) foo Support version " VERSION_STR " " __DATE__ " " __TIME__;

static void    *pt = NULL;

static int      foo_release(struct inode *inode, struct file *file);
static int      foo_open(struct inode *inode, struct file *file);
static int      foo_mmap(struct file *filp, struct vm_area_struct *vma);

struct file_operations foo_fops = {
    .owner = THIS_MODULE,
    .llseek = NULL,
    .read = NULL,
    .write = NULL,
    .readdir = NULL,
    .poll = NULL,
    .ioctl = NULL,
    .mmap = foo_mmap,
    .open = foo_open,
    .flush = NULL,
    .release = foo_release,
    .fsync = NULL,
    .fasync = NULL,
    .lock = NULL,
    .readv = NULL,
    .writev = NULL,
};

static int __init foo_init(void)
{
    int             i;
    printk(KERN_NOTICE "Loading foo support module\n");
    printk(KERN_INFO "Version %s\n", foo_version);
    printk(KERN_INFO "Preparing device /dev/foo\n");
    i = register_chrdev(FOO_MAJOR, FOO_NAME, &foo_fops);
    if (i != 0) {
        return -EIO;
        printk(KERN_ERR "Device couldn't be registered!");
    }
    printk(KERN_NOTICE "Device ready.\n");
    printk(KERN_NOTICE "Make sure to run mknod /dev/foo c %d 0\n", FOO_MAJOR);
    printk(KERN_INFO "Allocating memory\n");
    pt = ioremap(FOO_BUFFER_OFFSET, FOO_BUFFER_SIZE);
    if (pt == NULL) {
        printk(KERN_ERR "Unable to remap memory\n");
        return 1;
    }
    printk(KERN_INFO "ioremap returned %p\n", pt);
    return 0;
}
static void __exit foo_exit(void)
{
    printk(KERN_NOTICE "Unloading foo support module\n");
    unregister_chrdev(FOO_MAJOR, FOO_NAME);
    if (pt != NULL) {
        printk(KERN_INFO "Unmapping memory at %p\n", pt);
        iounmap(pt);
    } else {
        printk(KERN_WARNING "No memory to unmap!\n");
    }
    return;
}
static int foo_open(struct inode *inode, struct file *file)
{
    printk("foo_open\n");
    return 0;
}
static int foo_release(struct inode *inode, struct file *file)
{
    printk("foo_release\n");
    return 0;
}
static int foo_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int             ret;
    if (pt == NULL) {
        printk(KERN_ERR "Memory not mapped!\n");
        return -EAGAIN;
    }
    if ((vma->vm_end - vma->vm_start) != FOO_BUFFER_SIZE) {
        printk(KERN_ERR "Error: sizes don't match (buffer size = %d, requested size = %lu)\n", FOO_BUFFER_SIZE, vma->vm_end - vma->vm_start);
        return -EAGAIN;
    }
    ret = remap_pfn_range(vma, vma->vm_start, (unsigned long) pt, vma->vm_end - vma->vm_start, PAGE_SHARED);
    if (ret != 0) {
        printk(KERN_ERR "Error in calling remap_pfn_range: returned %d\n", ret);
        return -EAGAIN;
    }
    return 0;
}
module_init(foo_init);
module_exit(foo_exit);
MODULE_AUTHOR("Mike Miller");
MODULE_LICENSE("NONE");
MODULE_VERSION(VERSION_STR);
MODULE_DESCRIPTION("Provides support for foo to access direct memory");

foo-user.c

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>

int main(void)
{
    int             fd;
    char           *mptr;
    fd = open("/dev/foo", O_RDWR | O_SYNC);
    if (fd == -1) {
        printf("open error...\n");
        return 1;
    }
    mptr = mmap(0, 1 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 4096);
    printf("On start, mptr points to 0x%lX.\n",(unsigned long) mptr);
    printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr);
    mptr[0] = 'a';
    mptr[1] = 'b';
    printf("mptr points to 0x%lX. *mptr = 0x%X\n", (unsigned long) mptr, *mptr);
    close(fd);
    return 0;
}
도움이 되었습니까?

해결책

Kmalloc + MMAP 부품에 대한 많은 문서를 찾을 수 있다고 생각합니다. 그러나, 나는 당신이 연속적인 방식으로 많은 기억을 할 수 있고 항상 같은 장소에있게 할 수 있는지 확신하지 못합니다. 물론, 모든 것이 항상 동일하다면, 당신은 일정한 주소를 얻을 수 있습니다. 그러나 커널 코드를 변경할 때마다 다른 주소를 얻을 수 있으므로 Kmalloc 솔루션을 사용하지 않습니다.

부팅 시간에 약간의 메모리를 예약해야한다고 생각합니다. 즉, 물리적 메모리를 예약하여 커널이 닿지 않습니다. 그런 다음이 메모리를 ioremap하여 커널 가상 주소를 제공 한 다음 MMAP를 작성하고 멋진 장치 드라이버를 작성할 수 있습니다.

이것은 우리를 다시 데려갑니다 리눅스 장치 드라이버 PDF 형식으로. 15 장을 살펴보면 443 페이지 의이 기술을 설명하고 있습니다.

편집 : ioremap 및 mmap. 나는 이것이 두 단계로 작업을 디버깅하는 것이 더 쉬울 수 있다고 생각합니다. 먼저 ioremap을 오른쪽으로 가져 와서 문자 장치 작동 (예 : 읽기/쓰기)을 사용하여 테스트하십시오. 일단 읽기 / 쓰기를 사용하여 전체 ioremapped 메모리에 안전하게 액세스 할 수 있다는 것을 알게되면 전체 iormapped 범위를 MMAP로 만들려고합니다.

그리고 당신이 곤경에 빠지면 mmaping에 대한 또 다른 질문을 게시 할 수 있습니다.

편집 : remap_pfn_range ioremap은 virtual_adress를 반환합니다. 이제 PFN (페이지 프레임 번호)이 무엇인지 정확히 이해하지 못하지만 전화를받을 수 있다고 생각합니다.

virt_to_phys(pt) >> PAGE_SHIFT

이것은 아마도 올바른 방법 (TM)이 아니지만 시도해야합니다.

또한 foo_mem_offset이 RAM 블록의 물리적 주소인지 확인해야합니다. 즉, MMU에 어떤 일이 발생하기 전에, 메모리는 프로세서의 메모리 맵에서 0에서 사용할 수 있습니다.

다른 팁

답변을 드려 죄송하지만 답변이 정확하지 않습니다. 이미 질문을 편집하신 것으로 확인되었습니다.SO는 질문을 편집할 때 우리에게 알리지 않습니다.여기서는 일반적인 답변을 제공합니다. 질문을 업데이트할 때 댓글을 남겨주시면 답변을 수정하겠습니다.

예, 모듈을 작성해야 합니다.결론은 다음을 사용하는 것입니다. kmalloc() (커널 공간에 영역 할당) 또는 vmalloc() (사용자 공간에 지역 할당)

이전 항목을 노출하는 것은 쉽지만, 필요에 따라 설명하는 인터페이스 종류를 사용하면 후자를 노출하는 것이 어려울 수 있습니다.1.5MB는 실제로 예약해야 하는 대략적인 추정치라고 말씀하셨는데, 그게 철통인가요?즉, 커널 공간에서 가져오는 것이 편합니까?사용자 공간(또는 디스크 절전 모드)에서 ENOMEM 또는 EIO를 적절하게 처리할 수 있습니까?IOW, 이 지역에는 무엇이 들어가나요?

또한 동시성이 문제가 될까요?그렇다면 futex를 사용하시겠습니까?둘 중 하나에 대한 대답이 '예'(특히 후자)인 경우에는 과감하게 다음과 같이 해야 할 가능성이 높습니다. vmalloc() (또는 내부에서 커널이 부패할 위험이 있습니다).또한, 만약 당신이 ioctl() char 장치에 대한 인터페이스(특히 일부 임시 잠금 아이디어의 경우)를 사용하고 싶습니다. vmalloc().

또한, 읽어보셨나요? 이것?게다가 우리는 grsec / selinux가 이것을 어떻게 생각할 것인지에 대해서도 다루지 않습니다(사용 중인 경우).

/dev/mem은 간단한 레지스터 엿보기 및 찌르기에 적합하지만 일단 인터럽트와 DMA 영역으로 건너면 커널 모드 드라이버를 작성해야합니다. 이전 메모리 관리가없는 OS를 위해 한 일은 단순히 Linux와 같은 범용 OS로 잘 이식되지 않습니다.

이미 DMA 버퍼 할당 문제에 대해 생각했습니다. 이제 장치에서 "DMA 완료"인터럽트에 대해 생각해보십시오. 인터럽트 서비스 루틴을 어떻게 설치 하시겠습니까?

게다가, /dev /mem은 일반적으로 뿌리가 아닌 사용자를 위해 잠겨 있으므로 일반적인 용도로는 실용적이지 않습니다. 물론, 당신은 그것을 chmod 할 수 있지만, 시스템에 큰 보안 구멍을 열었습니다.

드라이버 코드베이스를 OS간에 유사하게 유지하려는 경우, IOCTL 유사 인터페이스를 사용하여 별도의 사용자 및 커널 모드 레이어로 리팩토링을 고려해야합니다. 사용자 모드 부분을 일반 C 코드 라이브러리로 작성하면 Linux와 다른 OS를 쉽게 포업 할 수 있어야합니다. OS- 특정 부분은 커널 모드 코드입니다. (우리는 운전자에게 이런 종류의 접근 방식을 사용합니다.)

이미 커널 드라이버를 쓸 때가되었다고 결론을 내린 것 같습니다. 그래서 당신은 올바른 길을 가고 있습니다. 내가 추가 할 수있는 유일한 조언은이 책들을 다루는 것입니다.

리눅스 장치 드라이버

Linux 커널 이해

(이 책들은 2005 년경이므로 정보는 약간 날짜가 있습니다.)

나는이 문제에 대해 전문가가 아니기 때문에 이것은 대답보다는 당신에게 질문이 될 것입니다. 작은 램 디스크 파티션을 만들어 응용 프로그램에만 사용할 수없는 이유가 있습니까? 그것은 당신에게 동일한 기억 덩어리에 대한 접근을 보장하지 않습니까? I/O 성능 문제 나 그와 관련된 추가 오버 헤드가 있는지 확실하지 않습니다. 이것은 또한 커널에 메모리의 특정 주소 범위를 분할하도록 알릴 수 있다고 가정합니다.

Newb 질문에 대해 사과드립니다. 그러나 귀하의 질문이 흥미로워졌으며 Ram 디스크가 그런 식으로 사용될 수 있는지 궁금합니다.

'Memmap'커널 매개 변수를 살펴 보셨습니까? i386 및 x64_64에서는 Memmap 매개 변수를 사용하여 커널이 매우 구체적인 메모리 블록을 수행하는 방법을 정의 할 수 있습니다 ( Linux 커널 매개 변수 선적 서류 비치). 귀하의 경우, Linux가 전혀 건드리지 않도록 메모리를 '예약'으로 표시하고 싶을 것입니다. 그런 다음 코드를 작성하여 해당 절대 주소와 크기를 사용할 수 있습니다 (해당 공간 밖으로 나가면 Woe Be To You).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top