Category Archives: 学科基础

computer sience fundermantals

理解http中的chunk编码

概述

http的chunk编码(分块传输编码)是HTTP/1.1(RFC2616)中新型的传输编码,是为了解决服务器动态产生的内容的传输问题. 一般情况下,HTTP的response是整体发送给客户端的,头中的Content-Length表示这个消息体的长度.数据的长度很重要,因为这直接决定了客户端如何正确解析返回的http response(应答). http的chunk编码就是把这一个整体的response分成多块数据进行传输,每块数据都有数据长度和具体的数据,最后一个数据块是0;具体的格式下面解释.

符号和名词定义

这里使用扩展的巴科斯范式(Augmented BNF)来描述下面的一些正式用语和语法.

chunk:分块;
CR = <US-ASCII CR, carriage return (13)>,ASCII表中的归位键;
LF = <US-ASCII LF, linefeed (10)>,ASCII表中的换行键;
CRLF = CR LF,一般是"\r\n";
HEX  = "A" | "B" | "C" | "D" | "E" | "F"
			| "a" | "b" | "c" | "d" | "e" | "f" | DIGIT |
OCTET = <any 8-bit sequence of data>

*rule,零条或者多条rule;完全的表达式"<n>*<m>rule",最少n条,之多m条rule,例如"*(rule)",代表任意数目的rule,包括0;"1*(rule)"代表至少有1条rule;"1*2rule",允许1条或者2条rule.

[rule],代表此条rule是可选的,例如"[foo bar]",等价于"*1(foo bar)";
"literal",字符串"literal",除非特别声明,要不然默认大小写敏感;

rule1 | rule2,或者的关系,rule1或者rule2.

英文版:

CR             = <US-ASCII CR, carriage return (13)>
LF             = <US-ASCII LF, linefeed (10)>
CRLF           = CR LF
HEX            = "A" | "B" | "C" | "D" | "E" | "F"
                      | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
OCTET          = <any 8-bit sequence of data>

*rule
      The character "*" preceding an element indicates repetition. The
      full form is "<n>*<m>element" indicating at least <n> and at most
      <m> occurrences of element. Default values are 0 and infinity so
      that "*(element)" allows any number, including zero; "1*element"
      requires at least one; and "1*2element" allows one or two.

[rule]
      Square brackets enclose optional elements; "[foo bar]" is
      equivalent to "*1(foo bar)".

rule1 | rule2
      Elements separated by a bar ("|") are alternatives, e.g., "yes |
      no" will accept yes or no.

更多请看 http://tools.ietf.org/html/rfc2616#section-2.2

格式

非正式格式:

chunk消息体 = (零个或者多个chunk) + 最后一个chunk + trailer + CRLF

正式格式:

Chunked-Body   = *chunk
                 last-chunk
                 trailer
                 CRLF

chunk          = chunk-size [ chunk-extension ] CRLF
                 chunk-data CRLF
chunk-size     = 1*HEX
last-chunk     = 1*("0") [ chunk-extension ] CRLF

chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val  = token | quoted-string
chunk-data     = chunk-size(OCTET)
trailer        = *(entity-header CRLF)

解释:一个chunk编码块是由0个或者多个chunk块组成,每个chunk块有长度和数据,并且结束块是0,最后加一个CRLF;trailer很少见,可以不管.

例子

数据包中的数据:

4\r\n
Wiki\r\n
5\r\n
pedia\r\n
E\r\n
 in\r\n
\r\n
chunks.\r\n
0\r\n
\r\n

解释:第一个数据块长度为4,数据内容为”Wiki”;第二个数据块长度为5,数据内容为”pedia”;第三个数据块长度为E(十六进制,十进制是14),数据内容为” in\r\n\r\nchunks.”;”0\r\n”是最后一个数据块.

浏览器显示的数据:

Wikipedia in

chunks.

一个发chunk编码的简单http server

code

代码贴在github gist: https://gist.github.com/inix/d682e4e72cb6aea16132

use IO::Socket::INET;
$| = 1;

#response头部
my $response_hdr = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nServer: perl tcp server\r\nTransfer-Encoding: chunked\r\nConnection: Keep-Alive\r\n\r\n";

#创建socket,并且监听8888端口
$socket = new IO::Socket::INET (
LocalHost => '0.0.0.0',
LocalPort => '8888',
Proto => 'tcp',
Listen => 5,
Reuse => 1
) or die "ERROR in Socket Creation : $!\n";

while(1)
{
    #循环等待client发完请求
    $client_socket = $socket->accept();
    $request = "";
    while(defined($bf = <$client_socket>)) {
        $request.=$bf;

        #两个\r\n代表请求头发完,详细请阅读rfc2616
        if ($request =~ /\r\n\r\n/ and $request =~ /GET/) {
            print "we got request \"$request\"\n";
            last;
        }
    }

    #开始返回response给client
    print $client_socket "$response_hdr"; #打印头
    for(my $i = 0;$i < 5;$i ++){ #打印5个数据块,每个数据块长度是6,数据块内容为"1</br>"
        print $client_socket "6\r\n1</br>\r\n";
    }
    print $client_socket "0\r\n\r\n"; #打印最后的一个数据块

    # notify client that response has been sent
    shutdown($client_socket, 1);
}

$socket->close();

运行代码:

perl ./chunk-tcp-server.pl

访问:

用telnet:

http-chunk-telnet-req.png

用浏览器: http-chunk-ie-req.png 请注意两者的 不同点

FAQ

1.http的chunk传输编码只有HTTP/1.1支持吗?

是的.只有HTTP 1.1支持这种编码,HTTP 1.0不支持这种传输编码,而且RFC2616说,HTTP 1.1的程序必须支持这种编码,详见:http://tools.ietf.org/html/rfc2616#section-3.6.1

2.如果HTTP 1.0中出现”Transfer-Encoding: chunked”,会发生什么情况?

出现编码错误.有两种情况:request和response其中之一为1.0,request和response都是1.0. 编码错误,如果是明文,我们能从浏览器看到chunk的长度大小。稍微修改一下我们的程序,把$response\_Hdr中的”Http/1.1″变为”HTTP/1.0″,用IE访问,如下图所示: http-chunk-ie-resp10.png 可以看到chunk的长度都被显示出来了。 如果非明文,那么浏览器有可能无法显示内容,并且报告内容编码错误。

3.”Transfer-Encoding”和”Content-Encoding”有什么区别?

“Transfer-Encoding”是传输属性,即以什么方式传输,一般就是”Transfer-Encoding: chunked”,response的内容可以是明文,也可以是压缩的内容;而”Content-Encoding”是response消息以什么方式编码送给客户端,通常用于说明response的压缩编码方式,比如”Content-Encoding: gzip”或者”Content-Encoding: deflate”,告诉客户端response是用gzip和deflate格式压缩的,而经过压缩后,他们可以用”Content-Length”固定长度的方式传输,也可以用”Transfer-Encoding: chunked”分块的方式传输给客户端。

php版本大于5.3.0导致日期函数抛警告异常

这两天碰到因为php版本升级,调用一些函数产生警告信息,从而导致编码不正确。 php版本大于5.3.0的时候,使用date(),mktime()等和时区相关的函数可能会产生以下错误:

Warning:  date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.
Warning:  mktime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.

产生该错误的原因是,php版本大于等于5.3.0之后,如果你的php没有设置默认的时区,使用时区相关函数,比如说date(),mktime()等函数就会产生这个警告。

1.解决办法:

1.1 设置变量date.timezone

在php.ini中设置变量date.timezone为一个有效的市区字符串,字符串值在这里有: http://php.net/manual/en/timezones.php 。例如:

date.timezone = "Asia/Jakarta";
date.timezone = "Asia/Shanghai";
date.timezone = "Europe/Berlin";

1.2 date_default_timezone_set()函数

在调用时区相关的函数之前,调用date_default_timezone_set()这个函数设置一个有效的时区字符串,有效的时区字符串在上面那个链接。例如:

date_default_timezone_set('America/Los_Angeles');
date_default_timezone_set('Asia/Shanghai');

常见操作系统操作静态路由命令

记录一下常见系统设置静态路由的命令,备忘,希望对大家有帮助;注意:以下命令需要root权限。

1.ipv4
1.1 FreeBSD
1.1.1 添加路由
添加一条到网段192.168.2.0/24,下一跳地址是192.168.1.2的静态路由 :

route add -net 192.168.2.0/24 192.168.1.2

添加一条到主机192.168.2.1,下一跳地址是192.168.1.2的路由:

route add -host 192.168.2.1 192.168.1.2

