Linux内核(2)——设备驱动实验

论坛 期权论坛 脚本     
匿名技术用户   2021-1-5 13:09   54   0

今天做了Linux课设关于设备驱动的实验。

实验采用模块的方法编写一个可以进行简单读写的字符设备驱动,该设备可以存储一定长的字符串,写入设备即可以将字符串存入设备,读出即可以获取该字符串,并编写了测试程序对其测试。

(1)首先新建并编写了字符驱动设备chardev.c文件,文件代码如下:

/*

* 创建一个字符设备(读写)

*/

/* 必要的头文件,内核模块标准头文件 */

#include<linux/init.h>

#include<linux/kernel.h> /*内核工作*/

#include<linux/slab.h>/**/

#include<linux/vmalloc.h>

#include<linux/module.h> /*明确指定是模块*/

#include<linux/moduleparam.h>

/*对于字符设备*/

#include<linux/fs.h> /*字符设备定义*/

#include<linux/cdev.h>

#include<asm/system.h>

#include<asm/uaccess.h>

MODULE_AUTHOR("author");

MODULE_LICENSE("GPL");

struct char_dev *char_device;

int dev_major=0;

int dev_minor=0;

module_param(dev_major,int,S_IRUGO);

module_param(dev_minor,int,S_IRUGO);

//设备存储区的指针

char *p_mem=NULL;

//设备存储区的大小

long len=1000;

//表示设备的数据结构

struct char_dev

{

char * data; // 模块中的数据

long len; // 数据长度

struct cdev cdev; //Linux 字符设备结构,由系统定义

};

int char_open(struct inode *inode,struct file *filp)

{

struct char_dev *dev;

//inode 获取设备结构体

dev=container_of(inode->i_cdev,struct char_dev,cdev);

//赋值给file 结构体

filp->private_data=dev;

return 0;

}

//读时将调用的函数

static ssize_t char_read(struct file * filp, char __user *buf, size_t count, loff_t *offset)

{

char * buffer = (char *)filp->private_data;

if(copy_to_user(buf, buffer, count))

{

printk("copy_to_user error/n");

return -EFAULT;

}

printk("You are using the read function!");

return count ;

}

//参数定义和char_read 类似

static ssize_t char_write(struct file * filp, const char __user *buf, size_t count, loff_t *offset)

{

char * buffer = (char *)filp->private_data;

//printk("new %p/n", buffer);

if(copy_from_user(buffer, buf, count))

{

printk("copy_from_user error/n");

return -EFAULT;

}

return count;

}

//读写完毕后调用的函数

int char_release(struct inode *inode,struct file *filp)

{

return 0;

}

//定义设备节点文件的操作

static struct file_operations char_ops={

.owner = THIS_MODULE,

.open = char_open,

.read = char_read,

.write = char_write,

.release =char_release

};

//设备初始化时调用的函数,用于获取存储区内存空间

int memory_init(void)

{

p_mem=vmalloc(len*sizeof(char));

if(!p_mem)

{

printk(KERN_ALERT "error-memory_init/n");

return -1;

}

return 1;

}

//设备初始化

static int dev_init(void)

{

int result=0;

dev_t dev=0;

dev_t devno=0;

int err=0;

//获取存储区

//如果定义了主设备号

if(dev_major)

{

//则按照定义的设备号注册设备

dev=MKDEV(dev_major,dev_minor);

result=register_chrdev_region(dev,1,"char_dev");

}else{

//否则分配新的设备号

result=alloc_chrdev_region(&dev,dev_minor,1,"char_dev");

dev_major=MAJOR(dev);

}

if(result < 0)

{

printk(KERN_ALERT "can't get major %d/n",dev_major);

return result;

}

//返回主设备号

printk("the dev_major %d/n",dev_major);

//获取全局char_dev 结构体

char_device = kmalloc(sizeof(struct char_dev),GFP_KERNEL);

if(!char_device)

{

result=-ENOMEM;

return result;

}

memset(char_device,0,sizeof(struct char_dev));

//使用定义了的文件操作char_ops 初始化cdev

cdev_init(&char_device->cdev, &char_ops);

char_device->cdev.owner = THIS_MODULE;

char_device->cdev.ops = &char_ops;

//使用后区的设备号注册设备

devno=MKDEV(dev_major,dev_minor);

//添加此字符设备到系统

err=cdev_add(&char_device->cdev,devno,1);

char_device->data=p_mem;

char_device->len=len;

return 0;

}

//设备被移出时调用

static void dev_exit(void)

{

dev_t devno=0;

devno = MKDEV(dev_major,dev_minor);

cdev_del(&char_device->cdev);

kfree(char_device);

unregister_chrdev_region(devno,1);

}

//注册模块初始化和卸载函数

module_init(dev_init);

module_exit(dev_exit);

(2)然后编写相应的Makefile文件:

# Makefile

# If KERNELRELEASE is defined, we've been invoked from the

# kernel build system and can use its language.

ifneq ($(KERNELRELEASE),)

obj-m := chardev.o

# Otherwise we were called directly from the command

# line; invoke the kernel build system.

else

KERNELDIR ?= /lib/modules/`uname -r`/build

default:

make -C $(KERNELDIR) M=`pwd` modules

endif

(3)使用make 命令,生成驱动程序chardev.ko

(4)root 挂载设备

insmod chardev.ko

(5)在文件系统为其创建一个代表节点(建立设备文件)

创建节点命令格式如下:

mknod /dev/<dev_name><type><major_number><minor_number>

例如(若主设备号为249)

mknod mychardev0 c 249 0

(6)修改属性:

chmod 666 mychardev*

(7)设备挂载后,就能够使用系统命令写入数据和读取数据啦~

如读操作:more mychardev0

(友情提示:mychardev0会很大,若想体验计算机编码的奇妙可尝试cat命令)

(8)编写测试程序test.cpp:

//test.cpp

#include<stdio.h>

#include<fcntl.h>

#include<stdlib.h>

#include<iostream>

using namespace std;

int main()

{

int fd;

char buffer_write[20] = "Hello World!";

char buffer_read[20] = "Hello China!";

fd=open("/home/Guest/dev/mychardev0", O_RDWR);

if(fd < 0)

{

cout<<"open dev error!/n";

exit(fd);

}

//向指定设备写入用户输入文本

cout<<"Please input the text:/n";

cin>>buffer_write;

write(fd, buffer_write, 20);

//输出设备中的内容

read(fd, buffer_read, 20);

cout<<"指定设备中的内容为:/n"<<buffer_read<<endl;

close(fd);

return 0;

}

代码功能:实现了设备的读数据写数据操作,有以下几点值得注意:

fd=open("/home/Guest/dev/mychardev0", O_RDWR);不能只看对应文件夹下是否有mychardev0设备,若打开不成功可能需要重新mknod一下。

iostream不能直接写#include<iostream.h>,而要写:

#include<iostream>

using namespace std;

③关于readwrite函数的第三个参数n

其作用要追溯到copy_to_usercopy_from_user两个函数上,其值就表示要写入或读出的字符串大小(以字节为单位)

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP