Linux驱动开发随笔
2021-08-19
关键字:
1、内核空间与用户空间数据交换方式
分两种情况:
1、需要交换的数据量较大时;
2、需要交换的数据量较小时;
第一种情况使用以下的函数:
unsigned long copy_to_user(void __user* to, const void* from, unsigned long n); unsigned long copy_from_user(void* to, const void __user* from, unsigned long n);
返回值表示成功拷贝的字节数。
第二种情况则使用以下所示的宏定义:
#include <asm/uaccess.h>
put_user(local, user);
get_user(local, user);
2、MKDEV申请的设备号与register_chrdev_region申请的设备号有什么区别?
两种方式所描述的“设备号”是相同的。其区别用一句话概括就是:是否得到了系统的承认。
MKDEV(ma, mi) 是一个宏定义,其作用就是将参数 major 和 minor 由两个分散的数值组合成统一的 dev_t 类型(unsigned long)。执行此宏定义后得到的“设备号”表示你想要得到这个设备号,想向全世界声明它属于我,不过此时它仅仅是你单方面的声明而已。
register_chrdev_region() 的用法通常是将 MKDEV() 出来的设备号作为参数传入。可以理解成是在开发者单方面声明想独占某个设备号后,将此设备号交由操作系统审批。当函数返回值为0时,表示Linux系统通过了开发者的申请,前面由 MKDEV() 出来的设备号就得到开发者以及操作系统的共同认可。
3、register_chrdev_region() 与 alloc_chrdev_region() 有什么区别?
两函数的原型分别如下所示:
int register_chrdev_region(dev_t from, unsigned int count, const chr* name); int alloc_chrdev_region(dev_t* dev, unsigned int baseminor, unsigned int count, const char* name);
两个函数的目的一样,都是从系统中得到(申请)一个专属设备号。但前者用于“静态申请”,而后者用于“动态申请”。
静态申请则表示开发者为某设备静态指定一个设备号,直接提交给系统审批。
但设备号资源是有限的,对某些动态加载的驱动来说可能会遇到静态指定的设备号已被占用的情况。此时最好的解决办法就是直接向系统申请一个可用的设备号。
4、创建内核线程
可以使用 kthread_run 宏定义来创建。其原型如下
#define kthread_run(threadfn, data, namefmt, ...)
参数 threadfn 表示用于在子线程中执行的函数名。子线程函数的原型为:int (*threadfn)(void *data)
参数 data 表示要传递到子线程函数去的数据地址。
参数 namefmt 及后面的可变长度个参数用于组合成此子线程的名称。
此宏定义返回值为一个 struct task_struct 结构体地址。可以用 IS_ERR(ts)来判断子线程是否成功创建。
5、内核中的内存分配
内核开发中常见的分配内存的函数有两个:
static inline void* kmalloc(size_t size, gfp_t flags); void* vmalloc(unsigned long size);
kmalloc函数与malloc函数类似,它所分配的内存空间在物理上是地址连续型的。但是它在内核中最大只能分配128KB的内存空间。此函数第二个参数常用的值有两个:1、GFP_KERNEL;2、GFP_ATOMIC;分别对应于在内存不足是是否阻塞调用。
vmalloc函数分配的内存空间在虚拟地址上是连续的,但是在物理地址上却不一定。因此此函数可用于分配大内存空间,最大可达1GB。
如果在内核开发中要涉及到频繁的申请与释放内存空间的,为避免产生内存碎片,可以使用内存池技术来解决。