SPDY协议介绍

SPDY的主页: http://www.chromium.org/spdy

我主要看的是SPDY Protocol Drafts 3,这个草稿现在还没完成,google的人将它放在github上面: http://mbelshe.github.com/SPDY-Specification/

阅读全文

c reference manual读书笔记(四)

主要是介绍c语言中的类型

1 c语言里面分为数值类型(Arithmetic,包括整数,浮点.enum 等),指针类型,数组类型,结构类型,联合类型,函数类型以及void类型。c99中添加了_Bool _Complex _Imaginary三种新的类型。

2 标准c只定义了整数类型的最小精度。char至少8位,short/int至少16位,long至少32位。 long long至少64位。所有的范围都保存在limits.h中。

3 一个有符号的整数所能表示的范围,不仅依赖于位数,还依赖于2进制编码,比如现在的计算机一般都是使用2进制补码的表示(two’s complement)。这里要注意整数类型默认都是signed.

4 一个有符号的整数和无符号的混合的表达式(四则运算,比较等),都会先将有符号的整数转为无符号整数再进行。而所有的无符号的算术运算,最后都会把结果对2的n次方取摸。

阅读全文

linux下tcp选项TCP_DEFER_ACCEPT详解

TCP_DEFER_ACCEPT这个选项可能大家都知道,不过我这里会从源码和数据包来详细的分析这个选项。要注意,这里我所使用的内核版本是3.0.

首先看man手册中的介绍(man 7 tcp):

TCP_DEFER_ACCEPT (since Linux 2.4)

Allow a listener to be awakened only when data arrives on the socket. Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection. This option should not be used in code intended to be portable.

我先来简单介绍下,这个选项主要是针对server端的服务器,一般来说我们三次握手,当客户端发送syn,然后server端接收到,然后发送syn + ack,然后client接收到syn+ack之后,再次发送ack(client进入establish状态),最终server端收到最后一个ack,进入establish状态。

而当正确的设置了TCP_DEFER_ACCEPT选项之后,server端会在接收到最后一个ack之后,并不进入establish状态,而只是将这个socket标记为acked,然后丢掉这个ack。此时server端这个socket还是处于syn_recved,然后接下来就是等待client发送数据, 而由于这个socket还是处于syn_recved,因此此时就会被syn_ack定时器所控制,对syn ack进行重传,而重传次数是由我们设置TCP_DEFER_ACCEPT传进去的值以及TCP_SYNCNT选项,proc文件系统的tcp_synack_retries一起来决定的(后面分析源码会看到如何来计算这个值).而我们知道我们传递给TCP_DEFER_ACCEPT的是秒,而在内核里面会将这个东西转换为重传次数.

这里要注意,当重传次数超过限制之后,并且当最后一个ack到达时,下一次导致超时的synack定时器还没启动,那么这个defer的连接将会被加入到establish队列,然后通知上层用户。这个也是符合man里面所说的(Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection.) 也就是最终会完成这个连接.

阅读全文

nginx中upstream的设计和实现(五)

这次主要来分析upstream中的发送数据给client, 以及当buf不足,将一部分写到temp file的部分,他们对应的函数分别是ngx_event_pipe_write_to_downstream和ngx_event_pipe_write_chain_to_temp_file.

先来看ngx_event_pipe_write_to_downstream,这个函数顾名思义,就是写buf到临时文件。而所写的buf就是p->in,也就是将要发送给client的数据。

这个函数,它会处理两类的情况,一类是cache打开,一类是cache未打开。我们这里主要来分析cache关闭的情况。

首先来看这个函数的第一部分的代码,这部分代码主要是遍历p->in,然后计算能写多少buf到文件(temp file的size是有限制的).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  
//out就是将要保存到file的数据

if (p->buf_to_file) {

//cache打开的情况

fl.buf = p->buf_to_file;

fl.next = p->in;

out = &fl;

} else {

//得到数据

out = p->in;

}

//如果cache没有打开

if (!p->cacheable) {

size = 0;

cl = out;

ll = NULL;

ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,

"pipe offset: %O", p->temp_file->offset);

//开始遍历out

do {

//计算大小

bsize = cl->buf->last – cl->buf->pos;

……………………………………………..

//看是否超过限制限制

if ((size + bsize > p->temp_file_write_size)

|| (p->temp_file->offset + size + bsize > p->max_temp_file_size))

{

break;

}

size += bsize;

ll = &cl->next;

cl = cl->next;

} while (cl);

ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);

if (ll == NULL) {

return NGX_BUSY;

}

//cl存在则说明只有一部分buf能够写入到temp file,此时p->in保存剩下的chain

if (cl) {

p->in = cl;

*ll = NULL;

} else {

//否则说明所有的buf都写入到了temp file,此时p->in则设置为空

p->in = NULL;

p->last_in = &p->in;

}

} else {

//cache打开的情况,可以看到和上面类似.

p->in = NULL;

p->last_in = &p->in;

}

阅读全文

nginx中upstream的设计和实现(四)

这此主要是分析发送数据到客户端的部分以及buffering状态下,nginx接收upstream数据的部分,这也是upstream的最复杂的部分,这里我还是忽略了cache部分,以后我会专门写blog来分析nginx的cache部分。

这部分的函数入口是ngx_http_upstream_send_response,这里有一个很重要的标记,那就是u->buffering,这个标记的含义就是nginx是否会尽可能多的读取upstream的数据。如果关闭,则就是一个同步的发送,也就是接收多少,发送给客户端多少。默认这个是打开的。也就是nginx会buf住upstream发送的数据。