1.1.2 删除刚才添加的路由

route del 192.168.2.0/24
route del 192.168.2.1

1.1.3 显示路由表信息

netstat -r

1.2 linux
1.2.1 添加路由

route add -net 192.168.2.0/24 gw 192.168.1.2
route add -host 192.168.2.1 gw 192.168.1.2

1.2.2 删除路由

route del 192.168.2.0/24
route del 192.168.2.1

1.2.3 显示路由表信息

netstat -r

1.3 windows xp
1.3.1 添加路由

route add 192.168.2.0 mask 255.255.255.0 192.168.0.10

1.3.2 删除路由

route delete 192.168.2.0

1.3.3 查看路由表信息

route print

 

2.ipv6
2.1 FreeBSD
2.1.1 添加路由

route add -net6 2012:2012::0/64 2012:2012::192:168:0:10
route add -net6 -host 2012:2012::192:168:2:1 2012:2012::192:168:0:10

2.1.2 删除路由

route del -inet6 2012:2012::0
route del -net6 -host 2012:2012::192:168:2:1

2.1.3 查看路由表,跟ipv4一样

2.2 linux
2.2.1 添加路由

route -A inet6 add 2012:2012::0/64 gw 2012:2012::192:168:0:10

2.2.2 删除路由

route -A inet6 del 2012:2012::0/64 gw 2012:2012::192:168:0:10

2.2.3 查看路由表

ip -6 route show

2.3 windows xp
有点罗嗦,请看这篇文章:

更详细的语法信息请看下面的参考文献。《WindowsXP下的IPv6配置》

参考文献:
1.FreeBSD手册 http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/network-routing.html
2.linux文档项目 http://tldp.org/HOWTO/Linux+IPv6-HOWTO/
3.MSDN http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sag_tcpip_pro_addstaticroute.mspx?mfr=true
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sag_ip_v6_pro_rt_add.mspx?mfr=true

4.来自百度一篇windows xp配置ipv6的文章,包括路由: http://hi.baidu.com/beimu/item/a2202bd0ea2fbaba33db90b7

深入理解FreeBSD中的TAILQ

