自制linux字符串设备的实验总结

84 篇文章 0 订阅

自制linux字符串设备的实验总结

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 <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>

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 <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h> /* copy_*_user */
#include <linux/slab.h>

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);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值