1

私はmmapでメモリマッピングを実装しています。私のカーネルモジュールは何かをこのメモリに書き込み、ユーザー空間アプリケーションはこれを読み込みます。要するに、私は0x10000のメモリを割り当てます(カーネル側にはkcalloc、ユーザ側にはmmap)。次に、私はアドレスオフセットに何かを書きます0x00xf000xf000を使ってmemcpyを使用します。カーネルサイドでは、メモリを正しく読み戻すことができます。しかし、ユーザ空間側では、最初の0x1000バイトの内容はメモリ全体(16回)にわたって繰り返されます。しかし、なぜ?mmap'ed memoryの内容が正しくありません(Kernelspace <> Userspace)

彼女は、カーネルモジュールのコードを付属しています。

#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/fs.h> 
#include <linux/device.h> 
#include <linux/slab.h> 
#include <linux/mm.h> 

#define DEV_MODULENAME "expdev" 
#define DEV_CLASSNAME "expdevclass" 
static int   majorNumber; 
static struct class *devClass = NULL; 
static struct device *devDevice = NULL; 

#ifndef VM_RESERVED 
# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) 
#endif 

struct mmap_info 
{ 
    char *data; 
    int reference; 
}; 

static void 
dev_vm_ops_open(struct vm_area_struct *vma) 
{ 
    struct mmap_info *info; 

    // counting how many applications mapping on this dataset 
    info = (struct mmap_info *)vma->vm_private_data; 
    info->reference++; 
} 

static void 
dev_vm_ops_close(struct vm_area_struct *vma) 
{ 
    struct mmap_info *info; 

    info = (struct mmap_info *)vma->vm_private_data; 
    info->reference--; 
} 

static int 
dev_vm_ops_fault(struct vm_area_struct *vma, 
        struct vm_fault  *vmf) 
{ 
    struct page  *page; 
    struct mmap_info *info; 

    info = (struct mmap_info *)vma->vm_private_data; 
    if (!info->data) 
    { 
    printk("No data\n"); 
    return 0; 
    } 

    page = virt_to_page(info->data); 
    get_page(page); 
    vmf->page = page; 

    return 0; 
} 

static const struct vm_operations_struct dev_vm_ops = 
{ 
    .open = dev_vm_ops_open, 
    .close = dev_vm_ops_close, 
    .fault = dev_vm_ops_fault, 
}; 

int 
fops_mmap(struct file   *filp, 
      struct vm_area_struct *vma) 
{ 
    vma->vm_ops   = &dev_vm_ops; 
    vma->vm_flags  |= VM_RESERVED; 
    vma->vm_private_data = filp->private_data; 
    dev_vm_ops_open(vma); 
    return 0; 
} 

int 
fops_close(struct inode *inode, 
      struct file *filp) 
{ 
    struct mmap_info *info; 
    info = filp->private_data; 

    free_page((unsigned long)info->data); 
    kfree(info); 
    filp->private_data = NULL; 
    return 0; 
} 

int 
fops_open(struct inode *inode, 
      struct file *p_file) 
{ 
    struct mmap_info *info; 
    char *data; 
    info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL); 

    // allocating memory on the heap for the data 
    data = kcalloc(0x10000,sizeof(char),GFP_KERNEL); 
    if(data==NULL) 
    { 
    printk(KERN_ERR "insufficient memory\n"); 
    /* insufficient memory: you must handle this error! */ 
    return ENOMEM; 
    } 

    info->data = data; 
printk(KERN_INFO " > ->data:   0x%16p\n",info->data); 
    memcpy(info->data, "Initial entry on mapped memory by the kernel module", 52); 
    memcpy((info->data)+0xf00, "Somewhere", 9); 
    memcpy((info->data)+0xf000, "Somehow", 7); 
printk(KERN_INFO " > ->data: %c%c%c\n", // the output here is correct 
     *(info->data+0xf000+0), 
     *(info->data+0xf000+1), 
     *(info->data+0xf000+2)); 
    /* assign this info struct to the file */ 
    p_file->private_data = info; 
    return 0; 
} 

static const struct file_operations dev_fops = 
{ 
    .open = fops_open, 
    .release = fops_close, 
    .mmap = fops_mmap, 
}; 

static int __init 
_module_init(void) 
{ 
    int ret = 0; 

    // Try to dynamically allocate a major number for the device 
    majorNumber = register_chrdev(0, DEV_MODULENAME, &dev_fops); 
    if (majorNumber<0) 
    { 
    printk(KERN_ALERT "Failed to register a major number.\n"); 
    return -EIO; // I/O error 
    } 

    // Register the device class 
    devClass = class_create(THIS_MODULE, DEV_CLASSNAME); 
    // Check for error and clean up if there is 
    if (IS_ERR(devClass)) 
    { 
    printk(KERN_ALERT "Failed to register device class.\n"); 
    ret = PTR_ERR(devClass); 
    goto goto_unregister_chrdev; 
    } 

    // Create and register the device 
    devDevice = device_create(devClass, 
          NULL, 
          MKDEV(majorNumber, 0), 
          NULL, 
          DEV_MODULENAME 
          ); 

    // Clean up if there is an error 
    if(IS_ERR(devDevice)) 
    { 
    printk(KERN_ALERT "Failed to create the device.\n"); 
    ret = PTR_ERR(devDevice); 
    goto goto_class_destroy; 
    } 
    printk(KERN_INFO "Module registered.\n"); 

    return ret; 

    // Error handling - using goto 
goto_class_destroy: 
    class_destroy(devClass); 
goto_unregister_chrdev: 
    unregister_chrdev(majorNumber, DEV_MODULENAME); 

    return ret; 
} 

static void __exit 
_module_exit(void) 
{ 
    device_destroy(devClass, MKDEV(majorNumber, 0)); 
    class_unregister(devClass); 
    class_destroy(devClass); 
    unregister_chrdev(majorNumber, DEV_MODULENAME); 
    printk(KERN_INFO "Module unregistered.\n"); 
} 

module_init(_module_init); 
module_exit(_module_exit); 
MODULE_LICENSE("GPL"); 

ここでアプリケーション

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

#define PAGE_SIZE (0x10000) 

int main (int argc, char **argv) 
{ 
    int fd; 
    char *address = NULL; 
    time_t t = time(NULL); 
    char *sbuff; 
    int i; 

    sbuff = (char*) calloc(PAGE_SIZE,sizeof(char)); 

    fd = open("/dev/expdev", O_RDWR); 
    if(fd < 0) 
    { 
    perror("Open call failed"); 
    return -1; 
    } 

    address = mmap(NULL, 
        PAGE_SIZE, 
        PROT_READ|PROT_WRITE, 
        MAP_SHARED, 
        fd, 
        0); 
    if (address == MAP_FAILED) 
    { 
    perror("mmap operation failed"); 
    return -1; 
    } 

    printf("%s: first userspace read\n",tbuff); 
    memcpy(sbuff, address,80); 
    printf("Initial message: %s\n", sbuff); 
    memcpy(sbuff, address+0xf00,80); 
    printf("Initial message: %s\n", sbuff); 
    memcpy(sbuff, address+0xf000,80); 
    printf("Initial message: %s\n", sbuff); 

    for(i=0; i<PAGE_SIZE; i++) 
    { 
    printf("%16p: %c\n",address+i, (char)*(address+i)); 
    } 

    if (munmap(address, PAGE_SIZE) == -1) 
    { 
    perror("Error un-mmapping the file"); 
    } 
    close(fd); 
    return 0; 
} 

のコードが付属しており、これはアプリケーションの出力です:

0x7fe61b522000: I 
    0x7fe61b522001: n 
    0x7fe61b522002: i 
    0x7fe61b522003: t 
    0x7fe61b522004: i 
    0x7fe61b522005: a 
    0x7fe61b522006: l 
... 
    0x7fe61b522f00: S 
    0x7fe61b522f01: o 
    0x7fe61b522f02: m 
    0x7fe61b522f03: e 
    0x7fe61b522f04: w 
    0x7fe61b522f05: h 
    0x7fe61b522f06: e 
    0x7fe61b522f07: r 
    0x7fe61b522f08: e 
... 
    0x7fe61b523000: I 
    0x7fe61b523001: n 
    0x7fe61b523002: i 
    0x7fe61b523003: t 
    0x7fe61b523004: i 
    0x7fe61b523005: a 
    0x7fe61b523006: l 
... 
    0x7fe61b523f00: S 
    0x7fe61b523f01: o 
    0x7fe61b523f02: m 
    0x7fe61b523f03: e 
    0x7fe61b523f04: w 
    0x7fe61b523f05: h 
    0x7fe61b523f06: e 
    0x7fe61b523f07: r 
    0x7fe61b523f08: e 
... 
    0x7fe61b524000: I 
    0x7fe61b524001: n 
    0x7fe61b524002: i 
    0x7fe61b524003: t 
    0x7fe61b524004: i 
    0x7fe61b524005: a 
    0x7fe61b524006: l 
... 

繰り返しは1ページ分の大きさだと私には思われます。しかし、これは私には意味がありません。


EDIT 1: 出力にSomewhereを追加します。注:Somehowは決して発生しません!


EDIT 2: 訂正フォルトハンドラ。これで呼び出しのオフセットが考慮されましたvmf。今は魅力のように走ります。 Tsyvarevに感謝します!

static int 
dev_vm_ops_fault(struct vm_area_struct *vma, 
        struct vm_fault  *vmf) 
{ 
    struct page  *page; 
    struct mmap_info *info; 

    info = (struct mmap_info *)vma->vm_private_data; 
    if (!info->data) 
    { 
    printk("No data\n"); 
    return 0; 
    } 

    page = virt_to_page((info->data)+(vmf->pgoff*PAGE_SIZE)); 
    get_page(page); 
    vmf->page = page; 

    return 0; 
} 
+0

カーネルモジュール内のメモリにコピーする2つ目の2つの文字列はゼロで終了しないことに注意してください。 –

+0

なぜでしょうか?この場合、 'chars'をコピーするのはmemcpyです。私はその投稿を編集して、「どこかに」が両側で起こっていることを示していますが、どういうわけかはありません。 – Alex44

+0

問題は、 'sbuff'にコピーしてから' sbuff'を印刷すると、ユーザ空間アプリケーションでデータをゼロ終了文字列として扱うことです。 –

答えて

2

しかし、ユーザ空間側の第1 0x1000`の内容

0x1000

page = virt_to_page(info->data); 
get_page(page); 
vmf->page = page; 

構造vm_operations_structのコールバック.faultが要求されるとマッピングされたページのサイズですユーザーがアクセスしたがまだマップされていないすべてのページ(4096バイト)。

あなたのコードは、ユーザー空間がアクセスするすべてのページに最初に4096バイト(0x1000)のdataをマッピングします。

+0

ああ - これは理にかなっています。フォルトハンドラを訂正するにはどうすればよいですか? – Alex44

+1

'virt_to_page(info-> data)'コールにオフセットをつけた 'data'を渡す必要があります。オフセットは、フォールトされたページのdiffrenceと 'vma'の開始ページによって決定することができます。必要な情報について 'vm_area_struct'と' vm_fault'構造体の定義をチェックしてください。 – Tsyvarev

+0

はい。私は私のポストにソリューションを追加します。ありがとう:) – Alex44

関連する問題