Hdu操作系统第二次课程设计,内核模块编译,读取两个文件中的矩阵并将乘积写入另一个文件

博客地址:Legroft

课程设计要求

以内核模块编程实现下属功能:打开本地两个文本文件a,b,各自保存了任意给定的一个3X3矩阵(自然数),例如:

将这两个文件的矩阵读取出来,Matrix_A x Matrix_B 相乘之后的结果(3X3矩阵)存入另一个文本文件c中

要求能够对于随意输入的3X3矩阵,都能执行正确的计算,即a,b中的矩阵是可变的

设计思路

使用kernel_read()读取文件,将文件内容写入到字符串中,再使用sscanf()函数将字符串中的内容格式化存储到3x3的数组中。

之后计算两个3x3矩阵的乘积,将计算后的结果存储到第三个矩阵中。

然后使用sprintf()将矩阵中的数据格式化存储到字符串中,最后使用kernel_write()将字符串写入文件。

源码

myos2.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
static int hello_init(void){
	printk(KERN_ALERT"start process\n");
	int maxA[3][3]={0};
	int maxB[3][3]={0};
	int maxC[3][3]={0};
	char *pa,*pb,*pc;
	pa=(char*)kmalloc(sizeof(char)*100,GFP_KERNEL);
	pb=(char*)kmalloc(sizeof(char)*100,GFP_KERNEL);
	pc=(char*)kmalloc(sizeof(char)*100,GFP_KERNEL);
	struct file *fp_a;
	struct file *fp_b;
	struct file *fp_c;
   	mm_segment_t fs;
    	loff_t pos;
    	fp_a = filp_open("a.txt", O_RDWR, 0644);
	fp_b = filp_open("b.txt", O_RDWR, 0644);
	fp_c = filp_open("c.txt",O_WRONLY | O_APPEND, 0644);

    	if (IS_ERR(fp_a)) {
        	printk("open file a.txt error\n");
        	return -1;
    	}
	if (IS_ERR(fp_b)) {
        	printk("open file b.txt error\n");
        	return -1;
    	}
	if (IS_ERR(fp_c)) {
        	printk("open file c.txt error\n");
        	return -1;
    	}
    	fs = get_fs();
    	set_fs(KERNEL_DS);
    	pos = 0;
    	kernel_read(fp_a,pa,100, &pos);
	pos=0;
	kernel_read(fp_b,pb,100, &pos);
	
	sscanf(pa,"%d %d %d\n%d %d %d\n%d %d %d",&maxA[0][0],&maxA[0][1],&maxA[0][2],&maxA[1][0],&maxA[1][1],&maxA[1][2],&maxA[2][0],&maxA[2][1],&maxA[2][2]);
	
	sscanf(pb,"%d %d %d\n%d %d %d\n%d %d %d",&maxB[0][0],&maxB[0][1],&maxB[0][2],&maxB[1][0],&maxB[1][1],&maxB[1][2],&maxB[2][0],&maxB[2][1],&maxB[2][2]);
	
	int i;
	for(i=0;i<3;i++){
		int j;
		for(j=0;j<3;j++){
			int k;
			for(k=0;k<3;k++){
			maxC[i][j]=maxC[i][j]+maxA[i][k]*maxB[k][j];
			} 
		} 
	}
	
	sprintf(pc,"%d %d %d\n%d %d %d\n%d %d %d\n",maxC[0][0],maxC[0][1],maxC[0][2],maxC[1][0],maxC[1][1],maxC[1][2],maxC[2][0],maxC[2][1],maxC[2][2]);	
	
	int size=0;
	int m;
	for(m=0;m<3;m++){
		int n;
		for(n=0;n<3;n++){
			int tsize=0;
			while(maxC[m][n]>0)
			{
				tsize++;
				maxC[m][n]/=10;
	 		}
			size+=tsize+1;				
		}
	}
	pos=0;
	kernel_write(fp_c,pc,size,&pos);	
	printk(KERN_ALERT"completed\n");
    	filp_close(fp_a, NULL);
	filp_close(fp_b, NULL);
	filp_close(fp_c, NULL);
    	set_fs(fs);
	
	return 0;
}
static void hello_exit(void){
	printk(KERN_ALERT"goodbye\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

Makefile

obj-m :=os2.o
os2-objs:=myos2.o
KDIR :=/usr/src/linux-5.6.2
PWD :=$(shell pwd)
default:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean
主要函数及解释说明
  • set_fs()/get_fs():

    改变(获取)内核对内存地址检查的处理方式,其参数(或返回值)仅有两个取值:USER_DS(默认,对用户空间地址检查)、KERNEL_DS(对内核空间地址检查)。例如:一些函数含有__user修饰的参数,表示这是一个指向用户空间的,在内核中需要用内核空间代替时,就需要用set_fs()来做变换。

  • filp_open():

    函数原型:struct file* filp_open(const char* filename, int open_mode, int mode)。第一个参数是文件的路径,第二个是文件打开方式(此处同open()函数),第三个参数在创建文件时设置文件读写权限,其他情况一般为0.返回值为一个文件指针。

  • filp_close():

    对应filp_open(),关闭文件。

  • kmalloc()/kfree():

    用于开辟(释放)一块内存空间。与平常使用的malloc()和free()函数类似。其中kmalloc()的函数原型为:void *kmalloc(size_t size, int flags)。size是需要开辟空间的大小,flags是分配标志,实验中用到的GFP_KERNEL是表示: 缺内存页的时候可以睡眠、允许启动磁盘IO、允许启动文件系统IO。

  • vfs_read()
    ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
    参数中__user 表示buf是用户态指针,在内核中无法使用,因此需要设置使用环境,如下:
    mm_segment_t old_fs;
    old_fs = get_fs();
    set_fs(get_ds());
    vfs_read(file, (void __user *)addr, count, &pos);
    set_fs(old_fs);

  • vfs_write():

    用于内核中文件的写操作。函数原型:ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos)。第一个参数是文件指针,第二个参数是需要写入的内容,第三个参数是写入的字节数,第四个参数为写入的位置。

  • sprintf():

    sprintf指的是字符串格式化命令,函数声明为 int sprintf(char *string, char *format [,argument,…]);,主要功能是把格式化的数据写入某个字符串中,即发送格式化输出到 string 所指向的字符串。sprintf 是个变参函数。使用sprintf 对于写入buffer的字符数是没有限制的,这就存在了buffer溢出的可能性。解决这个问题,可以考虑使用 snprintf函数,该函数可对写入字符数做出限制。

  • sscanf():

    sscanf 读取格式化的字符串中的数据。原型:int sscanf(const char *buffer,const char *format,[argument] … );

    参数:

    buffer 存储的数据

    format 窗体控件字符串。

    argument 可选自变量

    locale 要使用的区域设置

遇到问题及解决方案
问题一:

编译出错,出现 vfs_read[os2.ko] undefined !

原因:

因为linux-4.0以后的版本取消了vfs_read()的符号导出EXPORT_SYMBOL(vfs_read)。

解决方案:

使用int kernel_read(struct file *file, loff_t offset, char *addr, unsigned long count)函数

实现过程

在Home目录下新建文件夹,命名为os2

在文件夹中新建所需的文件

a.txt,b.txt中内容均为下(c.txt为空)

1 2 3
4 5 6
7 8 9

将源码写入myos2.c以及Makefile

然后终端中输入 make

输入 sudo insmod ./os2.ko

输入 lsmod 查看模块是否已经被加载

输入 dmesg 查看日志输出

查看文件内容是否正确,可以看到是两个矩阵相乘的结果

输入 sudo rmmod os2 卸载模块

输入 dmesg 查看日志

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值