Frage

Ich versuche, physischen Speicher zuzugreifen direkt für ein eingebettetes Linux-Projekt, aber ich bin nicht sicher, wie ich kann am besten bezeichnen Speicher für meine Verwendung.

Wenn ich mein Gerät regelmäßig starten und Zugang / dev / mem, kann ich leicht zu fast überall Ich will lesen und schreiben. Doch in diesem, ich Zugriff auf den Speicher, die leicht zu jedem Prozess zugeordnet werden können; die ich will nicht tun

Mein Code für / dev / mem ist (alle Fehlerprüfung usw. entfernt):

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);

Und das funktioniert. Ich möchte jedoch Speicher zu verwenden, die sonst niemand berühren. Ich habe versucht, die Menge an Speicher zu begrenzen, dass der Kernel mit mem = xxxM durch Booten sieht, und dann BASE_ADDRESS etwas über diese Einstellung (aber unter dem physischen Speicher), aber es scheint nicht konsequent den gleichen Zugriff auf den Speicher zu.

Nach dem, was ich online gesehen habe, vermute ich, ich kann ein Kernel-Modul benötigen (was in Ordnung ist), die entweder ioremap () oder remap_pfn_range () (oder beide ???) verwendet, aber ich habe absolut keine Ahnung, wie ; kann jemand helfen?

EDIT: Was ich will, ist ein Weg, um immer den gleichen physikalischen Speicher zugreifen (etwa 1,5 MB wert) und beiseite, dass der Speicher so eingestellt, dass der Kernel es zu einem anderen Verfahren nicht zuteilen wird.

Ich versuche, ein System zu reproduzieren wir (ohne Speicherverwaltung) in anderen OSes hatten, wobei ich über den Linker einen Platz im Speicher zuweisen konnte, und es Zugang mit so etwas wie

*(unsigned char *)0x12345678

EDIT2: Ich denke, ich sollte etwas mehr Details liefern. Dieser Speicherplatz wird für einen RAM-Puffer für eine Hochleistungs-Logging-Lösung für eine eingebettete Anwendung verwendet werden. In den Systemen, die wir haben, gibt es nichts, das oder während einer Soft-Reboot kriecht physischen Speicher löscht. Wenn ich also ein wenig in eine physikalische Adresse X zu schreiben, und das System neu starten, wird das gleiche Bit immer noch nach dem Neustart eingestellt werden. Dies wurde auf der exakt gleichen Hardware laufen VxWorks (diese Logik auch auf verschiedenen Plattformen funktioniert gut in Nucleus RTOS und OS20, FWIW) getestet. Meine Idee war, die gleiche Sache in Linux zu versuchen, durch physikalische Speicher direkt adressieren; Daher ist es wichtig, dass ich die gleichen Adressen jedes Boot bekommen.

Ich soll wohl klarstellen, dass dies für Kernel 2.6.12 und höher.

EDIT3: Hier ist mein Code, zunächst für das Kernel-Modul, dann für die User-Space-Anwendung.

Um es zu benutzen, ich mit mem = 95m booten, dann insmod foo-module.ko, dann mknod mknod / dev / foo c 32 0, dann foo-Benutzer ausgeführt werden, wo es stirbt. Laufen unter gdb zeigt, dass es bei der Zuordnung stirbt, obwohl innerhalb gdb, kann ich nicht dereferenzieren die Adresse, die ich von mmap bekommen (obwohl printf kann)

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;
}
War es hilfreich?

Lösung

Ich glaube, Sie viel Dokumentation über die kmalloc + Mmap finden können. Allerdings bin ich nicht sicher, dass Sie so viel Speicher in einer zusammenhängenden Weise kmalloc können, und haben es immer an der gleichen Stelle. Klar, wenn alles immer gleich ist, dann könnten Sie eine konstante Adresse. Doch jedes Mal, wenn Sie den Kernel-Code zu ändern, werden Sie eine andere Adresse bekommen, so würde ich nicht mit der kmalloc Lösung gehen.

Ich glaube, Sie beim Booten eine Erinnerung behalten sollte, das heißt einige physischen Speicher reservieren, damit wird nicht durch den Kern berührt. Dann können Sie diesen Speicher ioremap, die Ihnen eine virtuelle Kernel-Adresse, und dann können Sie es mmap und einen schönen Gerätetreiber schreiben.

Dies nimmt uns zurück zu Treiber Linux Geräten im PDF-Format. Werfen Sie einen Blick in Kapitel 15, es beschreibt diese Technik auf Seite 443

Edit: ioremap und mmap. Ich denke, das könnte einfacher sein, Dinge zu tun, zu debuggen in zwei Schritt: Holen Sie zuerst die ioremap rechts, und testen Sie es eine Zeichengerätebedienung unter Verwendung, dh Lese- / Schreib. Sobald Sie wissen, dass Sie sicher Zugriff auf den gesamten ioremapped Speicher mit Lese- / Schreib haben können, dann versuchen Sie den ganzen ioremapped Bereich mmap.

Und wenn Sie in Schwierigkeiten geraten kann eine andere Frage sein, schreiben über mmaping

Edit: remap_pfn_range ioremap gibt einen virtual_adress, die Sie zu einem PFN für remap_pfn_ranges konvertieren. Nun, ich weiß nicht genau verstehen, was ein PFN (Seitenrahmennummer), aber ich glaube, Sie einen Aufruf bekommen

virt_to_phys(pt) >> PAGE_SHIFT

Dies ist wahrscheinlich nicht der richtige Weg (tm), es zu tun, aber man sollte es versuchen