工作的主要内容是tcp/ip,平台是FreeBSD,而且在内核态开发,所以很多情况下会涉及内核的一些数据结构和宏,比如说mbuf和TAILQ等。
TAILQ是FreeBSD/linux内核对双向队列操作的一种抽象,抽象程度不亚于C++,能实现操作队列需要的各种操作:插入元素,删除元素,遍历队列等。这个队列的优点是插入元素很快。
这里先回顾一下队列的特点(来自维基百科 http://zh.wikipedia.org/wiki/%E9%98%9F%E5%88%97):

队列,又稱為佇列(英文queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。

FreeBSD中的TAILQ把整个队列头抽象为一个单独的数据结构,我们先看看FreeBSD中的TAILQ相关宏,然后再举例子理解这些宏。
这里用最简单的一个结构体来理解TAILQ,这个结构体中有一个int型整数,还有两个分别指向前方和后方的指针。

1.描述前一个和下一个元素的结构体

458 #define TAILQ_ENTRY(type)                       \
459 struct {                                \
460     struct type *tqe_next;  /* next element */          \
461     struct type **tqe_prev; /* address of previous next element */  \
462     TRACEBUF                            \
463 }

这是TAILQ对两个指向前后两个元素指针的抽象,抽象为TAILQ_ENTRY结构体:tqe_next是指向下一个元素的指针,tqe_prev是一个二级指针,指针变量的地址,是前一个元素的tqe_next的地址,解引用(*tqe_prev)之后就是本元素的内存地址;TRACEBUF是一个调试相关的宏,我们先不管它。举例:
我们声明一个结构体,这个结构体只有一个int型整数,还有前驱和后继指针。

struct int_node{
	int num;
	TAILQ_ENTRY(int_node);
};

宏展开之后就变成:

struct int_node{
	int num;
	struct int_node *tqe_next;  /* next element */ 
	sturct int_node **tqe_prev; /* address of previous next element */
};

例如:

2.队列头

TAILQ把整个队列头单独抽象为一个结构体TAILQ_HEAD,如下:

445 /*
446  * Tail queue declarations.
447  */
448 #define TAILQ_HEAD(name, type)                      \
449 struct name {                               \
450     struct type *tqh_first; /* first element */         \
451     struct type **tqh_last; /* addr of last next element */     \
452     TRACEBUF                            \
453 }

这个宏实际上使用的时候,会展开成为一个结构体,tqh_first是一个一级指针,指向队列中的第一个元素;tqh_last是一个二级指针,它指向最后一个元素中的tqe_next(请参考上面的TAILQ_ENTRY),也就是最后一个元素的tqe_next的地址,指针的地址就是二级指针;TRACEBUF是一个用来调试的宏,不用管它。举例:

声明一个叫做queue_head的队列头:

TAILQ_HEAD(my_int_struct, my_int) queue_head;

宏展开之后就会变成(不管TRACEBUF宏):

struct int_head {
	struct int_node *tqh_first; /* first element */
	struct int_node **tqh_last; /* addr of last next element */
} queue_head;

如图:

用下面的宏初始化这个队列头:

534 #define TAILQ_INIT(head) do {                       \
535     TAILQ_FIRST((head)) = NULL;                 \
536     (head)->tqh_last = &TAILQ_FIRST((head));            \
537     QMD_TRACE_HEAD(head);                       \
538 } while (0)

变成:

3.插入元素

插入元素用TAILQ_INSERT_TAIL宏,由于TAILQ中有一个tqh_last的二级指针,所以插入元素直接插到队尾,仅用O(1)时间。

578 #define TAILQ_INSERT_TAIL(head, elm, field) do {            \
579     QMD_TAILQ_CHECK_TAIL(head, field);              \
580     TAILQ_NEXT((elm), field) = NULL;                \
581     (elm)->field.tqe_prev = (head)->tqh_last;           \
582     *(head)->tqh_last = (elm);                  \
583     (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
584     QMD_TRACE_HEAD(head);                       \
585     QMD_TRACE_ELEM(&(elm)->field);                  \
586 } while (0)

QMD_TAILQ_CHECK_TAIL,QMD_TRACE_HEAD,QMD_TRACE_ELEM这三个宏和调试信息相关和做一些必要的检查,我们可以先不管;这个宏就是在调整相关的指针指向。我们向一个空队列插入两个元素2来理解这个宏:
3.1 580行让新元素的tqe_next指向空,执行完第580行:

3.2 581行让新元素的tqe_prev赋值为tqh_last,也就是指向队列头中的tqh_first的地址,执行完第581行:

3.3 582行让二级指针tqh_last中的内容指向新元素,也就是tqh_first指向新元素,执行完第582行:

3.4 583行,队列头的tqh_last赋值为新元素的tqe_next的地址(指针的地址,二级指针),执行完第583行:
这就是插入2后的整个链表。

4.删除元素

删除元素用TAILQ_REMOVE宏

596 #define TAILQ_REMOVE(head, elm, field) do {             \
597     QMD_SAVELINK(oldnext, (elm)->field.tqe_next);           \
598     QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);           \
599     QMD_TAILQ_CHECK_NEXT(elm, field);               \
600     QMD_TAILQ_CHECK_PREV(elm, field);               \
601     if ((TAILQ_NEXT((elm), field)) != NULL)             \
602         TAILQ_NEXT((elm), field)->field.tqe_prev =      \
603             (elm)->field.tqe_prev;              \
604     else {                              \
605         (head)->tqh_last = (elm)->field.tqe_prev;       \
606         QMD_TRACE_HEAD(head);                   \
607     }                               \
608     *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);      \
609     TRASHIT(*oldnext);                      \
610     TRASHIT(*oldprev);                      \
611     QMD_TRACE_ELEM(&(elm)->field);                  \
612 } while (0)

QMD_SAVELINK,QMD_TAILQ_CHECK_NEXT,QMD_TAILQ_CHECK_PREV,TRASHIT,同样先不管这几个宏。我们从队列中删除一个元素来理解这个宏:
4.1 假设经过上节插入元素2之后,我们用TAILQ_INSERT_TAIL再插入一个元素1,没有删除之前的链表如下图:

现在假设我们删除队列中的第一个元素2

4.2 602和603在调整当前元素的下一个元素的tqe_prev指针,执行完第602行和603行之后:

4.3 608调整当前元素tqe_prev中的内容,执行完第608行之后:

4.4 释放结点2的空间之后,最后的链表:

5.队列中的第一个元素

