存档在 ‘Linux编程问题’ 分类

CMake for Linux

2016年4月12日

CMake是一个跨平台的构建系统生成工具。它使用平台无关的CMake清单文件CMakeLists.txt,指定工程的构建过程;源码树的每个路径下都有这个文件。CMake产生一个适用于具体平台的构建系统,用户使用这个系统构建自己的工程。

一个工程或项目作管理时,咱们在Linux/unix或ELinux下采用Makefile,CodeBlocks,KDevelop, Eclipse,scons等管理,在Windows下则使用VS。他们可以明确哪些目录的那些文件需要被编译,以及编译成什么目标文件。

以最熟悉的Makefile为例,它明确说明了需要编译什么(应用程序,动态库,静态库),由什么编译器产生,需要的头文件和库文件目录,依赖的库文件等等。 但当项目足够庞大,这个维护工作就非常复杂。

于是就有人想到采用一个Makefile的生成工具,这其实就是Autotool和socos的角色。只需要告知依赖关系,他们就可以生成Makefile。 可以说,他们就是Makefile的生成器,极大地简化了手工维护Makefile的工作。

CMake则更进一步,它不光能够为Linux平台生成Makefile,还做到了跨平台,在Linux/Unix平台,它可以生成Makefile,还可以为Codeblocks,Eclipse等工具生成工程文件。 在Windows平台,可以生成vs工程文件,在iOS下,也可以生成xcode工程。

基础学习:

