月度归档:2012年12月

细看stdint和inittypes

stdint.h

stdint.h为所有基本类型限定了长度,并且兼容了32位和64位平台。

带符号的整形类型

intN_t int_leastN_t int_fastN_t intmax_t intptr_t

无符号的整形类型

uintN_t uint_leastN_t uint_fastN_t uintmax_t uintptr_t

定义常量的宏

INTN_C,INTMAX_C

UINTN_C,UINTMAX_C

已经定义为常量值的宏

  • INTN_MIN,INTN_MAX,INT_LEASTN_MIN,INT_LEASTN_MAX,INT_FASTN_MIN,INT_FASTN_MAX
  • UINTN_MIN,UINTN_MAX,UINT_LEASTN_MIN,UINT_LEASTN_MAX,UINT_FASTN_MIN,UINT_FASTN_MAX
  • INTPTR_MIN,INTPTR_MAX,SIZE_MAX,PTRDIFF_MIN,PTRDIFF_MAX

 

注意点

  • (u)intN_t 长度为N的整形类型

这些精准的类型在特殊的机器类型上不一定可用,例如9位机,一个字节表示9位。

  • (u)int_leastN_t 长度至少为N的的整形类型

由于精准长度的类型不一定在所有平台下都可用,因此这个类型变得有必要

  • (u)int_fastN_t 长度至少为N并且在当前机器上有最好性能的整形类型

打个比方,int_fast8_t 需要8位,但在32位机器上数据总线是32位的,实际上一次内存操作读取的是4个字节,因此进行整数个机器字长的内存操作肯定是最快的,因此 int_fast8_t的实际长度为4个字节,int_fast16_t同样也是,64位机器一次类推。

  • (U)INTN_C系列 定义常量时,会在后面加上标记符,例如UL,L等,保证定义的常量赋值时不会出现潜在的问题
  • (U)INTN_MIN系列 本身就是定义好了的常量
  • (u)intptr_t 指针尽量使用封装好的指针类型,不要用int或long

 

inttypes.h

inttypes.h包含了stdint.h这个头文件,更为重要的是它为stdint.h中的所有类型兼容了32位和64位平台下的输出格式。

printf(),fprintf()系列的整形(带符号/无符号)格式输出

PRIdN,PRIdLEASTN,PRIdFASTN,PRIdMAX,PRIdPTR

PRIiN,PRIiLEASTN,PRIiFASTN,PRIiMAX,PRIiPTR

PRIuN,PRIuLEASTN,PRIuFASTN,PRIuMAX,PRIuPTR

printf(),fprintf()系列的整形其他进制格式输出

PRIoN,PRIoLEASTN,PRIoFASTN,PRIoMAX,PRIoPTR

PRIxN,PRIxLEASTN,PRIxFASTN,PRIxMAX,PRIxPTR

PRIXN,PRIXLEASTN,PRIXFASTN,PRIXMAX,PRIXPTR

scanf(),fscanf()系列的整形其他进制格式输出

SCNdN,SCNdLEASTN,SCNdFASTN,SCNdMAX,SCNdPTR

SCNiN,SCNiLEASTN,SCNiFASTN,SCNiMAX,SCNiPTR

SCNuN,SCNuLEASTN,SCNuFASTN,SCNuMAX,SCNuPTR

scanf(),fscanf()系列的整形其他进制格式输出

SCNoN,SCNoLEASTN,SCNoFASTN,SCNoMAX,SCNoPTR

SCNxN,SCNxLEASTN,SCNxFASTN,SCNxMAX,SCNxPTR

 

注意点

  • 输出宏定义中有双引号限定,因此使用输出时放在引号外面。
  • C++的输出已经重载了所有基本类型的输出,没有必要引用inittypes.h,stdint.h就够用了。
基本用法
INT64_T temp=54;
printf(“INT64_T=%”PRId64,temp);

QQ临时会话的wordpress插件

由于最近对于QQ临时会话颇有喜感,于是乎总有想用用的冲动,不想每次添加临时会话都贴代码到主页,所以想弄个wp的插件来玩玩。

[效果图]

[使用流程]
1. 上传 wordpress-tencent-wpa插件到 /wp-content/plugins/ 目录
2. 在Wordpress后台控制面板”插件(Plugins)”菜单下激活”wordpress-tencent-wpa”
3. 在Wordpress后台控制面板”外观(Appearance)->小工具(Widgets)”下使用腾讯WPA
4. 首次使用需激活API,点击后前往 http://wp.qq.com获取,登录一次即可。
5. 将得到的IDKEY值填写到插件后台即可

[下载地址]
wordpress-tencent-wpa

[参考文献]

“并行,并发,异步,同步,阻塞,非阻塞”之我见

后台开发中总是在”并行,并发,异步,同步,阻塞,非阻塞”之间转,自己经常就被转得晕头转向。由于对基于经验的答复不满意,因此决定将我这几个月对这些概念千丝万缕的思绪整理出来。

【并发与并行】

  • 并发: 仅仅包含宏观上的意义,即多个任务同时被处理;而微观上可能仅仅是一个cpu分时间片去完成了多个任务,这个时候多个任务是被顺序处理的。
  • 并行: 则更加严格,在宏观和微观上都应该具有多个任务同时被处理的含义,这个意义上多个任务被并行处理的,单cpu永远不可能并行。
=>
  1. 并行和并发是站在两个不同角度上理解产生的概念。
  2. 并行一定是并发,并发却不一定是并行。

【同步与异步】

  • 同步: 任务的执行过程是顺序的。
  • 异步: 事情的执行过程不是线性的。
=>
  1. 异步和同步是从任务被执行的顺序上来区分的,从这个基本概念出发,异步并不见得比同步性能高,也就是说异步模型不见得会比同步模型有优势。
  2. 然而异步和同步这个概念却非常容易和阻塞与非阻塞混淆,因为任何一个任务理论上都不可能瞬间完成,而现实中的任务通常都需要与其他系统交互,因此可能被阻塞是不可避免的。
  3. 异步通常给人一个错觉,就是他比同步快。这里其实是有一个误区,因为一个任务实际被处理的时间并没有太大变化,只有在带阻塞的任务中,异步才可能 比同步快,因为异步快在将同步模式下带阻塞任务执行过程中cpu的等待时间利用起来处理其他任务,借此提升了多任务系统的整体并发能力,可见,一个任务的 异步不一定比同步快,同时,对于处理不阻塞的任务,异步模型也不会比同步模型快。

【阻塞与非阻塞】

  • 阻塞: 任务的执行会使cpu进入等待状态。
  • 非阻塞: 任务的执行不会主动让cpu进行等待。
=>
  1. 阻塞不阻塞的原始语义实际上是看任务在宏观执行过程中对cpu的主动占用情况是否连续来区分的。
  2. 在不考虑cpu时间片耗尽被重新调度或者其他干扰导致被抢占的情况下,非阻塞的任务在获得cpu后将一直执行到完成。
  3. 阻塞或非阻塞的任务与阻塞或非阻塞的接口是有区别的:前者隐含一个前提就是必须是任务执行从开始到结束的整个过程,而后者不用关心任务是否完整完成,仅仅关心当前这一步操作是否会阻塞。

【开发中的现状】

  1. 大部分业务都可能阻塞(网络IO,文件IO),常见的网络收发,数据库操作等,因此在追求海量高性能的server开发中很较少用到同步模型,这也是为什么我们潜意识里会觉得同步模型慢且容易与阻塞纠缠不清的原因 — 计算机世界的任务模型原本大多是阻塞任务。
  2. 开发中大多是将带阻塞的任务分解成多个不阻塞的部分(这就是异步模型中所谓的分割点),只有在这样的前提下配合异步模型才能大幅提升系统的并发能力,这就是为什么在高性能server开发中异步与非阻塞通常被混为一谈的原因 — 计算机处理海量任务只有这样才能整体最快。
  3. 对server业务,肯定提倡使用异步模型。但异步模型对开发者并不友好,因为人类的思维习惯是线性分析事务,这就是为什么大部分开发者愿意使用同步模型的原因 — 人类看待事物的习惯本就带有顺序性。

openSUSE12.2-13.1 硬盘安装问题(no reponsitory found)

