不正经的保姆级西电软工操作系统实验课教程 ==== 专题四 字符设备

1.本文仅仅是西电软件工程OS实验课的教程,原则上只接受关于本实验相关的问题

2.代码原则上仅仅作为参考,如果被查出因为抄袭而导致不良的后果,本人不负任何责任。

3.文章中的代码是经过整合而得到的,如有侵权,请及时联系

4.关于实验系统 本人使用的是Ubuntu 20.04 linux内核最低5.4.x vmware 15.x

5.接上条,如果你按照了这个配置还是出现了问题,那么可以at我

===============================================================

$0.99

本文参考自 https://blog.csdn.net/baidu_38661691/article/details/94601963侵权必删

终于到最后一个了(我的肝快爆了)

理论上来讲专题三的作业二和这个作业可以一起写,专题四可以稍微改一点东西就能成为专题三第二题。

这一部分说实话其实是最无聊的,原博主代码都给你写好了,其实你照抄就完事了(搞得我其他地方不是抄的一样)

我会把一些细节放到专题三作业二去讲(也可能不想讲了。。。)我还是奔着能抄就抄的原则,先让大家把最小实例写出来就行了
1.1创建内核
sudo su
mkdir charout
cd charout/ # 在哪创建都行
sudo gedit char.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h> 
#include <linux/device.h>
 
#define MAX_SIZE    0x100
#define MEM_CLEAR         0x1
 
static struct class *class;
static struct device *dev;
static int lock = 0;
 
struct chardev_dev           //定义设备
{
    struct cdev cdev;
    unsigned char mem[MAX_SIZE];
};
 
struct chardev_dev *devp;    
dev_t devno;                      //设备号
 
 
static int chardev_open(struct inode *inode, struct file *filp)   //open函数
{
    filp->private_data = devp;
    return 0;
}
 
 
static int chardev_release(struct inode *inode, struct file *filp)  //release函数
{
    return 0;
}
 
 
static long chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)   //ioctl函数
{
    struct chardev_dev *dev = filp->private_data;
    
    switch (cmd)
    {
    case MEM_CLEAR:
        memset(dev->mem, 0, MAX_SIZE);
        printk(KERN_INFO "chardev is set to zero\n");
        break;
    
    default:
        return -EINVAL;
    }
    
    return 0;
}
 
static ssize_t chardev_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)    //read函数
{

    if(lock = 1){
    
    	lock = 0;
    }

    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct chardev_dev *dev = filp->private_data;
    
    if (p >= MAX_SIZE)
        return -EINVAL;
    
    if (count > MAX_SIZE - p)
        count = MAX_SIZE - p;
        
    if (copy_to_user(buf, dev->mem + p, count))                             //从内核空间得到数据
        ret = -EINVAL;
    else
    {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "read %u byte(s) from %lu\n", count, p);
    }
    
    return ret;
}
 
 
static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t size,             //write函数
                    loff_t *ppos)
{

    if(lock == 0)
    {
    	lock = 1;
    }else{
    
    	return -EINVAL;
    
    }

    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct chardev_dev *dev = filp->private_data;
    
    if (p >= MAX_SIZE){
        chardev_ioctl(filp,1,1);
        return -EINVAL;
    }

    if (count > MAX_SIZE - p)
        count = MAX_SIZE - p;
        
    if (copy_from_user(dev->mem + p, buf, count))                //从用户空间得到写入的数据
        ret = -EINVAL;
    else                    
    {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "write %u byte(s) from %lu\n", count, p);
    }
    
    return ret;
}
 
 
static loff_t chardev_llseek(struct file *filp, loff_t offset, int orig)                     //llseek函数
{
    loff_t ret = 0;
    
    switch (orig)                                     //判断文件指针的位置,确定从何开始读写
    {
    case 0:
        if (offset < 0)
        {
            ret = -EINVAL;
            break;
        }
        if (offset > MAX_SIZE)
        {
            ret = -EINVAL;
            break;
        }
        filp->f_pos = offset;
        ret = filp->f_pos;
        break;
        
    case 1:
        if ((filp->f_pos + offset) < 0 )
        {
            ret = -EINVAL;
            break;
        }
        
        if ((filp->f_pos + offset) > MAX_SIZE)
        {
            ret = -EINVAL;
            break;
        }
        filp->f_pos += offset;
        ret = filp->f_pos;
        break;
        
    default:
        ret = -EINVAL;
        break;
    }
    
    return ret;
}
 
 
static const struct file_operations chardev_fops =                  //字符设备操作函数定义
{
    .owner  =  THIS_MODULE,
    .llseek =  chardev_llseek,
    .read   =  chardev_read,
    .write  =  chardev_write,
    .unlocked_ioctl = chardev_ioctl,
    .open   =  chardev_open,
    .release = chardev_release,
};
 
static char *chardev_devnode(struct device *dev, umode_t *mode)
{
    if (mode)
        *mode = 0666;
 
    return NULL;
}
 
