西电软工操作系统实验:字符设备

目录

  • 一、实验内容
  • 二、实验环境
  • 三、实验过程
    • 3.1 编写字符设备驱动程序
    • 3.2编写Makefile文件
    • 3.3 将字符设备载入模块
    • 3.4 测试文件编写
      • 3.4.1写文件的创建
      • 3.4.2 读文件的创建
      • 3.4.3 删除文件的创建
    • 3.5 测试

一、实验内容

编写字符设备程序,使之满足以下功能:
1、安装设备后从设备中读出字符串为你的学号;
2、设备支持每次写入字符不超过1024个,超过部分被丢弃;
3、用户可以读出最近写入到设备中的字符;
4、设备关闭前不能被多次打开;
5、设备支持系统调用ioctl(int d, int req,…),req = 0x909090, 清除设备中写入的字符串;
自己编写测试程序,验证以上功能

二、实验环境

Vmware:15.02
ubuntu:18.04
新内核:5.12.2

三、实验过程

3.1 编写字符设备驱动程序

新建charout文件夹,在charout文件夹,创建char.c文件,并输入以下内容:

#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
 
#define MAX_SIZE    0x1024
#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)
{
    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("hsy");
MODULE_LICENSE("GPL");

3.2编写Makefile文件

利用终端,在charout文件夹中生成Makefile文件。

touch Makefile
vim Makefile

在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

3.3 将字符设备载入模块

在终端执行以下命令:

sudo make 
sudo insmod char.ko

得到以下结果:
西电软工操作系统实验:字符设备_第1张图片

3.4 测试文件编写

3.4.1写文件的创建

在charout文件夹下创建写文件测试文件write.c。write.c主要功能是在字符设备中录入内容,当录入的内容超过1024大小时,将超出内容删除。

#include
#include
#include
#include
#include
#include
int main()
{
    int fd;
    char msg[1024];
    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;
}

3.4.2 读文件的创建

同样在charout文件夹下创建read.c。read.c的主要功能是读出字符设备的内容。

#include
#include
#include
#include
#include
#include
int main()
{
    int fd,i;
    char msg[1025];
    fd= open("/dev/chardev",O_RDWR,S_IRUSR|S_IWUSR);
    if(fd!=-1)
    {
            for(i=0;i<1025;i++)
                msg[i]='\0';
            read(fd,msg,1024);
            printf("%s\n",msg);
    }
    else
    {
        printf("device open failure,%d\n",fd);
    }
    return 0;
}

3.4.3 删除文件的创建

在charout文件夹下,编写delete.c文件。其功能用于清空字符设备中的内容。

#include
#include
#include
#include
#include
int main()
{
    int fd;
    fd= open("/dev/chardev",O_RDWR,S_IRUSR|S_IWUSR);
    if(fd!=-1)
    {
       ioctl(fd,1,1);
    }
	printf("文件已清空\n");
    return 0;
}

3.5 测试

将测试文件编译。

gcc write.c -o write
gcc read.c -o read
gcc delete.c -o delete

然后进行运行测试。

你可能感兴趣的:(操作系统,ubuntu)