函数cdev_init()用于初始化一个静态分配的cdev结构体变量,函数cdev_init会自动初始化cdev->ops
对象,将函数的第二个输入参数赋值给cdev->ops
对象,不会初始化cdev->owner
对象,因此在经过函数cdev_alloc()和函数cdev_init()处理之后的cdev结构体变量,在应用程序中只需要给cdev->owner
对象赋值,此结构变量就可以被插入Linux内核系统了,作为一个可用的字符设备使用。
cdev_init文件包含
#include <linux/cdev.h>
cdev_init函数定义
在内核源码中的位置:linux-3.19.3/fs/char_dev.c
函数定义格式:
void cdev_init(struct cdev *, const struct file_operations *)
cdev_init输入参数说明
函数cdev_init()输入两个参数,第一个参数表示一个字符设备,在函数中即将被初始化,此结构体在函数cdev_alloc()分析文档中已说明,详细信息请读者参考极客笔记函数cdev_alloc()分析文档的返回参数说明部分。
第二个输入参数是struct file_operations结构体类型的指针,通过这个结构体中提供的函数完成对设备的操作,其定义见文件linux-3.19.3/include/linux/fs.h,如下所示:
struct file_operations
{
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
void (*mremap)(struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long,unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t,unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
};
对于一个设备驱动并不需要完成所有函数的映射,可以有选择地进行使用,下面对最常用的几项进行说明:
struct module *owner;
这个域是用来设置指向“拥有”该结构的模块指针,内核使用该指针维护模块的使用计数。
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
read域用来从设备中读数据,需要提供字符串指针。从设备中读取数据时,成功返回所读取的字节数,read等于NULL时,将导致调用失败,并返回-EINVAL。
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
write域用来向字符设备写数据,需要提供所写指针内容。当向设备写入数据时,成功返回实际写入的字节数,write等于NULL时,将导致调用失败,并返回-EINVAL。
int (*open) (struct inode *, struct file *);
open域用来打开设备,并初始化设备,准备进行操作。如果该方法没有实现,系统调用open也会是成功的,但驱动程序得不到任何打开设备的通知。在实现中,可以给出一定的提示信息。
int (*release) (struct inode *, struct file *);
release域用来关闭设备,释放设备资源。当且仅当结构struct file释放时被调用,用来关闭一个文件或设备。
cdev_init返回参数说明
- 函数cdev_init()的返回值是void类型的变量,即不返回任何值。
cdev_init实例解析
编写测试文件:cdev_init.c
头文件引用、全局变量定义及相关函数声明:
/*头文件引用*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
struct cdev *mem_cdev; //字符设备对象指针
static int mem_open(struct inode *ind, struct file *filp); //设备打开函数声明
static int mem_release(struct inode *ind, struct file *filp); //设备关闭函数声明
/*设备读函数声明*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *fpos);
/*设备写函数声明*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size,loff_t *fpos);
/*定义设备驱动文件结构体*/
struct file_operations mem_fops =
{
.open = mem_open, //设备打开函数
.release = mem_release, //设备打开函数
.read = mem_read, //设备读函数
.write = mem_write, //设备写函数
};
模块加载函数定义:
static int __init cdev_init_init (void)
{
printk("into the cdev_init_init\n");
mem_cdev = cdev_alloc(); //动态分配一个新的字符设备对象
if (mem_cdev == NULL) //检查分配结果
{
printk("cdev_alloc failed! \n");
return -1;
}
/*显示字符设备内存空间地址*/
printk("cdev_alloc success! addr = 0x%x\n", (unsigned int)mem_cdev); if(mem_cdev->ops==NULL) //函数调用之前判断字段ops是否被初始化
{
printk("the ops the mem_cdev has not been initialized\n");
}
else
{
printk("the ops the mem_cdev has been initialized\n");
}
cdev_init(mem_cdev, &mem_fops); //初始化字符设备对象
if(mem_cdev->ops==NULL) //函数调用之后判断字段ops是否被初始化
{
printk("the ops the mem_cdev has not been initialized\n");
}
else
{
printk("the ops the mem_cdev has been initialized\n");
}
printk("out the cdev_init_init\n");
return 0;
}
模块退出函数定义:
static void __exit cdev_init_exit (void)
{
printk("into cdev_init_exit\n");
if (mem_cdev ! = NULL)
kfree(mem_cdev); //释放字符设备内存空间
printk("kfree mem_cdev OK! \n");
printk("out cdev_init_exit\n");
}
相关函数定义:
/*设备打开函数定义*/
int mem_open(struct inode *ind, struct file *filp)
{
printk("open vmalloc space\n");
try_module_get(THIS_MODULE); //模块引用自加
printk("open vmalloc space success\n");
return 0;
}
/*设备读函数定义,在此没有实际意义,只是为了初始化mem_fops变量*/
ssize_t mem_read(struct file *filp, char *buf, size_t size, loff_t *lofp)
{
printk("copy data to the user space\n");
return 0;
}
/*设备写函数定义,在此没有实际意义,只是为了初始化mem_fops变量*/
ssize_t mem_write(struct file *filp, const char *buf, size_t size, loff_t *lofp)
{
printk("read data from the user space\n");
return 0;
}
/*设备关闭函数定义*/
int mem_release(struct inode *ind, struct file *filp)
{
printk("close vmalloc space\n");
module_put(THIS_MODULE); //模块引用自减
printk("close vmalloc space success\n");
return 0;
}
模块加载、退出函数调用:
module_init(cdev_init_init); //模块加载函数调用
module_exit(cdev_init_exit); //模块卸载函数调用
实例运行结果及分析
首先编译模块,执行命令insmod cdev_init.ko插入内核模块,然后输入命令dmesg -c查看模块插入结果,会出现如图所示的结果。
结果分析:
由图中可以看出函数cdev_init()调用之前字符设备的字段ops没有被初始化,函数调用之后字段ops被初始化了。