P分支是很久前fork自Master,Master分支随后演进较多内容。

0,在P分支上,pull最新的分支代码。

1,在P分支上 rebase master。产生很多conflict,P分支的127个commits在Master分支上replay commits。(在rebase过程中ours和theirs和merge时的概念不一样。ours指的master,theirs指的是P分支)
2,每次需要解决冲突,很多冲突是历史commits产生的冲突。没有特别好的解决办法。

git status -uno --short  --ignore-submodules --porcelain | awk '{print $2}' | xargs git checkout --theirs  
git status -uno --short  --ignore-submodules --porcelain | awk '{print $2}' | xargs git add 
git add  git rebase --continue

更简单的办法,在碰到merge或rebase出现大量冲突下,手动处理冲突:

git diff --name-only | uniq | xargs mvim
:n/:prev 切换所有冲突项
:w | n 挨个儿保存切换

3, 这个过程实际上比较无聊。

4,在rebase完成后,确认feature合入完整。中间因为操作失误,合入了一些错误内容,修改之。提交修改。

5,git push失败,原因不是 P分支远程最新(疑问)。按照道理,这个地方应该是最新的才对。(疑。这个地方可能哪里有误操作。

6,重新对使用 git checkout --ours, 以本地修改为主。push到P分支。

总结:

feature的小批量合入,要不是想历史记录和Master保持一致,最简单的办法是直接将feature特性合入Master。然后将Master主线强制覆盖到P分支。但这丢失了真实的commit记录。分支的rebase大多数的时候做不到时间间隔短。

要会使用rebase,checkout,status以及脚本的组合。节约时间做其它事情。

git的三部分:暂存区,index,以及working tree。如果很久不操作一些大工程的合并,容易忘记自己操作的影响。anyway, repeat and google in case you forgot.

想灵活的使用时光机,在合并前,解决冲突前一定要思路清晰。

手工合并冲突是很痛苦的,尤其代码量巨大,而很多代码你还不熟悉的情况下。

-- typora 用来写本地的markdown笔记。比windows下的Markdown Pad好用。

-- 远程同步到github.gitee

-- 利用github,gitee,picGo提供基本的图床服务。

-- 目前适用七牛的免费图床服务。

-- evernote的服务适合用于私人笔记,不公开的那种。

这是一篇写在公司的老文章。

最近碰到的一个比较棘手的问题。在深度学习框架Torch7上测试所有用例fail,鉴于AARM64生态非常不完善,倒也不足为奇。

当前AARM64架构芯片上只支持48bit的VA,而以高效率著称的LuaJIT在代码中限制64位实地址限制使用47bit。高位用作NaN Tagging。

下面。至于为什么要这样,我们要从LUA的数据类型说起。

大家都知道LUA表示数据都是double双精度的,LUA在虚拟机实现时首先要考虑的是如何表示数据类型。

LUA有8种基本数据类型:

nil,
boolean
string,
heavy userdata
table,function
number
thread

typedef struct {
   int  t;
   Value V;
}TObject;
 
typedef union {
 GCObject *gc;
 void *p;
 lua_Number n;
 int b;
}Value

GCObject *gc

是一个垃圾回收对象指针,它用来表示复杂数据类型(string, tables,function,heavy userdata,threads)

b
表示一个布尔值,boolean

n
表示一个数字,number

t
nil 不需要Value这个Union来表示了,直接用t就可以。

不要忘了还有一个特殊的数据类型。

p

存储一个指针,所谓的light userdata 类型。我们的问题就出在这个数据类型上。
使用lua的人可以调用lua_pushlightuserdata(Lua_state L, void p)来存储一个指针到栈上。
lightuserdata没有GC,用来指向任一userdata数据类型 ,普通userdata是一个对象,只能和自己做比较。而lightuserdata通常用来指向userdata所在地址。
应用使用的API接口: void lua_pushlightuserdata (lua_State L, void p); (https://www.lua.org/pil/28.5.html)

这样看来LUA的8种数据类型,用一个TObject就可以完全表示了。其中Value的SIZE就是64bit,TObject在需要对齐的情况下16Byte,不需要对齐情况下12Byte。

再说LuaJIT。LuaJIT是Lua的JIT实现。对数据类型的表示进一步复杂化。涉及到了64位系统的VA配置选项。

1, 不开启LJ_GC64模式

对于非LJ_GC64模式:
Internal tags overlap the MSW of a number object (must be a double).
Interpreted as a double these are special NaNs. The FPU only generates
one type of NaN (0xfff8_0000_0000_0000). So MSWs > 0xfff80000 are available
for use as internal tags. Small negative numbers are used to shorten the
encoding of type comparisons (reg/mem against sign-ext. 8 bit immediate).

                      --MSW--.------LSW---------
primitive types       | itype |                      |
lightuserdata          | itype | void *           | (32 bit platforms)
lightuserdata          |ffff| void *                | (64 bit platforms, 47 bit pointers)
GC objects              | itype | GCRef          |
int (LJ_DUALNUM)  | itype | int                |
number                   -------double------

2, 开启LJ_GC64模式

                        ---MSW---.--------LSW---------
    
primitive types        |1..1|itype|1............1 |
GC objects/lightud     |1..1|itype|-------GCRef--------|
int (LJ_DUALNUM)       |1..1|itype|0..0|-----int----------|
numer                  ------------double-------------

对于LG_GC64模式
The upper 13 bits must be 1 (0xfff8...) for a special NaN. The next
4 bits hold the internal tag. The lowest 47 bits either hold a pointer,
a zero-extended 32 bit integer or all bits set to 1 for primitive types

   那么问题来了,高13bit被用作NaN,4bit无论如何要用来保存数据类型tag,剩下47bit可以表示数据。

你肯定要说,修改下数据类型结构不就得了? 非也。LuaJIT的所有垃圾回收机制都建立在这个数据结构上,工作量是一回事。对整套代码的稳定性才是关键。

  所以作者并不建议修改。最简单的办法莫过于配置内核的VA长度了。

在当前情况下,这意味着。在应用中使用lua_pushlightuserdata,传入的64bit指针如果不校验,那么指针就会跑飞。

这个问题一出现,各大开源社区立即鸡飞狗跳:

firefox社区:如何解决sparcx64 aarm64上跑firefox
https://bugzilla.mozilla.org/show_bug.cgi?id=1143022
https://bugzilla.mozilla.org/show_bug.cgi?id=1275204

linaro&csico 着手解决ARM64生态问题,开始支持luajit
https://collaborate.linaro.org/display/TCWGPUB/LuaJIT+for+ARM64
https://github.com/sindrom91/LuaJIT/commit/d63e0af3978f24e230cccc017ca98ed1653497de

Debian社区:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=818616

openResty社区:
https://github.com/openresty/lua-nginx-module/issues/757
https://github.com/openresty/lua-nginx-module/issues/1152

LuaJIT社区本身:
https://github.com/LuaJIT/LuaJIT/issues/156

luaJIT官网版本发布列表:
http://www.luatex.org/svn/tags/0.98.1/source/libs/luajit/LuaJIT-src/doc/changes.html

官网对当前状态中GC64模式不支持>47bit的VA:
file:///E:/luajit-2.0/doc/status.html

LuaJIT on 64 bit systems provides a limited range of 47 bits for the legacy lightuserdata data type. This is only relevant on x64 systems which use the negative part of the virtual address space in user mode, e.g. Solaris/x64, and on ARM64 systems configured with a 48 bit or 52 bit VA. Avoid using lightuserdata to hold pointers that may point outside of that range, e.g. variables on the stack. In general, avoid this data type for new code and replace it with (much more performant) FFI bindings. FFI cdata pointers can address the full 64 bit range.

luaJIT beta3更新日志:
http://repo.or.cz/luajit-2.0.git/shortlog/refs/tags/v2.1.0-beta3

支撑mike pall开发arm64版本的过程:
https://github.com/cbaylis/luajit-aarch64

linaro某位同学的私人proposal:
https://github.com/zhongweiy/LuaJIT/commit/3fa648b6d8b7ecd2d06ce8397089f244564265bb
https://github.com/zhongweiy/LuaJIT/commit/dc3bd1626b4c28da7aa58da30bbd174e73badb65
https://www.freelists.org/post/luajit/fix-lightud-type-for-48bit-virtual-address
https://www.freelists.org/post/luajit/Proposals-for-fixing-light-userdata-issue-on-virtual-address-47-bits-platform

linaro effort to port LuaJIt to AARM64: Sep 29, 2016
https://www.youtube.com/watch?v=ZTtCHF4FoqM&feature=youtu.be

综上所收集的社区状态,该bug只能通过软件适配修改。作者Mike Pall不准备给luajit做任何的workaround来支撑该问题的解决。

下一步要按照Mike的意见,使用ffi-binding来替换lightuserdata问题。JIT编译器的代码最好保证不动为妙(luajit代码相当难阅读。lua+c+asm)

torch7要改的代码涉及如下(已经将其列入issue讨论列表:https://github.com/torch/torch7/issues/1035)

01 update_on_definition in ctype.c (extraluaffifb) :
lua_pushlightuserdata(L, &to_define_key); 02 update_on_definition in
ctype.c (extraluaffifb) : lua_pushlightuserdata(L, &to_define_key);
03 set_defined in ctype.c (extraluaffifb) : lua_pushlightuserdata(L,
&to_define_key); 04 set_defined in ctype.c (extraluaffifb) :
lua_pushlightuserdata(L, &to_define_key); 05 push_upval in ffi.c
(extraluaffifb) : lua_pushlightuserdata(L, key); 06 set_upval in
ffi.c (extraluaffifb) : lua_pushlightuserdata(L, key); 07
ffi_metatype in ffi.c (extraluaffifb) : lua_pushlightuserdata(L,
&user_mt_key); 08 push_user_mt in ffi.c (extraluaffifb) :
lua_pushlightuserdata(L, &user_mt_key); 09 luaopen_libcutorch in
init.c (extracutorch) : lua_pushlightuserdata(L, state); 10 hookf in
ldblib.c (exeluajit-rockslua-5.1src) : lua_pushlightuserdata(L,
(void )&KEY_HOOK); 11 hookf in ldblib.c (exeluajit-rockslua-5.1src)
: lua_pushlightuserdata(L, L); 12 gethooktable in ldblib.c
(exeluajit-rockslua-5.1src) : lua_pushlightuserdata(L, (void
)&KEY_HOOK); 13 gethooktable in ldblib.c
(exeluajit-rockslua-5.1src) : lua_pushlightuserdata(L, (void
)&KEY_HOOK); 14 db_sethook in ldblib.c (exeluajit-rockslua-5.1src)
: lua_pushlightuserdata(L, L1); 15 db_gethook in ldblib.c
(exeluajit-rockslua-5.1src) : lua_pushlightuserdata(L, L1); 16
hookf in lib_debug.c (exeluajit-rocksluajit-2.1src) :
lua_pushlightuserdata(L, KEY_HOOK); 17 LJLIB_CF in lib_debug.c
(exeluajit-rocksluajit-2.1src) : lua_pushlightuserdata(L,
KEY_HOOK); 18 LJLIB_CF in lib_debug.c
(exeluajit-rocksluajit-2.1src) : lua_pushlightuserdata(L,
KEY_HOOK); 19 lj_cf_package_require in lib_package.c
(exeluajit-rocksluajit-2.1src) : lua_pushlightuserdata(L,
sentinel); 20 lj_api.c (exeluajit-rocksluajit-2.1src) line 697 :
LUA_API void lua_pushlightuserdata(lua_State L, void p) 21 ll_require
in loadlib.c (exeluajit-rockslua-5.1src) : lua_pushlightuserdata(L,
sentinel); 22 lua.h (exeluajit-rockslua-5.1src) line 170 : LUA_API
void (lua_pushlightuserdata) (lua_State L, void p); 23 lua.h
(exeluajit-rocksluajit-2.1src) line 171 : LUA_API void
(lua_pushlightuserdata) (lua_State L, void p); 24 lua.h
(exeluajit-rocksluarockswin32lua5.1include) line 170 : LUA_API
void (lua_pushlightuserdata) (lua_State L, void p); 25 lua.h
(exeluarockswin32lua5.1include) line 170 : LUA_API void
(lua_pushlightuserdata) (lua_State L, void p); 26 lua.h
(installinclude) line 171 : LUA_API void (lua_pushlightuserdata)
(lua_State L, void p); 27 luaT_iscdata in luaT.c (pkgtorchlibluaT)
: lua_pushlightuserdata(L, CDATA_MT_KEY); 28 luaT_iscdata in luaT.c
(pkgtorchlibluaT) : lua_pushlightuserdata(L, CDATA_MT_KEY); 29
json_process_value in lua_cjson.c (extralua-cjson) :
lua_pushlightuserdata(l, NULL); 30 lua_cjson_new in lua_cjson.c
(extralua-cjson) : lua_pushlightuserdata(l, NULL); 31 parse_record in
parser.c (extraluaffifb) : lua_pushlightuserdata(L, &g_name_key); 32
parse_record in parser.c (extraluaffifb) : lua_pushlightuserdata(L,
&g_name_key); 33 append_type_name in parser.c (extraluaffifb) :
lua_pushlightuserdata(L, &g_name_key); 34 append_type_name in parser.c
(extraluaffifb) : lua_pushlightuserdata(L, &g_front_name_key); 35
append_type_name in parser.c (extraluaffifb) :
lua_pushlightuserdata(L, &g_back_name_key); 36 find_canonical_usr in
parser.c (extraluaffifb) : lua_pushlightuserdata(L, &g_name_key); 37
find_canonical_usr in parser.c (extraluaffifb) :
lua_pushlightuserdata(L, &g_front_name_key); 38 find_canonical_usr in
parser.c (extraluaffifb) : lua_pushlightuserdata(L,
&g_back_name_key); 39 find_canonical_usr in parser.c (extraluaffifb)
: lua_pushlightuserdata(L, &g_name_key); 40 calculate_constant1 in
parser.c (extraluaffifb) : lua_pushlightuserdata(L, P); 41
add_tmpname in paths.c (pkgpaths) : lua_pushlightuserdata(L,
(void)tmpnames_key); 42 add_tmpname in paths.c (pkgpaths) :
lua_pushlightuserdata(L, (void)tmpnames_key); 43 luaQ_setup in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void)engineKey); 44 luaQ_setup in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void)d); 45 luaQ_setup in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void)metaKey); 46
luaQ_setup in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void)signalKey); 47 luaQ_setup in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void)objectKey); 48 luaQ_setup in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void)qtKey); 49 luaQ_private_noerr in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void)engineKey); 50 QtLuaEngine::Private::~Private in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void)engineKey); 51
luaQ_pushqt in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void)qtKey); 52 luaQ_buildmetaclass in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void)qtKey); 53 luaQ_buildmetaclass in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void)qtKey); 54
luaQ_pushmeta in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void)metaKey); 55 luaQ_pushmeta in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void)((((size_t)type)<<1)|1)); 56 luaQ_pushmeta in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)((((size_t)type)<<1)|1)); 57 luaQ_pushmeta in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void*)qtKey); 58
luaQ_pushmeta in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)metaKey); 59 luaQ_pushmeta in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)mo); 60 luaQ_pushmeta in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)mo); 61 luaQ_pushmeta in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)qtKey); 62 luaQ_pushqt in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)objectKey); 63 luaQ_pushqt in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)obj); 64 luaQ_pushqt in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)obj); 65
QtLuaEngine::Receiver::universal in qtluaengine.cpp (exeqtluaqtlua)
: lua_pushlightuserdata(L, (void*)signalKey); 66
QtLuaEngine::Receiver::universal in qtluaengine.cpp (exeqtluaqtlua)
: lua_pushlightuserdata(L, (void*)this); 67
QtLuaEngine::Private::processQueuedSignals in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void*)signalKey); 68
QtLuaEngine::Private::processQueuedSignals in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void*)this); 69
QtLuaEngine::Private::processQueuedSignals in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void*)signalKey); 70
QtLuaEngine::Private::processQueuedSignals in qtluaengine.cpp
(exeqtluaqtlua) : lua_pushlightuserdata(L, (void*)receiver); 71
luaQ_connect in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)signalKey); 72 luaQ_connect in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)r); 73 luaQ_disconnect in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)signalKey); 74 luaQ_disconnect in
qtluaengine.cpp (exeqtluaqtlua) : lua_pushlightuserdata(L,
(void*)r); 75 luaQ_disconnect in qtluaengine.cpp (exeqtluaqtlua) :
lua_pushlightuserdata(L, (void*)r);

