2020年马上过去了,一年年过得好快,有点想念师从陈哥的日子了,私下都叫他老陈,但是他总是让我们叫他大哥,哈哈哈其实一直当师傅
2019年遇到了陈莉君,听了陈老师2019年慷慨激昂的linux内核人才培养计划,决定要学习linux内核,2020年由于疫情的原因未尝再见陈老师
马上年末了,也对自己有一个交代,也对2020有一个交代,做一个linux字符串设备的学习总结吧
前面的学习已经对linux字符串驱动设备的理论知识做出了学习了解,并且对实验现象做出了大胆的假设,现在就让我们对假设进行试验,查看结果是否符合我的预期
根据linux第二章节的知识我们首先要进行模块初始化
//初始化设备驱动时候出发hello_init
module_init(hello_init);
//退出设备驱动时候出发hello_exit
module_exit(hello_exit);
1.申请设备号 主设备号自己使用 从设备号供给给linux内核使用
2.定义重要的数据结构
3.申请内存,建立一个字符串设备对象
4.初始化字符串设备
5.初始化字符串设备的缓冲区
6.挂载字符串设备到内核
1.卸载设备号
2.删除设备号
3.卸载自己注册的内存地址
初始化:
1)定义一个字符串设备对象
struct scull_dev {
struct scull_qset *data;
struct cdev cdev;
unsigned long size;
char buf[6];
};
2)定义重要的文件操作数据结构
之前做过假设,用户层的打开、关闭、读写设备驱动都可以触发里面的函数
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_seek,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release
};
2)注册设备号,初始化并且挂载字符串设备到内核
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err,devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
static int __init hello_init(void)
{
printk(KERN_ALERT "HELLO,KERNEL\n");
dev_t dev = 0;
int result;
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, 4, "scull");
}else {
result = alloc_chrdev_region(&dev, scull_minor, 4, "scull");
scull_major = MAJOR(dev);
printk("scull_major:%d\n", scull_major);
}
printk("dev:%d\n", MKDEV(scull_major, scull_minor));
scull_devices = kmalloc(4 * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices)
{
result = -ENOMEM;
}
int i = 0;
for(i=0; i<4;i++)
{
memset(scull_devices[i].buf, 0, 6);
scull_setup_cdev(&scull_devices[i], i);
}
return 0;
}
3)定义退出的函数地址
在结束的时候,卸载设备号,删除字符串设备
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Goodbye, Kernel!\n");
dev_t dev, devno;
int i;
dev = MKDEV(scull_major, scull_minor);
devno = MKDEV(scull_major, scull_minor);
if (scull_devices)
{
for(i=0;i<4;i++)
{
cdev_del(&scull_devices[i].cdev);
}
}
unregister_chrdev_region(devno, 4);
}
4)定义重要的文件操作的实现
尤其是要注意读写 copy_from_user和copy_to_user
这两个函数的作用不限于在内核空间和用户空间之间拷贝数据,他们还检查用户空间指针是否有效。如果指针无效,就不会拷贝;
另一方面在拷贝之中出现无效的地址,则只会拷贝部分数据。返回值返回还需要拷贝的内容值。scull如果发现这样的错误,会返回给用户 -EFAULT.
int scull_open(struct inode *inode, struct file *filp)
{
printk("scull_open\n");
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev, cdev);
filp->private_data = dev;
return 0;
}
int scull_release(struct inode *inode , struct file *filp) {
printk("scull_release\n");
return 0;
}
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
printk("scull_read\n");
copy_to_user(buf,dev->buf, count);
return count;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
printk("scull_write\n");
struct scull_dev *dev = filp->private_data;
struct scull_dev *dptr;
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset;
printk("itemsize:%d\n", itemsize);
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM;
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
printk("item:%d\n", item);
printk("rest:%d\n", rest);
printk("s_pos:%d\n", s_pos);
printk("q_pos:%d\n", q_pos);
memset(dev->buf, 0, 6);
copy_from_user(dev->buf, buf, count);
return count;
}
5)制作发布脚本
#!/bin/sh
module="scull"
device="scull"
mode="644"
/sbin/insmod ./main.ko $* || exit 1
rm -f /dev/${device}[0-3]
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
group="staff"
grep "^staff:" /etc/group|| group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]
6)制作卸载脚本
#!/bin/sh
module="scull"
device="scull"
/sbin/rmmod main.ko $* || exit 1
rm -f /dev/${device} /dev/${device}[0-3]
7)make file 的编写
obj-m:=main.o
KERNELBUILD := /lib/modules/$(shell uname -r)/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
rm -rf *.o *.ko
8)用户层调用代码
#include
#include
#include
#include
#include
int main ()
{
int fd = open("/dev/scull1", O_RDWR);
std::cout << fd << std::endl;
char data[6] = "haha";
int res = write(fd, data, sizeof(data));
std::cout << strerror(errno) << std::endl;
char newData[6];
bzero(newData, 6);
res = read(fd, newData, sizeof(data));
std::cout << newData << std::endl;
return 0;
}
9)实验现象
root@zhanglei-Latitude-5400:/home/zhanglei/ourc/test/cmake-build-debug# sudo ./demo
3
Success
haha
10)内核层面打印
[ 2966.168002] scull_open
[ 2966.168076] scull_write
[ 2966.168080] itemsize:4000000
[ 2966.168081] item:0
[ 2966.168083] rest:0
[ 2966.168084] s_pos:0
[ 2966.168085] q_pos:0
[ 2966.168117] scull_read
[ 2966.168645] scull_release
[ 3500.797747] Goodbye, Kernel!
[ 3504.192639] HELLO,KERNEL
[ 3504.192650] scull_major:236
[ 3504.192652] dev:247463936
[ 3515.287855] scull_open
[ 3515.287878] scull_write
[ 3515.287879] itemsize:4000000
[ 3515.287880] item:0
[ 3515.287880] rest:0
[ 3515.287881] s_pos:0
[ 3515.287881] q_pos:0
[ 3515.287890] scull_read
[ 3515.287985] scull_release
11)总体代码一阅
#include
#include
#include
#include
#include
#include
#include /* copy_*_user */
#include
int scull_major = 0;
int scull_minor = 0;
struct cdev cdev;
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0 /* dynamic major by default */
#endif
#define MAX_SIZE 10
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif
#ifndef SCULL_QSET
#define SCULL_QSET 1000
#endif
int scull_quantum = SCULL_QUANTUM;
int scull_qset = SCULL_QSET;
size_t size = 0;
char store[MAX_SIZE];
struct scull_qset{
void** data;
struct scull_qset *next;
};
struct scull_dev {
struct scull_qset *data;
struct cdev cdev;
unsigned long size;
int quantum;
int qset;
char buf[6];
};
loff_t scull_seek(struct file* filp,loff_t off, int whence);
int scull_open(struct inode *inode, struct file *filp);
int scull_release(struct inode *inode , struct file *filp);
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos);
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos);
static void scull_setup_cdev(struct scull_dev *dev, int index);
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_seek,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release
};
loff_t scull_seek(struct file* filp,loff_t off, int whence)
{
struct scull_dev *dev = filp->private_data;
loff_t newpos;
switch(whence)
{
case 0:
newpos = off;
break;
case 1:
newpos = filp->f_pos + off;
break;
case 2:
newpos = dev->size + off;
break;
default:
return -EINVAL;
}
if (newpos < 0)
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
int scull_open(struct inode *inode, struct file *filp)
{
printk("scull_open\n");
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev, cdev);
filp->private_data = dev;
return 0;
}
int scull_release(struct inode *inode , struct file *filp) {
printk("scull_release\n");
return 0;
}
struct scull_dev *scull_devices;
static int __init hello_init(void)
{
printk(KERN_ALERT "HELLO,KERNEL\n");
dev_t dev = 0;
int result;
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, 4, "scull");
}else {
result = alloc_chrdev_region(&dev, scull_minor, 4, "scull");
scull_major = MAJOR(dev);
printk("scull_major:%d\n", scull_major);
}
printk("dev:%d\n", MKDEV(scull_major, scull_minor));
scull_devices = kmalloc(4 * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices)
{
result = -ENOMEM;
}
int i = 0;
for(i=0; i<4;i++)
{
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
memset(scull_devices[i].buf, 0, 6);
scull_setup_cdev(&scull_devices[i], i);
}
return 0;
}
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err,devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
printk("scull_read\n");
copy_to_user(buf,dev->buf, count);
return count;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
printk("scull_write\n");
struct scull_dev *dev = filp->private_data;
struct scull_dev *dptr;
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset;
printk("itemsize:%d\n", itemsize);
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM;
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
printk("item:%d\n", item);
printk("rest:%d\n", rest);
printk("s_pos:%d\n", s_pos);
printk("q_pos:%d\n", q_pos);
memset(dev->buf, 0, 6);
copy_from_user(dev->buf, buf, count);
return count;
}
struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
struct scull_qset *qs = dev->data;
if (!qs) {
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL;
memset(qs, 0, sizeof(struct scull_qset));
}
while(n--)
{
if (!qs->next) {
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL;
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Goodbye, Kernel!\n");
dev_t dev, devno;
int i;
dev = MKDEV(scull_major, scull_minor);
devno = MKDEV(scull_major, scull_minor);
if (scull_devices)
{
for(i=0;i<4;i++)
{
cdev_del(&scull_devices[i].cdev);
}
}
unregister_chrdev_region(devno, 4);
}
module_init(hello_init);
module_exit(hello_exit);