Linux Kernel 学习笔记5:字符设备

(本章基于:linux-4.4.0-37)

本章介绍如何注册一个字符设备,并通过设备节点对这个字符设备进行open、close、read、write等操作。


一、字符设备注册

相关头文件:linux/cdev.h

1、初始化

void cdev_init(struct cdev *cdev, struct file_operations *fops);

1)        待初始化设备

2)        Fops结构

cdev_init(&my_dev,&hello_fops);

hello_dev.owner= THIS_MODULE;    //必要的成员初始化

hello_dev.ops =&hello_fops;

2、添加设备

intcdev_add(struct cdev *dev, dev_t num, unsigned int count);

1)        待添加的设备

2)        设备响应的第一个设备号

3)        响应的设备个数

result = cdev_add(&hello_dev,MKDEV(hello_major, hello_minor), 1);

3、删除设备

voidcdev_del(struct cdev *dev);

1)        待删除的设备

cdev_del(&hello_dev);


二、定义文件操作

相关头文件:linux/fs.h

使用struct file_operations 结构定义文件操作

static struct file_operations myFops = {
        .owner = THIS_MODULE,
        .read = my_read,
        .write = my_write,
        .open = my_open,
        .release = my_release,
};


三、接口解析

int (*open)(struct inode *, struct file *);

open方法主要进行下列操作

1)        检查设备特定的错误;

2)        如果他是第一次打开,初始化该设备;

 

int (*release)(struct inode *, struct file *);

release主要用来释放open时所申请的各种资源,并关闭设备。

注意:并不是每次close()都会执行release,fork出的子进程可以不用open来创建打开文件的拷贝,但每个拷贝都会在进程终止时关闭。内核中维持一个文件结构被使用多少次的计数,当计数为0 时才真正调用release,这样的机制保证了一次open只进行一次release。(备注:flush在每次close时都会被调用,但一般驱动很少实现flush)。

 

ssize_t (*read)(struct file *file, char __user *user, size_t size, loff_t *offp);

ssize_t (*write)(struct file *fule, const char __user *user, size_t size, loff_t *offp);

file: 文件指针;

count:请求传输数据大小;

user:指向持有被写入数据的缓存,或放入新数据的空缓存;

offp:当前存取的文件位置;

 

四、内核空间、用户空间数据交互

static inlinelong copy_from_user(void *to,

           const void __user * from, unsignedlong n)

从用户空间from拷贝n字节数据到to

static inlinelong copy_to_user(void __user *to,

           const void *from, unsigned long n)

从内核空间from拷贝n字节数据到用户空间to

 

这两个函数返回未拷贝数据的大小,也就是说返回0 表示已拷贝所有数据。


例:

hello.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

static dev_t devId;
static struct class *cls = NULL;
static struct cdev myDev;

static int
my_open(struct inode *inode, struct file *file)
{
        printk(KERN_WARNING "open success!\n");
        return 0;
}

static int
my_release(struct inode *inode, struct file *file)
{
        printk(KERN_WARNING "close success!\n");
        return 0;
}

static int
my_read(struct file *file, char __user *user, size_t size, loff_t *loff)
{
        char buf[] = "read kernel data!";
        size_t readSize;

        (sizeof(buf) > size)? (readSize = size) : (readSize = sizeof(buf));
        if(copy_to_user(user, buf, readSize) != 0) {
                return -EFAULT;
        }

        return readSize;
}

static int
my_write(struct file *file, const char __user *user, size_t size, loff_t *loff)
{
        char buf[64];
        size_t writeSize;

        (sizeof(buf) - 1 > size) ? (writeSize = size) : (writeSize = sizeof(buf) - 1);
        memset(buf, 0, sizeof(buf));
        if(copy_from_user(buf, user, writeSize) != 0) {
                return -EFAULT;
        }
        printk(KERN_ALERT "kernel read data:%s\n", buf);
        return writeSize;
}

//定义文件操作
static struct file_operations myFops = {
        .owner = THIS_MODULE,
        .read = my_read,
        .write = my_write,
        .open = my_open,
        .release = my_release,
};

static void
hello_cleanup(void)
{
        cdev_del(&myDev);
        device_destroy(cls, devId);
        class_destroy(cls);
        unregister_chrdev_region(devId, 1);
}

static __init int hello_init(void)
{
        int result;

        //动态注册设备号
        if(( result = alloc_chrdev_region(&devId, 0, 1, "stone-alloc-dev") ) != 0) {
                printk(KERN_WARNING "register dev id error:%d\n", result);
                goto err;
        } else {
                printk(KERN_WARNING "register dev id success!\n");
        }
        //动态创建设备节点
        cls = class_create(THIS_MODULE, "stone-class");
        if(IS_ERR(cls)) {
                printk(KERN_WARNING "create class error!\n");
                goto err;
        }
        if(device_create(cls, NULL, devId, "", "hello%d", 0) == NULL) {
                printk(KERN_WARNING "create device error!\n");
                goto err;
        }
        //字符设备注册
        myDev.owner = THIS_MODULE;      //必要的成员初始化
        myDev.ops = &myFops;
        cdev_init(&myDev, &myFops);
        //添加一个设备
        result = cdev_add(&myDev, devId, 1);
        if(result != 0) {
                printk(KERN_WARNING "add cdev error!\n");
                goto err;
        }

        printk(KERN_ALERT "hello init success!\n");
        return 0;
err:
        hello_cleanup();
        return -1;
}

static __exit void hello_exit(void)
{
        hello_cleanup();
        printk(KERN_WARNING "helloworld exit!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stone");

用户层应用程序:

a.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int
main(void)
{
        int fd;
        char buf[100];
        int size;

        fd = open("/dev/hello0", O_RDWR);
        if(!fd) {
                perror("open");
                exit(-1);
        }

        memset(buf, 0, sizeof(buf));
        size = read(fd, buf, sizeof(buf));
        if(size < 0) {
                close(fd);
                printf("read error!\n");
                exit(1);
        }
        printf("read %d:%s\n", size, buf);

        size = write(fd, "write data to kernel!\n", 23);
        if(size < 0) {
                close(fd);
                printf("read error!\n");
                exit(1);
        }
        printf("write %d\n", size);

        close(fd);
        return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stone8761

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值