更新:ARM64的适配问题,大Nvidia厂用自家的JetPack解决。
https://jkjung-avt.github.io/torch7-on-tx1/

鉴于作者Mike Pall建议使用LightUserData数据类型的用户修改为FFI(Foreign Function Interface)组件。修改工作量不少。

然而,Mike Pall 也忽略了FFI本身也使用了LightUserData。此外lua自己的api也使用了这种数据类型。

1 1,lightuserdata is a legacy data type, primarily intended for the
classic Lua/C API. And even there, it doesn't have many good use
cases.It's easy enough to work around the 47 bit limit. 2 2,The
Solaris/x64 community has had the same problem, due to their use of
the negative part of the address space in user mode. Have a look what
they did to change the (few) libraries that didn't play nicely。 3
3,lightuserdata used as a unique table key, e.g. for the registry:
with lua_pushlightuserdata(L, &x)and x on the C stack, replace x with
a static variable. 4 4,lightuserdata used as some kind of cross-object
pointer: don't do that -- it doesn't play nicely with the GC, anyway.
Replace with proper userdata references. 5 5,lightuserdata used as a
misguided userdata replacement, possibly even setting the
lightuserdatametatable (ugh): throw away any such library, replace
with a proper FFI binding.

lua_cpcall:因为lua_cpcall内部使用了light user
data,所以在48bit虚地址情况下这个API会有问题。而且这种内部使用light user
data的情况,FFI也帮不上忙。与它类似的还有luaJIT_setmode API。具体一些的细节可以参看
https://www.freelists.org/post/luajit/Proposals-for-fixing-light-userdata-issue-on-virtual-address-47-bits-platform

