linux设备模型理解

linux设备模型理解
linux设备模型理解

LINUX设备模型理解

1 LINUX设备底层模型

1.1 Kobject

Kobject 是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs 文件系统中的一个目录。Kobject是组成设备模型的基本结构。

kobject 结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重复实现。这些机能包括

- 对象引用计数.

- 维护对象链表(集合).

- 对象上锁.

- 在用户空间的表示.

struct kobject {

char * k name; 指向设备名称的指针

char name[KOBJ NAME LEN]; 设备名称

struct kref kref; 对象引用计数

struct list head entry; 挂接到所在kset中去的单元

struct kobject * parent; 指向父对象的指针

struct kset * kset; 所属kset的指针

struct kobj type * ktype; 指向其对象类型描述符的指针

struct dentry * dentry; sysfs文件系统中与该对象对应的文件节点路径指针

};

其中的kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两个函数kobject_get()、kobject_put()分别用于增加和减少引用计数,当引用计数为0时,所有该对象使用的资源释放。Ktype 域是一个指向kobj type结构的指针,表示该对象的类型

void kobject_init(struct kobject * kobj);

int kobject_set_name(struct kobject *kobj, const char *format, ...);

struct kobject *kobject_get(struct kobject *kobj);

void kobject_put(struct kobject * kobj);

int kobject_add(struct kobject * kobj);

int kobject_register(struct kobject * kobj);

void kobject_del(struct kobject * kobj);

void kobject_unregister(struct kobject * kobj);

1.2 Ktype

用于描述kojbect属性

struct kobj_type {

void (*release)(struct kobject *);

struct sysfs_ops * sysfs_ops;

struct attribute ** default_attrs;

};

Kobj type数据结构包含三个域:一个release方法用于释放kobject占用的资源;一个sysfs ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。

Attribute

struct attribute {

char * name;

struct module * owner;

mode_t mode;

};

attribute, 属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件名就是name。文件读写的方法对应于kobj type中的sysfs ops。

kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性。kobject 也会利用它了分辨自已是属于那一個类型,然後在/sys 下建立正确的目录位置。而kset 的优先权比较高,kobject会利用自已的*kset 找到自已所属的kset,並把*ktype 指定成該kset下的ktype,除非沒有定义kset,才会用ktype來建立关系。Kobject通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合

struct kset {

struct subsystem * subsys; 所在的subsystem的指针

struct kobj type * ktype; 指向该kset对象类型描述符的指针

struct list head list; 用于连接该kset中所有kobject的链表头

struct kobject kobj; 嵌入的kobject

struct kset hotplug ops * hotplug ops; 指向热插拔操作表的指针

};

包含在kset中的所有kobject被组织成一个双向循环链表,list域正是该链表的头。Ktype域指向一个kobj type结构,被该kset中的所有kobject共享,表示这些对象的类型。Kset数据结构还内嵌了一个kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的kobject对象的引用计数

void kset_init(struct kset * k);

int kset_add(struct kset * k);

int kset_register(struct kset * k);

void kset_unregister(struct kset * k);

1.3 Subsystem

如果说kset 是管理kobject 的集合,同理,subsystem 就是管理kset 的集合。它描述系统中某一类设备子系统,如block subsys表示所有的块设备,对应于sysfs文件系统中的block目录。类似的,devices subsys对应于sysfs中的devices目录,描述系统中所有的设备

struct subsystem {

struct kset kset; 内嵌的kset对象

struct rw semaphore rwsem; 互斥访问信号量

};

subsystem与kset的区别就是多了一个信号量,所以在后来的代码中,subsystem 已经完全被kset取缔了。

每个kset属于某个subsystem,通过设置kset结构中的subsys域指向指定的subsystem可以将一个kset加入到该subsystem。所有挂接到同一subsystem的kset共享同一个rwsem信号量,用于同步访问kset中的链表

1.4 底层设备模型关系图

图1 底层设备模型关系图

2 设备模型上层容器:BUS DEVICE DRIVER

2.1 BUS

