2013年2月5日 | 分类: 操作系统, 编程技术, 网络性能 | 标签:

nginx中有很多细节的地方做了性能优化,举几个例子。
例子1:
自定义字符串类型,在/src/core/config.h中,对字符串进行了封装,包含长度,和具体字符

typedef struct {
    size_t      len;
    u_char     *data;
} ngx_str_t;

typedef struct {
    ngx_str_t   key;
    ngx_str_t   value;
} ngx_keyval_t;

这样做的好处是,后续的字符操作,长度不需要再重复计算,而是直接引用,而且很多字符串string.h里面的操作差不多都重新实现了一遍。

例子2:
错误码的预定义,在main里面,刚开始会调用ngx_strerror_init,这个函数做的事情是把系统的错误码和错误信息,存在ngx_sys_errlist中,省去了出错时候再调用strerror()查找错误原因的开销,能减少系统调用就要避免。

ngx_int_t
ngx_strerror_init(void)
{
    char       *msg;
    u_char     *p;
    size_t      len;
    ngx_err_t   err;

    /*
     * ngx_strerror() is not ready to work at this stage, therefore,
     * malloc() is used and possible errors are logged using strerror().
     */
    
    //预分配NGX_SYS_NERR个字符对象,NGX_SYS_NERR在linux中是132
    len = NGX_SYS_NERR * sizeof(ngx_str_t);
    
    //分配全局错误列表空间
    ngx_sys_errlist = malloc(len);
    if (ngx_sys_errlist == NULL) {
        goto failed;
    }

    for (err = 0; err < NGX_SYS_NERR; err++) {
        //获取系统的每一个错误编号原因和长度
        msg = strerror(err);
        len = ngx_strlen(msg);

        p = malloc(len);
        if (p == NULL) {
            goto failed;
        }

        //赋值错误id和错误原因到全局列表中
        ngx_memcpy(p, msg, len);
        ngx_sys_errlist[err].len = len;
        ngx_sys_errlist[err].data = p;
    }

    return NGX_OK;

failed:

    err = errno;
    ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));

    return NGX_ERROR;
}

在这里nginx并没有使用其内存池,而是使用系统默认的malloc进行内存分配,因为在这里程序还没有创建内存池,而后续的初始化工作,可能出现未知的错误,那么,该处初始化的错误数组就可以派上用场。

例子3:
nignx的内存管理也用到了自己的内存池,但是注意到在申请内存时,有两个函数:ngx_palloc和ngx_pnalloc,两者的区别是一个进行了对齐,一个没有对齐,取整可以降低CPU读取内存的次数,提高性能。
对齐使用了ngx_align_ptr(p,a),把指针p按照a的size进行对齐。

 #define ngx_align_ptr(p, a)                            \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

总之nginx里面很多小的地方都做的很极致,希望有时间进一步发现,nginx的文章比较多,读起来比haproxy方便很多。

评论关闭
2013年2月3日 | 分类: 操作系统, 编程技术, 网络性能 | 标签:

通过源码编译和安装软件时,只需要简单的进行./configure;make;make install;就可以,但是如果按照makefile的格式要求,完全写出一个自己的makefile往往很难,幸好gnu提供了一系列的工具来帮我们快速的生成Makefile,帮我们在大项目中管理各个子模块,它们就是autoconf和automake。

autoscan是用来扫描源代码目录生成configure.scan文件的。autoscan可以用目录名做为参数,但如果你不使用参数的话,那么autoscan将认为使用的是当前目录。autoscan将扫描你所指定目录中的源文件,并创建configure.scan文件。

aclocal是一个perl 脚本程序。aclocal根据configure.in文件的内容,自动生成aclocal.m4文件。aclocal的定义是:“aclocal – create aclocal.m4 by scanning configure.ac”。

autoconf是用来产生configure文件的。configure是一个脚本,它能设置源程序来适应各种不同的操作系统平台,并且根据不同的系统来产生合适的Makefile,从而可以使你的源代码能在不同的操作系统平台上被编译出来。

我们使用automake –add-missing来产生Makefile.in。选项–add-missing的定义是“add missing standard files to package”,它会让automake加入一个标准的软件包所必须的一些文件。我们用automake产生出来的Makefile.in文件是符合GNU Makefile惯例的,接下来我们只要执行configure这个shell 脚本就可以产生合适的 Makefile 文件了。

整体流程是:
makefile
每个文件的介绍:

  • configure.scan:通过autoscan命令来帮助我们根据目录下的源代码生成一个configure.scan的模板文件
  • configure.in:修改scan文件,内容是一些宏定义,配合autoconf能够生成configure文件
  • aclocal.m4:要生成configure文件,你必须告诉autoconf如何找到你所用的宏。方式是使用aclocal程序来生成你的aclocal.m4
  • configure:经autoconf处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本,用来检查环境并生成Makefile
  • Makefile.am:是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成,用来生成Makefile.in
  • Makefile.in:由Makefile.am生成的一些基本的定义,符合GNU的规范,方便configure根据平台不同产生不同的Makefile文件
  • Makefile:该文件用来描述源程序之间的相互关系并自动维护编译工作和后续的安装卸载等