不管buffering是否打开,后端发送的头都不会被buffer,首先会发送header,然后才是body的发送,而body的发送就需要区分buffering选项了。如下图所示:

upstream_ac

阅读全文

c reference manual读书笔记(三)

这次主要是针对c语言中的声明(declaration)定义.

1 在c语言中声明的作用域(scope)可以分为4种,分别是 block/local scope, prototype scope, function scope以及file scope.其中file scope是最顶层的域. 如果一个声明不是external的,则默认这个声明就是被限制在它所出现的文件中.

2 一个标示符在它被完全声明之前是不能使用的。更精确的说就是从我们定义一个声明到这个声明的结束之间不能出现这个标示符.可是下面的例子是完全正确的,这是因为使用intsize(sizeof)之前,它已经被声明了:

1
2
  
static int intsize = sizeof(intsize)

而下面的例子则会编译出错,因为Test在使用之前还没有被声明.

1
2
  
typedef struct {Test *s} Test;

3 c语言中也支持forward reference,不过只允许下面三种类型,分别是 在goto语句中的label,label都是在goto语句之后声明的;还有就是允许在struct,union,array类型的完全声明之前使用当前的声明,比如下面的例子

1
2
  
typedef struct s {struct s *n} T;

最后一种例外是函数的声明,这个原因是在c99之前,c语言允许隐式(implicitly)的声明,也就是如果一个函数在调用之前没有被声明,则在函数被调用的地方会隐式的声明这个函数(后面会详细介绍这个)。不过c99是不允许隐式的声明的.

阅读全文

c reference manual读书笔记(二)

这次主要是预处理器和宏处理的部分。

1 预处理器主要是处理源代码中以#开头的行,预处理器执行完毕后的代码必定是一个合法的c程序.

2 预处理器的命令是完全不依赖于c语言的语法的.

3 预处理器不会parse源代码,预处理的词法处理和编译器的是不同的,预处理器能够理解合法的c标记,可是它也会忽略在c编译器中认为是不合法标记。比如下面的代码,对于预处理器来说,没有任何问题的.

阅读全文

c reference manual读书笔记(一)

我读的是第五版的影印版,简单的做了一些笔记.下面是第二章的笔记.

1 c 源文件的字符集(character set)包含在ISO/IEC 10646的Latin block中. 包括5种类型,分别是 52个lating大小写字母,10个数字(0-9),空格,horizontal tab(ht) vertical tab(VT) form feed(FF) 以及29个graphic 字符(这个可以去看c99的手册).而不在集合内的字符可能会出现在注释/字符常量/字符串常量/文件名中.在c中还有执行字符集(execution character)的概念,一般来说编译和运行都在相同的电脑,则source字符集和执行字符集都是相同的.

2 在c的源程序中,blank, end-of-line, vertical tab, form feed, horizontal-tab都会被认为是空格(whitespace characters).

3 c89中要求逻辑行的最大长度是509个字符,而c99是4095个字符.

4 c语言中可以使用 9个trigraphs(比如??/表示),而trigraphs的翻译会在词法分析之前(gcc默认编译没有打开trigraph,必须添加-trigraphs命令,或者使用-std命令指定c标准).

5 多字节字符(multibytes characters),主要针对非英语的环境.编码实现分为state-independent和state-depend,顾名思义,一个是编码依赖于前一个多字节字符,一个是不依赖于前一个多字节字符.

阅读全文

rfc 2988(Computing TCP’s Retransmission Timer)简介

rfc 2988是描述tcp如何计算定时器的一个rfc,是2000年发布的,而最近2988 已经被更新:

http://tools.ietf.org/html/draft-paxson-tcpm-rfc2988bis-02

并且google的jerry chu(2988bis的作者之一)最近在内核提交一个patch,主要是用来修改3次握手时的初始化rto的值(以及rfc 2988bis的一些改变),当前的内核默认是3,而patch修改这个值为1,主要的修改依据是rfc2988.

阅读全文

nginx fastcgi模块的一个bug

上周服务器更新到nginx的0.8.X之后,nginx出现了core dump的情况,而在0.7.X并不会出现,通过察看core dump文件以及nginx 0.8.x和0.7.x的比较,发现core dump是nginx 0.8.40引入下面这个feature才导致的:

*) Feature: a “fastcgi_param” directive with value starting with

“HTTP_” overrides a client request header line.

在nginx 0.8.40之后,如果你的fastcgi_param定义的变量以HTTP_开头,则传递给后端的头会忽略request header中的这个头,比如定义了一个 fastcgi_param $HTTP_HOST test, 那么传递给后端时,host这个头的值就是test.

这里的逻辑是这样子的,当nginx创建一个fastcgi request的时候,会先计算所需要的长度,首先是计算header的长度,在计算之前会先分配一个ignored数组(用来保存将要被忽略的头),它的大小是配置文件中fastcgi_param定义的以HTTP_开头的变量的个数. 然后遍历所有的request header,如果发现header的名字和fastcgi_param中定义的变量的(HTTP_开头)名字相同(使用hash),则将这个header指针放到ignored数组中,最后在拷贝request header的时候直接在这个数组里面查找,如果有则跳过,否则拷贝头以及它的值。

看起来没什么问题,可是这里忽略了request header有可能会有重复的这个情况,此时ignored数组可能就会越界,从而导致core dump.

阅读全文