C语言GNU扩展语法

上传人:仙*** 文档编号:95832686 上传时间:2022-05-24 格式:DOC 页数:14 大小:174.50KB
收藏 版权申诉 举报 下载
C语言GNU扩展语法_第1页
第1页 / 共14页
C语言GNU扩展语法_第2页
第2页 / 共14页
C语言GNU扩展语法_第3页
第3页 / 共14页
资源描述:

《C语言GNU扩展语法》由会员分享,可在线阅读,更多相关《C语言GNU扩展语法(14页珍藏版)》请在装配图网上搜索。

1、GNU C 9条扩展语法GNC CC是一个功能非常强大的跨平台C编译器,它对标准C语言进展了一系列扩展,以增强标准 C的功能,这些扩展对优化、目标代码布局、更平安的检查等方面提供了很强 的支持。本文把支持GNU扩展的C语言称为 GNU C。Linux内核代码使用了大量的 GNU C扩展,以至于能够编译Linux内核的唯一编译器是GNU CC,以前甚至出现过编译Linux内核要使用特殊的GNU CC版本的情况。本文是对Linux内核使用的GNU C扩展的一个汇总,希望当你读内核源码遇到不理解的语法和语 义时,能从本文找到一个初步的解答,更详细的信息可以查看gcc.i nfo。文中的例子取自。1、

2、零长度和变量长度数组GNU C允许使用零长度数组,在定义变长对象的头构造时,这个特性非常有用。例如:include/linux/minix_fs.hstruct minix_dir_entry_u16 inode;char name0; 构造的最后一个元素定义为零长度数组,它不占构造的空间。 在标准C中那么需要定义数组长度为1,分配时计算对象大小比拟复杂。GNU C允许使用一个变量定义数组的长度,比方:int n=0;scanf(%d,&n);int arrayn;2、case范围GNU C允许在一个case标号中指定一个连续范围的值,例如:arch/i386/kernel/irq.ccase

3、 0 . 9: c -= 0; break;case a . f: c -= a-10; break; case A . F: c -= A-10; break;3、语句表达式GNUC把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如:/include/linux/kernel.h#define min_t(type,x,y) ( type _x = (x); type _y = (y); _x window_clamp,tcp_full_space(sk);复合语句的最后一个语句

4、应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个平安的求最小值的宏,在标准 C中,通常定义为:#define min(x,y) (x) (y) ? (x) : (y)这个定义计算x和y分别两次,当参数有副作用时,将产生不正确的结果,使用语句表 达式只计算参数一次,防止了可能的错误。语句表达式通常用于宏定义。4、typeof关键字使用前一节定义的宏需要知道参数的类型,利用typeof可以定义更通用的宏,不必事先知道参数的类型,例如:/include/linux/kernel.h#define min(x,y) ( /const typeof(x) _x = (x);/这里type

5、of(x)表示x的值类型,第3行定义了一个与 x类型一样的局部变量 _x并初 使化为x,注意第5行的作用是检查参数x和y的类型是否一样。typeof可以用在任何类型可以使用的地方,通常用于宏定义。5、可变参数宏在GNU C中,宏可以承受可变数目的参数,就象函数一样,例如:/include/linux/kernel.h#define pr_debug(fmt,arg.) printk(KERN_DEBUG fmt,#arg)_这里arg表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构成0的值,在宏扩展时替换 arg,例如:pr_debug(%s:%d,filename,line)扩

6、展为printk( %s:%d, filename, line)使用#的原因是处理 arg不匹配任何参数的情况,这时arg的值为空,GNUC预处理器在这种特殊情况下,丢弃#之前的逗号,这样pr_debug(success!/n)扩展为printk( success!/)注意最后没有逗号。6、标号元素标准C要求数组或构造变量的初使化值必须以固定的顺序出现,在GNU C中,通过指定索引或构造域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写INDEX=,要指定一个范围使用FIRSTLAST=的形式,例如:arch/i386/kernel/irq.cstatic unsigned

7、long irq_affinity NR_IRQS = 0 . NR_IRQS-1= 0UL ; 将数组的所有元素初使化为_0UL,这可以看做是一种简写形式。 要指定构造元素,在元素值前写FIELDNAME:,例如:/fs/ext2/file.cstruct file_operations ext2_file_operations = llseek:generic_file_llseek,read:generic_file_read,write:generic_file_write,ioctl:ext2_ioctl,mmap:generic_file_mmap,open:generic_fil

8、e_open,release:ext2_release_file,fsync:ext2_sync_file,; 将构造 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,元素 read 初始化 为genenric_file_read,依次类推。我觉得这是GNU C扩展中最好的特性之一,当构造的定义变化以至元素的偏移改变时,这种初始化方法仍然保证元素的正确性。对于未出现在初始化中的元素,其初值为 0。标准C要求数组或构造体的初始化值必须以固定的顺序出现,在GNUC中,通过指定索引或构造体成员名,允许初始化以任意顺序出现。unsign

9、ed char dataMAX =0=10,10=100,;struct file_operations ext2_file_operations=open:ext2_open, close:ext2_close,; 在linux 2.6中推荐如下方式:struct file_operations ext2_file_operations=.read=ext2_read, .write=ext2_write,;7、当前函数名GNU C中预定义两个标志符保存当前函数的名字,_FUNCTION_ 保存函数在源码中的名字,_PRETTY_FUNCTION_ 保存带语言特色的名字。 在C函数中这两个名

10、字是一样 的。在C+函数中,_PRETTY_FUNCTION_包括函数返回类型等额外信息,Linux内核只使用了 FUNCTION。fs/ext2/super.cvoid ext2_update_dynamic_rev(struct super_block *sb)struct ext2_super_block *es = EXT2_SB(sb)-s_es;if (Ie32_to_cpu(es-s_rev_level) EXT2_GOOD_OLD_REV)return;ext2_warning(sb, _FUNCTION_,updating to rev %d because of new f

11、eature flag, running e2fsck is remended,EXT2_DYNAMIC_REV);这里 _FUNCTION_ 将被替换为函数名 ext2_update_dynamic_rev。虽然 _FUNCTION_ 看起来类似于标准C中的_FILE_,但实际上_FUNCTION_是被编译器替换的,不象_FILE_被预处理器替换。在C99中支持_func_宏,因此建议使用_func_替代_FUNCTION_8、特殊属性声明GNU C允许声明函数、变量和类型的特殊属性,以便进展手工的代码优化和定制代码检 查的方法。要指定一个声明属性,只需要在声明后添加_attribute_(

12、ATTRIBUTE)。其中 ATTRIBUTE 为属性说明,如果存在多个属性,那么以逗号分隔。GNU C支持noreturn、format、section、aligned、packed等十个属性。这里介绍最常用的:no return属性用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的代码, 最重要的是可以消除不必要的警告信息比方未初使化的变量。例如:/include/linux/kernel.h#define ATTRIB_NORET _attribute_(noreturn) .asmlinkage NORET_TYPE void do_exit(long error_code)

