任务

一、实验目的
1)理解设备是文件的概念。
2)掌握Linux模块、驱动的概念和编程流程
3)Windows /Linux下掌握文件读写基本操作
二、实验内容
1)编写一个Linux内核模块,并完成安装/卸载等操作。
2)编写Linux驱动程序并编程应用程序测试。功能:write几个整数进去,read出其和或差或最大值。
3)编写Linux驱动程序并编程应用程序测试。功能:有序读写内核缓冲区,返回实际读写字节数。

任务1 编写一个Linux内核模块,并完成安装/卸载等操作。

1. 提示

提示1:安装时和退出时在内核缓冲区显示不同的字符串。
提示2:相关函数:module_init( )、 module_exit( )
提示3: MODULE_LICENSE( )、 MODULE_AUTHOR ( )等可选
提示4:安装命令:insmod XXXX.ko
提示5:扩展:编写带参数的模块程序
int mytest = 100;
module_param(mytest, int, 0644);

2. 任务代码

先安装一个必要的软件包(否则会有warning):

sudo apt install libelf-dev

文件code1.c:

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/sched.h>

MODULE_LICENSE("GPL");
static char* yourID="shandianchengzi";
module_param(yourID,charp,0644);

static int code1_init(void){
	//输出欢迎信息
	printk(KERN_ALERT"Hello, dear %s!\n", yourID);
	return 0;
}

static void code1_exit(void){
	printk(KERN_ALERT"Goodbye, %s!\n", yourID);
}

module_init(code1_init);
module_exit(code1_exit);

Makefile:

obj-m += code1.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(pwd) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(pwd) clean

然后运行:

sudo make

3. 结果及说明

1)模块作用:实验文件编译生成的ko模块可接收charp型参数,默认为"shandianchengzi"。当用户加载模块时输出"Hello, dear 参数!\n",退出时输出"GoodBye, 参数!\n"。
2)如何向模块中传入参数:函数原型module_param(name, type, perm),参数name,type是自己定义的变量的类型,perm是权限。
其中常用的权限:①0755->用户具有读/写/执行权限,组用户和其它用户具有读/写权限;②0644->用户具有读/写权限,组用户和其它用户具有只读权限;
一般赋予目录0755权限,而文件赋予0644权限。
3)编译:
在这里插入图片描述
4)结果:
在这里插入图片描述
先用sudo dmesg -C清空缓冲区,然后使用sudo insmod code1.ko yourID='shandianchengzi'装入内核并修改参数值,再dmesg显示当前内容。再使用sudo rmmod code1.ko卸载内核,再dmesg显示当前内容。与预期相符。

参考:
主要:Linux中添加一个带参数的模块
传入字符串的设计:第四章Linux内核模块之五(模块参数)

任务2 编写Linux驱动程序并编程应用程序测试。

1. 提示

提示1:参考任务1
提示2:至少实现xx_open,xx_write,xx_read等函数
提示3:功能:
xx_write( )写进去2个整数
xx_read( )读回结果(和或差或最大值)
提示4: [可选的设备注册方式,其余方式参考baidu]
struct miscdevice mydemodrv_misc_device ;
ret = misc_register( &mydemodrv_misc_device )
在这里插入图片描述

2. 任务代码

code2.c:

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

#define NAME "code2"
MODULE_LICENSE("GPL");

static struct device *mydemodrv_device;
static int a=0,b=0;

static int demodrv_open(struct inode *inode, struct file *file)
{
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);
	printk("%s: major=%d, minor=%d\n", __func__, major, minor);
	return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
	printk("%s: a+b=%d\n", __func__,a+b);
	return 0;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
	if(count>64)
	{
		count = 64;
	}
 	char temp[64];
	if(copy_from_user(temp,buf,count))
	{
		return -EFAULT;
	}
	sscanf(temp, "%d%d",&a,&b);
	printk("%s: a=%d,b=%d\n", __func__,a,b);
	return count;
}

static const struct file_operations demodrv_fops = {
	.owner = THIS_MODULE,
	.open = demodrv_open,
	.release = demodrv_release,
	.read = demodrv_read,
	.write = demodrv_write
};

static struct miscdevice mydemodrv_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = NAME,
	.fops = &demodrv_fops,
};

static int __init code2_init(void)
{
	int ret;
	ret = misc_register(&mydemodrv_misc_device);
	if (ret) {
		printk("failed register code2 misc device\n");
		return ret;
	}
	mydemodrv_device = mydemodrv_misc_device.this_device;
	printk("succeeded register char device: %s\n", NAME);
	return 0;
}

static void __exit code2_exit(void)
{
	printk("removing device: %s\n", NAME);
	misc_deregister(&mydemodrv_misc_device);
}

module_init(code2_init);
module_exit(code2_exit);

测试程序:
test.c:

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

#define DEV_NAME "/dev/code2"