每个bus_type对象都内嵌一个subsystem对象,bus_subsys对象管理系统中所有总线类型的subsystem对象。每个bus_type对象都对应/sys/bus目录下的一个子目录,如PCI总线类型对应于/sys/bus/pci。在每个这样的目录下都存在两个子目录:devices 和drivers(分别对应于bus type结构中的devices和drivers域)。其中devices子目录描述连接在该总线上的所有设备,而drivers目录则描述与该总线关联的所有驱动程序。与device_driver对象类似,bus_type结构还包含几个函数(match()、hotplug()等)处理相应的热插拔、即插即拔和电源管理事件

struct bus_type {

char * name; 总线类型的名称

struct subsystem subsys; 与该总线相关的subsystem

struct kset drivers; 所有与该总线相关的驱动程序集合

struct kset devices; 所有挂接在该总线上的设备集合

struct bus attribute * bus_attrs; 总线属性

struct device attribute * dev_attrs; 设备属性

struct driver attribute * drv_attrs; 驱动程序属性

int (*match)(struct device * dev, struct device_driver * drv);

int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);

int (*suspend)(struct device * dev, u32 state);

int (*resume)(struct device * dev);

};

2.2 DEVICE

系统中的任一设备在设备模型中都由一个device对象描述

struct device {

struct list_head g_list;

struct list_head node;

struct list_head bus_list;

struct list_head driver_list;

struct list_head children

struct device *parent;

struct kobject kobj;

char bus_id[BUS_ID_SIZE];

struct bus_type *bus;

struct device_driver *driver;

void *driver_data;

/* Several fields omitted */

};

g_list 将该device对象挂接到全局设备链表中,所有的device对象都包含在devices subsys中,并组织成层次结构。Node域将该对象挂接到其兄弟对象的链表中,而bus list则用于将连接到相同总线上的设备组织成链表,driver list则将同一驱动程序管理的所有设备组织为链表。此外,children域指向该device对象子对象链表头,parent 域则指向父对象。Device对象还内嵌一个kobject对象,用于引用计数管理并通过它实现设备层次结构。Driver域指向管理该设备的驱动程序对象,而driver data则是提供给驱动程序的数据。Bus域描述设备所连接的总线类型。

内核提供了相应的函数用于操作device对象。其中device_register()函数将一个新的device对象插入设备模型,并自动在/sys/devices下创建一个对应的目录。device_unregister()完成相反的操作,注销设备对象。get_device()和put_device()分别增加与减少设备对象的引用计数。通常device结构不单独使用,而是包含在更大的结构中作为一个子结构使用,比如描述PCI设备的struct pci_dev,还有我们ldd_dev,其中的dev域就是一个device对象

2.3 DRIVER

系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:struct device_driver {

char *name; 设备驱动程序的名称

struct bus_type *bus; 该驱动所管理的设备挂接的总线类型

struct kobject kobj; 内嵌kobject对象

struct list_head devices; 该驱动所管理的设备链表头

int (*probe)(struct device *dev); 指向设备探测函数,用于探测设备是否可以被该驱动程序管理

int (*remove)(struct device *dev); 用于删除设备的函数

/* some fields omitted*/

};

与device 结构类似,device_driver对象依靠内嵌的kobject对象实现引用计数管理和层次结构组织。内核提供类似的函数用于操作device_driver对象,如get_driver()增加引用计数,driver_register()用于向设备模型插入新的driver对象,同时在sysfs文件系统中创建对应的目录。device_driver()结构还包括几个函数,用于处理热拔插、即插即用和电源管理事件

3 设备模型:lddbus虚拟总线分析

1)重要对象

#define subsys_set_kset(obj,_subsys) (obj)->subsys.kset.kobj.kset = &(_subsys).kset decl_subsys(bus, &ktype_bus, NULL);

#define decl_subsys(_name,_type,_hotplug_ops) \

struct subsystem _name##_subsys = { \

.kset = { \

.kobj = { .name = __stringify(_name) }, \

.ktype = _type, \

.hotplug_ops =_hotplug_ops, \

} \

}

#define decl_subsys_name(_varname,_name,_type,_hotplug_ops) \

struct subsystem _varname##_subsys = { \

.kset = { \

.kobj = { .name = __stringify(_name) }, \

.ktype = _type, \

.hotplug_ops =_hotplug_ops, \

} \

}

struct subsystem bus_subsys = { /

.kset = { /

.kobj = { .name = “bus” }, /

.ktype = ktype_bus, /

.hotplug_ops =NULL, /

} /

}

struct bus_type ldd_bus_type = {

.name = "ldd",

.match = ldd_match,

.hotplug = ldd_hotplug,

};

struct device ldd_bus = {

.bus_id = "ldd0",

.release = ldd_bus_release

};

2)L ddbus总线注册过程

static int __init ldd_bus_init(void)

{

int ret;

ret = bus_register(&ldd_bus_type);

if (ret)

return ret;

if (bus_create_file(&ldd_bus_type, &bus_attr_version))

printk(KERN_NOTICE "Unable to create version attribute/n");

ret = device_register(&ldd_bus);

if (ret)

printk(KERN_NOTICE "Unable to register ldd0/n");

subsystem_register(&bus->subsys)作用是向全局的bus_subsys”登记”, 把自己加入到bus_subsys的链表中去

subsystem_register() -> kset_add() -> kobject_add()

所谓的xx_register函数,就是通过其内嵌的kobject链入对应的subsystem,或是kset的层次结构中去。这样就可以通过一些全局的变量找到它们了

3)关系图

sybsystem

Int register_ldd_device(struct ldd_device *ldddev)

{

Ldddev->dev.bus = &ldd_bus_type;

Ldddev->dev.parant= &ldd_bus;

Ldddev->dev.release = ldd_dev_release;

Strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);

Return device_register(&ldd_dev->dev);

}

Linux设备驱动程序举例

Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

Linux设备驱动程序学习(18)-USB 驱动程序(三)

Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb

struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

Linux设备模型(文档翻译)_(整理文档)

Linux 内核文档翻译- driver-model/bus.txt Bus Types 总线类型 Definition 定义 ~~~~~~~~~~ See the kerneldoc for the struct bus_type. intbus_register(struct bus_type * bus); Declaration 声明 ~~~~~~~~~~~ Each bus type in the kernel (PCI, USB, etc) should declare one static object of this type. They must initialize the name field, and may optionally initialize the match callback. 内核中每个总线类型(PCI、USB 等等)都应该声明一个此类型的静态对象。它们必须初始化该对象的name 字段,然后可选的初始化match 回调函数。 structbus_typepci_bus_type = { .name = "pci", .match = pci_bus_match, }; The structure should be exported to drivers in a header file: 这个结构体应该在头文件中向驱动程序导出: extern struct bus_typepci_bus_type; Registration 注册 ~~~~~~~~~~~~ When a bus driver is initialized, it calls bus_register. This initializes the rest of the fields in the bus object and inserts it into a global list of bus types. Once the bus object is registered, the fields in it are usable by the bus driver. 当初始化一个总线驱动时,将会调用bus_register。这时这个总线对象剩下的字段将被初始化,然后这个对象会被插入到总线类型的一个全局列表里去。一旦完成一个总线对象的注册,那么对于总线驱动来说它里面的字段就已经可用了。

Linux设备驱动模型之platform总线深入浅出

Linux设备驱动模型之platform总线深入浅出 在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中 platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver //include\linux\platform_device.h struct platform_driver { int (*probe)(struct platform_device *); //探测函数,在注册平台设备时被调用int (*remove)(struct platform_device *); //删除函数,在注销平台设备时被调用void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); //挂起函数,在关机被调用int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢复函数,在开机时被调用struct device_driver driver;//设备驱动结构}; 1 2 3 4 5 6 7 8

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

linux设备模型介绍

第一节基本概念 在设备模型里面,所有的东西都是kobject,这也是linux建立设备设计模型的目的(对比2.4之前),实现了统一的实体;我们理解上,却可以分为两个层次,一个是kobject,一个是管理kobject的kobject(可以把它叫做kset虽然有点绕,但是没有办法了,毕竟就像那个“世界上先有鸡还是先有蛋的哲学问题一下”); kobject结构

1)前面两个顾名思义,就是name了,为什么会有两个呢?k_name就是指向name的,如何知道呢,呵呵,看一下代码 2)kref就是一个内核的原子计数结构,因为涉及内核的操作基本都需要是原子性的,为了大家的方便,kobject就把它包括进来了,所以大家就不必要各自定义自己的计数了(一般情况下:),poll也是类似,把等待队列包括进来; 3)entry 这个名字比较让人误解,其实看它的类型知道是list成员,它就是加入到kset的list 的那个零部件; 4)ktpye 要理解这个成员就稍微麻烦些了,先看一下定义 Default_attrs就是一种比较简单的设置属性文件的方法,它其实跟我们自己调用sysfs_create_file没有什么区别,呵呵,看一下代码就知道了,所以大家基本上可以把它忽略掉:),调用关系为kobject_add->create_dir->populate_dir 把一个忽略掉,剩下的两个就比较重要了;每个对象一般都有多个属性,用面向对象的角度来看,我们可以把对属性的操作抽象为show和store这一对方法,那么多个属性就会有多对show和store的方法;那么,为了实现对这些方法的统一调用,就利用ktype中的sysfs_ops 实现了多态;这样一来,对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops

Linux设备驱动程序简介

第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访

如何实现Linux设备驱动模型

文库资料?2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd. 如何实现Linux 设备驱动模型 设备驱动模型,对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了device 设备、driver 驱动、bus 总线和class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。总线、设备、驱动以及类之间的关系错综复杂,在Linux 内核中通过kobject 、kset 和subsys 来进行管理,驱动编写可以忽略这些管理机制的具体实现。 设备驱动模型的内部结构还在不停的发生改变,如device 、driver 、bus 等数据结构在不同版本都有差异,但是基于设备驱动模型编程的结构基本还是统一的。 Linux 设备驱动模型是Linux 驱动编程的高级内容,这一节只对device 、driver 等这些基本概念作介绍,便于阅读和理解内核中的代码。实际上,具体驱动也不会孤立的使用这些概念,这些概念都融合在更高层的驱动子系统中。对于大多数读者可以忽略这一节内容。 1.1.1 设备 在Linux 设备驱动模型中,底层用device 结构来描述所管理的设备。device 结构在文件中定义,如程序清单错误!文档中没有指定样式的文字。.1所示。 程序清单错误!文档中没有指定样式的文字。.1 device 数据结构定义 struct device { struct device *parent; /* 父设备 */ struct device_private *p; /* 设备的私有数据 */ struct kobject kobj; /* 设备的kobject 对象 */ const char *init_name; /*设备的初始名字 */ struct device_type *type; /* 设备类型 */ struct mutex mutex; /*同步驱动的互斥信号量 */ struct bus_type *bus; /*设备所在的总线类型 */ struct device_driver *driver; /*管理该设备的驱动程序 */ void *platform_data; /*平台相关的数据 */ struct dev_pm_info power; /* 电源管理 */ #ifdef CONFIG_NUMA int numa_node; /*设备接近的非一致性存储结构 */ #endif u64 *dma_mask; /* DMA 掩码 */ u64 coherent_dma_mask; /*设备一致性的DMA 掩码 */ struct device_dma_parameters *dma_parms; /* DMA 参数 */ struct list_head dma_pools; /* DMA 缓冲池 */ struct dma_coherent_mem *dma_mem; /* DMA 一致性内存 */ /*体系结构相关的附加项*/ struct dev_archdata archdata; /* 体系结构相关的数据 */ #ifdef CONFIG_OF

Linux设备模型 热插拔、mdev 与 firmware

Linux设备驱动程序学习(15) -Linux设备模型(热插拔、mdev 与firmware) 热插拔 有2 个不同角度来看待热插拔: 从内核角度看,热插拔是在硬件、内核和内核驱动之间的交互。 从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug、udev 和mdev)的交互。当需要通知用户内核发生了某种热插拔事件时,内核才调用这个用户空间程序。 现在的计算机系统,要求Linux 内核能够在硬件从系统中增删时,可靠稳定地运行。这就对设备驱动作 者增加了压力,因为在他们必须处理一个毫无征兆地突然出现或消失的设备。 热插拔工具 当用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在/proc/sys/kernel/hotplug文件里查找处理设备连接的用户空间程序。这个用户空间程序主要有 hotplug:这个程序是一个典型的bash 脚本,只传递执行权给一系列位于/etc/hot-plug.d/ 目录树的程序。hotplug 脚本搜索所有的有 .hotplug 后缀的可能对这个事件进行处理的程序并调用它们, 并传递给它们许多不同的已经被内核设置的环境变量。(基本已被淘汰,具体内容请参阅《LDD3》) udev :用于linux2.6.13或更高版本的内核上,为用户空间提供使用固定设备名的动态/dev目录的解 决方案。它通过在sysfs 的/class/ 和/block/ 目录树中查找一个称为dev 的文件,以确定所创建的 设备节点文件的主次设备号。所以要使用udev,驱动必须为设备在sysfs中创建类接口及其dev属性文件,方法和sculld模块中创建dev属性相同。udev的资料网上十分丰富,我就不在这废话了,给出以 下链接有兴趣的自己研究:

Linux设备驱动程序学习(10)-时间、延迟及延缓操作

Linux设备驱动程序学习(10)-时间、延迟及延缓操作 Linux设备驱动程序学习(10) -时间、延迟及延缓操作 度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在中定义或该文件包含的某个子平台相关文件中。作为通用的规则,即便如果知道HZ 的值,在编程时应当不依赖这个特定值,而始终使用HZ。对于当前版本,我们应完全信任内核开发者,他们已经选择了最适合的HZ值,最好保持HZ 的默认值。 对用户空间,内核HZ几乎完全隐藏,用户HZ 始终扩展为100。当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。对用户来说确切的HZ 值只能通过/proc/interrupts 获得:/proc/interrup ts 的计数值除以/proc/uptime 中报告的系统运行时间。 对于ARM体系结构:在文件中的定义如下: 也就是说:HZ 由__KERNEL__和CONFIG_HZ决定。若未定义__KERNEL__,H Z为100;否则为CONFIG_H Z。而CONFIG_HZ是在内核的根目录

的.config文件中定义,并没有在make menuconfig的配置选项中出现。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为: 所以正常情况下s3c24x0的HZ为200。这一数值在后面的实验中可以证实。 每次发生一个时钟中断,内核内部计数器的值就加一。这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。 使用jiffies 计数器 这个计数器和用来读取它的工具函数包含在,通常只需包含,它会自动放入jiffi es.h 。 jiffies 和jiffies_64 必须被当作只读变量。当需要记录当前jiffies 值(被声明为volatile 避免编译器优化内存读)时,可以简单地访问这个unsigned long 变量,如: 以下是一些简单的工具宏及其定义:

linux设备驱动

Linux设备驱动 操作系统的目的之一就是将系统硬件设备细节从用户视线中隐藏起来。例如虚拟文件系统对各种类型已安装的文件系统提供了统一的视图而屏蔽了具体底层细节。本章将描叙Linux核心对系统中物理设备的管理。 CPU并不是系统中唯一的智能设备,每个物理设备都拥有自己的控制器。键盘、鼠标和串行口由一个高级I/O芯片统一管理,IDE控制器控制IDE硬盘而SCSI控制器控制SCSI硬盘等等。每个硬件控制器都有各自的控制和状态寄存器(CSR)并且各不相同。例如Adaptec 2940 SCSI控制器的CSR与NCR 810 SCSI控制器完全不一样。这些CSR被用来启动和停止,初始化设备及对设备进行诊断。在Linux中管理硬件设备控制器的代码并没有放置在每个应用程序中而是由内核统一管理。这些处理和管理硬件控制器的软件就是设备驱动。Linux 核心设备驱动是一组运行在特权级上的内存驻留底层硬件处理共享库。正是它们负责管理各个设备。 设备驱动的一个基本特征是设备处理的抽象概念。所有硬件设备都被看成普通文件;可以通过和操纵普通文件相同的标准系统调用来打开、关闭、读取和写入设备。系统中每个设备都用一种特殊的设备相关文件来表示(device special file),例如系统中第一个IDE硬盘被表示成/dev/hda。块(磁盘)设备和字符设备的设备相关文件可以通过mknod命令来创建,并使用主从设备号来描叙此设备。网络设备也用设备相关文件来表示,但Linux寻找和初始化网络设备时才建立这种文件。由同一个设备驱动控制的所有设备具有相同的主设备号。从设备号则被用来区分具有相同主设备号且由相同设备驱动控制的不同设备。例如主IDE硬盘的每个分区的从设备号都不相同。如/dev/hda2表示主IDE 硬盘的主设备号为3而从设备号为2。Linux通过使用主从设备号将包含在系统调用中的(如将一个文件系统mount到一个块设备)设备相关文件映射到设备的设备驱动以及大量系统表格中,如字符设备表,chrdevs。 Linux支持三类硬件设备:字符、块及网络设备。字符设备指那些无需缓冲直接读写的设备,如系统的串口设备/dev/cua0和/dev/cua1。块设备则仅能以块为单位读写,典型的块大小为512或1024字节。块设备的存取是通过

Linux设备驱动程序说明介绍

Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__

Linux设备模型:基本概念

Linux设备模型:基本概念 1. 前言 在“Linux内核的整体架构”中,蜗蜗有提到,由于Linux支持世界上几乎所有的、不同功能的硬件设备(这是Linux的优点),导致Linux内核中有一半的代码是设备驱动,而且随着硬件的快速升级换代,设备驱动的代码量也在快速增长。个人意见,这种现象打破了“简洁就是美”的理念,是丑陋的。它导致Linux内核看上去非常臃肿、杂乱、不易维护。但蜗蜗也知道,这不是Linux的错,Linux是一个宏内核,它必须面对设备的多样性,并实现对应的驱动。 为了降低设备多样性带来的Linux驱动开发的复杂度,以及设备热拔插处理、电源管理等,Linux内核提出了设备模型(也称作Driver Model)的概念。设备模型将硬件设备归纳、分类,然后抽象出一套标准的数据结构和接口。驱动的开发,就简化为对内核所规定的数据结构的填充和实现。 本文将会从设备模型的基本概念开始,通过分析内核相应的代码,一步一步解析Linux设备模型的实现及使用方法。 2. Linux设备模型的基本概念 2.1 Bus, Class, Device和Device Driver的概念 下图是嵌入式系统常见的硬件拓扑的一个示例: 硬件拓扑描述Linux设备模型中四个重要概念中三个:Bus,Class和Device(第四个为Device Driver,后面会说)。 Bus(总线):Linux认为(可以参考include/linux/device.h中struct bus_type的注释),总线是CPU和一个或多个设备之间信息交互的通道。而为了方便设备模型的抽象,所有的设备都应连接到总线上(无论是CPU内部总线、虚拟的总线还是“platform Bus”)。 Class(分类):在Linux设备模型中,Class的概念非常类似面向对象程序设计中的Class (类),它主要是集合具有相似功能或属性的设备,这样就可以抽象出一套可以在多个设

《LINUX设备驱动程序》阅读笔记全十八章

《LINUX设备驱动程序》阅读笔记 目录 第1章:设备驱动程序简介 (1) 第2章:构造和运行模块 (1) 第3章:字符设备驱动程序 (1) 第4章:调试技术 (2) 第5章:并发和竞态 (2) 第6章:高级字符驱动程序操作 (3) 第7章:时间、延迟及延缓操作 (3) 第8章:分配内存 (3) 第9章:与硬件通信 (4) 第10章:中断处理 (4) 第11章:内核的数据类型 (4) 第12章:PCI 驱动程序 (5) 第13章:USB 驱动程序 (5) 第14章:Linux 设备模型 (5) 第15章:内存映射和 DMA (5) 第16章:块设备驱动程序 (6) 第17章:网络驱动程序 (6) 第18章:TTY 驱动程序 (6) 第1章:设备驱动程序简介 1、“通常,设备驱动程序就是这个进入Linux内核世界的大门”,“设备驱动程序在Linux 内核中扮演着特殊的角色,它们是一个个独立的黑盒子,使某个特定硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的驱动程序”。 2、Linux系统将设备分成三种基本类型:字符设备、块设备和网络设备。 第2章:构造和运行模块 1、“内核黑客通常拥有一个‘牺牲用的’系统,用于测试新的代码”。 2、模块在被使用之前需要注册,而退出时要仔细撤销初始化函数所做的一切。驱动模块只能调用由内核导出的那些函数。 3、公共内核符号表中包含了所有的全局内核项(即函数和变量)的地址。当模块被装入内核后,它所导出的任何符号都会变成内核符号表的一部分。 第3章:字符设备驱动程序 第一节-主设备号和次设备号。对字符设备的访问都是通过文件系统内的设备名称进行的。通常而言,主设备号标识设备对应的驱动程序,而次设备号用于正确确定设备文件所指的设备。对应的数据结构为dev_t 类型。分配设备号使用函数alloc_chrdev_region() ,释放就使用unregister_chrdev_region() 函数。 第二节-一些重要的数据结构。大部分基本的驱动程序的操作都要涉及到三个重要的内

