显示标签为“c++”的博文。显示所有博文
显示标签为“c++”的博文。显示所有博文

2013年10月30日星期三

[转] offsetof(TYPE, MEMBER)

offsetof(TYPE, MEMBER)

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://cutebunny.blog.51cto.com/301216/67517
offsetof(TYPE, MEMBER)
该宏在Linux内核代码(版本2.6.22)中定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER);
 
分析:
(TYPE *)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么 p->MEMBER 就是 MEMBER 这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转换为size_t型的就OK了,size_t其实也就是int。
typedef __kernel_size_t  size_t;
typedef unsigned int __kernel_size_t;   
 
可见,该宏的作用就是求出MEMBER在TYPE中的偏移量。
本文出自 “bunny技术坊” 博客,请务必保留此出处http://cutebunny.blog.51cto.com/301216/67517

2013年4月16日星期二

[分享] vi 实现高亮 C 语言常用 API

vi 是 Uninx 哲学的代表,可配置,可扩展,小巧灵活,将本职做到极致.
vi 的默认配置通常放在 /usr/share/vim 下,如果想要定制个性化的设置,建议在自家的工作目录下覆盖系统的配置,尽量不要修改系统的东西.系统已经提供了良好的覆盖思想来满足不同的需求了.

vi 对 c 源文件有天然的支持,能高亮数据类型和关键字,但对于常用的函数并没有实现高亮,对常常编写c 程序的同学来说,并不十分友好.好在我们可定制可扩展.

$ mkdir -p  ~/.vim/syntax
$ cd  ~/.vim/syntax
$ curl "https://raw.github.com/hy0kl/profile/master/.vim/syntax/c.vim" > c.vim

没错,就这么简单,如此可实现高亮常见的 c 语言 API.当然,前提是打开了 .vimrc 中的 syntax on.
有图有真相.


2013年4月9日星期二

日记一则

2012.06.01 星期五 多云
忙活了近兩个月的悲崔项目 cui 终于全部上线,一路跌跌撞撞,走得甚是辛苦.上线后 PM 陆继报一些样式方面的问题,无论如此,已经上线,算是对兩个月的劳动成果有一个交代.
项目上线了,不代表工作结束了.大型项目,更需要系统总结一下.我不知道别的同事会不会,或者有没有时间,或者有没有想法,来总结,反正我觉得非常有必要我自己先来一番:
简述: 和百度大搜索合作的阿拉丁导流项目,我开发的服务主要响应用户检索对音乐的需求,返回生成好的 HTML 或 XML 代码片段供百度结果在渲染页面时使用.由于百度的海量检索请求,他们要求我们的服务峰值能力达到 500q/s.我们交付上线的服务经 QA 同学压力测试,得出的性能指标为: 单机极限压力 1000q/s 时 CPU idle 在 50% 左右,基本上无法正常提供服务; 800q/s 时 CPU idle 80% 左右,服务能力正常.上线后在用户高峰时段,查看实时并发,大搜索真正过来的并发为 50q/s 左右. cui 从性能方面讲,无疑远超预期.

1. 项目名称.
项目名字一定要启好,哪怕不能响当当,不能惊心动魄,但绝不能再叫做 cui,明摆要成为一个悲崔的代表.否则,各种灵异现象,各种闻所末闻,见所末见的悲崔会不断给你带来意外的收获,不停的放大你发现问题,定位问题,解决问题的能力.

2. 技术选型.
很重要.一直在讲,没有最好的技术,只有更适合的场景.最开始,我们考虑要不要用 php 来实现,后来了解到大搜索那边使用的是 nshead + mc_pack 方式的网络通信,归根结底是 socket 通信,考虑到 php  的性能问题,以及以它来实现网络编程的不稳定性,我们最终选择了 C.后来的事实证明,我们当时的决择是英明的.如果当时采用 php 或 Python,八成要在性能那块卡死,那真要悲崔到底了.

3. 指针与内存.
C 语言玩的是指针,玩的是内存的分配与使用.第一版实现中,为了图快捷, cui 大量的栈上开辟临时缓冲区,几十K或几百K的开,高并发下常出现堆栈空间不足,不出 core 而服务自动退出,甚是郁闷.后继的优化中,我将能在堆上申请的内存全部放到初始化时从堆上开辟了.我们知道 malloc 系统调用是非常消耗资源的,应尽可能减少动态内存分配.所以通常我们的做法是初始化的时候一次 malloc 足够多的内存 pool,放着慢慢用. 实践证明,cui 的优化过程就是一个活生生的教科书式的案例.通过将临时缓存区放到线程空间,在程序启动的初始过程,为它他申请好足够的内存,一次 malloc 调用,重复利用,效果相当明显.nginx 大量使用了内存池技术, python 内存大户,也从侧面反映了其内存分配与管理的思想.相比之下 php 在内存使用方面受自身架构的影响,在一定程度制约了其性能.

4. 初始化的学问.
我们曾有多个项目遭遇过 memset 的性能问题.大块内存使用它来清 0 时候会耗用超乎想象的时间.大量的实战表明,对于字符型缓存区,如果已经确认对它进行操作的 API 会自行加 ’\0’,那没有必要全部清 0,初始化的时候仅清 0 首字节就可以了.对于数组型的大块内存,给它一个记数器,标记每次处理时使用多少个 item,初始化时也不用每次都整块内存清 0 了,仅清 0 必须的成员.读的时候判断一下记数器,万事大吉.

5. 超时.
再 NB 的后端服务也得设置合适的超时时间.在酷讯时候,有一次太自信后端服务了,将超时设置为永不超时,刚上线感觉良好,片刻之后,后端服务跨掉了.大搜索只给了 200 毫秒供我们处理一次用户请求,起先联调时发现丢包现象严重.联合排查后发现是由于响应用户请求时间过长,大量超过时的链接,大搜索认为 cui 服务不可用,主动关闭了链接.经过地毯式的排查,确定主要有兩个因素引起了处理时间过长:1).API 使用不正确,错误的将连接,读和写的超时设置成近乎永不超时;2) cui 和它的后端数据服务之间的超时也设置不当,造成了甚至花了 900 多毫秒才处理完一个请求的情况.这对于一个需要应对高并发的服务来讲简直是笑谈.再次敲响了超时不当的警钟:只要用网络交互,一定要设置适当的超时.

6. cache.
cache 绝对是高性能的必杀器,无论前端后端,加一层 cache,性能提升立竿见影. cui 最初设计的时候没有 cache 层,几轮压力测试下来发现,如果不加缓存想要扛到 500q/s 相当的吃力.后继的优化过程中加入 cache 机制,效果非常理想.拖延近一周的时间,全部精力都在寻找丢包与性能提升上面.虽然最终确定丢包一个非常重要的原因是由虚拟机本身造成的,但优化过程对服务的稳定性给予了有力的支持.

7. check list.
我觉得项目最大的败笔就是没有建立良好的 check list.原因很简单,大家都很忙.从一开始,我们的很多需求,策略,注意事项等等,都散列在各种各样的文档,以及讨论区中,从来没有人提出整理,或主动整理出供大家参考,抑或使用项目 WIKI 这样工具来大家协同工作,共同维护 check list.如此,上线了, PM 们开始陆继发现问题了.缀使 QA 同学再怎么给力捉虫,线上问题也无力回天.于是乎各种紧急修复,紧急流程纷纷上演.紧急流程通常是一个个悲剧的续幕,而更为悲崔的是,一个悲剧总会引发一连串的悲剧,蝴蝶效应真实上映,却没有彩排的机会.

项目总会有,成长是关键.

2013年4月8日星期一

一个逗号引发的血案


一个 C 模块,单模块压力没有出任何问题,链系统一压力,稳定复现不出 core 挂掉.
挂在 gdb 下压力后发现挂掉时的一条信息: Program received signal SIGPIPE, Broken pipe.
谷歌了一番,发现是 SIGPIPE 信号没有屏蔽掉.反复查看代码, main 函数有 signalsetup(); 一行,显示有屏蔽函数,可总是无法忽略 kill -SIGPIPE .怀疑原来的屏蔽方法有问题,又从网上找了段大家一致认为靠谱的方法:

    struct sigaction sa;
    
sa.sa_handler SIG_IGN;//设定接受到指定信号后的动作为忽略
    
sa.sa_flags   0;
    if (
sigemptyset(&sa.sa_mask) == -||   //初始化信号集为空
            
sigaction(SIGPIPE, &sa0) == -1)
    {   
//屏蔽SIGPIPE信号
        
fprintf(stderr"failed to ignore SIGPIPE; sigaction\n");
        exit(
EXIT_FAILURE);
    }



结果也不行.于是把代码单独拿出来编译来执行,却发现可以实现信号屏蔽.
求助同事,他建议将屏蔽信号函数的调用往后放置,排除非一些初始化操作的干扰.将 signalsetup(); 这一行后置编译时发现语法错误,编译不过,原来错将一个 ; 打做 ,了:

400     pthread_t *pt_server_core, pt_server_run,
401
402     signalsetup();

就这样,一个逗号让我折腾了大半天,引发一场惨烈的血案,血的教训.
编译器版本: gcc version 3.4.4 20050721 (Red Hat 3.4.4-2)
据朋友说,新版本的 gcc 能检测到如此低级的错误.我用老版本在编译时却没有给出相应的警告.