解决策略:

1, 修改FFI的内部函数。对于调用到lua_pushlightuserdata的接口,使用静态变量替代。
2, 修改lua_cpcall接口,增加一个新接口,避免使用lightuserdata.
3 , 修改torch所有使用到的该数据类型的接口,使用ffi和新的api规避lightsuerdata。能最大程度上保证luajit虚拟机的稳定性和性能。

ps:还有最后一个方法。。。期待第二个Mike Pall出现。。。Peter Cawley会是那个人???

   
  LuaJIT的虚拟机代码真的不是那么好看和好改。应该是神作,当前最精巧最快速小巧和可移植的JIT了。
   
  拜Mike!。

1, 设备文件/dev/uio0 , /dev/uio1

2, 通过文件映射到设备的寄存器,通过mmap实现。

3, 接下来访问设备寄存器变成了read、pread、write、pwrite的操作。

4, 比如读寄存器。

5,提到一个缺点:页表需要手动偏移,offset = N * getpagesize();

文章地址:The Userspace I/O HowTo
关于ARMV8的内存管理架构有一篇不错的文章:Address spaces in Armv8-A

  • 一般说栈的增长方向从高到低,是说每个函数调用的栈帧从高地址向低地址增长。
  • 在调用函数内部,变量还是从底地址向高地址安排变量。
  • 在main函数内部,一般情况下也是从低到高的分配变量,但是这个不是固定现象,并不是按照声明顺序分配,编译器会考虑变量对齐的问题。在某些情况下把变量的顺序打散。(不知道什么原因。)

整数在计算机中的表达

本质上是一个编码问题。

有符号数:在计算机中的表达非常有意思,体现了计算机科学的底层编码基本功。

如果一个N位的有符号数x,它的数学表达公式为:

公式一:有符号数的运算公式

$f(x)= -x_{N-1} \cdot 2^{N-1} + \sum_{n=0}^{N-2}x_{n}\cdot 2^n$

$x_{n}$ 分别代表每一个位,分别取值 1 或 0 。

注意到最高位上面默认带了一个负值,这意味有符号的数最高位$$x_{N-1}$$是正数时 $$x_{N-1}==0$$,这样有符号和无符号的正数在计算机里看到的编码统一起来。

在负数情况下,CPU的运算器可以很快的利用通用运算法则计算出二进制所表达的数值。如: (1,111, 1111)在CPU寄存器设置符号位的情况下,利用公式一可以计算出他的实际计算值: -1。 (很多教材上只告诉大家最高位是符号位,但是从来没告诉大家符号位实际上参与了计算。)

另外一点值得一提的是,因为负数用反码表示。多出了一个编码。

自然的负数编码思维: $$1000,0000 ~ 1111,111$$,表达 -0 ~ -127 共128个数。

自然的正数编码思维: $$0000,0000 ~ 0111,1111$$,表达 0 ~ 127 共128个数。