问题描述
可以肯定的是openSUSE-12.2-DVD-x86_64.iso肯定没有测试充分,硬盘安装一直会出现no reponsitory found的问题,多次重启,改镜像名,问题依旧。使用ALT+F3查看信息,发现其实是安装脚本中的一句mount出错了,因为找不到/bin/mount。
解决方法
而进入expert mode的shell中,可以发现,mount命令其实并不在/bin目录下,而是在/usr/bin/目录下。拷贝mount到/bin目录下,重新指定硬盘上的镜像,安装得以继续。

13.1也是如此,如法炮制

主流系统引导过程分析

主流系统引导过程分析

1.系统引导的各个阶段

FreeBSD(boot0) Linux(grub) Windows(nt5/nt6) grub4dos
第一阶段 boot0 stage1 Microsoft-MBR grldr.mbr
第二阶段 boot2 stage1.5 Microsoft-PBR
引导器 /boot/loader stage2 ntldr/bootmgr grldr
配置文件 loader.conf menu.lst/grub.conf boot.ini/BCD menu.lst

第一阶段,通常安装到MBR中,作用是存储分区信息,寻找第二阶段代码指令。
第二阶段,存储位置不详,通常在PBR(分区引导记录)中,作用是识别文件系统,寻找加载器并加载。
第三阶段,通常以文件形式存放在某一分区中,被加载后进入引导器的环境中,该环境下通常都基于配置文件进行配置,具有更加强大的功能。
最后,引导器加载OS内核,执行内核代码。

2.系统分阶段启动的原因

系统分阶段启动的原因在于磁盘扇区有限制,MBR只有512个字节,PBR大小也不是无限的。传统 的引导方法:MBR->PBR->loader,MBR代码需要识别可启动分区并跳转到该分区的PBR执行启动代码,PBR需要识别该分区的 文件系统并查找并加载相应的引导器。引导器环境下,就可以进行多种操作,配置文件是最常见的形式。像grub引导器就识别绝大部分文件系统,基于配置文 件,同时提供命令行环境;而传统的windows的引导器ntldr/bootmgr基于boot.ini/BCD配置文件。

3.grub4dos与传统grub的不同

grub4dos与传统grub引导器不同的是只有两个阶段,grldr.mbr并不是传统的1个 扇区(512字节),它还包含基本的文件系统(FAT/NTFS/EXT2/EXT3)识别代码和grldr查找代码,当然如果这个阶段无法找到 grldr,将无法进入引导器环境(例如,你将grldr文件放到MAC OS的文件系统上),当然一旦进入了grldr环境,将能够识别绝大部分的文件系统。

3.mbr引导与pbr引导

安装非windows系统通常会让你选择引导信息安装到磁盘还是分区:

  • 安装到MBR就是告诉磁盘,引导器在哪个分区。传统MBR的功能只是负责跳转,跳转到识别特定分区引导器的代码,这段代码通常在分区引导扇区PBR中,但不同的引导器可能有差别。
  • 安装到PBR就是告诉磁盘你已经有自己的引导器了,只需要用其他的引导器引导即可。此时,通常都在其他引导器中执行chainloader +1,即读取PBR来执行,因为分区的第一扇区是包含有引导信息的。
  • 当然也并非必须必须安装引导信息,因为假设我知道分区A上安装的fedora,B分区已经有ubuntu且已经将grub写入MBR,那么在安装 fedora的时候即便引导信息既不安装到MBR也不安装PBR,也是能够启动的,因为我们知道分区A的结构。使用configfile A分区/boot/menu.lst即可加载A分区上的fedora的启动菜单。

 

基于shell的程序安装文件制作

玩linux的人对驱动安装文件应该不会陌生吧,以前就很好奇:安装文件居然是一个可执行的 shell,参考:http://biancheng.dnbcw.info/shell/239690.html才明白这个文件的结构。基本道理是生成 一个大的文件,由一段sh脚本加上一个tar包组成。sh脚本负责把tar包解开并完成一些必要操作,实现安装的拷贝和设置。这样一个大的文件加上执行权 限就可以当安装程序使用。

小米插件aria2亦是如此:

下面提供一个example下载:
linux安装文件制作示例

cmake为什么好用

好东西绝总是有让你爱不释手的魔力,cmake就是其中一个。

