杂乱无章

Begin at 2014.

1.make的内置规则

可以能过make -p来查看make的内置规则,改变一些内置的变量值,Makefile就不用写那么长了,例如LDLIBS表示要连接的库,CFLAGS表示一些编译选项(头文件搜索路径之类的),LDFLAGS表示链接选项。但要注意的是这些变量是全局的,如果某一条规则没有写实现,那就会受影响。

2.链接顺序

gcc a.o -lm -o out

gcc -lm a.o out

不一定都能链接通过,当a.o里有对m库的引用时。因为gcc的(-l)符号搜索只向后,不往前。Get used to it.

3.elf文件的.dyamic段中类型为NEEDED的值记录了该文件引用的动态链接库,猜想ldd就是用这个来显示的吧。

4.tcpdump表达式的数组下标从0开始,数组每一项的大小为1字节,例如下面的ip[0]&0xf就是取ip数据包的首字节的低4位,需要理清的一个概念是"首4位"和"首字节的低4位"是相反的,例如ip数据包的首字节,前4位是版本号,后4位是首部长度。而内存的最小单元是"字节",假设版本号是4,首部长度是5,则内存表示是:0x45,首4位是0100,首字节的低4位是0101。

tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

另外ip[2:2]表示整个第2字节,毕竟ip包头的元素单位是"位"。

5.gdb的x指令。help x可以看到详细说明。例如要以16进制形式打印变量n地址开始的4个字节,则x/4xb &n。

6.svn 1.7以上的校验和出错解决办法:

svn update --set-depth empty

svn update --set-depth infinity

详细解释在这里

7.lua源码对不定长参数的处理。

首先,lua函数可接受不定长参数:

function add(...)

    local s = 0

    for i, v in ipairs{...} do s = s + v end

    return s

end

进行一个lua函数调用,lua源码里首先调用luaD_precall准备好新的栈帧,然后调用luaV_execute执行函数原型里编译好的指令。通常新的栈帧最底部为函数闭包对象,然后就是固定参数(ci->u.l.base指向第一个固定参数在栈上的位置),但是不定长参数并不在固定参数之后,而是遗留在上一个栈帧的尾部(其实一开始所有参数都在那里,不过固定参数在函数调用开始前被移动到了新的栈帧),代码可参见adjust_varargs。

但为什么要这样做呢?或者说为什么好像如此简单的道理自己为什么不明白呢?——在新的函数里'...'是一个表达式,用于获取整个不定长参数,那样必要将固定参数与不定长参数作区分,看起来栈帧应该是[固定参数], [分隔符], [不定长参数],但是你就不能倒过来吗?[不定长参数] [分隔符] [固定参数],这样甚至不用特地添加分隔符了,栈帧底部的闭包对象就是天然的分隔符了。

8.tcp连接是在底层完成的,在listen调用之后永不调用accept,connect依然可以成功。

9.listen的backlog参数指的是完成三次握手而未被accept的连接的队列最大长度(the queue length for completely established sockets waiting to be accepted),而不是状态为SYN_RECV的连接队列长度(the number of incomplete connection requests),具体可参见手册的NOTE。下面是验证的服务端代码:

 1 #include <sys/types.h>

 2 #include <sys/socket.h>

 3 #include <netinet/in.h>

 4 #include <stdio.h>

 5 #include <unistd.h>

 6 #include <string.h>

 7 #include <stdlib.h>

 8 

 9 int main(int argc, char **argv) {

10     if (argc < 2) {

11         return 1;

12     }

13     int fd;

14     fd = socket(AF_INET, SOCK_STREAM, 0);

15     if (fd == -1) {

16         perror("socket");

17         return 1;

18     }

19 

20     struct sockaddr_in addr;

21     memset(&addr, 0, sizeof(addr));

22     addr.sin_family = AF_INET;

23     addr.sin_port = htons(12345);

24     addr.sin_addr.s_addr = htonl(INADDR_ANY);

25     if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {

26         perror("bind");

27         return 1;

28     }

29 

30     if (listen(fd, atoi(argv[1])) == -1) {

31         perror("listen");

32         return 1;

33     }

34 

35     sleep(1000);

36     return 0;

37 }

