0、内核一个重要结构的简单解释

Q:内核中的如何描述设备号?

A:Struct kdev_t

Q:如何从kdev_t结构中分解出主设备号?

A:MAJOR(kdev_t dev)

Q:如何从kdev_t结构中分解出次设备号?

A:MINOR(kdev_t dev)

  1. 创建设备文件:
    1. 使用mknod命令手工创建。
    2. 使用devis提供的函数在驱动程序中自动创建。

2.Mknod用法:

Mknod filename type major minor

Filename:设备文件名

Type:设备文件类型

Major:主设备号

Minor:次设备号

例子:mknod serial0 c 100 0(创建的设备名字为serial0,c代表的是字符设备,主设备号为100,次设备号为0)。

3.重要结构:

在linux字符设备驱动程序设计中,有三种非常重要的数据结构。

Struct file

Struct inode

Struct file_operations

Struct file:代表一个打开的文件,系统中每个打开的文件在内核中都有一个关联的struct file. 它由内核在打开文件时创建,在文件关闭后,内核释放这个数据结构。

重要成员:

Loff_t f_pos /*文件读写位置*/

Struct file_operations *f_op

Struct inode:用来表示物理上存在的文件。因此,它和代表打开文件描述符的file是不同德。一个物理上存在的文件可以有多个打开描述符file,但只有一个inode结构。

重要成员:

Kdev_t i_dev:设备号

 

Struct file_operations:一个函数指针的集合,定义能在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为null。

例子:men_fops

Struct file_operations mem_fops={

.owner= THIS_MODULE,

.llseek=mem_seek,

.read = mem_read,

.write =mem_write,

.ioctl = mem_ioctl,

.open = mem_open,

.release = mem_release,

};

 

设备驱动安装:

        安装一个设备驱动的方法:

    Int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

    这里,major是主设备号,name是驱动的名字(出现在/proc/devices),fops是file_operations结构。

        卸载一个字符设备驱动的方法:

    Int unregister_chrdev(unsigned int major, const char *name);

    Major和name必须和传递给register_chrdev的相同,否则调用会失败。

设备操作:

Int (*pen)(struct inode *, struct file *)

这是操作在设备文件上的第一个操作,然而并不要求驱动程序一定要声明这个方法。如果该项为NULL,设备的打开操作永远成功。

Void (*release)(struct inode *, struct file *)

当设备文件被关闭时调用这个操作。与open相仿,release也可以没有。

Int (*read)(struct inode *, struct file *, char *, int);

用来从设备中读取数据。当其为null指针时将引起read系统调用返回-EINVAL。函数返回一个非负数表示成功的读取了一个字节。

 

Int (*write)(struct inode *, struct file *, const char *, int);

向设备发送数据。如果没有这个函数,write系统调用向调用程序返回一个-EINVAL。

Int (*select)(struct inode *, struct file *, int , select_table *);

Select一般用于程序询问设备是否可读和可写。

Int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);

系统调用ioctl提供调用设备相关命令的方法,对于任何内核没有定义的请求,ioctl系统调用将返回-EINVAL.当调用成功时,返回给程序一个非负返回值。

Int (*mmap)(struct inode *, struct file *, struct vm_area_struct *);

Mmap用来将设备内存映射到进程内存中。

Int (*lseek)(struct inode *, struct file *, off_t, int);

用来修改一个文件的当前读写位置,并将新位置作为返回值。

Open方法:

    Open方法是驱动程序用来为以后的操作完成初始化准备工作的。此外,open还会增加设备计数,以便于防止文件在关闭前模块被卸载出内核。在大部分驱动程序中,open完成如下工作:

初始化设备。表明次设备号。增加使用计数。

Release方法:

    Release方法的作用正好与open相反。这个设备方法又是也称为close。它应该:使用计数减1.关闭设备。

读和写:

    读和写方法都是进行类似的任务,从和到应用程序代码拷贝数据。因此,他们的原型相当类似:

    Ssize_t xxx(dev)_read(struct file *filp, char *buff, size_t count, loff_t *offp);

    Ssize_t xxx(dev)_write(struct file *filp, const char *buff, size_t count, loff_t *offp);

对于这两个方法,flip是文件指针,count是请求的传输数据大小。Buff参数指向数据缓存。最后,offp他指向用户正在存取的文件位置。

    Read和write方法的buff参数是用户空间指针。因此,他不能被内核代码直接引用,理由如下:

    用户空间指针在内核空间时可能根本是无效的----没有那个地址的映射。

    驱动必须能够存取用户空间缓存以完成它的工作。但是,为了安全起见这个存取必须使用特殊的,内核提供的函数,例如:

    Unsigned long copy_to_user(void __user *to, const void *from, unsigned long count).

    Unsigned long copy_from_user(void *to, const void __user *from, unsigned long count).

上面的例子中app=mem.c是测试的程序,memdev.c和memdev.h是驱动程序。该驱动程序实现的功能,它不驱动具体的设备,只是访问某个内存。

 

 

原文地址:https://www.cnblogs.com/FORFISH/p/5188417.html