因此整个makefile生成过程如下:
首先我们有一个helloworld的c程序:

#include <stdio.h>
int main(int argc, char** argv){
printf("%s", "Hello,World!\n");
return 0;
}

1.使用autoscan命令来帮助我们根据目录下的源代码生成一个configure.scan的 模板文件
2.将configure.scan 文件重命名为configure.in,并修改configure.in文件内容:

# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT(Makefile)

3.然后执行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件

[kongjian@v132172.sqa.cm4 helloworld]$ ls
configure.in  helloworld.c
[kongjian@v132172.sqa.cm4 helloworld]$ aclocal
[kongjian@v132172.sqa.cm4 helloworld]$ ls
aclocal.m4  autom4te.cache  configure.in  helloworld.c
[kongjian@v132172.sqa.cm4 helloworld]$ autoconf
[kongjian@v132172.sqa.cm4 helloworld]$ ls
aclocal.m4  autom4te.cache  configure  configure.in  helloworld.c

4.新建Makefile.am文件,内容如下:

AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c

automake会根据你写的 Makefile.am来自动生成Makefile.in,Makefile.am中定义的宏和目标,会指导automake生成指定的代码.例如,宏bin_PROGRAMS指定编译和连接的目标
5.automake会根据Makefile.am文件产生一些文件,包含最重要的 Makefile.in
6.至此,configure已经生成,可以按照./configure;make;make install;的顺序编译安装了。

[kongjian@v132172.sqa.cm4 helloworld]$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
[kongjian@v132172.sqa.cm4 helloworld]$ make
gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"helloworld\" -DVERSION=\"1.0\" -I.     -g -O2 -MT helloworld.o -MD -MP -MF .deps/helloworld.Tpo -c -o helloworld.o helloworld.c
mv -f .deps/helloworld.Tpo .deps/helloworld.Po
gcc  -g -O2   -o helloworld helloworld.o
[kongjian@v132172.sqa.cm4 helloworld]$ ./helloworld
Hello,World!
评论关闭
2013年1月31日 | 分类: 操作系统, 编程技术 | 标签:

看nginx的代码时,发现有些函数返回值之后,还有一个ngx_cdecl关键字,如:

int ngx_cdecl
main(int argc, char *const *argv)
{
    ngx_int_t         i;
    ngx_log_t        *log;
    ngx_cycle_t      *cycle, init_cycle;
    ngx_core_conf_t  *ccf;

查看其定义处发现竟然是个空:

#define ngx_cdecl

那么这个宏的作用是什么呢?如果了解函数调用的规则,则对这个宏基本上就能理解了:

在C语言中,假设我们有这样的一个函数:int function(int a,int b),调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。不同CPU,不同编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,例如 :

调用方法 参数传递顺序 谁负责清理参数占用的堆栈
__pascal 从左到右 调用者
__stdcall 从右到左 被调函数
__cdecl 从右到左 调用者

调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。
如前表所示,__cdecl和__stdcall的区别是:__cdecl是调用者清理参数占用的堆栈,__stdcall是被调函数清理参数占用的堆栈。假设函数A是__stdcall,函数B调用函数A。你必须通过函数声明告诉编译器,函数A是__stdcall。编译器自然会产生正确的调用代码。如果函数A是__stdcall。但在引用函数A的地方,你却告诉编译器,函数A是__cdecl方式,编译器产生__cdecl方式的代码,与函数A的调用约定不一致,就会发生错误。

由于__stdcall的被调函数在编译时就必须知道传入参数的准确数目(被调函数要清理堆栈),所以不能支持变参数函数,例如printf。而且如果调用者使用了不正确的参数数目,会导致堆栈错误。

因此,nginx中使用这个宏是为了跨平台支持,方便调整函数调用方式。在遇到问题时,可以修改上面的定义为:

#define ngx_cdecl stdcall

快速定位问题

评论关闭
2013年1月31日 | 分类: 操作系统, 编程技术 | 标签:

nginx中对整型做了一次重新定义:

typedef intptr_t        ngx_int_t;
typedef uintptr_t       ngx_uint_t;
typedef intptr_t        ngx_flag_t;

至于intptr_t,uintptr_t,intptr_t在头文件/usr/include/stdint.h 中:

/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int                intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned long int       uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int                     intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned int            uintptr_t;
#endif

可以看到注释是: Types for `void *’ pointers. 那么这是什么含义呢?

我们知道所谓的指针,其实是内存的地址,而内容是该地址内存中实际存储的数据。void *是指无类型指针,当需要进行转化时,这个类型可以被安全的在 void * 和整数间转换,而不会出现由于指针和整型的字宽不同导致的问题。

C语言中,指针和整数之间的转换经常用到(多用于需要精确控制数据在内存中的精确布局时),在32位平台上,由于指针类型的字宽和int相同,所以我们不太在意这个问题。但是到了 64 位平台上,由于目前几乎所有64位系统都采用LP64模型,既整数依旧是32位,而指针是64位的。intptr_t 这个数据类型就成了安全跨平台编程的保证。也就是说,当你需要把指针作为一个整数来运算时,转换成 intptr_t才是安全的,可以在运算完毕安全的转回指针类型。

评论关闭
2013年1月31日 | 分类: 生活点滴 | 标签:

pm
北京的天一如既往的阴霾,放眼望去到处都是雾蒙蒙的,蓝天白云已然是奢望,难道这就是要一直生活的地方?空气PM值从来没低于3位数,以前美国大使馆发布pm值总比国内环境质量检测中心发布的要严重,如今看看这天算是验证了,问题已经很严重,只是从来没有触及到统治者的利益,从来不关心,即便昨天环保部说要加大治理,但是从字面看只能让人失望:“欧美等发达国家30—50年才基本解决大气污染问题。我们要正确对待当前的大气污染形势,做好打持久战的思想准备”,有一句话挺好的,正视我们最好的镜子,往往是别人;而正视缺点最大的障碍,往往是自己。看看日复一日的形式主义,官僚主义就知道在中国除了利益,其它的事情都不会有人给你好好办的,还记得动车事件吗?那么大张旗鼓的彻查到底,到现在有任何交代吗,大众只会在段时间内关心一个事件,但是每次都被打太极的方式推脱的无影无踪,要么是临时工,要么是工作疏忽,再大不了来个党内严重警告,问题解决了吗。只有富人的权利才能受到保障,穷人永远是草芥,吸尘器。
在这种大环境中生存,只能希望自己和家人身体健康,有生之年没有大的动乱,也不奢望能有投票权,我们只是想有干净的空气,健康的食物,生命和财产不受侵犯,有这么难吗?
让一切不合理,不公平的事情来得更猛烈吧,这只会加剧变革和坚定我逃离的信念。

2013年1月16日 | 分类: 操作系统, 编程技术 | 标签:

tsar是淘宝的系统采集软件,用来采集基础信息和应用信息,2011已经开源:http://tsar.taobao.org/
放出去这么久也没什么大的进展,主要是内部和外部版本没有统一,断断续续的更新内部版本了,因此又做了一次merge,放在了github上,希望有兴趣的人可以提供建议和代码:https://github.com/kongjian/tsar
用法一般是:

查看应用swift的数据
[root@cache163 ~]# tsar --swift
Time           ------------------------------swift----------------------------- 
Time              qps      rt   r_hit   b_hit    objs   in_bw  out_bw     cpu   
15/01/13-18:20  10.3K    6.16   96.80   95.80  136.2M    5.8M  219.1M  182.02   
15/01/13-18:25  10.4K    6.27   96.80   95.89  136.2M    5.9M  221.4M  184.94   
15/01/13-18:30  10.6K    5.93   96.80   95.89  136.2M    6.0M  223.7M  187.60   
15/01/13-18:35  10.8K    6.48   96.80   95.89  136.2M    6.1M  227.6M  191.53   
15/01/13-18:40  11.1K    6.46   96.80   95.80  136.2M    6.3M  232.1M  198.98   

实时查看tcp的使用情况
[root@cache163 ~]# tsar --tcp -l -i 1      
Time           -------------------tcp------------------ 
Time           active  pasive    iseg  outseg  retran   
16/01/13-18:25  56.00    4.8K  184.1K  129.4K    7.74   
16/01/13-18:25  72.00    4.7K  181.1K  127.7K    7.90   
16/01/13-18:25 101.00    4.6K  181.5K  127.5K    7.54   
16/01/13-18:25  85.00    4.6K  184.5K  127.9K    7.80   

查看最近一次的采集数据,这是是给淘宝内部做监控用的,一般是报警用的,如nagios
[root@cache163 ~]# tsar -check
cache163.xxx  tsar     cpu=33.54 mem=85.40% load1=5.79 load5=6.15 load15=6.02 
sda=15.63 sdb=16.56 sdc=16.53 sdd=16.24 sde=16.68 sdf=16.88 sdg=16.55 sdh=16.73
ifin=244992.03 ifout=458732.15 TCPretr=7.92 df/=13.91% df/boot=18.44% 
df/var=20.29% df/swift=69.79% df/cache1=100.00% df/cache2=100.00% 
df/cache3=100.00% df/cache4=100.00% df/cache5=100.00% df/cache6=100.00% 
df/cache7=100.00% df/cache8=100.00%
评论关闭