512 #define TAILQ_FIRST(head)   ((head)->tqh_first)



6.当前元素的下一个元素

591 #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)

这个宏比较简单。

7.遍历链表中的每一个元素

用宏TAILQ_FOREACH

514 #define TAILQ_FOREACH(var, head, field)                 \
515     for ((var) = TAILQ_FIRST((head));               \
516         (var);                          \
517         (var) = TAILQ_NEXT((var), field))

这个宏就比较简单了,用临时变量var来遍历链表中的每一个元素。

这些宏就是几个操作TAILQ经常使用的宏,还有一些诸如TAILQ_INSERT_HEAD等宏,类似,请自行看代码,这里就不一一叙述了。这篇文章主要是理解相关的宏和插入删除过程,完整的应用例子请看下面的参考文献。

参考资料:

命令行测试ssl回话

工作到现在,经常在和HTTP协议打交道,所以HTTP相关的开发工具和调试工具是必备的;平时工作中调试bug的时候,一般都是调试单个回话的居多,所以经常使用telnet调试HTTP会话,telnet虽然功能极其简单,但是也极其的强大,因为你可以构造任何http header给服务器,这样你就可以研究各种条件下http服务器的返回内容;下面的一个例子。

但是telnet不支持ssl,需要这样测试https,怎么办,用openssl的s_client客户端就可以了,前提是你安装openssl:

openssl s_client -connect www.example.com:443

如下图:

 

参考资料:

1.http://www.bearfruit.org/2008/04/17/telnet-for-testing-ssl-https-websites/

谷歌搜索关键字telnet ssl,这是第一篇文章,也是从这里学到的;

2.http://www.ibm.com/developerworks/cn/linux/l-cn-sclient/index.html

这篇来自ibm的文章列举了openssl的s_client客户端的几个高级用法;

3.http://www.blogjava.net/ycyk168/archive/2009/11/27/303946.html

这篇博文举了几个s_client应用的简单例子;

4.http://www.openssl.org/docs/apps/s_client.html

这是openssl s_client客户端的官方手册。

推荐一些实现操作系统内核有用的书籍或资料

Table of Contents

1 前言

自己本科的毕业设计是实现一个简单的操作系统内核,但是由于时间原因,只完成了一点点,包括引导加载程序,异常处理;当时考虑到大二大三自己的底层知识基础打得不好,再加上目前用C实现的内核是在太多了,要从头实现肯定会有抄袭的痕迹,所以打算完全用x86汇编语言实现,编译器用NASM,实现完之后确实汇编的东西基本的东西都捡回来了;在这里我整理一下自己实现这个内核过程中参考过的书籍、资料或者文献,希望对大家有所帮助。

2 一、x86汇编语言

虽然现在很少有使用汇编语言的场合,即使有,也尽量会被C取代,但是掌握汇编语言对于理解CPU和一些接口的工作原理至关重要。

intel cpu的官方手册,这手册是特别重要的,实现操作系统一定要看,特别是这几章节:卷1的第三章,主要讲intel cpu的程序运行环境,包括所有的寄存器,内存组织,堆栈结构等等;卷1的第六章,函数的调用过程,还有几个相关的重要指令、堆栈环境以及相关寄存器等等;卷3的第三章,主要讲解地址空间,重要;卷3的第四章,分页模式;卷3的第五章,保护模式,包括类型检查,越界检查以及特权级检查等;卷3的第六章,讲解异常处理;卷3的第七章,主要讲解任务,包括任务的数据结构、切换以及连接等;

  • 2.(美)Barry B.Brey.intel微处理器[M].北京:机械工业出版社,2008.

这本书我只看了一点,主要参考了一些指令,还有对选择子和描述符的讲解;

  • 3.Jeff Duntemann.Assembly Language Step-by-Step[M].美国:Wiley,2009.

这本书是为数不多的以NASM来讲解x86汇编语言的,而且讲得很详细,没有汇编语言的知识也能读懂,强烈推荐当做nasm的入门教材;

  • 4.吕晓庆.80386/486系统编程实践[M].浙江:浙江大学出版社,1993.

这本书很古老很古老了,找不到电子书,我是去我们学校的另一个校区的图书馆里面才找到的这本书,里面提供了很多关于保护模式编程的具体例子,用来理解intel cpu官方手册的绝佳教材,强烈推荐;

  • 5.罗克露 等编.计算机组成原理[M].北京:电子工业出版社,2004.