13、ATTRIB_NORET;format (ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)属性用于函数,表示该函数使用printf, scanf或strftime风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹 配,指定format属性可以让编译器根据格式串检查参数类型。例如:/include/linux/kernel.h?asmlinkage int printk(const char * fmt, .) _attribute_ (format (printf, 1,2);表示第一个参数是格式串,从第二个参数起根据格式串检查参数。unused属性用于函

14、数和变量,表示该函数或变量可能不使用,这个属性可以防止编译器产生警告信息。section (section-name)属性用于函数和变量,通常编译器将函数放在.text区,变量放在.data区或.bss区,使用section属性,可以让编译器将函数或变量放在指定的节中。例如:/include/linux/init.h#define _exit#define _initdata #define _exitdata #define _initsetup #define _init_call#define _exit_call_attribute_ (unused,_section_(.text.e

15、xit) _attribute_ (_section_ (.data.init)_attribute_ (unused, _section_(.data.exit)_attribute_ (unused,_section_(.setup.init)_attribute_ (unused,_section_(.initcall.init)_attribute_ (unused,_section_(.exitcall.exit)连接器可以把一样节的代码或数据安排在一起,Linux内核很喜欢使用这种技术,例如系统的初始化代码被安排在单独的一个节,在初始化完毕后就可以释放这局部内存。 alig ned

16、 (ALIGNMENT)属性用于变量、构造或联合类型,指定变量、构造域、构造或联 合的对齐量,以字节为单位,例如:include/asm-i386/processor.h struct i387_fxsave_struct unsigned short cwd; _attribute_ (aligned (16);表示该构造类型的变量以16字节对齐。通常编译器会选择适宜的对齐量,显示指定对齐通常是由于体系限制、优化等原因。packed属性用于变量和类型,用于变量或构造域时表示使用最小可能的对齐,用于枚举、构造或联合类型时表示该类型使用最小的内存。例如:/include/asm-i386/des

17、c.hstruct Xgt_desc_struct unsigned short size;unsigned long address _attribute_(packed);_;域address将紧接着size分配。属性packed的用途大多是定义硬件相关的构造,使元素之间没有因对齐而造成的空洞。9、内建函数GNU C提供了大量的内建函数,其中很多是标准C库函数的内建版本,例如 memcpy,它们与对应的C库函数功能一样,本文不讨论这类函数,其他内建函数的名字通常以 _builtin 开场。内建函数 _builtin_return_address(LEVEL)返回当前函数或其调用者的返回地址

18、,参数 LEVEL指定调用栈的级数,女口0表示当前函数的返回地址,1表示当前函数调用者的返回地址,依此类推。例如:/kernel/sched.c printk(KERN_ERR schedule_timeout: wrong timeout value %lx from %p/n, timeout,_builtin_return_address(0);内建函数 _builtin_constant_p(EXP)用于判断一个值是否为编译时常数,如果参数EXP的值是常数,函数返回1,否那么返回 0。/ include/asm-i386/bitops.h#define test_bit(nr,addr

19、) /(_builtin_constant_p(nr) ? /constant_test_bit(nr),(addr) : / variable_test_bit(nr),(addr) 很多计算或操作在参数为常数时有更优化的实现,在GNU C中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。内建函数 _builtin_expect(EXP, C)用于为编译器提供分支预测信息,其返回值是整数表达式EXP的值,C的值必须是编译时常数。例如:/include/linux/piler.h#define likely(x) _bui

20、ltin_expect(x),1)#define unlikely(x) _builtin_expect(x),0)/ kernel/sched.cif (unlikely(in_interrupt() printk(Scheduling in interrupt/n);BUG();_这个内建函数的语义是EXP的预期值是,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第6-7行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。假设不想使用GNU C扩展,那么只需要在gcc参数后面加上-ansi -peda

21、ntic即可,使用上述参数后,所有 GNC C扩展语法局部将会有编译警报。linux gcc的属性解析GNU C的一大特色却不被初学者所知就是_attribute_机制。_attribute_可以设置函数属性Function Attribute 丨、变量属性Variable Attribute 丨和类型属性Type Attribute。_attribute_书写特征是:_attribute_前后都有两个下划线,并切后面会紧跟一对原括 弧,括弧里面是相应的_attribute_参数。_attribute_ 语法格式为:_attribute_ (attribute-list)其位置约束为:放于声明

22、的尾部“之前。函数属性Function Attribute 函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。_attribute_机制也很容易同非GNU应用程序做到兼容之成效。GNU CC需要使用-Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。 下面介绍几个常见的属性参数。_attribute_ format该_attribute_属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。format 的语法

23、格式为:format (archetype, string-index, first-to-check)format属性告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规那么对该函数的参数进展检查。“archetype指定是哪种风格;“stringindex指定传入函数的第几个参数是格式化字符串;“firstto-check 指定从函数的第几个参数开场按上述规那么进展检查。具体使用格式如下:_attribute_(format(printf,m,n)_attribute_(format(scanf,m,n)其中参数m与n的含义为:m:第几个参数为格式化

24、字符串format string;n:参数集合中的第一个,即参数“里的第一个参数在函数参数总数排在第几,注意,有时函数参数里还有隐身的呢,后面会提到;在使用上,_attribute_(format(printf,m,n)是常用的,而另一种却很少见到。下面举例 说明,其中myprint为自己定义的一个带有可变参数的函数,其功能类似于printf :/m=1 ; n=2extern void myprint(const char *format,.) _attribute_(format(printf,1,2);需要特别注意的是,如果myprin是一个函数的成员函数,那么m和n的值可有点悬 乎了,

25、例如:/m=3 ; n=4extern void myprint(int l ,const char *format,.) _attribute_(format(printf,3,4);其原因是,类成员函数的第一个参数实际上一个隐身的“this指针。有点C+根底的都知道点this指针,不知道你在这里还知道吗?这里给出测试用例:attribute.c,代码如下:extern void myprint(const char *format,.) _attribute_(format(printf,1,2);void test()myprint(i=%d,6);myprint(i=%s,6); my

26、print(i=%s,abc); myprint(%s,%d,%d,1,2); 运行$gcc -Wall attribute.c attribute 后,输出结果为:attribute.c: In function test:attribute.c:7: warning: format argument is not a pointer (arg 2)attribute.c:9: warning: format argument is not a pointer (arg 2)attribute.c:9: warning: too few arguments for format如果在attr

27、ibute.c中的函数声明去掉_attribute_(format(printf,1,2),再重新编译,即运 行$gcc -Wall -c attribute.c attribute后,那么并不会输出任何警告信息。注意,默认情况下,编译器是能识别类似printf的标准库函数。_attribute_ no retur n该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以防止出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式,如下所示:extern void exit(int) _attribute_(nor

28、eturn);extern void abort(void) _attribute_(noreturn);为了方便理解,大家可以参考如下的例子:extern void myexit();int test(int n)if ( n 0 )myexit();/*程序不可能到达这里*/else return 0; 编译显示的输出信息为:$ gcc -Wall oreturn.c: In function test:noreturn.c:12: warning: control reaches end of non-void function警告信息也很好理解,因为你定义了一个有返回值的函数test却

29、有可能没有返回值,程序当然不知道怎么办了!加上_attribute_(noreturn)那么可以很好的处理类似这种问题。把extern void myexit()修改为:extern void myexit() _attribute_(noreturn)之后,编译不会再出现警告信息。_attribute_ const该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于 返回值是一样的,所以此时编译器可以进展优化处理,除第一次需要运算外,其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态static state和副作用的一些函数,并且返回值

30、仅仅依赖输入的参数。为了说明问题,下面举个非常糟糕的例子,该例子将重复调用一个带有一样参数值的函数,具体如下:extern int square(int n) _attribute_(const);for (i = 0; i 100; i+ )total += square(5) + i;_通过添加_attribute_(const)声明,编译器只调用了函数一次,以后只是直接得到了一 样的一个返回值。事实上,const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的 参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。并且,带有该属性的函数不

31、能有任何副作用或者是访问全局或静态变量,所以,类似 getchar()或time()的函数是不适合使用该属性的。同时使用多个属性可以在同一个函数声明里使用多个_attribute_,并且实际应用中这种情况是十分常见的。使用方式上,你可以选择两个单独的_attribute_,或者把它们写在一起,可以参考下面的例子:/*把类似printf的消息传递给stderr并退出*/extern void die(const char *format, .) _attribute_(noreturn)_attribute_(format(printf, 1, 2);或者写成extern void die(co

32、nst char *format, .) _attribute_(noreturn, format(printf, 1,2);_如果带有该属性的自定义函数追加到库的头文件里,那么所以调用该函数的程序都要做_相应的检查。和非GNU编译器的兼容性庆幸的是,_attribute_设计的非常巧妙,很容易作到和其它编译器保持兼容,也就是说,如果工作在其它的非 GNU编译器上,可以很容易的忽略该属性。即使_attribute_使用了多 个参数,也可以很容易的使用一对圆括弧进展处理,例如:/*如果使用的是非 GNU C,那么就忽略_attribute_ */#ifndef _GNUC_#define _at

33、tribute_(x) /*NOTHING*/#endif需要说明的是,attribute 适用于函数的声明而不是函数的定义。所以,当需要使用该属性的函数时,必须在同一个文件里进展声明,例如:/*函数声明*/void die(const char *format,)_attribute_(noreturn) _attribute_(format(printf,1,2);void die(const char *format, .)/*函数定义*/变量属性Variable Attributes 关键字_attribute_也可以对变量variable或构造体成员structure field丨进

34、展属性设 置。这里给出几个常用的参数的解释,更多的参数可参考本文给出的连接。在使用_attribute_参数时,你也可以在参数的前后都加上“两个下划线,例如,使用_aligned_而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文 件里是否有重名的宏定义。alig ned (alig nment)该属性规定变量或构造体成员的最小的对齐格式,以字节为单位。例如:int x _attribute_ (aligned (16) = 0;编译器将以16字节注意是字节 byte不是位bit丨对齐的方式分配一个变量。也可以对 构造体成员变量设置该属性,例如,创立一个双字对齐的int对

35、,可以这么写:struct foo int x2 _attribute_ (aligned (8); ;如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果 aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有 益的对齐方式。例如:short array3 _attribute_ (aligned);选择针对目标机器最大的对齐方式,可以提高拷贝操作的效率。aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16

36、字节对齐,那么你此时定义32字节对齐也是无济于事的。packed使用该属性可以使得变量或者构造体成员使用最小的对齐方式,即对变量是一字节对齐,对域field丨是位对齐。下面的例子中,x成员变量使用了该属性,那么其值将紧放置在a的后面:struct testchar a;int x2 _attribute_ (packed); 其它可选的属性值还可以是:cleanup, mon, nomon, deprecated, mode, section, shared,tls_model , transparent_union , unused, vector_size , weak , dllimpo

37、rt 等,详纟田信息可参 考: 类型属性Type Attribute关键字_attribute_也可以对构造体struct或共用体union进展属性设置。大致有六个参数值可以被设定,即:aligned, packed, transparent_union, unused, deprecated 禾口may_alias。在使用_attribute_参数时,你也可以在参数的前后都加上“两个下划线,例如,使用_aligned_而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文 件里是否有重名的宏定义。alig ned (alig nment)该属性设定一个指定大小的对齐格式以字

38、节为单位,例如:struct S short f3; _attribute_ (aligned (8);typedef int more_alignednt _attribute_ (aligned (8);该声明将强制编译器确保尽它所能变量类型为struct S或者more-aligned-int的变量在分配空间时采用8字节对齐方式。如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有 益的对齐方式。例如:struct S short f3; _attribute_ (aligned)

39、;这里,如果sizeofshort的大小为2byte,那么,S的大小就为6。取一个2的次方 值,使得该值大于等于6,那么该值为8,所以编译器将设置S类型的对齐方式为8字节。aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。packed使用该属性对struct或者union类型进展定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型 it indicates that

40、the smallest integral type should be used。下面的例子中,my-packed-struct类型的变量数组中的值将会紧紧的靠在一起,但内部的成员变量s不会被“pack,如果希望内部的成员变量也被packed的话,my-unpacked-struct也需要使用packed进展相应的约束。struct my_unpacked_structchar c; int i;struct my_packed_structchar c;int i;struct my_unpacked_struct s;_attribute_ (_packed_);变量属性与类型属性举例下

41、面的例子中使用_attribute_属性定义了一些构造体及其变量,并给出了输出结果和对 结果的分析。程序代码为:struct pint a;char b;char c;_attribute_(aligned(4) pp;struct qint a;char b;struct p qn;char c;_attribute_(aligned(8) qq;int main() printf(sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%dn,sizeof(int),sizeof(short),sizeof(char); prin tf(pp=%d,qq=

42、%d n, sizeof(pp),sizeof(qq);return 0;输出结果:sizeof(int)=4,sizeof(short)=2.sizeof(char)=1 pp=8,qq=24分析:sizeof(pp):sizeof(a)+ sizeof(b)+ sizeof(c)=4+1+1=62 3 =8= sizeof(pp) sizeof(qq):sizeof(a)+ sizeof(b)=4+1=5sizeof(qn)=8即qn是采用8字节对齐的,所以要在a, b后面添3个空余字节,然后才能存储qn, 4+1+3+8+仁17因为qq采用的对齐是8字节对齐,所以qq的大小必定是8 的整

43、数倍,即qq的大小是一个比17大又是8的倍数的一个最小值,由此得到1724+8=24=sizeof(qq)Linux内核使用的GNU C扩展和Unix 样,Linux内核也是用 C语言实现的。谈到 C,几乎所有的人都会立即想到 ANSI C标准。但是Linux内核的实现,其实并不完全符合 ANSI C标准。实际上,内核开 发者总会使用许多 gcc提供的C语言的扩展局部。内核开发者使用的 C语言涵盖了 ISO C99标准和GNU C的扩展特性,我想,其中让人 感兴趣的,应该不在于 C99标准上,而是在于它的 GNU C扩展特性上。下面,我们就一起 来学学内核使用的 GNU C扩展特性吧。1. 内

44、联函数GNU的C编译器支持内联函数,这一点和ANSI标准完全不一样。 在ANSI C中是没有inline这个关键字的。内联函数会在函数调用的地方直接把函数体展开,这样就可以减少函 数调用的开销了存放器的保存和恢复。而且,由于编译器会把调用函数的代码和函数本身的代码放在一起优化,所以也会有进一步优化代码的可能。当然,天底下没有白吃的午餐,这样做也是有代价的,那就是生成的代码会变长,这就意味着你必须使用更多的内存空间或更多的指令缓存来执行代码。内核开发者通常把那些对时间要求较高,而本身长度又较短的函数定义成内联函数。当然了,对于大块头的程序,你想把它定义成内联函数也没人反对。 只不过,有这必要么?

45、定义一个内联函数,需要使用static作为关键字,并且用inline限定它。如:static inline void foo() ._内联函数必须在使用之前就定义好,否那么编译器没法儿将之展开。由于使用static关键字进展限制,编译时不会为内联函数单独建立一个函数体。内联函数一般定义在头文件中,当然了,如果你仅仅在某个源文件中使用内联函数,也可以把它定义在源文件的开头局部。在内核中,为了类型平安的原因,优先使用内联函数而不是复杂的宏。2. 内联汇编gcc支持在C函数中嵌入汇编指令。当然,在内核编程的时候,只有知道对应的体系构 造,才能使用这个功能。因为,不同的体系构造,其汇编指令往往是有很大

46、差异的。Linux的内核混合使用了 C和汇编语言。在偏近体系构造的底层或对执行时间要求严格 的地方,一般使用的是汇编语言。而内核其他局部的大局部代码那么都是C语言写的。内嵌汇编的语法如下:_asm_(7匚编语句模板 :输出局部:输入局部:破坏描述局部)共四个局部:汇编语句模板,输出局部,输入局部,破坏描述局部,各局部使用“:格开,汇编语句模板必不可少,其他三局部可选,如果使用了后面的局部,而前面局部为空,也需要用“:格开,相应局部内容为空。例如:asm volatile(cli: : :memory)1、汇编语句模板汇编语句模板由汇编语句序列组成,语句之间使用“;、n 或nt 分开。指令中的操

47、作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0 , %1 ,,%9。指令中使用占位符表示的操作数,总被视为long型4个字节,但对其施加的操作根据指令可以是字或者字节,当把操作 数当作字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,1代表低字节,“6代表高字节,例如: h1。2、输岀局部输岀局部描述输岀操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和C语言变量组成。每个输岀操作数的限定字符串必须包含“=表示他是一个输岀操作数。例:_asmvolatile_(pushfl ;

48、popl %0 ; cli:=g (x)描述符字符串表示对该变量的限制条件,这样GCC就可以根据这些条件决定如何分配存放器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。3、输入局部输入局部描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由限定字符串和C语言表达式或者 C语言变量组成。例如:_asmvolatile, (lidt %0 : : m (real_mode_idt);4、限制字符4.1、限制字符列表限制字符有很多种,有些是与特定体系构造相关,此处仅列出常用的限定字符和i386中可能用到的一些常用的限定符。它们的作用是指示编译器如何处理其后的C语言

49、变量与指令操作数之间的关系。分类限定符描述通用 存放 器a将输入变量放入eax,这里有一个问题:假设 eax已经被使用,那怎么办?其实很简单: 因为GCC知道eax已经被使用,它在这段汇编代码 的起始处插入一条语句 pushl %eax , 将eax内容保存到堆栈,然 后在这段代码完毕处再增加一条语句popl %eax,恢复eax的内容b将输入变量放入ebxc将输入变量放入ecxd将输入变量放入edxs将输入变量放入esid将输入变量放入ediq将输入变量放入 eax,ebx, ecx,edx中的一个r将输入变量放入通用存放器,也就是eax,ebx,ecx,edx,esi,edi中的一个A把e

50、ax和edx合成一个 64位的存放器(use long longs)内存m内存变量o操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址 寻址V操作数为内存变量,但寻址方式不是偏移量类型缺操作数为内存变量,但寻址方式为自动增量p操作数是一个合法的内存地址指针存放器或内存g将输入变量放入 eax,ebx,ecx,edx中的一个或者作为内存变量X操作数可以是任何类型立即数I0-31之间的立即数用于 32位移位指令j0-63之间的立即数用于 64位移位指令N0-255之间的立即数用于 out指令i立即数n立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n而不是“1

51、匹配0,1.9表示用它限制的操作数与某个指定的操作数匹配,也即该操作数就是指定的那个操作数,例如“0去描述 1操作数,那么“1弓1用的其实就是 “0操作数,注意作为限定 符字母的0 9与指令中的 0 - % 9 的区别,前者描述操作数,后者代表操作数。&该输岀操作数不能使用过和输入操作数一样的存放器操作数类型=操作数在指令中是只写的输岀操作数+操作数在指令中是读写类型的输入输岀操作数浮点数f浮点存放器t第一个浮点存放器u第二个浮点存放器G标准的80387浮点常数%该操作数可以和下一个操作数交换位置例如addl的两个操作数可以交换顺序当然两个操作数都不能是立即数#局部注释,从该字符到其后的逗号之

52、间所有字母被忽略*表示如果选用存放器,那么其后的字母被忽略5、破坏描述局部破坏描述符用于通知编译器我们使用了哪些存放器或内存,由逗号格开的字符串组成,每个字符串描述一种情况,一般是存放器名;除存放器外还有“memory。例如:eaX , “ebX , “mmory 等。3. 分支声明对于条件选择语句,gcc内建了一条指令用于优化,在一个条件经常出现的时候,或者该条件很少出现的时候,编译器可以根据这条指令对分支进展优化。内核把这条指令封装成了宏,比方likely()和unlikely(),这样使用起来非常方便。例如,下面是一个条件选择语句:if (foo) /* . */ 如果我们要把这个选择标

53、记成绝少发生的分支,我们可以这样做:/*我们认为foo绝大局部时间都会为0 */if (unlikely(foo) /* . */_相反,如果我们要把这个选择标记成绝大局部时间都会发生的分支,可以这样做:/*我们认为foo通常都不会为0 */if (likely(foo) /* . */_在想要对某个条件选择语句进展优化之前,一定要搞清楚其中是不是存在这么一个条件,在绝大多数 情况下都会成立。这点十分重要:如果你的判断正确,这个条件确实占压倒性的地位,那么性能就会提升, 相反,如果你搞错了,性能反而会下降。在对一些错误条件进展判断的时候,常常会用到likely()和unlikely()宏。我们看到,unlikely()在内核中得到了广泛的应用,因为if语句往往用于判断一种特殊情况,而这种情况是绝少发生的。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!