[linux-nopage]内存映射虚拟字符设备驱动【P119】
文章目錄
- 目的:內(nèi)核空間映射到用戶空間
- 環(huán)境:Ubuntu 20.04 linux內(nèi)核源碼5.11.0-37-generic(版本自選)
- 實驗結(jié)果
- 實驗知識點
- 實驗難點
- 實驗代碼
- nopage.c
- Makefile
- np_test.c
- 調(diào)試過程
- make
- sudo insmod nopage.ko
- sudo mknod /dev/nopage c 92 0 (這里的92是你前面申請的設(shè)備號)
- gcc np_test.c -o ntest
目的:內(nèi)核空間映射到用戶空間
一個虛擬字符設(shè)備驅(qū)動程序,將內(nèi)核空間映射到用戶空間
環(huán)境:Ubuntu 20.04 linux內(nèi)核源碼5.11.0-37-generic(版本自選)
實驗結(jié)果
實驗知識點
將自己編寫的驅(qū)動程序加載到內(nèi)核當(dāng)中,linux強大在一切皆文件,實現(xiàn)高類聚低耦合的特點,模塊封裝
給linux無限可能的機會
實驗難點
沒接觸過linux 編譯,前期知識點
Makefile 文件的作用
剛開是只有
這三個文件,其他文件大部分是經(jīng)過make 編譯產(chǎn)生的
這是教程的一張圖,解釋大部分內(nèi)容對應(yīng)我下面的Makefile 文件內(nèi)容
make 指令的作用
根據(jù)Makefile文件編譯源代碼、連接、生成目標文件、可執(zhí)行文件。 簡單理解就是執(zhí)行Makefile這個腳本;
- 向內(nèi)核添加模塊:
編寫驅(qū)動程序文件
將驅(qū)動文件放置到linux 內(nèi)核源碼相應(yīng)的目錄下
在目錄Kconfig 文件中添加新驅(qū)動程序?qū)?yīng)的項目編譯選擇
在目錄Makefile文件中添加新的驅(qū)動程序編譯語句
怎么編譯進內(nèi)核
根據(jù)Makefile 指定的內(nèi)核地址進入到內(nèi)核中;
這是我調(diào)試錯誤過程的一種圖,看見執(zhí)行make先進入指定的內(nèi)核地址,內(nèi)核內(nèi)也有Makefile文件;
定位到140行:
中間花費了一點時間在這里調(diào)試,后期發(fā)現(xiàn)主要bug不在這里;
主要bug 還是因為沒有安裝gcc
這里花費時間是因為一開始我檢測系統(tǒng)發(fā)現(xiàn)已經(jīng)有g(shù)cc, 后來在編譯文件才發(fā)現(xiàn)系統(tǒng)的gcc不能用,前兩個是系統(tǒng)自帶的。才導(dǎo)致我花了大量時間
最后一個是我后來安裝上的 sudo apt install gcc
google baidu 教材
make # 編譯
insmod xxx.ko # 根據(jù)編譯結(jié)果會產(chǎn)生.ko文件,此時會執(zhí)行module_init(xxx)函數(shù)
通過命令 dmesg | tail 最后幾行可以發(fā)現(xiàn) 經(jīng)過insmod 之后進入init函數(shù)
前兩句是插入的模塊未在模塊樹內(nèi),也就是外來模塊會提示這一消息,學(xué)習(xí)過程可以忽略;
實驗代碼
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> //重點學(xué)習(xí)#define MMAPNOPAGE_DEV_NAME "nopage" //字符設(shè)備名稱 #define MMAPNOPAGE_DEV_MAJOR 92 //字符設(shè)備號#define SHARE_MEM_PAGE_COUNT 4 //共享頁數(shù) #define SHARE_MEM_SIZE (PAGE_SIZE*SHARE_MEM_PAGE_COUNT)char *share_memory=NULL;vm_fault_t mmapnopage_vm_fault(struct vm_fault *vmf) //這個函數(shù)跟之前版本的函數(shù)不同,這個形參只有一個,這個要在mm.h下查看才能確定不同版本不同 {struct page *page;unsigned long offset;void *page_ptr;struct vm_area_struct *vma=vmf->vma; //早期版本該變量時形參,現(xiàn)在已經(jīng)在vm_fault 結(jié)構(gòu)體內(nèi)定義直接使用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); // 虛擬存儲區(qū)中斷地址變量為address 更之前也有差別,在mm.h 下可以找到對應(yīng) 變量為long unsigned int 跟之前的有區(qū)別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) //執(zhí)行insmod 時進入這個函數(shù) {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); //向字符設(shè)備寫入信息}printk("registing...!");return 0; }void mmapnopage_exit(void) //執(zhí)行rmmod 時進入該函數(shù) {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 //說是為了避開數(shù)字簽名,加了發(fā)現(xiàn)也沒用 PWD :=$(shell pwd) //pwd 表示當(dāng)前工作目錄 present work direction KERSRC := /lib/modules/$(shell uname -r)/build/ //unama -r 指向當(dāng)前的內(nèi)核版本modules:$(MAKE) -C $(KERSRC) M=$(PWD) modules // 執(zhí)行內(nèi)核模塊的編譯 moules_install:$(MAKE) -C $(KERSRC) M=$(PWD) modules_install // 將模塊安裝到對應(yīng)的模塊路徑只有modules_install 執(zhí)行才觸發(fā) .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); //設(shè)備打開成功,返回句柄大于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; }調(diào)試過程
實驗出現(xiàn)的錯誤:
Makefile missing separator. Stop.
這是由于Makefile 文件中的空格鍵個數(shù)和tab鍵導(dǎo)致的
No rule to make target ‘make’, needed by ‘modules’
解決方法:文本打開直接使用tab 鍵,不要用空格代替,Makefile以空格為命令行的分界,對符號敏感
進入Makefile文件
沒有進入ifeq條件
猜想應(yīng)該是.config 文件這個變量設(shè)置問題
找到.config 發(fā)現(xiàn)沒有找到該變量
.config 在當(dāng)前源碼里面下
針對這個問題查看資料源碼花費了很多時間還是沒能解決
就進入下一個問題
發(fā)現(xiàn)有安裝(最后一個是最后安裝上的)(其實主要原因還是沒有安裝gcc)系統(tǒng)自帶的不能用;
后面嘗試編譯一個文件gcc hello.c -o test # 這個是我用來測試用的
發(fā)現(xiàn)這個時候找不到gcc
于是
sudo apt-get install gcc
產(chǎn)生這么多錯誤的原因是沒有 sudo apt install gcc;
在排除上面這個問題的過程中花了很多時間
現(xiàn)在終于進入到錯誤里面
上面是環(huán)境問題還沒進入到代碼里;
接下來調(diào)試才真正進入調(diào)試階段;
進入到頭文件
這是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
這是因為申請的設(shè)備號被占用了
查看當(dāng)前字符設(shè)備號使用情況
cat /proc/devices
查看設(shè)備號使用情況
將240改為 90
重新執(zhí)行命令
sudo mknod /dev/nopage c 92 0 (這里的92是你前面申請的設(shè)備號)
92 可以通過命令 grep nopage /proc/devices 返回得到
參數(shù)c 代表的是字符設(shè)
gcc np_test.c -o ntest
./ntest
出現(xiàn)錯誤
np_test.c源碼
查看是否加載模塊成功
黃色代表字符設(shè)備
后來想到是不是權(quán)限問題
我先查看了一下權(quán)限
在/dev 下ll
先看mmapnopage字符設(shè)備對于普通用戶只有讀權(quán)限(nopage 是我修改過的,原本和mmapnopage一樣)
修改為可讀可寫
chmod 666 nopage #(666 = rw-=4+2+0)
之后再編譯一下gcc np_test.c -o ntest
./ntest
結(jié)束
總結(jié)
以上是生活随笔為你收集整理的[linux-nopage]内存映射虚拟字符设备驱动【P119】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [pytorch ] (a) must
- 下一篇: [IDEA 配置MYSQL数据库连接]