欢迎访问电子工程网!   登录 | 免费注册 ]   

yd2763132的个人空间 http://www.eechina.com/space-uid-36266.html [收藏] [复制] [分享] [RSS]

博客

linux/compile.h头文件说明

已有 352 次阅读2011-5-13 22:03 |个人分类:linux| 关键词:

#ifndef __ASSEMBLY__
??#ifdef __CHECKER__ //如果使用Sparse对代码进行检查,那么内核代码就会定义__CHECKER__宏,否则就不定义。
??
??# define __user __attribute__((noderef, address_space(1))) //是用来修饰一个变量的,这个变量必须是非解除参考(no dereference)的,即这个变量地址必须是有效的,而且变量所在的地址空间必须是1,即用户程序空间的。0表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。1表示用户地址空间,这个不用多讲,还有一个2,表示是设备地址映射空间,例如硬件设备的寄存器在内核里所映射的地址空间。所以在内核函数里,有一个copy_to_user的函数,函数的参数定义就使用了这种方式。当然,这种特性检查,只有当机器上安装了Sparse这个工具,而且进行了编译的时候调用,才能起作用的。
??# define __kernel /* default address space */
??根据定义,就是默认的地址空间,即0,定义成__attribute__((noderef, address_space(0)))也是没有问题的。
??# define __safe __attribute__((safe))
??在内核里,有好多函数,当它们被调用的时候,这些个参数必定不为空,所以根本用不着去对这些个参数进行非空的检查,所以呢,就增加了一个__safe的属性,如果这样声明变量
??# define __force __attribute__((force))
??表示所定义的变量类型是可以做强制类型转换的,在进行Sparse分析的时候,是不用报告警信息的。
??# define __nocast __attribute__((nocast))
??这里表示这个变量的参数类型与实际参数类型一定得对得上才行,要不就在Sparse的时候生产告警信息。
??# define __iomem __attribute__((noderef, address_space(2)))
??这个定义与 __user是一样的,只不过这里的变量地址是需要在设备地址映射空间的。
??# define __acquires(x) __attribute__((context(0,1)))
??# define __releases(x) __attribute__((context(1,0)))
??这是一对相互关联的函数定义,第一句表示参数x在执行之前,引用计数必须为0,执行后,引用计数必须为1,第二句则正好相反,这个定义是用在修饰函数定义的变量的。
??# define __acquire(x) __context__(1)
??# define __release(x) __context__(-1)
??这是一对相互关联的函数定义,第一句表示要增加变量x的计数,增加量为1,第二句则正好相反,这个是用来函数执行的过程中。
??# define __cond_lock(x) ((x) ? ({ __context__(1); 1; }) : 0)
??这句话的意思就是条件锁。当c这个值不为0时,则让计数值加1,并返回值为1。不过这里我有一个疑问,就是在这里,有一个__cond_lock定 义,但没有定义相应的__cond_unlock,那么在变量的释放上,就没办法做到一致。而且我查了一下关于spin_trylock()这个函数的定 义,它就用了__cond_lock,而且里面又用了_spin_trylock函数,在_spin_trylock函数里,再经过几次调用,就会使用到 __acquire函数,这样的话,相当于一个操作,就进行了两次计算,会导致Sparse的检测出现告警信息,经过我写代码进行实验,验证了我的判断, 确实是会出现告警信息,如果我写两遍unlock指令,就没有告警信息了,但这是与程序的运行是不一致的。
??extern void __chk_user_ptr(void __user *);
??extern void __chk_io_ptr(void __iomem *);
??这里只是定义了函数,但是代码里没有函数的实现。这样做的目的,就是在进行Sparse的时候,让Sparse给代码做必要的参数类型检查,在实际的编译过程中,并不需要这两个函数的实现。
??#define likely(x) __builtin_expect(!!(x), 1) //所有非零值为1
??#define unlikely(x) __builtin_expect(!!(x), 0)//为0则为0
??这两句是一对对应关系。
??#ifndef barrier
??# define barrier() __memory_barrier()
??#endif
??定义barrier()为__asm__ __volatile__("": : :"memory")。barrier翻译成中文就是屏障的意思,在这里,为什么要一个屏障呢?这是因为CPU在执行的过程中,为了优化指令,可能会对部分指令以它自己认为最优的方式进行执行,这个执行的顺序并不一定是按照程序在源码内写的顺序。编译器也有可能在生成二进制指令的时候,也进行一些优化。这 样就有可能在多CPU,多线程或是互斥锁的执行中遇到问题。那么这个内存屏障可以看作是一条线,内存屏障用在这里,就是为了保证屏障以上的操作,不会影响 到屏障以下的操作。然后再看看这个屏障怎么实现的。__asm__表示后面的东西都是汇编指令,当然,这是一种在C语言中嵌入汇编的方法,语法有其特殊 性,我在这里只讲跟这条指令有关的。__volatile__表示不对此处的汇编指令做优化,这样就会保证这里代码的正确性。""表示这里是个空指令,那么既然是空指令,则所对应的指令所需要的输入与输出都没有。在gcc中规定,如果以这种方式嵌入汇编,如果输出没有,则需要两个冒号来代替输出操作数的位 置,所以需要加两个::,这时的指令就为"" : :。然后再加上为分隔输入而加入的冒号,再加上空的输入,即为"" : : :。后面的memory是gcc中的一个特殊的语法,加上它,gcc编译器则会产生一个动作,这个动作使gcc不保留在寄存器内内存的值,并且对相应的内存不会做存储与加载的优化处理,这个动作不产生额外的代码,这个行为是由gcc编译器来保证完成的。
??#ifndef __section
??# define __section(S) __attribute__ ((__section__(#S)))
??#endif这个比较容易理解了,用来修饰一个函数是放在哪个区域里的,不使用编译器默认的方式。这个区域的名字由定义者自己取,格式就是__section__加上用户输入的参数。
??I/O 寄存器和常规内存
??side effect是指:访问I/O寄存器时,不仅仅会像访问普通内存一样影响存储单元的值,更重要的是它可能改变CPU的I/O端口电平、输出时序或CPU对I/O端口电平的反应等等,从而实现CPU的控制功能。CPU在电路中的意义就是实现其side effect 。I/O 寄存器和 RAM 的主要不同就是 I/O 寄存器操作有side effect, 而内存操作没有。因为存储单元的访问速度对 CPU 性能至关重要,编译器会对源代码进行优化,主要是: 使用高速缓存保存数值 和 重新编排读/写指令顺序。但对I/O 寄存器操作来说,这些优化可能造成致命错误。因此,驱动程序必须确保在操作I/O 寄存器时,不使用高速缓存,且不能重新编排读/写指令顺序。
??解决方法:
??硬件缓存问题:只要把底层硬件配置(自动地或者通过 Linux 初始化代码)成当访问 I/O 区域时(不管内存还是端口)禁止硬件缓存即可。
??硬件指令重新排序问题:在硬件(或其他处理器)必须以一个特定顺序执行的操作之间设置内存屏障memory barrier,见上面的说明

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

回顶部