随便写个客户端连接上来,使用netstat会发现最多只有backlog+1(backlog对应队列的长度各个系统使用的策略不一样)个连接状态为ESTABLISHED,其它的连接状态都是SYN_RECV。虽说tcp_max_syn_backlog可以控制SYN_RECV连接队列的长度,但我设置之后似乎没有起作用。

还有一个注意的地方是,尽管服务端显示连接的状态是SYN_RECV,但客户端显示的状态却是ESTABLISHED,根据别人的说法是客户端把三次握手最后的ACK发过去就当作完事了,而服务端恰恰是在这个位置进行队列长度是否超出backlog的判断。

10.protobuf采用的编码方式是tag + len + value,其中tag由类型[wire type]和字段编号组成,len可选(固定长度的数据类型不需要)。对于在proto文件中定义的required字段,如果写入是缺少,没问题,但再解释的时候就会报错,采用的方式是在解释时将已经解释到的字段记录到_has_bits_,然后在解释结束时检测_has_bits_是否已经包含所有的required字段:

 1 //message_lite.cc

 2 bool InlineMergeFromCodedStream(io::CodedInputStream* input,

 3                                 MessageLite* message) {

 4   if (!message->MergePartialFromCodedStream(input)) return false;

 5   if (!message->IsInitialized()) {

 6     GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *message);

 7     return false;

 8   }

 9   return true;

10 }

 11.lua里只有nil和false是false,其他都是true,而python里空列表,空字典,零值等都是false.

12.gdb内存断点:watch(写), rwatch(读), awatch(读写),例如变量int n,则先找到n的地址,然后watch *(int *)xxxx

13.Makefile自动处理头文件依赖:http://learn.akae.cn/media/ch22s04.html

14.迭代器(iterator),PIL里有一节"True Iterators",里面说"iterators do not iterate, what iterates is the for loop",的确,迭代器需要循环的帮助才能产生遍历的效果。

15.lisp的一些猜想。

“Languages in which program code is represented as the language's fundamental data type are called 'homoiconic'. ”

lisp里的'code'和'data'都是用'表'来表示的,也就是所谓的code is data,例如:

(+ 1 3)这是'code',(1 2 3)这是'data'。

“This allows structured self-modifying code.”

其一: 'runtime modify',例如:

> (define a '(set! b 3))

> (eval a)

> b

3

> (set-car! (cdr (cdr a)) 4)

> (eval a)

> b

4

当然这需要eval的协助,目前我也不知道什么时候会用到这种方法。

其二: 'compile modify',方法是使用宏。那这又与c语言的宏有什么区别呢?区别在于实现的方式,而这也导致了lisp宏强大得多。c宏只不过是在预处理阶段进行的字符替换,而lisp宏却是在编译阶段执行的生成s表达式的运算,我想具体的过程大致会是:

(define result_code (macro_to_code source_code))



(set-car! code_fragment result_code)

能这样做除了"code is data"的功劳外,还基于lisp的编译器本身就是一个lisp程序——"lisp开发者如此热爱这个语言,因此他们用lisp本身来写它的编译器,因为先有解析器在。"

如果code is not data,则lisp不能操作code,如果编译器不是lisp,则code不能被处理,这就是所谓的——"爱是天时地利的迷信"。c语言你不能说它错了,只不过走的是另一条路罢了,想一想c语言怎么能处理code fragment呢。

 16.学一门语言,它是干什么的,有什么特色,为什么要这么设计;它是怎么实现的,为什么要这么实现。

17.过滤列表

http://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt

18.全局偏移表(global offset table)

由于共享模块有可能被多个可执行文件所引用,也就是会被映射到不同的虚拟进程地址上,所以共享模块的代码段需要被编译为地址无关码,例如gcc编译时指定-fPIC,即position independent code,具体来说是要使数据寻址及函数的调用不能使用绝对地址。

首先,模块内部的静态变量被放到.bss段或.data段,代码访问数据时通常是movl $0x1 xxx之类,由于xxx绝对地址不确定,所以必须先计算出xxx。由于.text与.bss或.data之间的偏移在模块编译后是能确定的,而xxx在自己所在段的偏移同样也是确定的,所以xxx = PC + 段偏移 + xxx段内偏移 - 当前指令段内偏移。地址无关码相比普通的编译多出的就是求址这一部分的代码。

然而,模块内部引用全局变量的方式却有所不同,假设全局变量定义在其他目标文件中: 

extern int a;

那么编译时不可能知道所谓的"其他目标文件"是否属于同一个模块,假若答案是否,那么上面的计算方式将失效,模块之间的偏移是不确定的。

另一种情况,全局变量定义在本目标文件中:

int a = 4;

模块内部的访问当然可以使用上述的计算方式,然而假如其他模块引用了本共享模块的全局变量怎么办呢?他们当然是可以使用extern int a;之类的声明啊!那些文件的编译以要怎么样去访问a呢?特别的一点是,如果可执行文件引用了这个变量要怎么办呢?可执行文件是不会使用-fPIC的,他只会一如既往的产生对绝对地址的引用,而为了使静态链接能顺利进行,可执行文件会将a放到.bss段去。

那么当共享模块被加载的时候,a就同时在一个进程的虚拟空间内存在两个副本了,并且各自为政。为解决这种情况,共享模块顾全大局使用了全局偏移表,在里面放一个指针,指向要使用的那个变量的地址,条件是优先使用可执行文件的.bss段的那个,当可执行文件没有引用过该变量时,地址将会是自己模块内的那个。所以说,不要过多的使用全局变量。可以想像,这种方式对定义在其他目标文件中的全局变量也适用。

说完数据的引用,其实函数的调用也差不多就是如此了。

19.golang手动安装第三方包

有些时候go get xxx不管用,或者是git没有安装,或者是安装了但不支持https,那么直接把xxx下载回来,放到src下,然后在import的时候使用正确的路径就可以了。

20.why function programming matters

The most important difference between structured and unstructured programs is that structured programs are designed in a modular way.

Modular design brings with it great productivity improvements. First of all, small modules can be coded quickly and easily. Second, general-purpose modules can be reused, leading to faster development of subsequent programs. Third, the modules of a program can be tested independently, helping to reduce the time spent debugging. 

However, there is a very important point that is often missed. When writing a modular program to solve a problem, one first divides the problem into subproblems, then solves the subproblems, and finally combines the solutions. The ways in which one can divide up the original problem depend directly on the ways in which one can glue solutions together. Therefore, to increase one’s ability to modularize a problem conceptually, one must provide new kinds of glue in the programming language. 

This is the key to functional programming’s power — it allows improved modularization. It is also the goal for which functional programmers must strive — smaller and simpler and more general modules, glued together with the new glues we shall describe.


21.cocos2dx对opengl的封装

基本的viewport和vao,vbo在

void Director::setOpenGLView(GLView *openGLView)

_renderer->initGLView();

各个glProgram在

 void GLProgramCache::loadDefaultGLPrograms()

在renderer目录下可以找到各种shader

正交投影矩阵在

 void Director::setProjection(Projection projection)

22.lua的异常处理

lua的调用顺序是c->lua->c->lua...一直循环,但无论如何都是从c开始的,所以才叫做extention language。lua内部遇到异常时(例如对nil进行调用)时会调用longjmp,所以如果不想一有异常就退出进程的话,在最顶层的c调用要设置好setjmp,做法是使用lua_pcall去调用lua层。当然在lua层想要自己捕捉异常也是可以的,使用pcall或pcallx就可以了。另外除了lua本身自动抛出异常,自己编写lua代码可以使用error,编写c代码可以使用lua_error去主动抛出异常。

你可能感兴趣的:(杂乱无章)