linux内核分析基础
来源:互联网 发布:矢量图用什么软件制作 编辑:程序博客网 时间:2024/05/08 13:54
http://blog.chinaunix.net/uid-9185047-id-4123876.html
本文转自http://netwalker.blog.chinaunix.net,作者写了一系列名为linux模式设计,这里将他整理放到一起
1-数据大小:
- include/linux/kernel.h
- #define USHORT_MAX ((u16)(~0U))
- #define SHORT_MAX ((s16)(USHORT_MAX>>1))
- #define SHORT_MIN (-SHORT_MAX- 1)
- #define INT_MAX ((int)(~0U>>1))
- #define INT_MIN (-INT_MAX- 1)
- #define UINT_MAX (~0U)
- #define LONG_MAX ((long)(~0UL>>1))
- #define LONG_MIN (-LONG_MAX- 1)
- #define ULONG_MAX (~0UL)
- #define LLONG_MAX ((long long)(~0ULL>>1))
- #define LLONG_MIN (-LLONG_MAX- 1)
- #define ULLONG_MAX (~0ULL)
- #include <stdio.h>
- #define UCHAR_MAX ((unsigned char)(~0U))
- #define CHAR_MAX ((char)(UCHAR_MAX>> 1))
- #define CHAR_MIN (-CHAR_MAX- 1)
- int main()
- {
- printf("UCHAR_MAX:\t%u\n", UCHAR_MAX);
- printf("CHAR_MAX:\t%d\n", CHAR_MAX);
- printf("CHAR_MIN:\t%d\n", CHAR_MIN);
- return 0;
- }
- UCHAR_MAX: 255
- CHAR_MAX: 127
- CHAR_MIN: -128
定义数据大小的方法有很多种,比如通过接下来数据对齐小节中的偏移位机制,但是这会在生成第一个数值时产生移位运算,并且在生成宽度大于32位的类型时要分别考虑,没有上面方法的效率高。一个完成相同功能的示例如下,它的思想参考数据对齐小节。
- #define CHAR_SHIFT (sizeof(char)<< 3 + 1)
- #define UUCHAR_MAX ((unsigned char)((1<< CHAR_SHIFT)- 1))
- ......
- int a= ({typeof(a) _a= 0; ++_a;});
上例中符合表达式中声明了局部变量_a,而返回值为++_a的结果,所以a的值为1。基于这种扩展,内核可以通过在复合语句中定义局部变量而表面自加自减运算符的副作用问题。内核中的min_t和max_t宏就是这样实现的。
include/linux/kernel.h
- #define min_t(type, x, y)({ \
- type __min1 =(x); \
- type __min2 =(y); \
- __min1 < __min2? __min1: __min2;})
- #define max_t(type, x, y)({ \
- type __max1 =(x); \
- type __max2 =(y); \
- __max1 > __max2? __max1: __max2;})
- #define min(a, b)((a)> (b)?(b) : (a))
- #define max(a, b)((a)> (b)?(a) : (b))
- include/linux/kernel.h
- #define min(x, y)({ \
- typeof(x) _min1= (x); \
- typeof(y) _min2= (y); \
- (void)(&_min1 == &_min2); \
- _min1 < _min2? _min1 : _min2;})
- #define max(x, y)({ \
- typeof(x) _max1= (x); \
- typeof(y) _max2= (y); \
- (void)(&_max1 == &_max2); \
- _max1 > _max2? _max1 : _max2;})
- xxx.c:35: warning: comparison of distinct pointer types lacks a cast
3-数据圆整
- #define roundup(x, y)((((x)+ ((y)- 1))/ (y))* (y))
- 那么如何理解roundup的定义呢?看起来是尝试将(x) + ((y) - 1)的结果对y做向下取整,为何这样就可以实现x对y的向上取整呢?除法的本质在于对量的均分,那么
- #define roundown(x, y)(((x)/ (y))* (y))
对于x = βy + δ来说,β>=0,y>0,并且0<=δ<y。因为y>0且为整数,那么0<=δ<y等价于0<=δ<=y-1。对于圆整运算来说,可以将x中可以整除y的部分βy提取出来,只对剩下的δ部分做圆整运算然后加上βy。同理这里对δ+y-1部分进行圆整,由于0<=δ<=y-1,得到y-1 <= δ + y-1 <= 2y-2。考虑两种情况:当可以整除时,δ=0,也即取y-1,显然圆整值为0,也即不用向上圆整;而不可整除时,δ>0,所以y-1 < δ + y-1 <= 2y-2,又因为y为整数,所以y <= δ + y-1 <= 2y-2成立,由于y=1时符合第一种情况,所以只需考虑y>=2的情况。y==y并且2y-2在y>=2时>=y且<2y,所以保证δ + y-1的圆整值为1,也即不整除则要始终向上圆整。
一种更易被人理解的定义方式如下,它根据取余的结果计算圆整,由于整除的概率很低,所以这种算法每次都要多计算一次取余,而不能完全避免对除法的运算以消减取余算法的影响,它的效率要低。
- #define roundup(x, y)((x)%(y)? ((x)/(y)+ 1) * (y): x)
- int divisor= 0;
- printf("divisor\troundup\trounddown\n");
- for(; divisor< 5; divisor++)
- printf("%d:\t%d\t%d\n", divisor, roundup(divisor, 2), roundown(divisor, 2));
- divisor roundup rounddown
- 0: 0 0
- 1: 2 0
- 2: 2 2
- 3: 4 2
- 4: 4 4
- #define DIV_ROUND_UP(n,d)(((n)+ (d)- 1) / (d))
4-数据对齐
内核在某些应用中,为了实现某种机制,比如分页,或者提高访问效率需要保证数据或者指针地址对齐到某个特定的整数值,比如连接代码脚本。这个值必须是2N。数据对齐,可以看做向上圆整的一种运算。
- include/linux/kernel.h
- #define ALIGN(x, a) __ALIGN_MASK(x,(typeof(x))(a)- 1)
- #define __ALIGN_MASK(x, mask)(((x)+ (mask))&~(mask))
- #define PTR_ALIGN(p, a)((typeof(p))ALIGN((unsigned long)(p),(a)))
- #define IS_ALIGNED(x, a)(((x)& ((typeof(x))(a)- 1))== 0)
- arch/arm/include/asm/page.h
- /* PAGE_SHIFT determines the page size*/
- #define PAGE_SHIFT 12
- #define PAGE_SIZE (1UL<< PAGE_SHIFT)
- include/linux/mm.h
- /*to align the pointer to the (next) page boundary*/
- #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
- #include <stdio.h>
- ......
- int main()
- {
- int a = 0 ,i = 0;
- int *p = &a;
- for(; i< 6; i++)
- printf("ALIGN(%d, 4): %x\n", i, ALIGN(i, 4));
- printf("p:%p, PTR_ALIGN(p, 8): %p\n", p, PTR_ALIGN(p, 8));
- printf("IS_ALIGNED(7, 8): %d, IS_ALIGNED(16, 8): %d\n", IS_ALIGNED(7, 8), IS_ALIGNED(16, 8));
- return 0;
- }
- ALIGN(0, 4): 0
- ALIGN(1, 4): 4
- ......
- ALIGN(4, 4): 4
- ALIGN(5, 4): 8
- p:0xbf96c01c, PTR_ALIGN(p, 8): 0xbf96c020
- IS_ALIGNED(7, 8): 0, IS_ALIGNED(16, 8): 1
- include/linux/types.h
- #define DECLARE_BITMAP(name,bits)\
- unsigned long name[BITS_TO_LONGS(bits)]
- include/linux/bitops.h
- #define BITS_PER_BYTE 8
- #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE* sizeof(long))
内核定义了一系列的宏和函数来对DECLARE_BITMAP定义的位图进行操作,它们分别位于bitmap.h和bitmap.c中。这些宏和函数的操作思想是一致的。
- /arch/arm/include/asm/types.h
- #define BITS_PER_LONG 32
- include/linux/bitmap.h
- static inline void bitmap_zero(unsigned long*dst, int nbits)
- {
- if (nbits <= BITS_PER_LONG)
- *dst = 0UL;
- else {
- int len = BITS_TO_LONGS(nbits)* sizeof(unsigned long);
- memset(dst, 0,len);
- }
- }
- bitmap_zero 清所有比特位为0,被用来初始化位图。圆整到unsigned long。
- bitmap_fill 置nbits指定个数的比特位为1。精确到位。
- bitmap_copy 从src复制所有比特位到dst。圆整到unsigned long。
- bitmap_and 将nbits指定的位数按位与结果存放到dst。圆整到unsigned long。
- bitmap_or 将nbits指定的位数按位或结果存放到dst。圆整到unsigned long。
- bitmap_xor 将nbits指定的位数按位异或结果存放到dst。圆整到unsigned long。
- bitmap_andnot 根据nbits指定的位数, 将src1按位与上src2的按位非,结果存放到dst。圆整到unsigned long。
- bitmap_complement 根据nbits指定的位数, 按位取反后存放到dst。精确到位。
- bitmap_equal 比较nbits指定的位数,如果这些为全部相同返回1,否则返回0。精确到位。
- bitmap_intersects 比较nbits指定的位数中是否有重合(相交Overlap)的1比特位,也即src1和src2中有共同设置为1的标志位。有则返回1,否则返回0。精确到位。
- bitmap_subset src1的nbits指定位数中设置1的比特位是src2中nbits指定位数中设置1的比特的子集,则返回1,否则返回0。精确到位。
- bitmap_empty 测试src中的低nbits位是否全为0,是则返回1,否则返回0。精确到位。
- bitmap_full 测试src中的低nbits位是否全为1,是则返回1,否则返回0。精确到位。
- bitmap_weight 返回低nbits位的汉明重量(Hamming Weight)。
- bitmap_shift_right 逻辑右移n位,左边补0。n可以大于nbits数。圆整到unsigned long。
- bitmap_shift_left 逻辑左移n位,右边补0。n可以大于nbits数。圆整到unsigned long。
由于内核中定义了很多复杂的数据结构,而它们的实例中的成员在作为函数参数传递的时,函数中可能需要对它的包含者中的其他的兄弟成员进行处理,这就需要只根据成员地址就可以获取整个结构体变量的地址的操作。container_of提供了这样的操作:
- include/linux/kernel.h
- /**
- * container_of- cast a member of a structure out to the containing structure
- * @ptr: the pointerto the member.
- * @type: the type of the container struct thisis 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));})
- include/linux/compiler-gcc4.h
- #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
- include/linux/stddef.h
- #ifdef __compiler_offsetof
- #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
- #else
- #define offsetof(TYPE, MEMBER)((size_t)&((TYPE*)0)->MEMBER)
- #endif
如果定义了__compiler_offsetof,则使用Gcc编译器内建的offsetof宏,它的作用和此处定义的offsetof相同。它将0地址作为当前结构的首地址,从而直接通过指针访问成员得到的地址即为偏移。将实际使用的结构体中的成员指针__mptr减去offsetof,就得到了结构体的地址。
- #include <stdio.h>
- ......
- typedef struct man
- {
- char name[32];
- unsigned int id;
- unsigned char age;
- char address[64];
- }man_t;
- int main()
- {
- man_t tom ={"Tom", 0, 24,"ShangHai China"};
- man_t *man= NULL;
- printf("tom:%p, tom.age:%p, offsetof(man_t, age): %d\n",
- &tom,&tom.age, offsetof(man_t, age));
- man = container_of(&tom.age, man_t, age);
- printf("tom.name:%s, tom.id:%d, tom.age:%u, tom.address:%s\n",
- man->name, man->id, man->age, man->address);
- return 0;
- }
- tom:0xbf85cda4, tom.age:0xbf85cdc8, offsetof(man_t, age): 36
- tom.name:Tom, tom.id:0, tom.age:24, tom.address:ShangHai China
- include/linux/kernel.h
- #define FIELD_SIZEOF(t, f)(sizeof(((t*)0)->f))
它通过对0指针灵活运用,是对sizeof的一种变相扩展。
2.ARRAY_SIZE获取数组维数
ARRAY_SIZE用来获取一维或者多维数组的第一维大小,并对非数组指针进行合法性检查。
- include/linux/kernel.h
- #define ARRAY_SIZE(arr)(sizeof(arr)/ sizeof((arr)[0])+ __must_be_array(arr))
- include/linux/kernel.h
- #define BUILD_BUG_ON_ZERO(e)(sizeof(char[1- 2 * !!(e)])- 1)
- include/linux/compiler-gcc.h
- #define __must_be_array(a)\
- BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
- #define IS_ARRAY_PTR(p) (!__builtin_types_compatible_p(typeof(p), typeof(&p[0])))
- xxx.c:17:error: subscripted valueis neither array nor pointer
- include/linux/kernel.h
- /* Force a compilationerror if conditionis true */
- #define BUILD_BUG_ON(condition)((void)sizeof(char[1- 2*!!(condition)]))
- /* Force a compilationerror if conditionis true, but also produce a
- result (of value 0and type size_t), so the expression can be used
- e.g.in a structure initializer (or where-everelse comma expressions
- aren't permitted).*/
- #define BUILD_BUG_ON_ZERO(e)(sizeof(char[1- 2 * !!(e)])- 1)
- BUILD_BUG_ON:只有条件condition为0时可编译,但不返回值,否则编译器报错。
- BUILD_BUG_ON_ZERO:只有条件e为0时返回0,否则编译器报错。
- xxx.c:42:error: size ofarray ‘type name’ is negative
内核对BUILD_BUG_ON的使用则要普遍的多,它被用来做编译期间的参数检查,广泛存在于内核的源码文件中。某些情况下需要一个数据结构满足特定的大小,比如jffs2文件系统中的jffs2_raw_inode结构的大小必须为68。另外可能需要为了兼容性考虑,可能需要定义一些别名,比如:
- include/linux/net.h
- #define SOCK_CLOEXEC O_CLOEXEC
- net/socket.c
- BUILD_BUG_ON(SOCK_CLOEXEC!= O_CLOEXEC);
- struct map
- {
- int used[2]; /* 8 */
- int empty[5]; /* 20*/
- char pad[32- 28];
- } men = {{1, 2},{0, 3, 4, 5, 6}};
- int main()
- {
- BUILD_BUG_ON(sizeof(men)!= 32);
- printf("BUILD_BUG_ON_ZERO(0):%d, %d\n", BUILD_BUG_ON_ZERO(0), sizeof(men));
- return 0;
- }
- linux内核分析基础
- linux内核分析基础
- linux内核裁剪分析 - 基础
- Linux内核分析 X86汇编基础测验
- 《Linux内核分析》-X86汇编基础及实验总结
- Linux内核分析——x86汇编基础
- linux内核基础
- linux内核相关基础
- Linux内核模块基础
- Linux内核编译基础
- Linux内核定时器基础
- Linux内核模块基础
- Linux内核基础
- Linux内核基础
- Linux内核基础-container_of
- linux内核基础
- Linux内核中断基础
- Linux内核编程基础
- 【GO】Ready To Work
- jndiName配置
- xcode自带的svn无法上传“.a”(静态库)文件解决
- 【编程好习惯】借助隐式初始化简化程序逻辑
- 深入浅出,以咖啡店为例演示Web应用程序扩展
- linux内核分析基础
- 李代沫同性亲密照疑取向有问题 38万身价恐成泡沫
- 总结OnOK()、OnCancel()、OnClose()、OnDestroy()之间的区别
- android 部署环境的时候遇到不能创建页面的问题
- 边缘检测(10)总结
- 计算机网络(3)网络层
- 一步步学习SPD2010--附录C--使用SP2010管理任务(4)--在Web应用程序层次限制SPD2010使用
- ios获取联系人信息
- Ubuntu下配置FTP服务器并用CuteFTP登陆