第一章(linux设备驱动程序).doc

第1章Linux内核简介 世界各地都有人在钻研Linux内核,大多是在写设备驱动程序。尽管每个驱动程序都不一样,而且你还要知道自己设备的特殊性,但是这些设备驱动程序的许多原则和基本技术技巧都是一样的。通过本书,可以学会写自己的设备驱动程序,并且可以钻研内核的相关部分。本书涉及到的是设备无关编程技巧,不会将例子跟特殊设备绑定在一起。 本章没有实际编写代码。但我要介绍一些关于Linux内核的背景概念,这样到我们稍后开始介绍实际编程时,就很顺利了。 当你学习编写驱动程序的时候,你也会发现很多关于Linux内核的知识,这对理解你机器怎么工作很有帮助,并且还可以知道为什么你的机器没有希望的那么快,或者为什么不按照你象要它做的那样做。我们会逐渐介绍一些新概念,先从简单的驱动程序开始,每介绍一些新概念都会看到相关例子代码,这些代码都不需要特殊硬件。 驱动程序作者的作用 作为一个程序员,你可以选择自己的驱动程序,在编程所需时间和结果的灵活性之间做个可以接受的权衡。尽管说驱动程序的灵活性看起来有那么点怪,我喜欢这个词是因为它强调了设备驱动程序提供的是机制,而不是策略。 机制和策略之间的差别是Unix设计背后最好的点子之一。实际编程中遇到的大多数问题都可以被划分成两个部分:“需要作什么”(机制)和“这个程序怎么用”(策略)。如果这两个主题是由程序不同部分来承担的,或者是由不同的程序组合一起承担的,那么这个软件包就很容易开发,也很适合特殊需求。 举个例子,Unix的图形显示管理在X服务器和窗口管理器之间划了一道线,X服务器了解硬件并给用户程序提供唯一的接口,而窗口管理器实现特殊的策略并不需要知道硬件的任何信息。人们可以在不同硬件上使用同样的窗口管理器,并且不同用户在同一台工作站上可以使用不同的设置。另一个例子是TCP/IP的网络分层结构:操作系统提供抽象的套接字操作,是设备无关的,不同服务器主管这个服务。另外,ftpd服务器提供文件传输机制,而用户可以使用任何客户端程序;命令行的客户端和图形化界面的客户端都存在,并且谁都可以为传输文件写一个新的用户界面。 只要涉及到驱动程序,就会运用这样的功能划分。软盘驱动程序是设备无关的——这不仅表现在磁盘是一个连续读写的字节数组上。如何使用设备是应用程序要做的事:tar要连续地写数据,而mkfs则为要安装的设备做准备工作,mcopy依赖于设备上存在的特殊数据结构。在写驱动程序时,程序员应该特别留心这样的基本问题:我们要写内核代码访问硬件,但由于不同用户有不同需要,我们不能强迫用户采用什么样的特定策略。设备驱动程序应该仅仅处理硬件,将如何使用硬件的问题留给应用程序。如果在提供获得硬件能力的同时没有增加限制,我们就说驱动程序是灵活的。不过,有时必须要作一些策略决策。 可以从不同侧面来看你的驱动程序:它是位于应用层和实际设备之间的软件。驱动程序的程序员可以选择这个设备应该怎样实现:不同的驱动程序可以提供不同的能力,甚至相同的设备也可以提供不同能力。实际驱动程序设计应该是在众多需求之间的一个平衡。例如,不同程序可以同时使用同一个设备,而驱动程序的开发者可以完全自由地决定如何处理同步机制。你可以实现到设备上的内存映射,而完成独立于硬件的具体能力,或者你可以提供给用

相关文档
最新文档