来看看这种人类思维的问题,一步一步的提问解决:

  • 1000,0000 和 0000,0000 在自然世界里没有意义,是属于冗余编码。他们两个编码中必然有一个可以拿出来另做他用。
  • 看起来0单独用0000,0000表达很符合逻辑,但这个 1000,0000怎么才能重复利用呢。
  • 用来表达 -127 的 1111,1111 在计算机里 +1 的话变成了 1000,0000,这就溢出变成我们的 -0了。正常情况下应该是 -126 才对。再 +1 变成 $$(1000,0001)_{b} = (-1)_{d} \ne (-125)_{d}$$
  • 这种编码看来满足不了需求。

解决方案

1,让 -1 + 1 = 0 在计算机中计算二进制非常的自然。如果 0000,0000表示0 。那么 -1 应该表示为 1111,1111。依次减1,最后一个负数应该是1000,0000。那么应该是表示了-128 到 -1 这些负数。

2,这就是补码,使用two's complement 补码来给负数编码。让计算机的算术计算看起来就这么自然。

3,补码有一个运算规则,计算机的前辈们发现的。相应的转换关系可以通过原码和补码来转换。 原码= 反码 + 1。

4, 一直以来,我只是记住了这个计算公式。但没有深入理解其背后的思考过程。

有一本编码的书,详细的解释了各种编码的规则。