以V4L2_Utils模块作为例子学习.
V4L2_Utils模块在Linux 下包含2个.cpp文件,他们将用来编译成动态库,然后被另一个测试程序main.cpp调用。

  • 1.1: 把所有.cpp文件编译成一个可执行程序。
  • 这个例子,将不再建立动态或静态库,而是把main.cpp和另外两个cpp文件一起直接编译成一个可执行程序。
    CMakeLists.txt 内容如下:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    add_executable(V4L2_Utils main.cpp  v4l2_util.cpp  tran_data.cpp)
    

    讲解如下:
    a. cmake_minimum_required 设置一个工程所需要的最低CMake版本,如果CMake的当前版本低于指定的版本,它会停止处理工程文件,并报告错误
    cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]] [FATAL_ERROR])
    b. project (V4L2_Utils):表明工程名称。
    project (PROJECTNAME [CXX] [C] [Java])
    可以指定工程支持的语言,如果忽略,表明支持所有语言。
    c. add_executable(V4L2_Utils main.cpp v4l2_util.cpp tran_data.cpp)
    生成可执行文件:V4L2_Utils. 由main.cpp v4l2_util.cpp tran_data.cpp生成。
    add_executable([WIN32] [MACOSX_BUNDLE]
    [EXCLUDE_FROM_ALL]
    source1 source2 … sourceN)
    引入一个名为的可执行目标,该目标会由调用该命令时在源文件列表中指定的源文件来构建。对应于逻辑目标名字,并且在工程范围内必须是全局唯一的

    构建Unix/Linux标准Makefile
    在src目录下,创建build目录。进入build目录
    #mkdir build
    #cd build
    #cmake .. -G”Unix Makefiles”
    此时,声称标准Makefile。
    #make clean;make


    cmake语法如下:

    cmake [options]
    cmake [options]
    cmake [选项] <源码路径>
    cmake [选项] <现有构建路径>
    

    因为source 目录在../ 所以指定../为源码目录。
    -G:指定构建系统,当前只想创建Linux/Unix系统标准Makefile。 所以指定为Unix Makefile.
    具体支持何种构建系统:man cmake
    可以看到类似入下内容:

    Generators
    The following generators are available on this platform:
      Ninja                       = Generates build.ninja files (experimental).
      Unix Makefiles              = Generates standard UNIX makefiles.
      CodeBlocks - Ninja          = Generates CodeBlocks project files.
      CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
      Eclipse CDT4 - Ninja        = Generates Eclipse CDT 4.0 project files.
      Eclipse CDT4 - Unix Makefiles
                                  = Generates Eclipse CDT 4.0 project files.
      KDevelop3                   = Generates KDevelop 3 project files.
      KDevelop3 - Unix Makefiles  = Generates KDevelop 3 project files.
    

    这个CMakeLists.txt 内容中,构成可执行文件的文件个数很少,但如果源文件很多,则最好如Makefile中一样,指定一个源文件列表:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (SRC_LIST main.cpp v4l2_util.cpp tran_data.cpp)
    message (${SRC_LIST})
    add_executable(V4L2_Utils ${SRC_LIST})
  • 1.2: 编译一个静态库,并使用这个静态库和main.cpp,最终再生成一个可执行程序:
  • 即先生成libv4l2_utils.a.
    main.cpp使用libv4l2_utils.a中的符号并最终生成V4L2_Utils.
    CMakeLists.txt:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp tran_data.cpp)
    set (EXEC_SRC_LIST main.cpp)
    add_library(V4L2_Utils STATIC  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)
    

    说明:

     add_library( [STATIC | SHARED | MODULE]
                  [EXCLUDE_FROM_ALL]
                  source1 source2 ... sourceN)

    创建一个名为 name 的库文件,STATIC, SHARED指定为静态或动态库。
    库文件由源文件列表生成。
    add_library(V4L2_Utils STATIC ${LIB_SRC_LIST})
    生成一个静态库libV4l2_Utils.a 由v4l2_util.cpp tran_data.cpp生成
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    可执行程序由哪些.o组成。
    target_link_libraries(Test_V4L2 V4L2_Utils)
    可执行程序Test_V4L2需要链接库V4L2_Utils
    target_link_libraries( [item1 [item2 […]]] [[debug|optimized|general] ] …)
    将给定的库链接到目标target上。

  • 1.3: 编译一个动态库,并使用这个动态和main.cpp,最终再生成一个可执行程序:
  • cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (EXEC_SRC_LIST main.cpp)
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    只变化了STATIC–>SHARED

  • 1.4: 指定include 路径:
  • main.cpp中,需要include “v4l2_util.h”,但这个头文件并不在src目录内,而是在include目录,include, src为同级别目录。则main.cpp会找不到头文件。所以,实际的CMakeLists.txt文件写法为:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    include_directories(${INCLUDE_DIRECTORIES})
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    将头文件目录添加进去。

  • 1.5: 添加第三方库和库路径:
  • 如果libV4L2.so是以第三方库形式出现。不需要编译,它放在resource目录下。则需要制定搜索库路径

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    set (LINK_DIR ../../resource)
    include_directories(${INCLUDE_DIRECTORIES})
    link_directories(${LINK_DIR})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)

    说明:
    link_directories 指定连接器查找库的路径。
    link_directories(directory1 directory2 …)

  • 1.6:创建Release和Debug版本:
  • #set (CMAKE_BUILD_TYPE Release)
    set (CMAKE_BUILD_TYPE Debug)
    分别指定为Release或者Debug模式。区别在于:
    Release: -O3 -DNDEBUG
    Debug:-g
    也可以不加在txt内。在产生Makefile时才加入:
    cmake -DCMAKE_BUILD_TYPE=Release

  • 1.7: 增加编译和链接选项:
  • CMAKE_C_FLAGS
    CMAKE_CXX_FLAGS
    CMAKE_EXE_LINKER_FLAGS
    分别相当于:CFLAGS, CXXFLAGS, LDFLAGS。
    set (CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -DEMBED”)
    这种写法的好处是,不会覆盖CMAKE_CXX_FLAGS本来的信息。只是把需要添加的内容添加进去。

  • 1.8: 多目录结构的cmake 使用:
  • V4L2_Utils实际的目录结构其实并不是所有源码都存放在src目录内。
    它的目录结构是:
    src: 存放生成库的源码。v4l2_util.cpp, tran_data.cpp
    test:存放使用库的测试程序: main.cpp
    include: 存放头文件: v4l2_util.h
    resource: 存放第三方库
    build:存放编译过程的文件
    build/lib: 存放生成的libv4l2_utils.so
    build/bin:存放main.cpp所产生的测试程序可执行文件。
    此时,可以采用顶层目录和每个有源码的目录中均创建CMakeLists.txt的方式来处理(和Makefile处理方式类似)
    顶层目录的CMakeLists.txt 内容如下:

    cmake_minimum_required (VERSION 2.6)
    project (V4L2_Utils)
    add_subdirectory(src lib)
    add_subdirectory(test bin)

    说明:
    add_subdirectory(source_dir [binary_dir]
    [EXCLUDE_FROM_ALL])
    构建添加一个子路径。source_dir选项指定了CMakeLists.txt源文件和代码文件的位置。如果source_dir是一个相对路径,那么source_dir选项会被解释为相对于当前的目录,但是它也可以是一个绝对路径。binary_dir选项指定了输出文件的路径。如果binary_dir是相对路径,它将会被解释为相对于当前输出路径。
    请注意两个相对路径的不同。因为Sam是在Build目录内执行cmake ../
    所以src指的是当前 CMakeLists.txt所在路径下的src. 而bin, lib 指的是当前输出路径下的bin,lib. 也就是build/bin build/lib

    src CMakeLists.txt :
    set (LIB_SRC_LIST  v4l2_util.cpp)
    set (CMAKE_BUILD_TYPE Release)
    include_directories(../include)
    add_library(V4L2_Utils SHARED  ${LIB_SRC_LIST})
    test CMakeLists.txt :
    set (EXEC_SRC_LIST main.cpp)
    set (INCLUDE_DIRECTORIES ../include)
    set (LINK_DIR ../../resource)
    set (LINK_DIR "${LINK_DIR} ../../libs/")
    set (CMAKE_BUILD_TYPE Release)
    include_directories(${INCLUDE_DIRECTORIES})
    link_directories(${LINK_DIR})
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    target_link_libraries(Test_V4L2 V4L2_Utils)
    

    这个做法一直不满意,其实有更好的做法
    总结:
    有了以下这些选项,写过Makefile的人可以很容易的使用CMake了。
    生成可执行程序:
    add_executable(Test_V4L2 ${EXEC_SRC_LIST})
    生成静态库
    add_library(V4L2_Utils STATIC ${LIB_SRC_LIST})
    生成动态库:
    add_library(V4L2_Utils SHARED ${LIB_SRC_LIST})
    指定头文件路径:  -L
    include_directories(${INCLUDE_DIRECTORIES})
    指定库文件路径: -L
    link_directories(${LINK_DIR})
    指定链接库: -l
    target_link_libraries(Test_V4L2 V4L2_Utils)
    CFLAGS,CXXFLAGS,LDFLAGS:
    CMAKE_C_FLAGS,CMAKE_CXX_FLAGS,CMAKE_EXE_LINKER_FLAGS

    注1:set 和message
    set用来指定变量
    如:
    set (SRC_LIST main.cpp)
    则变量SRC_LIST内容为main.cpp
    ${SRC_LIST} 则为取变量内容(与Bash类似)
    message:用来显示变量
    message (${SRC_LIST})

    注2:
    如何显示编译细节:
    方法1:
    在CMakeLists.txt中,
    set(CMAKE_VERBOSE_MAKEFILE ON)

    方法2:
    有时不希望修改CMakeLists.txt文件,则可以在创建Makefile时加入:
    cmake -DCMAKE_VERBOSE_MAKEFILE=ON

    方法3:
    有时连Makefile都不希望修改:
    make VERBOSE=1

    unp《unix高级环境编程》书中的err_sys和err_quit等函数实现

    2015年3月10日
    #include <errno.h> /* for definition of errno */
    #include <stdarg.h> /* ISO C variable aruments */
    
    static void err_doit(int, int, const char *, va_list);
    
    /*
     * Nonfatal error related to a system call.
     * Print a message and return.
     */
    void
    err_ret(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
    }
    
    
    /*
     * Fatal error related to a system call.
     * Print a message and terminate.
     */
    void
    err_sys(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Fatal error unrelated to a system call.
     * Error code passed as explict parameter.
     * Print a message and terminate.
     */
    void
    err_exit(int error, const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, error, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Fatal error related to a system call.
     * Print a message, dump core, and terminate.
     */
    void
    err_dump(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(1, errno, fmt, ap);
        va_end(ap);
        abort(); /* dump core and terminate */
        exit(1); /* shouldn't get here */
    }
    
    
    /*
     * Nonfatal error unrelated to a system call.
     * Print a message and return.
     */
    void
    err_msg(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(0, 0, fmt, ap);
        va_end(ap);
    }
    
    
    /*
     * Fatal error unrelated to a system call.
     * Print a message and terminate.
     */
    void
    err_quit(const char *fmt, ...)
    {
        va_list ap;
    
        va_start(ap, fmt);
        err_doit(0, 0, fmt, ap);
        va_end(ap);
        exit(1);
    }
    
    
    /*
     * Print a message and return to caller.
     * Caller specifies "errnoflag".
     */
    static void
    err_doit(int errnoflag, int error, const char *fmt, va_list ap)
    {
        char buf[MAXLINE];
       vsnprintf(buf, MAXLINE, fmt, ap);
       if (errnoflag)
           snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
             strerror(error));
       strcat(buf, "\n");
       fflush(stdout); /* in case stdout and stderr are the same */
       fputs(buf, stderr);
       fflush(NULL); /* flushes all stdio output streams */
    }

    用gperftools的TcMalloc管理内存

    2014年1月8日

    TcMalloc(Thread-CachingMalloc)是google-perftools工具中的一个内存管理库,与标准的glibc库中malloc相比,TcMalloc在内存分配的效率和速度上要高很多,可以提升高并发情况下的性能,降低系统的负载。
    Google-perftools项目的网址为:http://code.google.com/p/google-perftools/,该项目包括TcMalloc、heap-checker、heap-profiler和cpu-profiler共4个组件。在只使用TcMalloc情况下可以不编译其他三个组件。

    注:使用线程内存池的方法,在小对象是在内存池中进行分配,使用分配较多的内存空间来优化分配时间。
    实现原理请参考网址http://goog-perftools.sourceforge.net/doc/tcmalloc.html。

    简介

    TcMalloc是一个由Google开发的,比glibc的malloc更快的内存管理库。通常情况下ptmalloc2能在300ns执行一个malloc和free对,而TcMalloc能在50ns内执行一个malloc和free对。
    TcMalloc可以减少多线程程序之间的锁争用问题,在小对象上能达到零争用。
    TcMalloc为每个线程单独分配一个线程本地的Cache,少量的地址分配就直接从Cache中分配,并且定期做垃圾回收,将线程本地Cache中的空闲内存返回给全局控制堆。
    TcMalloc认为小于(<=)32K为小对象,大对象直接从全局控制堆上以页(4K)为单位进行分配,也就是说大对象总是页对齐的。 TcMalloc中一个页可以存入一些相同大小的小对象,小对象从本地内存链表中分配,大对象从中心内存堆中分配。

    安装

    Linux下tcmalloc的安装过程如下:
    1) 从Google源代码网址上下载源代码包,现在最新版本为2.0;
    2) 解压缩源代码包

    # unzip gperftools-2.0.zip          或
    # tar zxvf gperftools-2.0.tar.gz

    3) 编译动态库

    # cd gperftools-2.0
    # ./ configure  --disable-cpu-profiler --disable-heap-profiler --disable-heap-checker 
    --disable-debugalloc --enable-minimal

    加入上面的参数是为了只生成tcmalloc_minimal动态库,如果需要所有组件,命令如下:

    # ./configure
    # ./configure -h           用于查看编译选项。

    编译和安装:

    # make&& make install

    使用最小安装时把tcmalloc_minimal的动态库拷贝到系统目录中:

    # cp lib/tcmalloc_minimal.so.0.0.0 /usr/local/lib

    创建软连接指向tcmalloc:

    # ls –s /usr/local/lib/libtcmalloc_minimal.so.0.0.0  /usr/local/lib/libtcmalloc.so

    启动程序之前,预先加载tcmalloc动态库的环境变量设置:

    # export LD_PRELOAD=”/usr/local/lib/libtcmalloc.so

    使用losf检查程序是否已经加载tcmalloc库:

    # lsof -n | grep tcmalloc

    在Linux下使用的tcmalloc安装完成,在Windows下使用VS(2003以上版本)打开工程项目gperftools.sln进行编译。

    使用

    将libtcmalloc.so/libtcmalloc.a链接到程序中,或者设置LD_PRELOAD=libtcmalloc.so。这样就可以使用tcmalloc库中的函数替换掉操作系统的malloc、free、realloc、strdup内存管理函数。可以设置环境变量设置如下:

             TCMALLOC_DEBUG=<level>       调试级别,取值为1-2
             MALLOCSTATS=<level>                    设置显示内存使用状态级别,取值为1-2
             HEAPPROFILE=<pre>                     指定内存泄露检查的数据导出文件
             HEAPCHECK=<type>                        堆检查类型,type=normal/strict/draconian

    TcMalloc库还可以进行内存泄露的检查,使用这个功能有两种方法:
    1)将tcmalloc库链接到程序中,注意应该将tcmalloc库最后链接到程序中;
    2)设置LD_PRELOAD=”libtcmalloc.so”/HEAPCHECK=normal,这样就不需重新编译程序
    打开检查功能,有两种方式可以开启泄露检查功能:
    1) 使用环境变量,对整个程序进行检查: HEAPCHECK=normal /bin/ls
    2) 在源代码中插入检查点,这样可以控制只检查程序的某些部分,代码如下:

    HeapProfileLeakCheckerchecker("foo");        // 开始检查
    Foo();                                       // 需要检查的部分
    assert(checker.NoLeaks());                   // 结束检查
    

    调用checker建立一个内存快照,在调用checker.NoLeaks建立另一个快照,然后进行比较,如果内存有增长或者任意变化,NoLeaks函数返回false,并输出一个信息告诉你如何使用pprof工具来分析具体的内存泄露。

    执行内存检查:

    #LD_PRELOAD=libtcmalloc.so HEAPCHECK=strict HEAPPROFILE=memtm ./a.out

    执行完成后会输出检查的结果,如果有泄露,pprof会输出泄露多少个字节,有多少次分配,也会输出详细的列表指出在什么地方分配和分配多少次。

    比较两个快照:

    #pprof --base=profile.0001.heap 程序名 profile.0002.heap

    已知内存泄漏时,关闭内存泄露检查的代码:

    void *mark =HeapLeakChecker::GetDisableChecksStart();
    <leaky code>           //不做泄漏检查的部分
    HeapLeakChecker::DisableChecksToHereFrom(mark);

    注:在某些libc中程序可能要关闭检查才能正常工作。
    注:不能检查数组删除的内存泄露,比如:char *str = new char[100]; delete str;。

    用gperftools的CPU profiler分析程序性能

    2014年1月8日

    gperftools是Google提供的一套工具,其中的一个功能是CPU profiler,用于分析程序性能,找到程序的性能瓶颈。

    1.安装

    gperftools:http://code.google.com/p/gperftools/downloads/list
    libunwind:http://download.savannah.gnu.org/releases/libunwind/

    64位操作系统需要安装libunwind,官方推荐版本是libunwind-0.99-beta
    安装过程:./configure [–disable-shared] && make && make install

    Graphviz是一个由AT&T实验室启动的开源工具包,用于绘制DOT语言脚本描述的图形,gperftools依靠此工具生成图形分析结果。
    安装命令:yum install graphviz

    2.用法

    1.目标程序中引入头文件,链接libprofiler库,64位操作系统同时链接libunwind库,在需要分析代码的起点和终点调用ProfilerStart()函数和ProfilerStop()函数
    2.编译链接,运行程序
    分析输出
    pprof脚本用于分析profile文件并输出结果,包括文本和图形两种输出风格。
    例如:demo是目标程序,my.prof是profile文件
    生成文本风格结果:pprof –text ./demo my.prof > profile.txt
    生成图形风格结果:pprof –pdf ./demo my.prof > profile.pdf

    对于一个函数的CPU使用时间分析,分为两个部分:
    1.整个函数消耗的CPU时间,包括函数内部其他函数调用所消耗的CPU时间
    2.不包含内部其他函数调用所消耗的CPU时间(内联函数除外)

    2.1关于文本风格输出结果

    序号 说明
    1 分析样本数量(不包含其他函数调用)
    2 分析样本百分比(不包含其他函数调用)
    3 目前为止的分析样本百分比(不包含其他函数调用)
    4 分析样本数量(包含其他函数调用)
    5 分析样本百分比(包含其他函数调用)
    6 函数名

    2.1关于图形风格输出结果

    1.节点
    每个节点代表一个函数,节点数据格式:
    Class Name
    Method Name
    local (percentage)
    of cumulative (percentage)
    local时间是函数直接执行的指令所消耗的CPU时间(包括内联函数);性能分析通过抽样方法完成,默认是1秒100个样本,一个样本是10毫秒,即时间单位是10毫秒;
    cumulative时间是local时间与其他函数调用的总和;
    如果cumulative时间与local时间相同,则不打印cumulative时间项。
    2.有向边
    调用者指向被调用者,有向边上的时间表示被调用者所消耗的CPU时间

    3.示例

    代码如下,可以看出,CPU消耗集中在func1()和func2()两个函数,func2()消耗时间约为func1()的两倍。

    #include <google/profiler.h>
    #include <iostream>
    using namespace std;
    void func1() {
        int i = 0;
        while (i < 100000) {
            ++i;
        }  
    }
    void func2() {
        int i = 0;
        while (i < 200000) {
            ++i;
        }  
    }
    void func3() {
        for (int i = 0; i < 1000; ++i) {
            func1();
            func2();
        }  
    }
    int main(){
        ProfilerStart("my.prof"); // 指定所生成的profile文件名
        func3();
        ProfilerStop(); // 结束profiling
        return 0;
    }

    然后编译链接运行,使用pprof生成分析结果

    g++ -o demo demo.cpp -lprofiler -lunwind
    pprof --text ./demo my.prof > output.txt
    pprof --pdf ./demo my.prof > output.pdf

    查看分析结果,程序是122个时间样本,其中,func1()是40个时间样本,约为400毫秒;func2()是82个时间样本,约为820毫秒。

    Total: 122 samples
          82  67.2%  67.2%       82  67.2% func2
          40  32.8% 100.0%       40  32.8% func1
           0   0.0% 100.0%      122 100.0% __libc_start_main
           0   0.0% 100.0%      122 100.0% _start
           0   0.0% 100.0%      122 100.0% func3
           0   0.0% 100.0%      122 100.0% main

    参考文献

    使用 google-perftools 剖析程序性能瓶颈(包括安装):http://www.ibm.com/developerworks/cn/linux/l-cn-googleperf/

    Window和Linux动态库搜索路径的次序

    2013年12月8日

    Windows和Linux都提供了相应的函数来打开动态库、获取函数指针和关闭动态库 ,在打开动态库函数(dlopen或LoadLibrary)中指定动态库的文件名后,在程序运行时是以什么次序来搜索动态库呢?

    Linux下搜索路径的次序:
    1) ELF可执行文件中动态段中DT_RPATH所指定的路径,不常用但是比较使用的方法;
    2) 编译目标代码时指定的动态库搜索路径(-WI,-rpath=./);
    3) 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
    4) 配置文件/etc/ld.so.conf(或ld.so.cache)中指定的动态库搜索路径;
    5) 默认的动态库搜索路径/lib;
    6) 默认的动态库搜索路径/usr/lib。
    在上述1-3中指定的动态库搜索路径都可以指定多个搜索目录,其先后顺序是按指定路径的先后顺序搜索的。如果动态库之间存在依赖关系,那么先加载那些被依赖的动态库。

    Windows下搜索路径次序
    1) 可执行文件所在的目录(当前目录);
    2) Windows的系统目录(该目录可以通过GetSystemDirectory函数获得);
    3) 16位的系统目录(Windows目录下的system子目录);
    4) Windows目录(该目录可以通过GetWindowsDirectory函数获得);
    5) 进程当前所在的工作目录;
    6) PATH环境变量中所列出的目录。
    注:工作目录位于Windows目录之后,这一改变开始于Windows XP SP2之后。

    awk正则表达式

    2013年9月11日

    对于awk而言,正则表达式是置于两个正斜杠之间、由字符组成的模式。Awk支持使用正则表达式元字符对正则表达式进行某种方式修改。如果输入行中的某个字符串与正则表达式相匹配,则最终条件为真,于是执行与该表达式关联的所有操作。如果没有指定操作,则打印与正则表达式匹配的记录.

    元字符 说明
    ^ 在串首匹配
    $ 在串尾匹配
    . 匹配单个任意字符
    * 匹配零个或多个前导字符
    + 匹配一个或多个前导字符
    匹配零个或一个前导字符
    |(A|B) 指定如果以 |(垂直线)隔开的字符串的任何一个在字符串中,则字符串匹配,匹配A或B
    [ABC] 匹配指定字符组(即A、B和C)中任一字符
    [^ABC] 匹配任何一个不在指定字符组(即A、B和C)中的字符
    [A-Z] 匹配A至Z之间的任一字符
    (AB)+ 将字符串组合在一起,匹配一个或多个AB的组合,例如:AB、ABAB
    ~,!~ 表示指定变量与正则表达式匹配(代字号)或不匹配(代字号、感叹号)的条件语句
    {m, n} 指定如果 m 和 n 之间(包含的 m 和 n)个模式的具体值在字符串中(其中m<= n),则字符串匹配
    \ 转义字符。位于正则表达式中具有特殊含义的任何字符之前时,转义字符除去其任何特殊含义
    & 用于替代串中,代表查找串中匹配到的内容

    表-Awk的正则表达式元字符

    元字符 说明
    \<>/ 单词定位
    \(\) 向前引用
    \{\} 重复

    shell中的expr注意事项

    2013年8月9日

    对于一些具有特殊意义的操作符号,需要添加反斜线来反转义。比如两个字符串操作:

    expr "test" \< "unix8"
    expr "test" \& "unix8"

    等于与不等于则可以不用转义

    expr "test" = "unix8"
    expr "test" != "unix8"

    整数操作:

    $expr 10 + 10

    20

    $expr 30 / 3

    10

    $expr 30 / 3 / 2

    5 但是由于*号是shell中的通配符,所以需要转义。

    $expr 30 \* 3

    还可以测试一个数是否时整数:

    r=1.1 expr $r + 1 expr: 非整数参数 

    可以看出只能进行整数操作。 注意以上的操作符的前后都需要有空格,否则shell会当作字符来处理。比如:

    r=1.1 expr $r+1 1.1+1 

    上边的测试不好在程序中使用,可以使用下面的方法: 测试最后命令状态,如果为0,证明这是一个数,其他则表明为非数值。

     $value=12 $expr $value + 10 > /dev/null 2>&1
    $echo $?
    0

    这是一个数。

    $value=hello
    $expr $value + 10 > /dev/null 2>&1
    $echo $?
    2

    这是一个非数值字符。

    expr模式匹配
    expr支持正则表达式,如下因为正则表达式中的.代表任意一字符,*代表前面0个或者多个字符,所以.*.*意即任何字符重复0次或多次。:

    value="www.unix8.net"
    expr $value : '.*'
    13

    特别需要注意的时:冒号左右需要有空格。

    提取字串
    当中的5表示从第5个字符开始,6开始长度为6.

    expr substr $value 5 6
    unix8.
    

    下面列出expr的具体用法

    NAME
           expr - evaluate expressions
    
    SYNOPSIS
           expr EXPRESSION
           expr OPTION
    
    DESCRIPTION
           --help display this help and exit
    
           --version
                  output version information and exit
    
           Print the value of EXPRESSION to standard output.  A blank line below separates increasing precedence groups.  EXPRESSION may be:
    
           ARG1 | ARG2
                  ARG1 if it is neither null nor 0, otherwise ARG2
    
           ARG1 & ARG2
                  ARG1 if neither argument is null or 0, otherwise 0
    
           ARG1 < ARG2
                  ARG1 is less than ARG2
    
           ARG1 <= ARG2               ARG1 is less than or equal to ARG2        ARG1 = ARG2               ARG1 is equal to ARG2        ARG1 != ARG2               ARG1 is unequal to ARG2        ARG1 >= ARG2
                  ARG1 is greater than or equal to ARG2
    
           ARG1 > ARG2
                  ARG1 is greater than ARG2
     ARG1 + ARG2
                  arithmetic sum of ARG1 and ARG2
    
           ARG1 - ARG2
                  arithmetic difference of ARG1 and ARG2
    
           ARG1 * ARG2
                  arithmetic product of ARG1 and ARG2
    
           ARG1 / ARG2
                  arithmetic quotient of ARG1 divided by ARG2
    
           ARG1 % ARG2
                  arithmetic remainder of ARG1 divided by ARG2
    
           STRING : REGEXP
                  anchored pattern match of REGEXP in STRING
    
           match STRING REGEXP
                  same as STRING : REGEXP
    
           substr STRING POS LENGTH
                  substring of STRING, POS counted from 1
    
           index STRING CHARS
                  index in STRING where any CHARS is found, or 0
    
           length STRING
                  length of STRING
    
           + TOKEN
                  interpret TOKEN as a string, even if it is a
    
                  keyword like `match' or an operator like `/'
    
           ( EXPRESSION )
                  value of EXPRESSION
     Beware that many operators need to be escaped or quoted for shells.  Comparisons are arithmetic if both ARGs are numbers, else lex‐
           icographical.  Pattern matches return the string matched between \( and \) or null; if \( and \) are not used, they return the num‐
           ber of characters matched or 0.
    
    

    将可变参数列表格式化为一个字符串

    2013年7月4日

    需要使用以下3个宏

    #include <stdarg.h>
    
    void va_start(va_list ap, last);
    type va_arg(va_list ap, type);
    void va_end(va_list ap);
    void va_copy(va_list dest, va_list src);

    讲这个得资料多如牛毛.比如http://linux.die.net/man/3/stdarg

    将可变参数列表格式化为一个字符串,利用vsnprintf就可以了。不采用vsprintf是为了防止溢出。
    另外vsprintf使用va_list作为参数,相对来说比用”…”更方便明了。

    void Format(const char *format, ...)
    {
    	va_list ap;
    	va_start(ap, format);
    	char slask[5000];
    	vsnprintf(slask, sizeof(slask), format, ap);
    	va_end(ap);
    	Send( slask );
    }