Sie sollten auch prüfen, ob FOO_MEM_OFFSET die physische Adresse des RAM-Blocks ist. Dh, bevor irgendetwas mit der MMU geschieht, Ihr Gedächtnis ist bei 0 in der Speicherkarte des Prozessors zur Verfügung.

Andere Tipps

Sorry, aber nicht ganz die Antwort zu beantworten, bemerkte ich, dass Sie schon die Frage bearbeitet haben. Bitte beachten Sie, dass SO uns nicht benachrichtigt werden, wenn Sie die Frage bearbeiten. Ich bin hier, um eine allgemeine Antwort zu geben, wenn Sie die Frage aktualisieren Sie bitte einen Kommentar hinterlassen, dann werde ich meine Antwort bearbeiten.

Ja, Sie gehen zu müssen, um ein Modul zu schreiben. Worauf es ankommt, ist die Verwendung von kmalloc() (eine Region im Kernel-Raum Zuteilung) oder vmalloc() (eine Region in User-Space Zuweisung).

Offenlegen der Stand der einfach ist, kann letzteres Aussetzen mit der Art der Schnittstelle ein Schmerz in der Rückseite, die Sie je nach Bedarf beschreiben. Sie bemerkt, 1,5 MB ist eine grobe Schätzung, wie viel Sie tatsächlich reservieren müssen, ist, dass Eisen gekleidet? D.h sind Sie sicher, dass aus dem Kernel-Raum zu nehmen? Können Sie sich ausreichend mit ENOMEM oder EIO von Anwenderseite (oder sogar Platten Schlaf) umgehen? IOW, was in dieser Region gehen?

Auch wird Gleichzeitigkeit mit diesem Thema sein? Wenn ja, werden Sie mit einem futex werden? Wenn die Antwort auf entweder ‚Ja‘ ist (vor allem der letztere), seine wahrscheinlich, dass Sie die Kugel beißen und geht mit vmalloc() (oder Risiko kernel rot von innen). Auch, wenn Sie sind auch über eine ioctl() Schnittstelle zum char Gerät DENKEN (vor allem für einige Ad-hoc-Sperr Idee), wünschen Sie wirklich mit vmalloc() gehen.

Außerdem haben Sie gelesen?

/ dev / mem ist in Ordnung für eine einfache Register Peeks und stochert, aber sobald man in Interrupts und DMA-Gebiet durchqueren, sollten Sie wirklich einen Kernel-Modus-Treiber schreiben. Was Sie getan haben für Ihren vorherigen Speicher-Management-less OSes nicht pfropfen einfach nicht gut auf einem Mehrzweck-Betriebssystem wie Linux.

Sie haben schon gedacht, über die DMA-Pufferzuweisung Problem. Nun denkt, über den „DMA done“ von Ihrem Gerät unterbrechen. Wie wollen Sie eine Interrupt Service Routine installieren?

Außerdem / dev / mem ist in der Regel für Nicht-Root-Benutzer gesperrt, so dass es für den allgemeinen Gebrauch nicht sehr praktisch ist. Sicher, man könnte es chmod, aber dann haben Sie eine große Sicherheitslücke im System geöffnet.

Wenn Sie versuchen, die Treiber-Codebasis ähnlich zwischen dem OSes zu halten, sollten Sie Refactoring es in separate Benutzern & Kernel-Modus-Schichten mit einer IOCTL-ähnlicher Oberfläche in-between. Wenn Sie den Benutzermodus-Teil als generische Bibliothek von C-Code zu schreiben, sollte es in dem Hafen zwischen Linux und anderen Betriebssystemen einfach sein. Der OS-spezifischer Teil ist der Kernel-Modus-Code. (Wir verwenden diese Art von Ansatz für unsere Fahrer.)

Es scheint, wie Sie bereits zu dem Schluss gekommen, dass es Zeit ist, einen Kernel-Treiber zu schreiben, so dass Sie auf dem richtigen Weg. Der einzige Rat, den ich hinzufügen kann, ist diese Bücher Cover-to-cover lesen.

Linux Device Drivers

der Linux-Kernel zu verstehen

(Beachten Sie, dass diese Bücher sind circa 2005, so dass die Informationen etwas veraltet.)

Ich bin bei weitem kein Experte in diesen Fragen, so wird dies eine Frage für Sie, anstatt eine Antwort. Gibt es einen Grund, warum Sie können nicht nur eine kleine RAM-Disk-Partition und verwenden sie nur für Ihre Anwendung? Wäre das nicht geben Sie den Zugriff auf den gleichen Teil des Speichers gewährleistet? Ich bin nicht sicher, es gäbe keine I / O-Performance-Probleme oder zusätzlicher Aufwand im Zusammenhang mit dem zu tun. Dies setzt auch voraus, dass Sie die Kernel anweisen können einen bestimmten Adressbereich im Speicher zu partitionieren, nicht sicher, ob das möglich ist.

Ich entschuldige mich für die newb Frage, aber ich fand Ihre Frage interessant, und bin neugierig, wenn RAM-Disk in einer solchen Art und Weise verwendet werden könnten.

Haben Sie sich das 'memmap' Kernel-Parameter? Auf i386 und X64_64, können Sie den memmap Parameter zu definieren, wie der Kernel sehr spezifische Speicherblöcke Hand wird (siehe Linux-Kernel-Parameter Dokumentation). In Ihrem Fall würden Sie Speicher markieren als ‚reserviert‘, so dass Linux es gar nicht berühren. Dann können Sie Ihren Code schreiben zu verwenden, die absolute Adresse und Größe (wehe euch, wenn Sie außerhalb dieses Raumes Schritt).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top