[linux-nopage]内存映射虚拟字符设备驱动【P119】
文章目錄
- 目的:內核空間映射到用戶空間
- 環境:Ubuntu 20.04 linux內核源碼5.11.0-37-generic(版本自選)
- 實驗結果
- 實驗知識點
- 實驗難點
- 實驗代碼
- nopage.c
- Makefile
- np_test.c
- 調試過程
- make
- sudo insmod nopage.ko
- sudo mknod /dev/nopage c 92 0 (這里的92是你前面申請的設備號)
- gcc np_test.c -o ntest
目的:內核空間映射到用戶空間
一個虛擬字符設備驅動程序,將內核空間映射到用戶空間
環境:Ubuntu 20.04 linux內核源碼5.11.0-37-generic(版本自選)
實驗結果
實驗知識點
將自己編寫的驅動程序加載到內核當中,linux強大在一切皆文件,實現高類聚低耦合的特點,模塊封裝
給linux無限可能的機會
實驗難點
沒接觸過linux 編譯,前期知識點
Makefile 文件的作用
剛開是只有
這三個文件,其他文件大部分是經過make 編譯產生的
這是教程的一張圖,解釋大部分內容對應我下面的Makefile 文件內容
make 指令的作用
根據Makefile文件編譯源代碼、連接、生成目標文件、可執行文件。 簡單理解就是執行Makefile這個腳本;
- 向內核添加模塊:
編寫驅動程序文件
將驅動文件放置到linux 內核源碼相應的目錄下
在目錄Kconfig 文件中添加新驅動程序對應的項目編譯選擇
在目錄Makefile文件中添加新的驅動程序編譯語句
怎么編譯進內核
根據Makefile 指定的內核地址進入到內核中;
這是我調試錯誤過程的一種圖,看見執行make先進入指定的內核地址,內核內也有Makefile文件;
定位到140行:
中間花費了一點時間在這里調試,后期發現主要bug不在這里;
主要bug 還是因為沒有安裝gcc
這里花費時間是因為一開始我檢測系統發現已經有gcc, 后來在編譯文件才發現系統的gcc不能用,前兩個是系統自帶的。才導致我花了大量時間
最后一個是我后來安裝上的 sudo apt install gcc
google baidu 教材
make # 編譯
insmod xxx.ko # 根據編譯結果會產生.ko文件,此時會執行module_init(xxx)函數
通過命令 dmesg | tail 最后幾行可以發現 經過insmod 之后進入init函數
前兩句是插入的模塊未在模塊樹內,也就是外來模塊會提示這一消息,學習過程可以忽略;
實驗代碼
nopage.c
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/fcntl.h>#include <linux/vmalloc.h> //空間分配到堆 #include <linux/uaccess.h> #include <linux/io.h>#include <asm/page.h> #include <linux/mm.h> //重點學習#define MMAPNOPAGE_DEV_NAME "nopage" //字符設備名稱 #define MMAPNOPAGE_DEV_MAJOR 92 //字符設備號#define SHARE_MEM_PAGE_COUNT 4 //共享頁數 #define SHARE_MEM_SIZE (PAGE_SIZE*SHARE_MEM_PAGE_COUNT)char *share_memory=NULL;vm_fault_t mmapnopage_vm_fault(struct vm_fault *vmf) //這個函數跟之前版本的函數不同,這個形參只有一個,這個要在mm.h下查看才能確定不同版本不同 {struct page *page;unsigned long offset;void *page_ptr;struct vm_area_struct *vma=vmf->vma; //早期版本該變量時形參,現在已經在vm_fault 結構體內定義直接使用printk("\n");printk("%-25s %08x\n","1)vma->flags",vmf->flags);printk("%-25s %08lx\n","2)vmf->pgoff",vmf->pgoff);printk("%-25s %08lx\n","3)vmf->virtual_address",vmf->address); // 虛擬存儲區中斷地址變量為address 更之前也有差別,在mm.h 下可以找到對應 變量為long unsigned int 跟之前的有區別printk("%-25s %08lx\n","4)vma->vm_start",vma->vm_start);printk("%-25s %08lx\n","5)vma->vm_end",vma->vm_end);printk("%-25s %08lx\n","6)vma->vm_pgoff",vma->vm_pgoff);/*printk("%-25s %d\n","7)PAGE_SHIFT",PAGE_SHIFT);*/page_ptr=NULL;if((NULL==vma)||(NULL==share_memory)){printk("return VM_FAULT_SIGBUS!\n");return VM_FAULT_SIGBUS;}offset=vmf->address-vma->vm_start; //偏移量if(offset>=SHARE_MEM_SIZE){printk("return VM_FAULT_SIGBUS!");return VM_FAULT_SIGBUS;}page_ptr=share_memory+offset;page=vmalloc_to_page(page_ptr);get_page(page);vmf->page=page;return 0; }struct vm_operations_struct mmapnopage_vm_ops={.fault=mmapnopage_vm_fault, //中斷 };int mmapnopage_mmap(struct file *filp,struct vm_area_struct *vma) {vma->vm_flags |= VM_NORESERVE; //缺頁映射vma->vm_ops=&mmapnopage_vm_ops;return 0; }struct file_operations mmapnopage_fops={ //文件操作.owner=THIS_MODULE, .mmap=mmapnopage_mmap, };int mmapnopage_init(void) //執行insmod 時進入這個函數 {int lp;int result;result=register_chrdev(MMAPNOPAGE_DEV_MAJOR,MMAPNOPAGE_DEV_NAME,&mmapnopage_fops);if(result<0){printk("regist fails!");return result;}share_memory=vmalloc(SHARE_MEM_SIZE);for(lp=0;lp<SHARE_MEM_PAGE_COUNT;lp++){sprintf(share_memory+PAGE_SIZE*lp,"TEST %d",lp); //向字符設備寫入信息}printk("registing...!");return 0; }void mmapnopage_exit(void) //執行rmmod 時進入該函數 {if(share_memory!=NULL){vfree(share_memory);}unregister_chrdev(MMAPNOPAGE_DEV_MAJOR,MMAPNOPAGE_DEV_NAME); }module_init(mmapnopage_init); module_exit(mmapnopage_exit);MODULE_LICENSE("Dual BSD/GPL");Makefile
下面的Makefile 文件我添加了注釋可能在命令后面多了空格,make會識別錯誤
ifeq ($(KERNELRELEASE),) //搭配上面的教程使用 CONFIG_MODULE_SIG=n //說是為了避開數字簽名,加了發現也沒用 PWD :=$(shell pwd) //pwd 表示當前工作目錄 present work direction KERSRC := /lib/modules/$(shell uname -r)/build/ //unama -r 指向當前的內核版本modules:$(MAKE) -C $(KERSRC) M=$(PWD) modules // 執行內核模塊的編譯 moules_install:$(MAKE) -C $(KERSRC) M=$(PWD) modules_install // 將模塊安裝到對應的模塊路徑只有modules_install 執行才觸發 .PHONY: modules modules_install clean //wei clean:-rm -rf *.o *.cmd.* *.ko //刪除中間標識文件 else modules-objs :=nopage.o obj-m := nopage.o //將nopage.o 編譯為nopage.koendif這個Makefile 和 上面是一樣的;
ifeq ($(KERNELRELEASE),) CONFIG_MODULE_SIG=n PWD :=$(shell pwd) KERSRC := /lib/modules/$(shell uname -r)/build/modules:$(MAKE) -C $(KERSRC) M=$(PWD) modules moules_install:$(MAKE) -C $(KERSRC) M=$(PWD) modules_install .PHONY: modules modules_install clean clean:-rm -rf *.o *.cmd.* *.ko else modules-objs :=mmapnopage.o obj-m := mmapnopage.oendifnp_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h>// #define DEVICE_FILENAME "/dev/mmapnopage" #define DEVICE_FILENAME "/dev/nopage"#define SHARE_MEM_PAGE_COUNT 4 #define SHARE_MEM_SIZE (4096*SHARE_MEM_PAGE_COUNT)int main() {int dev;int loop;char *ptrdata;dev=open(DEVICE_FILENAME,O_RDWR|O_NDELAY); //設備打開成功,返回句柄大于0if(dev < 0)printf("can't open nopage\n"); // return < 0 fail! if(dev>=0){printf("open file success!\n");ptrdata=(char*)mmap(0,SHARE_MEM_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,dev,0);for(loop=0;loop<SHARE_MEM_PAGE_COUNT;loop++){printf("[%-10s----%s]\n",ptrdata+4096*loop,ptrdata+4096*loop);}munmap(ptrdata,SHARE_MEM_SIZE);close(dev);}return 0; }調試過程
實驗出現的錯誤:
Makefile missing separator. Stop.
這是由于Makefile 文件中的空格鍵個數和tab鍵導致的
No rule to make target ‘make’, needed by ‘modules’
解決方法:文本打開直接使用tab 鍵,不要用空格代替,Makefile以空格為命令行的分界,對符號敏感
進入Makefile文件
沒有進入ifeq條件
猜想應該是.config 文件這個變量設置問題
找到.config 發現沒有找到該變量
.config 在當前源碼里面下
針對這個問題查看資料源碼花費了很多時間還是沒能解決
就進入下一個問題
發現有安裝(最后一個是最后安裝上的)(其實主要原因還是沒有安裝gcc)系統自帶的不能用;
后面嘗試編譯一個文件gcc hello.c -o test # 這個是我用來測試用的
發現這個時候找不到gcc
于是
sudo apt-get install gcc
產生這么多錯誤的原因是沒有 sudo apt install gcc;
在排除上面這個問題的過程中花了很多時間
現在終于進入到錯誤里面
上面是環境問題還沒進入到代碼里;
接下來調試才真正進入調試階段;
進入到頭文件
這是mm.h文件下
兩個錯誤修改地址后
note: each undeclared identifier is reported only once for each function it appears in
71行20列定位;
make
Make 編譯成功:
sudo insmod nopage.ko
insmod: ERROR: could not insert module mmapnopage.ko: Device or resource busy
這是因為申請的設備號被占用了
查看當前字符設備號使用情況
cat /proc/devices
查看設備號使用情況
將240改為 90
重新執行命令
sudo mknod /dev/nopage c 92 0 (這里的92是你前面申請的設備號)
92 可以通過命令 grep nopage /proc/devices 返回得到
參數c 代表的是字符設
gcc np_test.c -o ntest
./ntest
出現錯誤
np_test.c源碼
查看是否加載模塊成功
黃色代表字符設備
后來想到是不是權限問題
我先查看了一下權限
在/dev 下ll
先看mmapnopage字符設備對于普通用戶只有讀權限(nopage 是我修改過的,原本和mmapnopage一樣)
修改為可讀可寫
chmod 666 nopage #(666 = rw-=4+2+0)
之后再編譯一下gcc np_test.c -o ntest
./ntest
結束
總結
以上是生活随笔為你收集整理的[linux-nopage]内存映射虚拟字符设备驱动【P119】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [pytorch ] (a) must
- 下一篇: [IDEA 配置MYSQL数据库连接]