安全

  • 符号整数和非符号整数的互相转换
  • 加一溢出
  • 整数翻转
  • 负数移出
  • 乘积溢出

当处理乘积问题时,有一个很好的实践。为乘积分配一个两个因子中最大的那个数的两倍。

对无符号数来说:$$(2^n-1) \cdot (2^n-1) = 2^{2n} - 2^{n+1} +1 < 2^{2n} $$

对有符号数来说:$$(2^{n-1}) \cdot (2^{n-1}) = 2^{2n-2} - 2^{n+1} +1 < 2^{2n} $$

有一组很好的胶片来说明这个问题。

考察auto推导规则: 1、对于指针类型,声明为 auto、auto* 是没有区别的。 2、声明引用类型,必须使用
auto &。 3、不能得到推导变量的 const、volatile 属性

技巧:基类指针指向派生类是一种常用操作。
dynamic_cast 在这种场景下,他会检查这个指针是不是真的指向一个派生类对象。如果是一个派生类对象,这种转型是合法的,否则是失败的。

dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

作用:将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理,

**对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;
对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。**

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

当我们在头文件中定义了const变量后,每个包含该头文件的源文件都有了自己的const变量,其名称和值都一样。

如果const变量不是用常量表达式初始化,那么它就不应该在头文件中定义。相反,和其他的变量一样,该const变量应该在一个源文件中定义并初始化。应在头文件中为它添加extern声明,以使其能被多个文件共享。

如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字

static变量也是仅在文件中有效

但是extern跟static是不允许同时出现的

const对象默认为文件的局部变量。《C++ Primer 4》p86,

头文件不应该含有定义这一规则,有三个例外。头文件中可以定义类、值在编译时就已知道的const对象(数组的大小可以用const int型变量定义,这在C中是不行的),和inline函数。《C++ Primer 4》p100,在补充一个,模板"必须"定义在头文件中,包括模板类和模板函数。

定义和声明的区别:定义只可以出现一次,而声明则可以出现多次。下面这些是定义(怎样判断的?),都不应该出现在头文件中:
extern int ival = 10;
double fica_rate;

头文件中不可以放变量的定义!一般头文件中只是放变量的声明,因为头文件要被其他文件包含#include,如果把定义放在头文件的话,就不能避免多次定义变量。C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次。
三个例外:1)值在编译时就已知的const变量的定义可放在头文件中,如:const int num=10;

      2)类的定义可放在头文件中。
      3)inline函数

摘自《C++ Primer 4nd》

自然对齐(数据类型的长度)

1)对于char型数据,自然对齐的长度为1字节,short为2字节,int为4字节,float也是4字节,当然这些数据类型的对齐长度可能和编译器有关

2)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值

指定对齐

#pragma pack(n)中n值,如果没有指定,则为CPU默认对齐值,32位 默认4字节对齐,64位默认8字节对齐

有效对齐

自然对齐和指定对齐的最小值

结构体对齐步骤

  1. 每个成员变量分别按自己的“有效对齐”作对齐。确定每个成员的有效对齐。逐个添加对应的padding。
  2. 取所有成员变量的“有效对齐”的最大值,作为结构体的“有效对齐”
  3. 结构体成员及结构体自己的起始地址和长度均为其“有效对齐”的整数倍

基本分块

.text 指令语句
.data 初始化不为零额数据
.bss 初始化为零或未初始化。预留符号位置,不占据空间。
.rdata 字符常量区,const字符串

程序的局部性原理应用

  • 数据和指令缓存分离
  • 分离对Cache的命中有好处。
  • 只需要一份指令,但需要多份不同的数据副本。共享库的原理,将指令独立。

gdb的阅读顺序,把32个bit/4个Bytes/1 word作为一个显示单元。每行显示4个Word/128bit/16个bytes。这样第一列的地址可以按照16bytes的地址做偏移。(1~F)

x/20 puc
0x7ffeb6f45f20:    0x00002601    0x00000000    0x00000000    0x00000000
0x7ffeb6f45f30:    0x6c6c6568    0x0000006f    0xf65e2500    0xbb846972
0x7ffeb6f45f40:    0xb6f46030    0x00007ffe    0x00000000    0x00000000
0x7ffeb6f45f50:    0x00406e60    0x00000000    0xf4226830    0x00007f5d

内存的阅读顺序反过来。

01 26 00 00 00 00 00 00

   unsigned char puc[4];

struct tagPIM
{

 unsigned char pim;
 unsigned char pim1:1;
 unsigned char pim2:2;
 unsigned char pim3:3;

}*pstPimData;

pstPimData = (struct tagPIM *)puc;
memset(puc,0,4);
pstPimData->pim = 1;
pstPimData->pim1 = 2;
pstPimData->pim2 = 3;
pstPimData->pim3 = 4;

printf("%02x %02x %02x %02x n", puc[0],puc[1],puc[2],puc[3]);

这种表达竟然是合法的,是 ISO 9899:2011 C 的标准语法。但因为可移植性的原因,并不被推荐使用。

StackOverFlow的这篇回答说明了 multi-character character 字符常量的意义。

比如:

 short c = 'bc';
 short c = '\x62\x63';

在内存里依次为 0x63 0x62; 按照小端顺序从右到左的在内存中存储。

如果是:

char c = 'abcdefg';

内存中 abcdefg 程序不会写到内存中,编译器只会将 'g' 赋值给c。

利用这种方式可以写一个很精简的大小端测试程序。

char c= 'x01x02';
bool big_endian = ( (c=='x01')?(1):(0) );

异步信号安全的函数一定是线程安全的函数。异步不安全的函数一定使用了静态、全局变量。
线程安全说的并发无误。可以用锁来实现。

可重入 = 线程安全 + 中断安全。

malloc,free是不可重入的。

strok,strdup是异步不安全的,不可重入的。 strok也不是线程安全的。

