惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
D
Docker
Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
WordPress大学
WordPress大学
IT之家
IT之家
H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
B
Blog
D
DataBreaches.Net

博客园 - 有安科技

好消息,OpenResty Manager专业版来了,如何免费升级? NanoPi R3S PVE 8.X 优化 鹊桥-零信任安全网格网络 森罗-攻击面管理平台 天机-免费的新一代办公安全平台 南墙WAF-最好的免费Web应用防火墙 Elastic Stack 7.4.0~7.5.0 白金版永不过期 JVM CPU占满问题定位 SQL2008数据库优化常用脚本 nginx url解码引发的waf漏洞 http长连接200万尝试及调优 发个在owasp上演讲web应用防火墙的ppt linux程序调试 Using Internet Explorer from .NET scrapy结合webkit抓取js生成的页面 快速构建实时抓取集群 Safe3 Web漏洞扫描系统 v9.6免费版 Varnish+Xcache构建高性能WEB构架初探 IIS监控请求脚本
Tips of Linux C programming
有安科技 · 2013-04-09 · via 博客园 - 有安科技

转自http://rdc.taobao.com/blog/cs/?p=1675

1. 优雅地使用链表

        链表是编程中经常要用到的数据结构,结构体描述时分为数据域和指针域,本没有什么好讲。但有没有想过教科书上的这种方式有什么问题?通过这种方式定义和使用链表,对于不同的链表类型,都要定义各自的链表结构,繁琐的很。linux kernel中链表的用法才应该是教科书中出现的。

        基本思想:在Linux内核链表中,不是在链表结构中包含数据,而是在数据结构中包含链表节点。

1) 链表定义:

struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }

2) 链表使用者定义:

struct user_t {
data domain;
struct list_head node;
};
struct list_head g_user_list = LIST_HEAD_INIT(g_user_list);

3) 通过node定位user_t:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({
const typeof(((type *)0)->member) * __mptr = (ptr);
(type *)((char *)__mptr - offsetof(type, member)); })
struct user_t* next = container_of(&(g_user_list.next->node), struct user_t, node);

这里用到了container_of,container_of又用到了offsetof。offsetof是通过将结构体起始地址强制对齐到0来计算出node和起始地址的偏移offset;而container_of在node地址基础上减去offset得到user_t结构体的地址并返回user_t。

2. 高效地分支判断

        写程序不可避免的需要使用if/else,如何高效地进行分支判断呢?使用likely/unlikely。

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
long __builtin_expect (long EXP, long C)

        __builtin_expect是一个GCC内置的函数,语义是表示EXP == C,返回值是表达式的值。这两个宏定义是为了提示编译器正确地进行分支判断的优化。

        优化的原理是:通过调整生成汇编代码的顺序,将经常发生的分支代码放在cmp指令之后顺序执行,将不经常发生的分支代码通过jump指令跳过去,从而降低jump指令清空处理器流水线的影响。

3. 汇编实现的原子操作

__asm__ __volatile__(
" lock ;n"
" addl %1,%0 ;n"
: "=m" (my_var) //output
: "ir" (my_int), "m" (my_var) //input
: //modify /* no clobber-list */
);

        这条汇编表示给my_var原子加my_int,lock前缀就是用来保证原子性的。

        Intel CPU有3种保证原子性的方式,lock前缀是其中之一,原理是通过锁总线来阻止其它处理器操作相应的地址。

4. 0长度数组

        定义数组时,长度必须是一个编译时确定的值,如果想使用运行时的值来确定数组的长度,可以使用0长度数组。

struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

只有GNU C才支持的特性,对于定义不确定长度的数组非常有用。

5. 三目运算的另类表达

        GNU 允许C 语言省略条件表达式中的表达式2省略, 此时表示表达式2与表达式1相同.例如

等价于

但是如果 x 是一个表达式, 仅求值一次.

        也来分享一下你正在使用的tips吧!

参考文章:

http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html

http://kernelnewbies.org/FAQ/LikelyUnlikely

http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

http://zh.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E8%BF%90%E7%AE%97%E7%AC%A6