三.初始化和添加字符设备
在include/linux/cdev.h中定义有这样一个数据结构.
struct cdev {
struct kobject kobj;//内嵌的一个kobject结构
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;//设备的设备号的
unsigned int count;//设备引用计数器
};
这个数据结构就是内核中对字符设备的描述.内核还提供了一组用于操作该数据结构的函数:
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *, const struct file_operations *);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_put(struct cdev *p);
void cdev_del(struct cdev *);
以上函数和结构体都在include/linux/cdev.h声明,但是函数的实现是在fs/char_dev.h中.具体分析如下:
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
//在cdev_init也会有这样一个函数,不过这里的初始化会在最终执行时会释放上面申请
//的空间.而下面的那个函数最终执行时只会清空cdev这个结构体,而不释放.
}
return p;
}
/**
@cdev: 这个结构体就是已经申请好的struct cdev.
@fops: 是针对于这个设备所写的file_operations.
这个操作须在cdev_add()之前完成.
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);//将cdev这个申请好的数据结构请0.
INIT_LIST_HEAD(&cdev->list);//这个结构体见下面的解释4
kobject_init(&cdev->kobj, &ktype_cdev_default);//见解释5 解释6
//这里主要是对cdev中kobject这个对象初始化成默认方式
cdev->ops = fops; //初始化操作方法
}
解释4:include/linux/list.h中.
struct list_head {
struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
解释5:lib/kobject.h
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!"; goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
//* do not error out as sometimes we can recover
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();//
}
kobject_init_internal(kobj);//解释A
kobj->ktype = ktype;
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();//这个函数的理解看字面意思就可以了,我没有进行进一步的追查
}
解释A:
Kobject_init_internal
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
lib/kref.c
void kref_init(struct kref *kref) //struct kref { atomic_t refcount; };
{ //这个kref的里面只有一项,它的定义:typedef struct { int counter; } atomic_t;
kref_set(kref, 1);
}//
void kref_set(struct kref *kref, int num)
{
atomic_set(&kref->refcount, num);//
smp_mb();//设置内存障碍
}
#define atomic_set(v,i) (((v)->counter) = (i))
include/asm-x86/atomic_32.h
#define atomic_set(v,i) (((v)->counter) = (i))
fs/char_dev.c
static struct kobj_type ktype_cdev_default = {
.release = cdev_default_release,
};
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);//见解释B
//由下面的分析解释可以轻松的看出,此处的p指向的是包含kobj这个结构的
//struct cdev结构体.
cdev_purge(p);//见解释C 有下面的解释可以看出这里是对cdev的清空操作.
}//下面都是源码分析解释
解释B:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \//1
(type *)( (char *)__mptr - offsetof(type,member) );})//2
container_of()宏的功能是什么?
A: 第1行:将__mptr转化成type结构中member类型.并且把ptr的地址给它
第2行:先将__mptr转换成字符型指针,再减去member在type中的偏移量.最后在将它装换成type类型.
B: 其中offsetof宏定义在[include/linux/stddef.h]中定义为:
C: #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof(type,member)是获取member在type中的偏移量.最后装换成整数值.
D: 其实最后的结果就是:指针ptr减去了一个偏移量.这里肯定ptr是指向了其它数据结构.即是ptr- offsetof(),这也就相当于指向了包含该ptr的type类型的结构体地址,并且强制将其转换为(type*)类型.
如下图:
指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址。
type
|----------|
| |
| |
|----------|
ptr->| member --|
|----------|
| |
| |
|----------|
解释C:于上面的在同一个文件中定义
static void cdev_purge(struct cdev *cdev)
{
spin_lock(&cdev_lock);
while (!list_empty(&cdev->list)) {//见解释D
struct inode *inode;
inode = container_of(cdev->list.next, struct inode, i_devices);
list_del_init(&inode->i_devices);//见解释E
inode->i_cdev = NULL;
}
spin_unlock(&cdev_lock);
}
解释D:inclued/linux/list.h
static inline int list_empty(const struct list_head *head)
{
return head->next == head;//就是将这个链表清空
}
解释E:include/linux/list.h //将entry从链表中删除并从新初始化
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
来源:许振文博客 作者:许振文 2008-08-07 14:30:14.0 网友评论:0条 点击:164
三.初始化和添加字符设备
【发表评论 0条】