这本书是我们大二计算机组成原理课程的教材,主要用来复习一下微机接口的知识;

  • 6.谭毓安,张雪兰,李元章.Windows汇编语言程序设计实验指导[M].北京:清华大学出版社,2008.

当时我看intel cpu手册的时候,对卷3中的大部分内容理解得不是很透彻,通过这本书里面几个关于保护模式编程的例子,一下子就豁然开朗了好多,非常推荐配合intel cpu手册看的一本书;

  • 7.熊桂喜,赵海.PC机各类显示系统的编程及应用[M].北京:清华大学出版社,1992.

这也是一本很古老很古老的书籍,主要用来理解vga显示器编程的书籍,很好很强大;

  • 8.赵国相,张健,赵大鹏等.微型计算机原理与汇编语言程序设计[M].北京:科学出版社,2004.
  • 9.赵国相,于秀峰.微型计算机原理与接口技术[M].北京:科学出版社,2003.

这两本书是我们大三微机接口原理的课本,不推荐看,写得比较烂,而且还是实模式的讲解,太落后了,也是主要用来温习微机接口的知识;

3 二、操作系统理论及实现

  • 1.赵炯.Linux内核完全剖析[M].北京:机械工业出版社,2006

这本书的作者赵炯博士对linux的内核是非常熟悉的,是一位博士,国内市面上唯一对linux 0.12内核作出透彻剖析的一本书,非常强烈推荐,我就是通过这本书才知道怎么写cpu第0号到第20号的异常处理,当然,由于时间原因,我只看了前面几章,非常遗憾;书的开头有句非常经典的话:Read The F***ing Source Code;

  • 2.(荷)Andrew S.Tanenbaum.现代操作系统(英文影印版.第3版)[M].北京:机械工业出版社,2009.

这是荷兰科学家Tanenbaum写的操作系统理论教材;

  • 3.(美)ANDREW S.TANENBAUM,(美)ALBERT S.WOODHULL.操作系统设计与实现(第3版)[M].北京:清华大学出版社,2008.

这也是AST写的书,其实是Minix 3的最全面的手册,minix 是一个非常优秀的、适合学习操作系统实现的、但是内核代码量不是很大的操作系统,如果要想实现一个比较实际的、能跑起来的操作系统内核,建议好好研读minix的源代码和这本书;

  • 4.(美)Marshall Kirk McKusick,(美)George V. Neville-Neil.FreeBSD操作系统设计与实现[M].北京:中国电力出版社,2008.

这本书从一个比较高的角度分析FreeBSD操作系统的架构,里面的代码很少,主要讲OS的数据结构以及这些数据结构之间的联系;

  • 5.Behrouz A. Forouzan With Sophia Chung Fegan.计算机科学基础–从数据操纵到计算理论(英文影印版)[M].北京:高等教育出版社,2007.

这本书比较基础,讲一些很底层的运算和对计算机各个方面进行简单的入门介绍;

  • 6.John R. Levine.Linkers and Loaders[M].美国:Morgan Kaufmann,1999.

这本书是唯一一本讲解连接器和加载器的著作,非常难以理解,推荐;

  • 7.Maurice J.Bach.UNIX操作系统设计[M].北京:机械工业出版社,2006.

这本书是类似于上面介绍的那本freebsd的书,也是从一个很高的角度介绍unix的架构;

+8.Randal E. Bryant,David R. O’Hallaron.Computer Systems:A Programmer’s Perspective (2nd Edition)[M].美国:Addison Wesley,2010.
这本书强烈推荐,里面讲解程序的链接和装载过程非常精彩,让我深刻理解了动态链接过程;

  • 9.于渊.Orange’s:一个操作系统的实现[M].北京:电子工业出版社,2009.