1. cmake管理外部库
如果要使用很多库,而这些库又都没有安装到系统中,使用makefile的工作量真是难以想象,而CMake却能很轻松的处理这种场景。

  • 库的CMakeLists.txt中添加
    #导出目标
    export(PACKAGE config_parse)
    #指定要生成的导出文件
    export(TARGETS config_parse FILE config_parse-exports.cmake)
    #指定供find_package使用的配置文件
    configure_file(config_parse-config.cmake.in ${CMAKE_BINARY_DIR}/config_parse-config.cmake)
  • 库工程根目录添加config_parse-config.cmake.in文件
    include_directories(${CMAKE_SOURCE_DIR}/src) #指定库所需要的头文件目录
    include(“${CMAKE_BINARY_DIR}/config_parse-exports.cmake”) #指定库导出文件

接下来,编译该库之后,将在用户目录的.cmake目录下生成该库的信息文件。

  • 需要使用该库时,只需添加find_package(目标名),就可以按常规使用方法使用库了。
    find_package(config_parse REQUIRED)
    find_package会在用户目录的.cmake目录下找到对应的库编译路径,然后加载路径下的配置文件。

2. cmake来添加编译前/后的任务
cmake告诉我们编译管理系统有的不仅仅是编译。想格式化源代码,生成doxygen代码文档等操作都可以集成到编译管理系统中来。截一段

  • add_custom_command(TARGET config_parse
    POST_BUILD
    COMMENT “[ generate doc ]”
    COMMAND ${DOXYGEN_EXECUTABLE} 2>/dev/null
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    #VERBATIM
    )
  • add_custom_command(TARGET config_parse
    PRE_BUILD
    COMMENT “[ astyle code ]”
    COMMAND astyle src/*.c src/*.h 1>/dev/null 2>/dev/null
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    #VERBATIM
    )

3. 查找需要的库/命令
如果安装前缀是/usr的话,/usr/share/cmake/Modules下就是CMake提供的所有模块了,加载模块之后,就拥有了很多变量,不仅可以用来测试系统是否包含所查找的东西,同时提供很多。

  • 命令(Doxygen)
    CMakeLists.txt中添加include(FindDoxygen)之后就可以直接使用${DOXYGEN_EXECUTABLE}来代替Doxygen二进制文件了。
  • 库(Gtest)
    CMakeLists.txt中添加include(FindGTest)之后就可以直接使用${GTEST_LIBRARIES}和${GTEST_INCLUDE_DIRS},免去了查找库和头文件位置的麻烦。

4. cmake生成安装包
cpack应该是cmake中最强大的模块了,而且可以独立运行。用它来生成源码包和二进制包,也就是传说中的安装包。通过CPACK模块可以看出,它支持
DEB/RPM/NSIS/TGZ/ZIP/TBZ2/STGZ/ZIP等,需要注意的是cpack依赖于install指令,install中必须使用相对路径才能使cpack可用(CMAKE_INSTALL_PREFIX表示安装的根路径),同时tgz这类generator默认会忽略空目录。

SET(CPACK_GENERATOR RPM)
SET(CPACK_SOURCE_GENERATOR TGZ)
INCLUDE(CPack)

通过make package_source 可以生成源码包
通过make package 可以生成二进制包

5. cmake生成项目依赖图
添加cmake –graphviz来编译,就会生出graphviz识别的文件,然后调用dot命令,
例如dot graphviz文件 -Tpng则生成png文件

6. cmake外部编译
编译的中间文件总是使人烦乱,cmake推荐外部编译,这就是你为什么会通常会看到一个build文件夹的原因。官方目前仍未提供限制内部编译的指令,so只能委婉的禁用内部编译
IF(“${PROJECT_SOURCE_DIR}” STREQUAL “${PROJECT_BINARY_DIR}”)
MESSAGE(FATAL_ERROR “in-source builds are not allowed.”)
ENDIF(“${PROJECT_SOURCE_DIR}” STREQUAL “${PROJECT_BINARY_DIR}”)

关于覆盖率测试的一点认识

1.常用工具的使用:

  • gcov+lcov是最常用的覆盖率测试工具,使用gcov+lcov最常见的问题发生在后台程序中,由于没有显示调用exit,gcov扑捉不到测试率信息,因此需要扑捉所有信号,在信号中显示调用exit。
  • 重新编译程序,将会对之前的覆盖率造成影响,尽量在重新编译前,先调用lcov统计测试率结果。

2.覆盖率测试方法
设计用各种方式运行程序,或者使用外部数据来覆盖程序的更多分支。

  • server程序运行起来之后的逻辑覆盖主要靠外界的数据交互,通过设计不同的数据包可以覆盖到绝大部分的分支。
  • 最容易遗忘的是程序的运行方式。用不同的命令行参数或者设置不同的配置文件参数将可以影响程序运行的整个状态。

3.覆盖率误区

  • 是否一定要100%
    当然覆盖率越高越好,但即便是100%的覆盖率也绝对不能避免和纠正程序上的逻辑错误,因为覆盖率测试在测试环节中还处于相对较低的层次。它所担保的是,你写的代码就是你要用到的代码,仅此而已。
  • 为了覆盖率而改动代码
    例如,现网环境不会出错的语句,是否不需要进行错误判断或者异常处理。从设计程序的思路来讲,肯定应该有这个判断,但从覆盖率的测试方面来讲,这个分支却永远覆盖不到。首先,需要改动代码的测试都不是一个好的测试。并不能为了追求测试率而轻易变动程序架构。

4.测试率的意义
测试率既然是测试中的一个环节,肯定有其存在的价值。

  • 帮开发者理清逻辑
    哪些分支被覆盖得多,覆盖得少,哪些分支没有覆盖。
  • 用于指导后续测试
    如果覆盖率测试得到位的话,分支覆盖次数多的肯定是关键业务,说明在后续测试中应该重点关注,特别是单元测试的关注点,可以从覆盖率测试中获得参考。
  • 用于指导开发者调整代码
    如果覆盖率测试到位的话,排除上述提到的情况,无法覆盖到的部分很大程度上就意味着,那段代码没啥作用,开发者可以考虑是否去掉。

并发之libtask

【概述】
提到任务并发首先想到的肯定是传统的多进程,多线程模型,不过linux提供了函数库实现用户层的上下文切换 (sys/ucontext.h,ucontext.h)。libtask就是在单进程单线程下通过用户层的上下文切换来实现多任务并发的 库,libtask很小,轻量到只有几个文件。libtask与传统多进程或多线程并发模型相比的有两点优越性:

  • 用户层面的上下文切换只需要保存寄存器信息,开销很小
  • 没有资源竞争,不需要加锁

【主要结构】

【使用中的注意】

  • libtask核心文件是task.h,taskimpl.h,task.c,其他文件不是必须的
  • 根据业务修改维护任务指针的slot增长算法,原算法是每次用满时向上增加64个slot
  • libtask包含了main,自己只需要实现任务逻辑即可,任务逻辑入口taskmain(int argc, char *argv[])
  • 任务之间的通信,chann实现得有点繁琐,这些东西最好根据业务自身来定制

【libtask相关】
libtask源码
Mongrel2基于libtask实现

总结几种常见的心跳机制

1.单向心跳
概述: 此种方式,心跳发送方不关心接收方是否收到心跳包,心跳接收方收心跳包超过时间阈值则判决对端失效。
应用场景:业务端A关注业务端B是否可用,A对B进行监测。

服务提供方和服务接收方都可以发送心跳包但心跳接收方更加关注对端的可用性,前者通常用于PULL类的被动服务(服务接收方更加需要服务提供方),后者主要用于PUSH类的主动服务(服务提供方更加需要服务接收方)。

2.心跳+确认
概述: 此种方式,心跳发送方发包,心跳接收方回包,心跳发送方关心接收方是否收到心跳包,心跳发送方超过时间阈值没收到回包触发重试机制,达到重试阈值判决对端失效。
应用场景:业务端A关注业务端B是否可用,但是B端不方便或者不适合发送心跳包。

服务提供方对应到多个服务接收方时不适合发送心跳包,服务提供方那端业务或负载太重不方便一直发送心跳包,此时服务接收方主动发送心跳包,多见于一个服务端对应多个被服务对象。

3.双向心跳
概述: 此种方式,两端都既是心跳发送方又是心跳接收方,心跳发送方发包,心跳接收方不回包,两端都关注对端可用性。
应用场景:业务端A和业务端B互为关注。

业务端A和业务端B互为关注对端的变动和失效,常用于对对端的变动和失效敏感的业务中。