一、实验目的
1、理解Linux中虚拟文件系统的内容
2、学习编写内核模块的方法
3、在虚拟文件系统/proc中实现文件操作算法
二、实验环境
Centos7.9,vim
三、实验内容
编写一个内核模块,在/proc文件系统中增加一个目录hello,并在这个目录中增加一个文件world,文件的内容为hello world。
四、实验原理 实验中用到的系统调用函数
/proc 文件系统是Linux上的一种虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以更改其中某些文件来改变内核的运行状态。
1、在proc中创建目录函数 proc_mkdir 该函数原型为: struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent); name:要创建的目录的名称 parent:指向该目录的父目录的指针 返回指向当前目录的指针结构体。
2、创建version文件的函数为proc_create,该函数原型为: static inline struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops); 第一个参数name为文件名, 第二个参数mode为文件的读写权限, 第三个参数parent为其父目录的结构体指针,第四个参数pr
3、实现内核空间与用户空间的数据传递函数: copy_to_user()和copy_from_user()这两个函数。 (旧内核是raw_copy_to_user() 和 raw_copy_from_user()这两个函数) copy_to_user() 函数的完整形态为 unsigned long copy_to_user(void *to, const void *from, unsigned long count); 函数的作用是将内核空间的数据复制到用户空间。其中 to:目标地址(用户空间)
4、从 /proc 中删除一个文件函数 remove_proc_entry() , 函数的原型是: remove_proc_entry(const char *name, struct proc_dir_entry *parent) name:要删除的文件名 parent :文件在 /proc 文件系统中的位置
五、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)
1、获取内核源码
apt-get install linux-source-3.10.0
默认下载到/usr/src/目录下
得到linux-source-3.10.0.tar.xz压缩包,后移动到桌面
解压tar -xjf *.tar.xz
得到linux-source-3.10.0
打开~/linux-source-3.10.0/fs/proc可以查看到proc文件系统的文件
2.创建目录文件编写proc_hello.c和Makefile
编写proc_hello.c文件
编写Makefile文件
3.运行与验证
将proc_hello.c文件拷贝至Linux/fs/proc目录下,由于该目录下原本就有Makefile文件,因此要对原Makefile文件备份后再拷贝本实验中的
mv Makefile Makefile.bak
mv my_makefile Makefile
1.首先用make编译…# sudo make
2、modinfo命令查看make生成的.ko文件的属性…#modinfo proc_hello.ko
则使用“vim /etc/profile”指令进入/etc/profile文件中修改PATH为
PATH=
P
A
T
H
:
PATH:
PATH:HOME/bin:/sbin:/usr/bin:/usr/sbin
重启虚拟机再次执行“modinfo proc_hello.ko”即可成功
3、加载内核模块: ……….# insmod proc_hello.ko
4、查看找到新模块:…# lsmod
5.查看内核日志信息:…#dmesg | tail
6、打开/proc/hello/world验证:…#cat /proc/hello/world
7、卸载模块:…#rmmod proc_hello.ko
8、查看卸载时的打印信息:…#dmesg | tail
map_driver.c
#include <linux/kernel.h> //常用的内核函数
#include <linux/module.h> //对模块的版本控制
#include <linux/init.h> //包含了宏
#include<linux/fs.h>
#include <linux/cdev.h>
#include<linux/string.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/vmalloc.h>
#include<linux/slab.h>
#include<linux/sched.h>
#include<linux/io.h>
#include<linux/mman.h>
#define MAP_PAGE_COUNT 10
#define MAPLEN(PAGE_SIZE *MAP_PAGE_COUNT)
#define MAP_DEV_MAJOR 240
#define MAP_DEV_NAME "mapnopage"
extern struct mm_struct init_mm;
void map_vopen(struct vm_area_struct *vma);
void map_vclose(struct vm_area_struct *vma);
/* device mmap */
static int mapdrv_mmap(struct file *file, struct vm_area_struct *vma);
static int mapdrv_open(struct inode *inode, struct file *file);
/*vm.area nopage */
static int map_fault(struct vm_fault *vmf);
static struct file_operations mapdrvo_fops={
.owner=THIS_MODULE,
.mmap=mapdrv_mmap,
.open=mapdrv_open,
};
static struct vm_operations _struct map_vm_ops={
.open=map_vopen,
.close=map_vclose,
.fault=map_fault,
};
static char *vmalloc_area=NULL;
MODULE_LICENSE("GPL");
static int __init mapdrv_init(void) /* 装载函数 */
{
int result;
unsigned long virt_addr;
int i=1;
result=register_chrdev(MAP_DEV_MAJOR,MAP_DEV_NAME,&mapdrvo_fops);
if(result<0)
{
return result;
}
vmalloc_area=vmalloc(MAPLEN);
virt_addr=(unsigned long)vmalloc_area;
for(virt_addr=(unsigned long)vmalloc_area; virt_addr<(unsigned long) vmalloc_area+MAPLEN; virt_addr+=PAGE_SIZE)
{
SetPageReserved(vmalloc_to_page((void*)virt_addr));
Sprintf((char*)virt_addr, "test %d",i++);
}
printk("vmalloc_area apply completely!");
return 0;
}
static void __exit mapdrv_exit(void) /* 卸载函数 */
{
unsigned long virt_addr;
/* unreserve all pages */
for(virt_addr=(unsigned long) vmalloc_area; virt_addr<(unsigned long) vmalloc_area+MAPLEN; virt_addr+=PAGE_SIZE)
{
ClearPageReserved(vmalloc_to_page((void*)virt_addr));
}
/* and free the two areas */
if(vmalloc_area)
vfree(vmalloc_area);
unregister_chrdev(MAP_DEV_MAJOR,MAP_DEV_NAME);
}
static int mapdrv_mmap(struct file *file, struct vm-area_struct *vma)
{
unsigned long offset=vma->vm_pgoff<<PAGE_SHIFT;
unsigned long size=vma->vm_end-vma->vm_start;
if(size>MAPLEN)
{
printk("size too big\n");
return FNXIO;
}
/* only support shared mapping */
if((vma->vm_flags&VM_WRITE)&&!(vma->vm_flags&VM_SHARED))
{
printk("writeable mappings must be shared, rejecting\n");
return –EINVAL;
}
/* do not want to have this area swapped out, lock it */
vma->vm_flags|=VM_LOCKONFAULT;
if(offset==0)
vma->vm_ops=&map_vm_ops;
else
{
printk("offset out of range\n");
return –ENXIO;
}
return 0;
}
static int mapdrv_open(struct inode *inoe, struct file *file)
{
printk("process: %s(%d)\n", current->comm, current->pid);
return 0;
}
/* open handler for vm area */
void map_vopen(struct vm_area_struct *vma)
{
printk("mapping vma is opened..\n");
}
/* close handler for vm.area */
void map_vclose(struct vm_area_struct *vma)
{
printk("mapping vma is closed ..\n");
}
/* page fault handler */
static int map_fault(struct vm_fault *vmf)
{
struct page *page;
void *page_ptr;
unsigned long offset=vmf->address-vmf->vma->vm_start;
unsigned long virt_start=(unsigned long)vmalloc_area+( unsigned long)(vmf->pgoff<<PAGE_SHIFT);
printk("\n");
page_ptr=NULL;
if((vmf->vma==NULL)||(vmalloc_area==NULL))
{
printk("return VM_FAULT_SIGBUS!\n");
return VM_FAULT_SIGBUS;
}
if(offset>=MAPLEN)
{
printk("return VM_FAULT_SIGBUS!\n");
return VM_FAULT_SIGBUS;
}
page_ptr=vmalloc_area+offset;
page=vmalloc_to_page(page_ptr);
get_page(page);
vmf->page=page;
printk("%s: map ox%lx (ox%016lx) to ox%lx,size: ox%lx,
page:%ld \n",_func_, virt_start, pfn_start<<PAGE_SHIFT, vmf->address, PAGE_SIZE, VMF->PGOFF);
return 0;
}
module_init(mapdrv_init);
module_exit(mapdrv_exit);