1. 程式人生 > >例說linux核心與應用資料通訊(四):對映裝置核心空間到使用者態

例說linux核心與應用資料通訊(四):對映裝置核心空間到使用者態

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include <linux/kernel.h>
#include "chr_memdev.h"

int                 chrmem_major;
struct chrmem_dev   *chrmem_devp;

int chrmem_open(struct inode *inode, struct file *filp)
{
    filp->private_data = chrmem_devp;
    
    return 0;
}

......

void sln_vma_open(struct vm_area_struct *vma)
{
    printk("===vma_open: %s===\n", chrmem_devp->data);
}

void sln_vma_close(struct vm_area_struct *vma)
{
    printk("===vma_close: %s===\n", chrmem_devp->data);
}

static struct vm_operations_struct sln_remap_vm_ops = {
    .open   = sln_vma_open,
    .close  = sln_vma_close
};

int chrmem_release(struct inode *inode, struct file *filp)
{
  return 0;
}

static int chrmem_dev_mmap(struct file*filp, struct vm_area_struct *vma)
{
      struct chrmem_dev *dev = filp->private_data;
     
      if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
          return  -EAGAIN;
                
    vma->vm_ops = &sln_remap_vm_ops;

    sln_vma_open(vma);
      return 0;
}

static const struct file_operations chrmem_fops =
{
  .owner    = THIS_MODULE,
  .open     = chrmem_open,
  .release  = chrmem_release,
  .read     = chrmem_read,
  .write    = chrmem_write,
  .llseek   = chrmem_llseek,
  .ioctl    = chrmem_ioctl,
  .mmap     = chrmem_dev_mmap

};

static int chrmem_dev_init(void)
{
    int result;
    dev_t devno;

    /* 分配裝置號 */
    result = alloc_chrdev_region(&devno, 0, 1, "chrmem_dev");
    if (result < 0) {
        return result;
    }

    // 為自定義裝置結構體分配記憶體空間
    mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
    if (!mem_devp) {
        result =  - ENOMEM;
        goto err;
    }
    memset(mem_devp, 0, sizeof(struct mem_dev));
 
    /*初始化字元裝置*/
    cdev_init(&mem_devp->cdev, &mem_fops);
    mem_devp->cdev.owner = THIS_MODULE;
 
    /*添加註冊字元裝置 */
    mem_major = MAJOR(devno);
    cdev_add(&mem_devp->cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
   
    /*初始化自定義裝置資料內容*/
    mem_devp->data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
    memset(mem_devp->data, '*', MEMDEV_SIZE / 100 );
    
    return 0;

err:
  unregister_chrdev_region(devno, 1);
 
  return result;
}


static int chrmem_dev_init(void)
{
    int result;
    dev_t devno;

    /* 分配裝置號 */
    result = alloc_chrdev_region(&devno, 0, 1, "chrmem_dev");
    if (result < 0) {
        return result;
    }

    // 為自定義裝置結構體分配記憶體空
    chrmem_devp = kmalloc(CHR_MEMDEV_NUM * sizeof(struct chrmem_dev), GFP_KERNEL);
    if (!chrmem_devp) {
        result =  - ENOMEM;
        goto err;
    }
    memset(chrmem_devp, 0, sizeof(struct chrmem_dev));
 
    /*初始化字元裝置*/
    cdev_init(&chrmem_devp->cdev, &chrmem_fops);
    chrmem_devp->cdev.owner = THIS_MODULE;
 
    /*添加註冊字元裝置 */
    chrmem_major = MAJOR(devno);
    cdev_add(&chrmem_devp->cdev, MKDEV(chrmem_major, 0), CHR_MEMDEV_NUM);
   
    /*初始化自定義裝置資料內容*/
    chrmem_devp->data = kmalloc(CHR_MEMDEV_DATA_SIZE, GFP_KERNEL);
    memset(chrmem_devp->data, '*', CHR_MEMDEV_DATA_SIZE / 100 );
    
    return 0;

err:
  unregister_chrdev_region(devno, 1);
 
  return result;
}

static void chrmem_dev_exit(void)
{
    cdev_del(&chrmem_devp->cdev); //delete device
    kfree(chrmem_devp); // release device memory
    unregister_chrdev_region(MKDEV(chrmem_major, 0), 1); // unregister char device No.
}

module_init(chrmem_dev_init);
module_exit(chrmem_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("shallnet");
MODULE_DESCRIPTION("blog.csdn.net/shallnet");
在應用程式中呼叫mmap來實現記憶體對映,應用程式程式碼如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>

#define SHR_MEMSIZE             4096
#define MEM_CLEAR               0x0
#define MEM_RESET               0x1
#define MEM_DEV_FILENAME       "/dev/sln_memdev"

int main()
{
    int         fd;
    char        *shm = NULL;
    
    fd = open(MEM_DEV_FILENAME, O_RDWR);
    if (fd < 0) {
        printf("open(): %s\n", strerror(errno));
        return -1;
    }
        
    shm = mmap(NULL, SHR_MEMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == shm) {
        printf("mmap: %s\n", strerror(errno));
    }
    
    printf("Before Write, shm = %s\n", shm);

    strcpy(shm,"User write to share memory!");
    
    printf("After write, shm = %s\n", shm);

    if (0 > ioctl(fd, MEM_CLEAR, NULL)) {
        printf("ioctl: %s\n", strerror(errno));
        return -1;
    }

    printf("After clear, shm = %s\n", shm);

    if (0 > ioctl(fd, MEM_RESET, NULL)) {
        printf("ioctl: %s\n", strerror(errno));
        return -1;
    }
    printf("After reset, shm = %s\n", shm);

    munmap(shm, SHR_MEMSIZE);
    close(fd);  
    return 0;    
}
應用程式在實現對映之後,首先讀取輸出共享記憶體內容,然後寫入,然後清空該共享記憶體內容以及重設共享記憶體。在編譯驅動和應用程式之後首先插入驅動,在建立裝置節點,最後執行應用程式看是否成功,如下: