Hello驱动补充知识-主要函数内核实现
1. module_init和module_exit源码解析
module_init和module_exit定义在 include/linux/init.h文件中。
MODULE宏是否被定义,module_init和module_exit实现是有区别的。如果 MODULE 这个宏没有定义,表明该模块是要编译进内核的(obj-y)。反之,则MODULE宏被定义,则该被编译成模块动态加载。
#ifndef MODULE
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module. If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
#else /* MODULE */
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
#endif
1.1 静态编译
__define_initcall(level,fn)对于内核的初始化很重要,它指示
编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序
放在一个section中。在内核初始化阶段,do_initcalls() 将按顺序从该
section中以函数指针的形式取出这些函数的起始地址,来依次完成相应
的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化
的完成,因此这个顺序排列常常非常重要。
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
......
#define __define_initcall(level,fn) \
static initcall_t __initcall_##fn __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
其中 initcall_t 是一个函数指针类型,属性 attribute((section())) 则表示把对象放在由括号中的名称所指代的section中。:
typedef int (*initcall_t)(void);
宏定义的含义:
- 声明一个类型为initcall_t,名称为__initcall_##fn的函数指针(其中##表示替换连接);
- 将这个函数指针初始化为fn;
- 编译的时候需要把这个函数指针变量放置到名称为 “.initcall” level ".init"的section中(比如level=“1”,代表这个section的名称是 “.initcall1.init”)。
initcall函数在Linux系统启动过程中被调用( start_kernel() -> rest_init() -> kernel_init() -> do_basic_setup() -> do_initcalls() 中),因此 module_init(x)也是在Linux系统启动过程中被调用。
1.2 动态编译
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
__inittest 仅仅是为了检测定义的函数是否符合 initcall_t 类型,如果不是initcall_t类型在编译时将会报错。alias 属性是 gcc 的特有属性,定义函数 initfn 的别名为init_module 。其中#是字符化运算符,#initfn相当于"initfn"。同理,__exittest检测exitfn是否为exitcall_t类型。alias命名exitfn的别名为cleanup_module。
3. register_chrdev源码解析
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
/**
* __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
*/
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
//
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
// 动态分配一个字符设备cdev
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
//向内核注册cdev字符设备,通知内核cdev字符设备可以使用
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
4. class_create源码解析
/* This is a #define to keep the compiler from merging different
* instances of the __key variable */
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
* @name: pointer to a string for the name of this class.
* @key: the lock_class_key for this class; used by mutex lock debugging
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
*
* Returns &struct class pointer on success, or ERR_PTR() on error.
*
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
struct class *__class_create(struct module *owner, const char *name,
struct lock_class_key *key)
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(__class_create);
5. class_destroy源码解析
/**
* class_destroy - destroys a struct class structure
* @cls: pointer to the struct class that is to be destroyed
*
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
*/
void class_destroy(struct class *cls)
{
if ((cls == NULL) || (IS_ERR(cls)))
return;
class_unregister(cls);
}
void class_unregister(struct class *cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
remove_class_attrs(cls);
kset_unregister(&cls->p->subsys);
}
参考引用: