从linux内核中学到的编程技巧 【转】
分类: LINUX
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; }) 大家看了就明白是什么意思了。但是我还有几点疑问:
(1)
(void) (&_min1 == &_min2);这行代码是用来干什么的?
(2)为什么{}的外面要加(),不加的时候编译是不通过的,具体是什么原因?
2 范围的扩展
(1) switch 语句
switch(a)
{
case 1 ... 3:
printf("fafadsf");
break;
case 4 ... 8:
printf("dsafaf");
break;
}
(2)数组的初始化 int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
以上部分内核中用的很多。 3 零长度的数组
struct iso_block_store {
atomic_t refcount;
size_t data_size;
quadlet_t data[0];
};
这允许结构中的元素引用结构实例后面紧接着的内存。在需要数量可变的数组成员时,这个特性很有用
应用实例:
struct iso_block_store * p =(void *)malloc(sizeof(struct iso_block_store) + data_size); 4 获得函数的返回地址
如下面的代码所示,__builtin_return_address
接收一个称为 level
的参数。这个参数定义希望获取返回地址的调用堆栈级别。例如,如果指定 level
为 0
,那么就是请求当前函数的返回地址。如果指定 level
为 1
,那么就是请求进行调用的函数的返回地址,依此类推。
void * __builtin_turn_address( unsigned int level ); |
在下面的示例中(见 ./linux/kernel/softirq.c),local_bh_disable
函数在本地处理器上禁用软中断,从而禁止在当前处理器上运行 softirqs、tasklets 和 bottom halves。使用 __builtin_return_address
捕捉返回地址,以便在以后进行跟踪时使用这个地址。
void local_bh_disable(void) |
5 常量检测
在编译时,可以使用 GCC 提供的一个内置函数判断一个值是否是常量。这种信息非常有价值,因为可以构造出能够通过常量叠算(constant folding)优化的表达式。__builtin_constant_p
函数用来检测常量。
__builtin_constant_p
的原型如下所示。注意,__builtin_constant_p
并不能检测出所有常量,因为 GCC 不容易证明某些值是否是常量。
int __builtin_constant_p( exp ) |
Linux 相当频繁地使用常量检测。在清单 3 所示的示例中(见 ./linux/include/linux/log2.h),使用常量检测优化 roundup_pow_of_two
宏。如果发现表达式是常量,那么就使用可以优化的常量表达式。如果表达式不是常量,就调用另一个宏函数把值向上取整到 2 的幂。
#define roundup_pow_of_two(n) \ |
6 函数属性
GCC 提供许多函数级属性,可以通过它们向编译器提供更多数据,帮助编译器执行优化。本节描述与功能相关联的一些属性。
属性通过其他符号定义指定了别名。可以以此帮助阅读源代码参考,了解属性的使用方法(见 ./linux/include/linux/compiler-gcc3.h)。
# define __inline__ __inline__ __attribute__((always_inline)) |
定义是 GCC 中可用的一些函数属性。它们也是在 Linux 内核中最有用的函数属性。下面解释如何使用这些属性:
always_inline
让 GCC 以内联方式处理指定的函数,无论是否启用了优化。deprecated
指出函数已经被废弃,不应该再使用。如果试图使用已经废弃的函数,就会收到警告。还可以对类型和变量应用这个属性,促使开发人员尽可能少使用它们。__used__
告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。这对于从汇编代码中调用 C 函数有帮助。__const__
告诉编译器某个函数是无状态的(也就是说,它使用传递给它的参数生成要返回的结果)。warn_unused_result
让编译器检查所有调用者是否都检查函数的结果。这确保调用者适当地检验函数结果,从而能够适当地处理错误。
下面是在 Linux 内核中使用这些属性的示例。deprecated
示例来自与体系结构无关的内核(./linux/kernel/resource.c),const
示例来自 IA64 内核源代码(./linux/arch/ia64/kernel/unwind.c)。
int __deprecated __check_region(struct resource |
7 分支预测提示
在 Linux 内核中最常用的优化技术之一是 __builtin_expect
。在开发人员使用有条件代码时,常常知道最可能执行哪个分支,而哪个分支很少执行。如果编译器知道这种预测信息,就可以围绕最可能执行的分支生成最优的代码。
如下所示,__builtin_expect
的使用方法基于两个宏 likely
和 unlikely
(见 ./linux/include/linux/compiler.h)。
#define likely(x) __builtin_expect(!!(x), 1) |
通过使用 __builtin_expect
,编译器可以做出符合提供的预测信息的指令选择决策。这使执行的代码尽可能接近实际情况。它还可以改进缓存和指令流水线。
例 如,如果一个条件标上了 “likely”,那么编译器可以把代码的 True 部分直接放在分支指令后面(这样就不需要执行分支指令)。通过分支指令访问条件结构的 False 部分,这不是最优的方式,但是访问它的可能性不大。按照这种方式,代码对于最可能出现的情况是最优的。
下面给出一个使用 likely
和 unlikely
宏的函数(见 ./linux/net/core/datagram.c)。这个函数预测 sum
变量将是零(数据包的 checksum
是有效的),而且 ip_summed
变量不等于 CHECKSUM_HW
。
unsigned int __skb_checksum_complete(struct sk_buff *skb) |
8 预抓取
另一种重要的性能改进方法是把必需的数据缓存在接近处理器的地方。缓存可以显著减少访问数据花费的时间。大多数现代处理器都有三类内存:
- 一级缓存通常支持单周期访问
- 二级缓存支持两周期访问
- 系统内存支持更长的访问时间
为了尽可能减少访问延时并由此提高性能,最好把数据放在最近的内存中。手工执行这个任务称为预抓取。GCC 通过内置函数 __builtin_prefetch
支持数据的手工预抓取。在需要数据之前,使用这个函数把数据放到缓存中。如下所示,__builtin_prefetch
函数接收三个参数:
- 数据的地址
rw
参数,使用它指明预抓取数据是为了执行读操作,还是执行写操作locality
参数,使用它指定在使用数据之后数据应该留在缓存中,还是应该清除
void __builtin_prefetch( const void *addr, int rw, int locality ); |
Linux 内核经常使用预抓取。通常是通过宏和包装器函数使用预抓取。下面是一个辅助函数示例,它使用内置函数的包装器(见 ./linux/include/linux/prefetch.h)。这个函数为流操作实现预抓取机制。使用这个函数通常可以减少缓存缺失和停顿,从而 提高性能。
#ifndef ARCH_HAS_PREFETCH |
10 变量属性
除了本文前面讨论的函数属性之外,GCC 还为变量和类型定义提供了属性。最重要的属性之一是 aligned
属性,它用于在内存中实现对象对齐。除了对于性能很重要之外,某些设备或硬件配置也需要对象对齐。aligned
属性有一个参数,它指定所需的对齐类型。
下面的示例用于软件暂停(见 ./linux/arch/i386/mm/init.c)。在需要页面对齐时,定义 PAGE_SIZE
对象。
char __nosavedata swsusp_pg_dir[PAGE_SIZE] |
packed
属性打包一个结构的元素,从而尽可能减少它们占用的空间。这意味着,如果定义一个char
变量,它占用的空间不会超过一字节(8 位)。位字段压缩为一位,而不会占用更多存储空间。- 这段源代码使用一个
__attribute__
声明进行优化,它用逗号分隔的列表定义多个属性。
static struct swsusp_header { |
分类: LINUX
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; }) 大家看了就明白是什么意思了。但是我还有几点疑问:
(1)
(void) (&_min1 == &_min2);这行代码是用来干什么的?
(2)为什么{}的外面要加(),不加的时候编译是不通过的,具体是什么原因?
2 范围的扩展
(1) switch 语句
switch(a)
{
case 1 ... 3:
printf("fafadsf");
break;
case 4 ... 8:
printf("dsafaf");
break;
}
(2)数组的初始化 int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
以上部分内核中用的很多。 3 零长度的数组
struct iso_block_store {
atomic_t refcount;
size_t data_size;
quadlet_t data[0];
};
这允许结构中的元素引用结构实例后面紧接着的内存。在需要数量可变的数组成员时,这个特性很有用
应用实例:
struct iso_block_store * p =(void *)malloc(sizeof(struct iso_block_store) + data_size); 4 获得函数的返回地址
如下面的代码所示,__builtin_return_address
接收一个称为 level
的参数。这个参数定义希望获取返回地址的调用堆栈级别。例如,如果指定 level
为 0
,那么就是请求当前函数的返回地址。如果指定 level
为 1
,那么就是请求进行调用的函数的返回地址,依此类推。
void * __builtin_turn_address( unsigned int level ); |
在下面的示例中(见 ./linux/kernel/softirq.c),local_bh_disable
函数在本地处理器上禁用软中断,从而禁止在当前处理器上运行 softirqs、tasklets 和 bottom halves。使用 __builtin_return_address
捕捉返回地址,以便在以后进行跟踪时使用这个地址。
void local_bh_disable(void) |
5 常量检测
在编译时,可以使用 GCC 提供的一个内置函数判断一个值是否是常量。这种信息非常有价值,因为可以构造出能够通过常量叠算(constant folding)优化的表达式。__builtin_constant_p
函数用来检测常量。
__builtin_constant_p
的原型如下所示。注意,__builtin_constant_p
并不能检测出所有常量,因为 GCC 不容易证明某些值是否是常量。
int __builtin_constant_p( exp ) |
Linux 相当频繁地使用常量检测。在清单 3 所示的示例中(见 ./linux/include/linux/log2.h),使用常量检测优化 roundup_pow_of_two
宏。如果发现表达式是常量,那么就使用可以优化的常量表达式。如果表达式不是常量,就调用另一个宏函数把值向上取整到 2 的幂。
#define roundup_pow_of_two(n) \ |
6 函数属性
GCC 提供许多函数级属性,可以通过它们向编译器提供更多数据,帮助编译器执行优化。本节描述与功能相关联的一些属性。
属性通过其他符号定义指定了别名。可以以此帮助阅读源代码参考,了解属性的使用方法(见 ./linux/include/linux/compiler-gcc3.h)。
# define __inline__ __inline__ __attribute__((always_inline)) |
定义是 GCC 中可用的一些函数属性。它们也是在 Linux 内核中最有用的函数属性。下面解释如何使用这些属性:
always_inline
让 GCC 以内联方式处理指定的函数,无论是否启用了优化。deprecated
指出函数已经被废弃,不应该再使用。如果试图使用已经废弃的函数,就会收到警告。还可以对类型和变量应用这个属性,促使开发人员尽可能少使用它们。__used__
告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。这对于从汇编代码中调用 C 函数有帮助。__const__
告诉编译器某个函数是无状态的(也就是说,它使用传递给它的参数生成要返回的结果)。warn_unused_result
让编译器检查所有调用者是否都检查函数的结果。这确保调用者适当地检验函数结果,从而能够适当地处理错误。
下面是在 Linux 内核中使用这些属性的示例。deprecated
示例来自与体系结构无关的内核(./linux/kernel/resource.c),const
示例来自 IA64 内核源代码(./linux/arch/ia64/kernel/unwind.c)。
int __deprecated __check_region(struct resource |
7 分支预测提示
在 Linux 内核中最常用的优化技术之一是 __builtin_expect
。在开发人员使用有条件代码时,常常知道最可能执行哪个分支,而哪个分支很少执行。如果编译器知道这种预测信息,就可以围绕最可能执行的分支生成最优的代码。
如下所示,__builtin_expect
的使用方法基于两个宏 likely
和 unlikely
(见 ./linux/include/linux/compiler.h)。
#define likely(x) __builtin_expect(!!(x), 1) |
通过使用 __builtin_expect
,编译器可以做出符合提供的预测信息的指令选择决策。这使执行的代码尽可能接近实际情况。它还可以改进缓存和指令流水线。
例 如,如果一个条件标上了 “likely”,那么编译器可以把代码的 True 部分直接放在分支指令后面(这样就不需要执行分支指令)。通过分支指令访问条件结构的 False 部分,这不是最优的方式,但是访问它的可能性不大。按照这种方式,代码对于最可能出现的情况是最优的。
下面给出一个使用 likely
和 unlikely
宏的函数(见 ./linux/net/core/datagram.c)。这个函数预测 sum
变量将是零(数据包的 checksum
是有效的),而且 ip_summed
变量不等于 CHECKSUM_HW
。
unsigned int __skb_checksum_complete(struct sk_buff *skb) |
8 预抓取
另一种重要的性能改进方法是把必需的数据缓存在接近处理器的地方。缓存可以显著减少访问数据花费的时间。大多数现代处理器都有三类内存:
- 一级缓存通常支持单周期访问
- 二级缓存支持两周期访问
- 系统内存支持更长的访问时间
为了尽可能减少访问延时并由此提高性能,最好把数据放在最近的内存中。手工执行这个任务称为预抓取。GCC 通过内置函数 __builtin_prefetch
支持数据的手工预抓取。在需要数据之前,使用这个函数把数据放到缓存中。如下所示,__builtin_prefetch
函数接收三个参数:
- 数据的地址
rw
参数,使用它指明预抓取数据是为了执行读操作,还是执行写操作locality
参数,使用它指定在使用数据之后数据应该留在缓存中,还是应该清除
void __builtin_prefetch( const void *addr, int rw, int locality ); |
Linux 内核经常使用预抓取。通常是通过宏和包装器函数使用预抓取。下面是一个辅助函数示例,它使用内置函数的包装器(见 ./linux/include/linux/prefetch.h)。这个函数为流操作实现预抓取机制。使用这个函数通常可以减少缓存缺失和停顿,从而 提高性能。
#ifndef ARCH_HAS_PREFETCH |
10 变量属性
除了本文前面讨论的函数属性之外,GCC 还为变量和类型定义提供了属性。最重要的属性之一是 aligned
属性,它用于在内存中实现对象对齐。除了对于性能很重要之外,某些设备或硬件配置也需要对象对齐。aligned
属性有一个参数,它指定所需的对齐类型。
下面的示例用于软件暂停(见 ./linux/arch/i386/mm/init.c)。在需要页面对齐时,定义 PAGE_SIZE
对象。
char __nosavedata swsusp_pg_dir[PAGE_SIZE] |
packed
属性打包一个结构的元素,从而尽可能减少它们占用的空间。这意味着,如果定义一个char
变量,它占用的空间不会超过一字节(8 位)。位字段压缩为一位,而不会占用更多存储空间。- 这段源代码使用一个
__attribute__
声明进行优化,它用逗号分隔的列表定义多个属性。
static struct swsusp_header { |
从linux内核中学到的编程技巧 【转】的更多相关文章
- [转]透过 Linux 内核看无锁编程
非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...
- linux内核学习推荐书籍
<UNIX环境高级编程>,推荐指数:★★★★★ <UNIX环境高级编程>是 Unix/ Linux 程序员案头必备的一本书籍.可以说,Linux 程序员如果没有读过这本书,就好 ...
- Linux内核剖析 之 进程简单介绍
1.概念 1.1 什么是进程? 进程是程序运行的一个实例.能够看作充分描写叙述程序已经运行到何种程度的数据结构的汇集. 从内核观点看.进程的目的就是担当分配系统资源(CPU时间,内存 ...
- 2018-2019-1 20189215《Linux内核原理与分析》第二周作业
本周学习了<庖丁解牛>第1章,以及<Linux内核设计与实现>第1.2.18章.通过视频和实验,学会了反汇编一个简单的C程序,也学习了Linux内核调试的一些小技巧和print ...
- Linux内核编程、调试技巧小集
1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时可能比较复杂不知道具体使用哪一个函数.这是可以通过lookup_symbol_name来获取符号表名称 ...
- Linux内核编程、调试技巧小集【转】
转自:https://www.cnblogs.com/arnoldlu/p/7152488.html 1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时 ...
- [转] LINUX内核代码编程规范
这是一个简短的文档,描述了linux内核的首选代码风格.代码风格是因人而异的,而且我 不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格, 并且我也希望绝大多数其他代码也能 ...
- 初探linux内核编程,参数传递以及模块间函数调用
一.前言 我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间 ...
- linux内核编程笔记【原创】
以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...
随机推荐
- Windows版本搭建安装React Native环境配置及相关问题
此文档整理参考地址: http://www.lcode.org/%E5%8F%B2%E4%B8%8A%E6%9C%80%E8%AF%A6%E7%BB%86windows%E7%89%88%E6%9C% ...
- DataTable无法使用AsEnumerable ()的解决办法
本人定义了DataSet后将表1赋给datatable,在写linq时调用datatable.asenumerable(),但报datatable不包含asenumerable的定义,求高手指点.Sy ...
- LeetCode201 Bitwise AND of Numbers Range Java 题解
题目: Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all num ...
- innodb结构解析工具---innodb_ruby
1.下载ruby并安装ruby: ftp://ftp.ruby-lang.org/pub/ruby/ ftp://ftp.ruby-lang.org/pub/ruby/ruby-2.3-stable. ...
- [转] Android学习系列(29)--App调试的几个命令实践
在Android的应用开发中,我们会用到各种代码调试:其实在Android的开发之后,我们可能会碰到一些随机的问题,如cpu过高,内存泄露等,我们无法简单的进行代码调试,我们需要一个系统日志等等,下面 ...
- Java基础知识强化02:import static 和 import
1.import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com.....ClassName;而静态导入是这样:import static com..... ...
- media query
accepted Another useful media feature is device-aspect-ratio. Note that the iPhone 5 does not have a ...
- Linux 关闭及重启方式
一.shutdown 命令 作用:关闭或重启系统 使用权限:超级管理员使用 常用选项 1. -r 关机后立即重启 2. -h关机后不重启 3. -f快速关机,重启时跳过fsck(file system ...
- RMQ 与 LCA-ST算法
RMQ算法 区间求最值的算法,用区间动态规划(nlogn)预处理,查询O(1) http://blog.csdn.net/y990041769/article/details/38405063 (PO ...
- mysql修改root密码的方法
方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password('新密码'); 例子:my ...