Linux内核编程接口函数

转载请注明出处: http://blog.csdn.net/drivelinux/article/details/8656280

字符设备相关函数

1.alloc_chrdev_region()

功能: 自动分配一个主设备号及基于此主设备号的若干个连续的指定数量的次设备号。

函数原型如下:

/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers.  The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev.  Returns zero or a negative error code.*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)

2.register_chrdev_region()

功能:注册连续的若干个设备号,用这个函数要自己指定主设备号。

函数原型:

/*** register_chrdev_region() - register a range of device numbers* @from: the first in the desired range of device numbers; must include*        the major number.* @count: the number of consecutive device numbers required* @name: the name of the device or driver.** Return value is zero on success, a negative error code on failure.*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)

注:register_chrdev() 函数也用于分配设备号,但是用于2.6版本之前的内核中,在之后的版本中不再使用。

3.unregister_chrdev_region()

功能:注销由alloc_chrdev_region()或者register_chrdev_region()注册的设备号,一般在驱动模块的退出函数中要执行此操作,将注册的设备号还给系统。

函数原型:

/*** unregister_chrdev_region() - return a range of device numbers* @from: the first in the range of numbers to unregister* @count: the number of device numbers to unregister** This function will unregister a range of @count device numbers,* starting with @from.  The caller should normally be the one who* allocated those numbers in the first place...*/
void unregister_chrdev_region(dev_t from, unsigned count)

4.cdev_alloc()

功能:分配一个表示字符设备的cdev结构体。
函数原型:
/*** cdev_alloc() - allocate a cdev structure** Allocates and returns a cdev structure, or NULL on failure.*/
struct cdev *cdev_alloc(void)

5.cdev_init()

功能:初始化由cdev_alloc()分配的表示字符设备的cdev结构体,并通过向该函数传入的struct file_operations *fops参数来指定该字符设备的文件操作函数。
函数原型:
/*** cdev_init() - initialize a cdev structure* @cdev: the structure to initialize* @fops: the file_operations for this device** Initializes @cdev, remembering @fops, making it ready to add to the* system with cdev_add().*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)

6.cdev_add()

功能:添加一个(由cdev_alloc()分配并通过cdev_init()初始化的)字符设备到系统中。
函数原型:
/*** cdev_add() - add a char device to the system* @p: the cdev structure for the device* @dev: the first device number for which this device is responsible* @count: the number of consecutive minor numbers corresponding to this*         device** cdev_add() adds the device represented by @p to the system, making it* live immediately.  A negative error code is returned on failure.*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)

7.cdev_del()

功能:执行和cdev_add()相反的操作,将一个指定的字符设备从系统中删除。

函数原型:
/*** cdev_del() - remove a cdev from the system* @p: the cdev structure to be removed** cdev_del() removes @p from the system, possibly freeing the structure* itself.*/
void cdev_del(struct cdev *p)

以下为一个简单的字符设备初始化函数,函数中列出了向系统中添加一个字符设备的简单步骤:

struct cdev *myDev_dev;
/*主设备和从设备号变量*/
static int myDev_major = 0;
static int myDev_minor = 0;static int __init myDev_init(void)
{ int err = -1;dev_t dev = 0;/*1.动态分配主设备号和从设备号,第三个参数表示分配的此设备号的数量*/err = alloc_chrdev_region(&dev, 0, TOTAL_DEVICE_COUNT, "myDev");if(err < 0) {printk(KERN_ALERT"Failed to alloc char dev region.\n");return err;}myDev_major = MAJOR(dev);myDev_minor = MINOR(dev);        /*2.分配cdev字符设备结构体变量*/myDev_dev = cdev_alloc();if(!myDev_dev) {err = -ENOMEM;printk(KERN_ALERT"Failed to alloc myDev_dev.\n");return err;}        /*3.初始化cdev字符设备结构体*/cdev_init(myDev_dev, &myDev_fops); /*4.添加字符设备到系统中*/err = cdev_add(myDev_dev,dev, TOTAL_DEVICE_COUNT);if(err) {return err;}              return err;
}

8.register_chrdev()

这个函数也是实现注册字符设备的功能,但是相对于上面提到的alloc_chrdev_region()和register_chrdev_region()而言,是比较老的形式。并且register_chrdev()一次能够注册统一主设备号下的256个不同的次设备。并且,在2.6之后的内核版本中不再使用这一函数。

自动创建设备节点相关函数

1.class_create()

功能:创建一个struct class结构体,作为device_create()函数的参数。
函数原型:
#define class_create(owner, name)     \
({                      \static struct lock_class_key __key;    \__class_create(owner, name, &__key);   \
})

2.device_create()

功能:该函数创建一个设备并将其注册到sysfs中,同时在系统的sys/class和sys/device目录下会生成相应的类和设备入口。并且,该函数还会出发用户空间udev的动作,udev会根据sysfs下的class在/dev目录下创建设备节点,这也为自动创建设备节点提供了一种途径。通过device_create()函数,我们就可以不用通过mknod命令手动的创建设备节点了。

函数原型:
/*** device_create - creates a device and registers it with sysfs* @class: pointer to the struct class that this device should be registered to* @parent: pointer to the parent struct device of this new device, if any* @devt: the dev_t for the char device to be added* @drvdata: the data to be added to the device for callbacks* @fmt: string for the device's name** This function can be used by char device classes.  A struct device* will be created in sysfs, registered to the specified class.** A "dev" file will be created, showing the dev_t for the device, if* the dev_t is not 0,0.* If a pointer to a parent struct device is passed in, the newly created* struct device will be a child of that device in sysfs.* The pointer to the struct device will be returned from the call.* Any further sysfs files that might be required can be created using this* pointer.** Returns &struct device pointer on success, or ERR_PTR() on error.** Note: the struct class passed to this function must have previously* been created with a call to class_create().*/
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)

3.device_create_file()

功能:该函数用于为指定设备在sysfs下建立属性文件,如:
device_create_file(dev, &dev_attr_val);
其中的dev_attr_val可以通过DEVICE_ATTR宏进行定义,也可以直接通过struct device_attribute xxx=__ATTR(_name,_mode,_show,_store)进行定义,区别就是用DEVICE_ATTR宏进行定义时得到的变量名都是以dev_attr_为前缀的,而通过struct device_attribute xxx=__ATTR(_name,_mode,_show,_store)进行定义时,变量名可以任意指定。建立属性文件之后,我们就可以通过属性文件对设备的属性进行配置和读取了,这也是应用程序和设备(驱动程序)进行信息交互的一种方式。
函数原型:
/*** device_create_file - create sysfs attribute file for device.* @dev: device.* @attr: device attribute descriptor.*/int device_create_file(struct device *dev,const struct device_attribute *attr)#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

如下代码可以实现自动创建设备节点:

   static struct class* myDev_class = NULL;struct device* devc= NULL;      /*5.创建一个struct class结构体*/myDev_class = class_create(THIS_MODULE,"myDevClass");if(IS_ERR(myDev_class)) {err = PTR_ERR(myDev_class);printk(KERN_ALERT"Failed to create  class.\n");return err;}        /*6.在sysf中生成设备文件并在/dev下自动生成设备节点*/devc = device_create(myDev_class,NULL,dev,NULL,"myDev");if(IS_ERR(devc)) {err = PTR_ERR(devc);printk(KERN_ALERT"Failed to create device.");return err;}   

以下贴出以上分两部分介绍的字符设备的初始化代码:

struct cdev *myDev_dev;
/*主设备号和从设备号变量*/
static int myDev_major = 0;
static int myDev_minor = 0;static int __init myDev_init(void)
{ int err = -1;dev_t dev = 0;struct device *devc = NULL;/* 1.动态分配主设备号和从设备号,第三个参数表示分配的次设备号的数量 */err = alloc_chrdev_region(&dev, 0, TOTAL_DEVICE_COUNT, "myDev");if(err < 0) {printk(KERN_ALERT"Failed to alloc char dev region.\n");return err;}myDev_major = MAJOR(dev);myDev_minor = MINOR(dev);        /* 2.分配cdev字符设备结构体变量 */myDev_dev = cdev_alloc();if(!myDev_dev) {err = -ENOMEM;printk(KERN_ALERT"Failed to alloc myDev_dev.\n");return err;}        /* 3.初始化cdev字符设备结构体 */cdev_init(myDev_dev, &myDev_fops); /* 4.添加字符设备到系统中 */err = cdev_add(myDev_dev,dev, TOTAL_DEVICE_COUNT);if(err) {return err;}              /* 5.创建一个struct class结构体 */myDev_class = class_create(THIS_MODULE,"myDevClass");if(IS_ERR(myDev_class)) {err = PTR_ERR(myDev_class);printk(KERN_ALERT"Failed to create  class.\n");return err;}        /* 6.在sysf中生成设备文件并在/dev下自动生成设备节点 */devc = device_create(myDev_class,NULL,dev,NULL,"myDev");if(IS_ERR(devc)) {err = PTR_ERR(devc);printk(KERN_ALERT"Failed to create device.");return err;}    return err;
}

通用函数

1.kmalloc()和kzalloc()的异同

在内核include/linux/slab.h文件中有如下定义:

/*** kzalloc - allocate memory. The memory is set to zero.* @size: how many bytes of memory are required.* @flags: the type of memory to allocate (see kmalloc).*/
static inline void *kzalloc(size_t size, gfp_t flags)
{return kmalloc(size, flags | __GFP_ZERO);
}

从注释以及函数原型均能看出kzalloc()函数的作用,即申请一块内存并且该内存会在函数调用过程中被清零。

2.module_platform_driver()

该函数实际是一个宏,它在include/linux/platform_device.h中定义如下:

/* module_platform_driver() - Helper macro for drivers that don't do* anything special in module init/exit.  This eliminates a lot of* boilerplate.  Each module may only use this macro once, and* calling it replaces module_init() and module_exit()*/
#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)

其中的module_driver在/include/linux/device.h中定义,如下:

/*** module_driver() - Helper macro for drivers that don't do anything* special in module init/exit. This eliminates a lot of boilerplate.* Each module may only use this macro once, and calling it replaces* module_init() and module_exit().** @__driver: driver name* @__register: register function for this driver type* @__unregister: unregister function for this driver type* @...: Additional arguments to be passed to __register and __unregister.** Use this macro to construct bus specific macros for registering* drivers, and do not use it on its own.*/
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

由上述定义可知,module_platform_driver()宏的作用就是定义指定名称的平台设备驱动注册函数和平台设备驱动注销函数,并且在函数体内分别通过platform_driver_register()函数和platform_driver_unregister()函数注册和注销该平台设备驱动。

3.read_cpuid_id()

读取CPU ID,定义于arch/arm/include/asm/cputype.h文件中。

4.container_of()

该函数其实是一个宏,定义于include/linux/kernel.h,如下所示:
/*** container_of - cast a member of a structure out to the containing structure* @ptr:   the pointer to the member.* @type:  the type of the container struct this is embedded in.* @member: the name of the member within the struct.**/
#define container_of(ptr, type, member) ({          \const typeof( ((type *)0)->member ) *__mptr = (ptr);    \(type *)( (char *)__mptr - offsetof(type,member) );})
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

该宏定义的功能是,通过结构体某一个已知成员的指针(成员变量的地址),反向计算出该结构体的地址。其原理也很简单,就是用已知成员变量的地址,减去该成员变量在结构体中的偏移量。

这个功能在kernel编程中十分有用。举一个简单的例子,如下:
struct child_struct
{int m;int n;
};struct parent_struct
{int a;int b;struct child_struct c;
};struct parent_struct test;int *address = &test.c.n;
struct parent_struct *test_addr = container_of(address,struct parent_struct,c.n);通过上述语句,获取到的就是test变量的地址。
注意:需要注意的是,在使用container_of()宏来获取父结构体的地址时,一定不要用指针类型的成员变量进行计算。
因为指针类型的成员变量本身就是用来保存一个地址,并且该地址是不确定的,它可以被赋值成任意值,因此不能用来计算父结构体的地址。

Linux内核编程接口函数相关推荐

  1. linux 内核编程 延时函数,linux中内核延时编程

    #include //定义使用定时来计时的宏 #ifndef SLEEP_MILLI_SEC #define SLEEP_MILLI_SEC(nMilliSec)\ do { \ long timeo ...

  2. linux内核_Linux驱动编程的本质就是Linux内核编程

    由于Linux驱动编程的本质属于Linux内核编程,因此我们非常有必要熟悉Linux内核以及Linux内核的特点. 这篇文章将会帮助读者打下Linux驱动编程的基础知识. 本篇文章分为如下三个小节进行 ...

  3. Linux 内核编程指南

    Linux 内核编程指南   PeterJay Salzman MichaelBurian OriPomerantz Copyright© 2001 Peter Jay Salzman 2007−05 ...

  4. linux内核编程_内核线程kthread_run

    linux内核编程_内核线程kthread_run 1. 简述: 2. 使用示例: 3. 详述: 1. 简述: 头文件: include/linux/kthread.h 数据类型: struct ta ...

  5. 初探linux内核编程,参数传递以及模块间函数调用

    一.前言 我们一起从3个小例子来体验一下linux内核编程.如下: 1. 内核编程之hello world 2. 模块参数传递 3. 模块间函数调用 二.准备工作 首先,在你的linux系统上面安装l ...

  6. 【华为云技术分享】Linux内核编程环境 (1)

    在上一期中,我们介绍了Linux内核的源码结构,这一期我们介绍Linux内核编程环境,首先介绍的是Linux内核的编译方法. 一.Linux内核编译方法 本期中我们以Linux 4.19.94版内核来 ...

  7. linux内核编程(hello world示例程序)

    linux内核编程(hello world) Linux可加载内核模块是 Linux 内核的最重要创新之一.它们提供了可伸缩的.动态的内核.其它开发者可以不用重新编译整个内核便可以开发内核层的程序,极 ...

  8. Linux 内核编程风格

    Linux 内核编程风格 (518 个字於此篇帖子) (已阅读: 1177 次) 这篇短小的文章是对Linux内核编程风格的建议.编程风格非常的个性化,而且,我并不想将我的观点强加给任何人,但是为了变 ...

  9. linux内核编程(一)

    这些天在学习linux内核编程,就在这里小小的show以下. 首先编写如下的linux代码.并命名为hello.c 这里你应该注意亮点: 第一.linux内核编程,不同于普通的用户态下的编程:有一个入 ...

最新文章

  1. 谷歌AI乳腺癌检测超过人类,LeCun质疑引起讨论
  2. 【python之路8】python基本数据类型(二)
  3. python 正则表达式 re.findall()方法
  4. Android内存泄漏总结
  5. 简单聊一下makefile中的 =, :=, ?=和+=
  6. mybatis+spring mvc 完美整合方案 查询,保存,更新,删除自动生成
  7. Kafka 设计架构原理详细解析(超详细图解)
  8. python 中 enumerate() 函数使用
  9. JavaEE 启示录
  10. JAVA JSP网上订餐系统JSP餐厅点餐系统源码JSP点餐系统JSP网上订餐系统JSP在线订餐系统
  11. solr删除数据的4种方便快捷的方式
  12. 湖北文理学院毕业论文(设计)任务书
  13. SD卡 TF卡 引脚定义
  14. 【通讯录自动导入】txt格式转vcf格式
  15. IllegalStateException: For MAC signing you do not need to specify the verifier key separately异常解决
  16. 简单人口问题(指数增长)
  17. 1427.分解质因数
  18. STM32F1和F4的区别
  19. 【ML】第 1 章 :分布式机器学习:术语和概念
  20. Urban Traffic System 创建行人路线

热门文章

  1. ubuntu19.04 设置中文环境
  2. 基于DeepDive实现从股权交易公告获取企业与企业之间存在交易关系的概率--实践篇
  3. Java生成条形码图片到本地
  4. 网页前端实现五星好评效果
  5. 如何管理一台集群的虚拟机
  6. 攻防世界-MISC:glance-50
  7. ES6 之 Promise用法详解
  8. 02 固定效应模型与Stata实现
  9. 均方根误差(RMSE)的含义是什么以及误差大小多少适合?
  10. linux的标准输入输出