嵌入式培训
达内IT学院
400-996-5531
任何一个行业、任何一份工作,只要你足够专业,你就能引万千粉丝膜拜,从而满足你的虚荣心,学嵌入式Linux内核也是的:
就当我还在学校的时候,我就曾在一个装机群里听一位装机圣手说,驱动程序的安装没你想的那么简单,分类型的,分为字符设备驱动和块设备驱动。我当时就纳闷了,我说我装机的时候好像没看到啊,我就把光盘放过去然后就一直点下一步,然后重启就好了啊。后面我在群里被几位高手围攻,败下阵来,时过境迁,哥现在也算是道上混的兄弟了,再也没那么容易被蒙了。就算你DIY再牛,你也不要和我说装驱动要分类。否则我就和你讲内核,讲晕你再说。看谁更能吹,哈哈。我得意的笑。我发现学内核的一个好处,就是非常好装B。你只要把内核里面的名词背熟了,拿出来去吓唬吓唬人,挺管用的,不过撞到行家的话,你就要注意了。
好了,学内核不是为了吓唬人的,是为了掌握其原理,学习其技巧与方法,知其然而知其所以然,另外内核代码是具有一定复杂度的,看了内核代码再看看我们自已写的,和玩具没啥两样,这就是学内核的好处!
如果你已经看过驱动模型应该有这种感受:你这玩意折腾来折腾去半天的,昨不干活呢?
字符设备是传说中的东西,玩过linux的人都知道这个东西,很多同志也可以照猫画虎的写出一个字符设备。但哥不,哥是有追求的人,知其然,必需得知其所以然。我决不会不负责任的把大家领进门后就不管了。我依然会不惜笔墨的把该说的全都说清楚。
我们先不用去抠概念,不要说,什么是字符设备啊,什么是块设备啊。这些都没意义,你最需要知道的是这个叫字符设备的东西究竟都干了些啥?他到底是怎么工作的?搞清楚后,什么是字符设备你就明白了。如果再学块设备,一对比,差异在哪?你就明白了。我学习一向都不喜欢抠概念。有的同志你叫字符设备他回答你说char设备,你说块设备他说block设备,你说底半部他说下半部。你说NXP他说恩智浦,还好哥是道上混的,多少知道一点。否则就被人家给唬住了。好了,闲话不多说了,总的来说要表达的就是一种学习态度:不用抠概念。
接下来我们欣赏一下字符设备。
看过驱动模型系列的朋友现在应该有一种意识了,我们暂且把它叫做“初始化意识”。就是说你用register_chrdev()注册的时候是很爽,但是那是因为前人把路铺好了,好,我们就来看看前人都做了些啥,再提醒一次一定要有“初始化意识”。
我们在“初始化意识”的指引下找到了一个文件:char_dev.c。打开这个文件一看。有这么一个初始化函数:
void __init chrdev_init(void) { cdev_map = kobj_map_init(base_probe, &chrdevs_lock); bdi_init(&directly_mappable_cdev_bdi); } base_probe是一个很简单的函数: static struct kobject *base_probe(dev_t dev, int *part, void *data) { if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0) /* Make old-style 2.4 aliases work */ request_module("char-major-%d", MAJOR(dev)); return NULL; }
request_module这个函数先大概知道意思就行了,他的意思是请求加载一个模块。
chrdevs_lock是一把大大的锁。没别的,就这两玩意。
关键在:
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock) { struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL); int i; if ((p == NULL) || (base == NULL)) { kfree(p); kfree(base); return NULL; } base->dev = 1; base->range = ~0; base->get = base_probe; for (i = 0; i < 255; i++) p->probes[i] = base; p->lock = lock; return p; }
最关键的一个角色就在这种神不知鬼不觉的情况下登场了,那就是struct kobj_map。
我们可以看到首先用kmalloc分配了一块内存并赋值给struct kobj_map *p了。
struct kobj_map { struct probe { struct probe *next; dev_t dev; unsigned long range; struct module *owner; kobj_probe_t *get; int (*lock)(dev_t, void *); void *data; } *probes[255]; struct mutex *lock; };
里面内嵌了一个长度为255的结构体数组和一把锁。
Linux内核里面如果是直接分配比较大块的内存,基本都是有hash思想在里面的,主要是为了效率。这个结构体中的成员等会大家就知道干嘛用的了。
接下来
struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
内核作者你就卖弄吧。写成struct probe *base = kzalloc(sizeof(struct probe), GFP_KERNEL)这样多好?不管了,随便了,反正我只取其精华。
接下来:
if ((p == NULL) || (base == NULL)) { kfree(p); kfree(base); return NULL; }
如果对这个有疑问的同志可以仔细研究一下kfree函数。这个是没有问题的。我再说一个思想,有疑问就看源码,不要去翻书,或者google百度的。Linux内核里面的函数全都是自给自足的,你所有的疑问都可以通过翻阅内核源码本身得到解决。当然啦,不是说不要去看书,我的意思是能不看就尽量不看。
接下来:
base->dev = 1; base->range = ~0; //取反,比你写一堆0xff...好多了,并且可移植性更好 base->get = base_probe;//把函数指针指向传进来的那个回调函数。
接下来:
for (i = 0; i < 255; i++) p->probes[i] = base; 用base初始化整个kobj_map.probe[255]。 p->lock = lock; return p;
最后把锁也传过来,并返回指针。
接下来:
bdi_init(&directly_mappable_cdev_bdi);
这个我们就先不用管了,这个对我们理解字符设备目前没有任何帮助。
感谢您的阅读,很多时候我们都觉得虚荣心不好,甚至会让我们盲目攀比,其本质都是因为不自信导致的,增强自身实力才是王道!即便是学习嵌入式Linux内核,你也要学到足够专业!更多嵌入式Linux相关的问题,欢迎你来达内嵌入式培训机构进行咨询。
免责声明:内容和图片源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。
填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved