Linux驅動開發筆記(六):用戶層與內核層進行數據傳遞的原理和Demo

前言

  驅動作爲橋樑,用戶層調用預定義名稱的系統函數與系統內核交互,而用戶層與系統層不能直接進行數據傳遞,進行本篇主要就是理解清楚驅動如何讓用戶編程來實現與內核的數據交互傳遞。

 

溫故知新

  • 設備節點是應用層(用戶層)與內核層交互;
  • 使用預先的結構體進行操作,如系統open函數對應了驅動中文件操作及的open指針結構體:struct file_operations;
  • 文件操作集結構體,填充結構體對應指針,填充自己使用到的就行了,多餘的可以不填充,調用也不會崩潰或返回錯誤,會返回0;
      在這裏插入圖片描述

  那麼如何將應用層的輸入寫入進去可用,如何將內核層的數據通過read返回出來,就是本篇學習了。

 

驅動模板準備

  首先複製之前的testFileOpts的驅動,改個名字爲:testFileOpts:

cd ~/work/drive/
ls
cp -arf 003_testFileOpts 004_testReadWrite
cd 004_testReadWrite/
make clean
ls
mv testFileOpts.c testReadWrite.c
vi Makefile 
ls

  在這裏插入圖片描述

  其中修改makefile裏面的模塊名稱(obj-m模塊名稱),模板準備好了

gedit Makefile 

  在這裏插入圖片描述

  下面基於testReadWrite.c文件進行註冊雜項設備,修改.c文件:

gedit testReadWrite.c

  在這裏插入圖片描述

  在這裏插入圖片描述

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
    return 0;
}

// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
    printk("int misc_release(struct inode * pInde, struct file * pFile)\n");
    return 0;
}

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

struct file_operations misc_fops = {
  .owner = THIS_MODULE,
  .open = misc_open,
  .release = misc_release,
  .read = misc_read,
  .write = misc_write,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 這個宏是動態分配次設備號,避免衝突
    .name = "register_hongPangZi_testReadWrite", // 設備節點名稱
    .fops = &misc_fops,  // 這個變量記住,自己起的,步驟二使用
};

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在內核裏面無法使用基礎c庫printf,需要使用內核庫printk
    printk("Hello, I’m hongPangZi, registeraMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);
 

概述

  內核層和用戶層不能中是不能直接與用戶數據交互,需要使用內核函數copy_to_user和copy_from_user。
  在內核中可以使用printk,memset,memcpy,strlen等函數。

 

內核函數

  頭文件是:linux/uaccess.h(我們這是ubuntu,不是arm)
  可以在內核根目錄下搜索下:

find . -type f -exec grep -l "copy_to_user(void" {} \;

  在這裏插入圖片描述

  在這裏插入圖片描述

copy_from_user函數:從用戶層複製到內核層

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)

  簡化下:

static unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)

  參數分別是,複製到的地址(內核空間),從什麼地址複製(用戶空間),複製長度;

copy_to_user函數:從內核層複製到用戶層

static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)

  簡化下:

static unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

  參數分別是,複製到的地址(用戶空間),從什麼地址複製(內核空間),複製長度;

 

雜項設備驅動添加數據傳遞函數Demo

步驟一:加入頭文件和定義static緩存區

  在這裏插入圖片描述

#include <linux/uaccess.h>      // Demo_004 add
static char kBuf[256] = {0x00};  // Demo_004 add

步驟二:初始化緩存區

  在這裏插入圖片描述

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
    memcpy(kBuf, "init kBuf", sizeof("init kBuf"));
    printk("kBuf = %s\n", kBuf); 

    return 0;
}

步驟三:在驅動函數read中,添加從內核層到用戶層的函數

  在這裏插入圖片描述

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    if(copy_to_user(pUser, kBuf, strlen(kBuf)) != 0)
    {
        printk("Failed to copy_to_user(pUser, kBuf, strlen(kBuf)\n");
        return -1;
    }
    return 0;
}

步驟四:在驅動函數wirte中,添加從用戶層到內核層的函數

  在這裏插入圖片描述

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    if(copy_from_user(kBuf, pUser, size) != 0)
    {
        printk("Failed to copy_from_user(kBuf, pUser, size)\n");
        return -1;
    }
    return 0;
}

步驟五:在程序中讀取、寫入、再讀取

  在這裏插入圖片描述

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd = -1;
  char buf[32] = {0};
  int ret = -1;

  const char devPath[] = "/dev/register_hongPangZi_testReadWrite";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("Failed to open %s\n", devPath);
    return -1;
  }else{
    printf("Succeed to open %s\n", devPath);
  }
  // 讀取
  ret = read(fd, buf, sizeof(buf) < 0);
  if(ret < 0)
  {
    printf("Failed to read %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to read [%s]\n", buf);
  }
  // 修改內容
  memset(buf, 0x00, sizeof(buf));
  memcpy(buf, "Get you content", strlen("Get you content"));
  // 寫入
  ret = write(fd, buf, sizeof(buf));
  if(ret < 0)
  {
    printf("Failed to write %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to write [%s]\n", buf);
  }
  // 讀取
  ret = read(fd, buf, sizeof(buf) < 0);
  if(ret < 0)
  {
    printf("Failed to read %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to read [%s]\n", buf);
  }

  close(fd);

  printf("exit\n");

  fd = -1;


  return 0;
}

步驟六:編譯加載驅動

  在這裏插入圖片描述

make
sudo insmod testReadWrite.ko

步驟七:編譯程序運行結果

gcc test.c
sudo ./a.out

  在這裏插入圖片描述

  測試結果與預期相同

 

入坑

入坑一:測試程序讀取與預期不同

問題

  在這裏插入圖片描述

原因

  在這裏插入圖片描述

解決

  在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章