int main()
{
	char buffer[64];
	int fd,a,b;

	fd = open(DEV_NAME, O_RDWR | O_CREAT);
	if (fd < 0) {
		printf("open device %s failded\n", DEV_NAME);
		return -1;
	}
    printf("请输入两个整数:");
    scanf("%d%d",&a,&b);
    sprintf(buffer,"%d %d",a,b);
    write(fd,buffer,64);
	read(fd, buffer, 64);
	close(fd);
	
	return 0;
}

3. 结果及说明

1)编译:
在这里插入图片描述
2)结果:完成的是求和功能。
在这里插入图片描述
3)遇到的问题:最开始,打开文件指针的时候用了O_RDONLY,只读。后来写write的时候死活读不进去。。。
怀疑是中间文件没删、模块代码返回值不能size_t强转ssize_t等问题,结果原来是因为这个。。。。。。。
改成O_RDWR | O_CREAT。

参考:《内核kernel:misc机制字符设备驱动模块编写》
O_RDWR | O_CREAT参考:3.8 write函数-文件数据写

4)用的方法是先读入缓冲区,再用sscanf读。我觉得传整型指针有点麻烦,要写两个write。
5)设备读不了写不了装不了用不了没输出:
甚至把模块卸载了,它还在!
在这里插入图片描述
这是因为在装入模块之前,不慎调用test程序,已经提前申请打开了设备,以致于装载的时候未装载成功。
如果出现这种情况,运行:

sudo rm /dev/code2

任务3 编写Linux驱动程序并编程应用程序测试。

1. 提示

提示1:参考任务1
提示2:至少实现xx_open,xx_write,xx_read等函数
提示3:功能:
内核分配一定长度的缓冲区,比如64字节。
在这里插入图片描述
xx_write()写进去若干字符,注意维护写入位置。下次继续写的话,接着该位置往后写,直到缓冲区末尾。要返回实际写入字数。
xx_read()读出若干字符串,注意维护读出位置。下次继续读的话,接着该位置往后读,直到缓冲区末尾。要返回实际读回字数。
扩展:
▲缓冲区设置为循环缓冲区?
▲如何避免写覆盖,避免读重复?

2. 任务代码

code3.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/slab.h> /*kmalloc*/

#define NAME "code3"
MODULE_LICENSE("GPL");

static struct device *mydemodrv_device;
static char *device_buffer;
static size_t pos; 
#define MAX_DEVICE_BUFFER_SIZE 64

static int demodrv_open(struct inode *inode, struct file *file)
{
	device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
	pos=0;
	return 0;
}

static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
{
	if(pos < lbuf)
	{
		lbuf = pos;
	}
	if(copy_to_user(buf,device_buffer+pos-lbuf,lbuf))
	{
		return -EFAULT;
	}
	pos=pos-lbuf;
	printk("%s: 读出%ld字节,读出后指针位置为%ld\n", __func__, lbuf, pos);
	return lbuf;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
	if(pos + count > 64)
	{
		count = 64-pos;
		if(count < 0)
			return count;
	}
	if(copy_from_user(device_buffer+pos,buf,count))
	{
		return -EFAULT;
	}
	pos=pos+count;
	printk("%s: 写入%ld字节,写完后缓冲区为%s\n", __func__, count, device_buffer);
	return count;
}

static const struct file_operations demodrv_fops = {
	.owner = THIS_MODULE,
	.open = demodrv_open,
	.read = demodrv_read,
	.write = demodrv_write
};

static struct miscdevice mydemodrv_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = NAME,
	.fops = &demodrv_fops,
};

static int __init code3_init(void)
{
	int ret;
	ret = misc_register(&mydemodrv_misc_device);
	if (ret) {
		printk("failed register code2 misc device\n");
		return ret;
	}
	mydemodrv_device = mydemodrv_misc_device.this_device;
	printk("succeeded register char device: %s\n", NAME);
	return 0;
}

static void __exit code3_exit(void)
{
	kfree(device_buffer);
	printk("removing device: %s\n", NAME);
	misc_deregister(&mydemodrv_misc_device);
}

module_init(code3_init);
module_exit(code3_exit);

测试程序:
test.c:

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

#define DEV_NAME "/dev/code3"

int main()
{
	char buffer[64];
	int fd,a,b;

	fd = open(DEV_NAME, O_RDWR | O_CREAT);
	if (fd < 0) {
		printf("open device %s failded\n", DEV_NAME);
		return -1;
	}
	printf("请输入向缓冲区写的内容:");
	scanf("%s",buffer);
	write(fd,buffer,strlen(buffer));
	read(fd, buffer, 64);
	printf("从缓冲区读:%s\n", buffer);
	close(fd);
	
	return 0;
}

3. 结果及说明

1)kmalloc头文件是#include <linux/slab.h>。
2)编译:
在这里插入图片描述
3)运行:
在这里插入图片描述

4)遇到的问题2:copy_to_user和copy_from_user的to和from参数是反过来的,一开始没注意。
5)遇到的问题3:
下面这个不行:

if(pos - lbuf < 0)
{
	lbuf = pos;
}

改成这个才行:

if(pos < lbuf)
{
	lbuf = pos;
}

因为size_t是unsigned类型的,所以不能直接跟0比较。

Logo

更多推荐