static int __init chardev_init(void)                              //初始化,入口函数
{
    int ret;
    int err;
    
    ret = alloc_chrdev_region(&devno, 0, 1, "chardev");            //动态申请设备号
    if (ret < 0)
        return ret;
        
    devp = kzalloc(sizeof(struct chardev_dev), GFP_KERNEL);         //分配内存空间
    if (!devp)
    {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    
    class = class_create(NULL, "chardev");                          //创建类节点
    if (IS_ERR(class))
    {
        ret = PTR_ERR(class);
        printk(KERN_ERR "class create error %d\n", ret);
        goto fail_malloc;
    }
    
    class->devnode = chardev_devnode;                              //创建设备节点
    dev = device_create(class, NULL, devno, NULL, "chardev");
    if (IS_ERR(class))
    {
        ret = PTR_ERR(dev);
        printk(KERN_ERR "device create error %d\n", ret);
        goto bad_device_create;
    }
    
    
    cdev_init(&devp->cdev, &chardev_fops);                          //绑定操作函数的结构体
    devp->cdev.owner = THIS_MODULE;
    err = cdev_add(&devp->cdev, devno, 1);                          //调用cdev_add函数将cdev结构体注册到内核
    if (err)
        printk(KERN_NOTICE "Error %d adding chardev", err);
        
    return 0;
    
    
bad_device_create:
    class_destroy(class);
fail_malloc:
    unregister_chrdev_region(devno, 1);
    return ret;
}
 
 
static void __exit chardev_exit(void)                             //注销
{
    device_destroy(class, devno);
    class_destroy(class);
    cdev_del(&devp->cdev);
    kfree(devp);
    unregister_chrdev_region(devno, 1);
}
 
module_init(chardev_init);                                      //加载模块
module_exit(chardev_exit);                                      //退出模块
 
MODULE_AUTHOR("lql");
MODULE_LICENSE("GPL");

sudo gedit Makefile
ifneq ($(KERNELRELEASE),)
# call from kernel build system
	obj-m	:= char.o
else
	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	PWD       := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

sudo make 
sudo insmod char.ko

直接make就行
i

在这里插入图片描述

$$1.2 测试

虽然说ioctl可以单独拿出来测试 但是编译器死活不给我放活路,所以我把write的代码改了 如下

static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t size,             //write函数
                    loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    struct chardev_dev *dev = filp->private_data;
    
    if (p >= MAX_SIZE){
        chardev_ioctl(filp,1,1);
        return -EINVAL;
    }

    if (count > MAX_SIZE - p)
        count = MAX_SIZE - p;
        
    if (copy_from_user(dev->mem + p, buf, count))                //从用户空间得到写入的数据
        ret = -EINVAL;
    else                    
    {
        *ppos += count;
        ret = count;
        printk(KERN_INFO "write %u byte(s) from %lu\n", count, p);
    }
    
    return ret;
}
记得重新make remod insmod

然后我把最大的字符给改了 原题是1024 但是我不想输入那么多了
(也就是说 现在如果驱动里面写到了100个字 那就直接清空,相当于完成了两个任务)

在这里插入图片描述

twrite.c 代码

#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    int fd;
    char msg[100];
    fd= open("/dev/chardev",O_RDWR,S_IRUSR|S_IWUSR);
    if(fd!=-1)
    {
        while(1)
        {
            printf("Please input the globar:(input quit go out)\n");
            scanf("%s",msg);
            if(strcmp(msg,"quit")==0)
              {
                close(fd);
                break;
              }
            write(fd,msg,strlen(msg));
            
        }
    }
    else
    {
        printf("device open failure\n");
    }
    return 0;
}

tread.c

#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    int fd,i;
    char msg[101];
    fd= open("/dev/chardev",O_RDWR,S_IRUSR|S_IWUSR);
    if(fd!=-1)
    {
            for(i=0;i<101;i++)
                msg[i]='\0';
            read(fd,msg,100);
            printf("%s\n",msg);
    }
    else
    {
        printf("device open failure,%d\n",fd);
    }
    return 0;
}

gcc -o write twrite.c 
gcc -o read tread.c 

./write./read 就可以分别测试了
先write 再read

后记

终于更完了… 其实这是本人第一次这么仔细地写教程,然而越写越发现写的很不理想,很多地方只是扔个代码上去,也没讲清楚原理。导致很多同学后面还是来找我问我这步之后干啥干啥…而且自己还是不看题,专题一就坑了不少人,最后还是被助教打回来了重做,现在后面的几个专题其实还没有发布,本来我可以细细写的,但是一方面我真的太菜了,而是我后面也越来越没耐心了,写专题一和专题二的时候还有点精力,专题四就想着赶紧肝完就完事了。
还有就是我也仅仅是学过一点运维方面的知识,编译内核说实话是我第一次碰(希望也是最后一次),我本来是想着试个水,给大伙们开个新版本的坑,让没用过linux的大伙看看linux界面是多么的漂亮,我最熟悉的还是archlinux,但是如果我用arch的话那基本上就更没人来看我开坑了,所以我选择了ubuntu20.04,其一就是想着让大家接触linux,其二就是觉得高版本的东西比较好康,低版本的问题会很多。
但是其实恰恰相反,我室友低版本的都已经划过去了,我还在这和编译器击剑。高版本容错率并不高,而且很多错误都是查不到的,就那个头文件我看了一晚上还是没绕过去,索性不绕了。而且很多同学其实并没有我这么能折腾,他们可能更多的是想能快速的拿到实验结果就行了。并且我的强项是web后端,不是linux和运维,本人性格也比较倔强,觉得自己就是想折腾点花样出来,结果现在发现自己就是个小丑。。。这就导致我写的越来越不走心。如果这个教程没有达到你的预期,那么T某我真的很抱歉啊。。。
专题三那道题等我有时间了再看吧,我现在还得整点其他东西去了,晚安各位。。

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值