对于这本书的推荐,我是有所保留的,网上书评也给得挺高,大一大二的时候翻过这本书,曾经很冲动的想自己写个内核;作者自己实现了一个内核orange,讲得非常详细,贴了挺多代码,希望只有一点C语言基础的读者通过阅读他的书籍就能实现自己的OS,个人认为有些时候好心办坏事,好多地方讲得有点罗嗦,结果很多读者因为没有具备相应的知识只有囫囵吞枣,照着他的代码敲一遍,当然这也是一种学习方法,不过我觉得读者应该具备相应的知识再去阅读此书,不要连汇编语言都不会使就去阅读,否则碰到不会知识的时候,像书中序言所说的那样,学到再用,又得去翻其他的书籍,理解之后又回来看,来来回回折腾会需要更多的时间去理解这个内核的整体性,我希望读者具有足够的知识的时候再去阅读就能够理解书中的大部分内容,如果要阅读源代码,还是推荐linux 0.12和minix的代码,对理解实现很有帮助,本书的作者也是参考minix的代码,书中的很多地方有很深的模仿minix痕迹;当然,我没有看完这本书,只看了前几章,个人愚见,如有不妥,欢迎拍砖;

  • 10.俞甲子,石凡,潘爱民.程序员的自我修养–副标题: 链接、装载与库[M].北京:电子工业出版社,2009.

这本书也比较深奥,讲解连接器和装载器的入门书籍,作者的内功很深厚,推荐;

这个链接是博主实现操作系统内核的一个博客,要很好地理解intel卷3的内容,推荐阅读这里的代码;

评论同上。

这个链接讲解ELF的文件格式,浅显易懂;

ELF文件格式

这个网站是操作系统内核开发爱好者聚集的社区,它的维基页面有很多入门的资料和文献,强烈推荐;

4 三、工具

  • 1.Richard M. Stallman,Roland McGrath,Paul D. Smith.GNU Make[M].美国:Free Software Foundation,2004.

GNU make 的官方手册

  • 2.《GNU Make项目管理》(Managing projects with GNU Make)

Oreilly介绍gnu make的动物书,有很多gnu make的语法例子,强烈推荐;

  • 3.uClinux源代码中Make文件完全解析

详细讲解uClinux makefile的书籍,和上面一本书作为学习写makefile的绝佳书籍组合,如有疑问,可配合gnu make的官方手册查看;

  • 4.鸟哥.鸟哥的Linux私房菜 基础学习篇(第二版)[M].北京:人民邮电出版社,2007.

linux基础,命令,工具链啊,什么的;

NASM的官方手册,NASM的宏很强大,请仔细学习手册里面的宏的用法;

bochs模拟器的用户手册;

晦涩难懂的AT&T语法的gnu汇编器的官方手册

这本书是gnu ld连接器的手册。

以上书籍的电子书在网上基本都能找到,一些国内的书找不到,只能到图书馆里面翻,有的链接可能已经失效,如果这样,请自行Google之。

Date: 2012-11-27T04:06-0500

Author: User

Org version 7.9.1 with Emacs version 23

Validate XHTML 1.0

 

http请求中的via头可能会影响iis6 web服务器的压缩行为

哎,终于更新博客了,这是在大猫那边买空间之后为数不多的文章之一;工作之后发现时间真的是越来越紧张,不像在学校那样时间宽裕,又想念学校生活了。。几个月之前就想更新,学习、加班什么乱七八糟的事耽误着,总让人有种心有余力不足的感觉,差不多空间又要续费了,再不更新资源严重浪费了。

今天记录一下工作中修bug中遇到的一个技术问题,主要是http的via头会影响iis6 web服务器的默认压缩行为;更具体一点,就是如果请求中有via头,并且希望得到压缩内容,也就是”Accept-Encoding”头中的含有有效压缩字符,如”Accept-Encoding: gzip”,那么iis6服务器默认情况下是不回复压缩的内容;因为iis6服务器认为,含有via头的http请求来自代理服务器,它们不能很好的处理有编码的内容;确实是这样,最近修了公司产品几个关于压缩的bug。

我们举例子来理解它的行为:

1.环境:iis6服务器,我用windows 2003装的iis6;任何能发送含有via头请求的浏览器,我用telnet,非常简单但是非常强大;如下图所示。

有via,没压缩内容

没via头,有压缩内容

 

2.分别发送含有via头的请求和没有via头的请求,我们发现,来自iis6服务器的内容不一样。原因上面已经提及。那么,如何解决?简单,在请求中屏蔽via头,还有一个办法,调整iis6的一个参数,就能让它回复含有编码的内容给含有via头的请求:在配置文件中,C:\WINDOWS\system32\inetsrv\MetaBase.xml,把变量HcNoCompressionForProxies的值变为FALSE;这样,就能收到压缩编码了。如下图:

有via,有压缩

Page 1 of 612345...Last »