socket,read,gethostbyname,gethostbyaddr 等是可重入的。

文章很好的讨论这些概念。

总的来说,大小端的战争来源于人类的阅读顺序。

人类可视的数据表现形式为: 0x0810

  • 计算机的世界: 内存里排列,按照低地址到高地址为:0x10, 0x08。在内存的排列里,程序去读的时候

    • 小端的计算机世界: 读取是从低地址开始。
    • 大端的计算机世界: 读取也是从低地址开始。:)
    • 机器根本不认识大小端,他只是按照顺序从低地址向高地址读字节。

假设ptr指向的值为0x20150801。当我们从某个地址强转为一个结构体时: int a = (int)ptr;
在大端的机器里,它读取的顺序是 0x20 0x15 0x08 0x01 , 按照地址从低到高。 这是写的顺序,机器帮我们做了。
在小端的机器里,它读取的顺序是 0x01 0x08 0x15 0x20 , 按照地址从低到高。 这是写的顺序,机器帮我们做了。

真正要区分大小端是在读的过程中:
那么如果要将ptr翻译为一个short。 short b = (int)ptr;

  • 则在大端的机器里,b = 0x2015. 解释为:0x20 << 8 + 0x15 . 高位数据在低地址,我们就得按照这个规则还原数字。
  • 在小端的机器里, b = 0x0801, 解释为: 0x08 << 8 + 0x01. 低位数据在高地址,我们按照规则还原。

也就是说第一个区别,同一行代码在不同机器上得到的会是不同的结果。

人类的世界: 人类的世界阅读是 0x20, 0x15, 0x08, 0x10。这是合乎我们直觉的大端模式。所以网路中的http报文总是按照大端传输自己的数据,比如一个端口号 0x8088,传到小端机器上会被解释为 0x88 << 8 + 0x80 。数值反转过来。传到大端机器上则是 0x80 << 8 + 0x88

总结为两点:

  1. 如果在内存里 x address 观察指针地址,大小端会有差异。
  2. 大小端的差异存在对表现层的text进行解析的时刻。序列化阶段要还原成原来字段的意义。

这篇文章很好的解释了这个问题。

我们可以在Qemu上模拟大端系统行为。这里有一个PPC的大端QEMU镜像。

qemu-system-ppcw -hda debian_wheezy_powerpc_standard.qcow2 -L bios -m 1G -g 1024x768x8

后发现一个很有意思的CPU EMULATOR,基于QEMU。名字叫Unicore

代码的安全漏洞分析,需要分析人员格外细心。粗心大意是导致漏洞的根本原因。

首先要学会各种套路,常常会犯的错误。要总结下来。

其次才是具体业务场景的逻辑分析。安全问题通常隐藏在业务的实现细节里。即使你熟悉套路,未必能找到。

这样说来,更像一个挑战者游戏。

Linux 的realpath函数竟然存在这种低级漏洞

一些事实

大家都知道在计算机里,正数的最高为0,负数的最高位为一。
这个个非常有趣的事情。有一本《编码》的书曾专门谈论这些问题。

正负与中国传统阴阳的关系

最高位定位为阴阳位。占位为阴,空位为阳。(现实生活拟物思维不一样,一般1为男性,0为女性。不过占位为1的思想更原始,1表示有,0表示无。)
剩下的数位都是:000,0000 ~ 111,1111 的递增。
这种编码方法,很自然的每个数加1进位。按照自然整数递增往上。

先看计算机对负数的表达:

表示自然数阴阳属性
1000,0000-1281个1,7个0,极阴。符号为阴
1000,0001-127
1000,0010-126
......
......
1111,1110-2
1111,1111-1
0000,00000万物初生,生物最开始都是从细胞分裂开始。亦阳亦阴
0000,00011阳。符号位为阳
0000,00102
0000,00113
......
......
0111,1111...极阳

为什么函数里通常以 -1 作为错误码。

因为 -1 在内存中显示为,1111,11111。非常有特征。
gdb查看,或者一些错误捕捉能很快的让人记住。
计算机的前辈们应该就是看到了这个特点。
从POSIX,标准库等函数的使用都可以看到这个影子。