<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>fleurer &#187; 翻译</title>
	<atom:link href="http://www.fleurer-lee.com/category/%e7%bf%bb%e8%af%91/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.fleurer-lee.com</link>
	<description>rage and love, story of my life.</description>
	<lastBuildDate>Thu, 02 Sep 2010 11:07:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>简介多任务的实现</title>
		<link>http://www.fleurer-lee.com/2010/09/01/%e5%ae%9e%e7%8e%b0%e5%a4%9a%e4%bb%bb%e5%8a%a1/</link>
		<comments>http://www.fleurer-lee.com/2010/09/01/%e5%ae%9e%e7%8e%b0%e5%a4%9a%e4%bb%bb%e5%8a%a1/#comments</comments>
		<pubDate>Wed, 01 Sep 2010 12:22:08 +0000</pubDate>
		<dc:creator>fleurer</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[ASM]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Kernel]]></category>

		<guid isPermaLink="false">http://www.fleurer-lee.com/?p=645961</guid>
		<description><![CDATA[原文：http://hosted.cjmovie.net/TutMultitask.htm
翻译：fleurer
恩，打算给你的操作系统实现多任务？可以说，多任务是现代操作系统里面最重要的元素之一──没多任务你没脸见人。只跑Bash的linux也有多任务一说──比如debain下边你可以使用Alt+F1,F2,F3,F4切换虚拟终端。不过话说回来，只有一颗cpu的电脑为什么能同时执行10件任务？
答案就是——不为什么，只不过看着像而已。它们切换的速度非常之快，以至于人可以觉得它们是同时执行的。当然也有双核和多核，不过那就不是本文讨论的范畴了。即使有双核多核，像一台普通的的Windows NT(包括XP)内核机器，没有用户登录、没有程序执行，也有约100条线程同时执行。
我们先列些名词好了：

线程 &#8211; 进程的一个分子，可以同时执行。比如你玩的一个游戏是个进程，而这个游戏里面的背景音乐、键盘事件、3D绘图则是独立的线程。
进程 - 在电脑上运行的一个完整程序，有自己的地址空间（通常使用分页实现）。
调度算法 &#8211; 选出下一个要执行任务的方法。可以简单如Round Robina，也可以考虑上优先级，使一进程可以优先得到充足的时间片。调度算法与任务切换的实现无关。
基于栈的任务切换 &#8211; 切换任务的方法之一。按照这个方法，在发生切换时我们把一些信息都&#8221;PUSH&#8221;到进程的栈里，于是只需要切换一个栈把用到的东西（eax, ebx, ds, es）都POP出来即可。这个方法比基于硬件的切换（这里不作讨论）更快，已经几乎是切换的首选。
Round Robin &#8211; 调度算法的一种，可以选出下一个执行的任务。它的实现很简单：把所有的进程（或线程）列出来放到一个表里，反复轮询之，公平分配时间片。

接下来动手吧。添加多任务，你的OS准备好了吗？我这里选择了最简单的方法（Round Robin，上面有介绍），需要：一个内存管理器（memory manager, 只要物理内存就够了）；正确设置的IDT；PIT（可编程中断定时器，译者注） IRQ的hook；在保护模式之下。
我们首先得有一个结构体来表示每个进程的信息。简单起见，先这样：

typedef struct&#123;        //Simple structure for a thread
 unsigned int esp0;   //Stack for kernel
 unsigned int esp3;   //Stack for process
&#125; Thread;
&#160;
Thread Threads&#91;2&#93;;    [...]]]></description>
			<content:encoded><![CDATA[<p>原文：<a href="http://hosted.cjmovie.net/TutMultitask.htm">http://hosted.cjmovie.net/TutMultitask.htm</a><br />
翻译：fleurer</p>
<p>恩，打算给你的操作系统实现多任务？可以说，多任务是现代操作系统里面最重要的元素之一──没多任务你没脸见人。只跑Bash的linux也有多任务一说──比如debain下边你可以使用Alt+F1,F2,F3,F4切换虚拟终端。不过话说回来，只有一颗cpu的电脑为什么能同时执行10件任务？</p>
<p>答案就是——不为什么，只不过看着像而已。它们切换的速度非常之快，以至于人可以觉得它们是同时执行的。当然也有双核和多核，不过那就不是本文讨论的范畴了。即使有双核多核，像一台普通的的Windows NT(包括XP)内核机器，没有用户登录、没有程序执行，也有约100条线程同时执行。</p>
<p>我们先列些名词好了：</p>
<ul>
<li><strong>线程</strong> &#8211; 进程的一个分子，可以同时执行。比如你玩的一个游戏是个进程，而这个游戏里面的背景音乐、键盘事件、3D绘图则是独立的线程。</li>
<li><strong>进程 </strong>- 在电脑上运行的一个完整程序，有自己的地址空间（通常使用分页实现）。</li>
<li><strong>调度算法</strong> &#8211; 选出下一个要执行任务的方法。可以简单如Round Robina，也可以考虑上优先级，使一进程可以优先得到充足的时间片。调度算法与任务切换的实现无关。</li>
<li><strong>基于栈的任务切换</strong> &#8211; 切换任务的方法之一。按照这个方法，在发生切换时我们把一些信息都&#8221;PUSH&#8221;到进程的栈里，于是只需要切换一个栈把用到的东西（eax, ebx, ds, es）都POP出来即可。这个方法比基于硬件的切换（这里不作讨论）更快，已经几乎是切换的首选。</li>
<li><strong>Round Robin</strong> &#8211; 调度算法的一种，可以选出下一个执行的任务。它的实现很简单：把所有的进程（或线程）列出来放到一个表里，反复轮询之，公平分配时间片。</li>
</ul>
<p>接下来动手吧。添加多任务，你的OS准备好了吗？我这里选择了最简单的方法（Round Robin，上面有介绍），需要：一个内存管理器（memory manager, 只要物理内存就够了）；正确设置的IDT；PIT（可编程中断定时器，译者注） IRQ的hook；在保护模式之下。</p>
<p>我们首先得有一个结构体来表示每个进程的信息。简单起见，先这样：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span><span style="color: #009900;">&#123;</span>        <span style="color: #666666; font-style: italic;">//Simple structure for a thread</span>
 <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> esp0<span style="color: #339933;">;</span>   <span style="color: #666666; font-style: italic;">//Stack for kernel</span>
 <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> esp3<span style="color: #339933;">;</span>   <span style="color: #666666; font-style: italic;">//Stack for process</span>
<span style="color: #009900;">&#125;</span> Thread<span style="color: #339933;">;</span>
&nbsp;
Thread Threads<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>     <span style="color: #666666; font-style: italic;">//Space for our simple threads. Just 2!</span>
<span style="color: #993333;">int</span> CurrentTask <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>  <span style="color: #666666; font-style: italic;">//The thread currenlty running (-1 == none)</span></pre></div></div>

<p>还得有个地方来存放线程信息。不过最好事先搞清楚——这个教程已是尽可能的简化了，我们没有让进程在Ring 3下执行，因为那样一来就得考虑TSS——我不想掉这个大坑。Beyond_Infinity同学在一篇类似的教程里用考虑了这个，如果感兴趣不妨一读。</p>
<p>在考虑创建新任务的方法之前，我先说下如何切换任务。其实很简单。</p>
<p>在收到来自PIC的IRQ时，你的IRQ Handler很可能会通过一个&#8217;pusha&#8217;或者&#8217;pushad&#8217;来储存一些寄存器。很好，这就清楚了，你可能使用&#8217;popa&#8217;或&#8217;popad&#8217;以相反的顺序重新得到这些寄存器。大约可以像这样：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">_irq0<span style="color: #339933;">:</span>
<span style="color: #00007f; font-weight: bold;">cli</span>    <span style="color: #666666; font-style: italic;">;Disable interrupts</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #0000ff;">0</span> <span style="color: #666666; font-style: italic;">;Push IRQ number</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #0000ff;">0</span> <span style="color: #666666; font-style: italic;">;Push dummy error code</span>
 <span style="color: #00007f; font-weight: bold;">jmp</span> IRQ_CommonStub
&nbsp;
<span style="color: #339933;">..</span> <span style="color: #666666; font-style: italic;">;Other IRQS are here, similiar to above</span>
&nbsp;
IRQ_CommonStub<span style="color: #339933;">:</span>
 <span style="color: #00007f; font-weight: bold;">pusha</span>          <span style="color: #666666; font-style: italic;">;Push all standard registers</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">ds</span>        <span style="color: #666666; font-style: italic;">;Push segment d</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">es</span>        <span style="color: #666666; font-style: italic;">;Push segmetn e</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">fs</span>        <span style="color: #666666; font-style: italic;">; ''</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">gs</span>        <span style="color: #666666; font-style: italic;">; ''</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">eax</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">0x10</span>  <span style="color: #666666; font-style: italic;">;Get kernel data segment</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">ds</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>    <span style="color: #666666; font-style: italic;">;Put it in the data segment registers</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">es</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">fs</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">gs</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">esp</span>       <span style="color: #666666; font-style: italic;">;Push pointer to all the stuff we just pushed</span>
 <span style="color: #00007f; font-weight: bold;">call</span> _IRQ_Handler <span style="color: #666666; font-style: italic;">;Call C code</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">gs</span>         <span style="color: #666666; font-style: italic;">;Put the data segments back</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">fs</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">es</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">ds</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">popa</span>           <span style="color: #666666; font-style: italic;">;Put the standard registers back</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">add</span> <span style="color: #00007f;">esp</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">8</span>     <span style="color: #666666; font-style: italic;">;Take the error code and IRQ number off the stack</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">iret</span>           <span style="color: #666666; font-style: italic;">;Interrupt-Return</span></pre></div></div>

<p>考你个问题：这些pop可以将当前栈中的数据装回CPU，如果在这个C函数调用时将栈换掉又会怎样？哈哈~到点子上了。如果这样做，整个CPU的状态就切换了。把上面的代码稍微改下：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">_irq0<span style="color: #339933;">:</span>
 <span style="color: #666666; font-style: italic;">;Notice there is no IRQ number or error code - we don't need them</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">pusha</span>          <span style="color: #666666; font-style: italic;">;Push all standard registers</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">ds</span>        <span style="color: #666666; font-style: italic;">;Push segment d</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">es</span>        <span style="color: #666666; font-style: italic;">;Push segmetn e</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">fs</span>        <span style="color: #666666; font-style: italic;">; ''</span>
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">gs</span>        <span style="color: #666666; font-style: italic;">; ''</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">eax</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">0x10</span>  <span style="color: #666666; font-style: italic;">;Get kernel data segment</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">ds</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>    <span style="color: #666666; font-style: italic;">;Put it in the data segment registers</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">es</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">fs</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">gs</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">push</span> <span style="color: #00007f;">esp</span>       <span style="color: #666666; font-style: italic;">;Push pointer to all the stuff we just pushed</span>
 <span style="color: #00007f; font-weight: bold;">call</span> _TaskSwitch <span style="color: #666666; font-style: italic;">;Call C code</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">esp</span><span style="color: #339933;">,</span> <span style="color: #00007f;">eax</span>   <span style="color: #666666; font-style: italic;">;Replace the stack with what the C code gave us</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">mov</span> <span style="color: #00007f;">al</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">0x20</span>   <span style="color: #666666; font-style: italic;">;Port number AND command number to Acknowledge IRQ</span>
 <span style="color: #00007f; font-weight: bold;">out</span> <span style="color: #00007f;">al</span><span style="color: #339933;">,</span> <span style="color: #00007f;">al</span>     <span style="color: #666666; font-style: italic;">;Acknowledge IRQ, so we keep getting interrupts</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">gs</span>         <span style="color: #666666; font-style: italic;">;Put the data segments back</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">fs</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">es</span>
 <span style="color: #00007f; font-weight: bold;">pop</span> <span style="color: #00007f;">ds</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">popa</span>           <span style="color: #666666; font-style: italic;">;Put the standard registers back</span>
&nbsp;
 <span style="color: #666666; font-style: italic;">;We didn't push an error code or IRQ number, so we don't have to edit esp now</span>
&nbsp;
 <span style="color: #00007f; font-weight: bold;">iret</span>           <span style="color: #666666; font-style: italic;">;Interrupt-Return</span></pre></div></div>

<p>有一点需要注意 &#8211; 只要你的C代码返回一个unsigned int，编译器就会把它放到eax寄存器里 &#8211; 简单漂亮（GCC才可以！）。好，接下来做什么？一半了，是的！</p>
<p>接下来考虑创建新任务。这就意味着，我们需要申请内存，并令它的堆栈看起来像是push了所有的寄存器的状态（这一来在切换栈之后，才能有东西pop）。恩，x86体系结构下栈是向下增长的。这就意味着你的push相当于设置esp指向的dword（双字），随后esp减去4。我们在C里模拟出来就行了。幸运的是，这很简单——只需要搞个unsigned int的指针，指向栈顶。每次push后都使用 &#8212; 运算符下移就好。好，就这么创建一个任务：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">//This will create a task</span>
<span style="color: #666666; font-style: italic;">//It will make a stack that looks like it has all</span>
<span style="color: #666666; font-style: italic;">//of the stuff of an IRQ handler 'pushed' on it</span>
<span style="color: #993333;">void</span> CreateTask<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> id<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>thread<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
  <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>stack<span style="color: #339933;">;</span>
  Threads<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">esp0</span> <span style="color: #339933;">=</span> AllocPage<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #0000dd;">4096</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//This allocates 4kb of memory, then puts the pointer at the end of it</span>
&nbsp;
  stack <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span>Threads<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">esp0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//This makes a pointer to the stack for us</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">//First, this stuff is pushed by the processor</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x0202</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//This is EFLAGS</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x08</span><span style="color: #339933;">;</span>   <span style="color: #666666; font-style: italic;">//This is CS, our code segment</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>UINT<span style="color: #009900;">&#41;</span>thread<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//This is EIP</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">//Next, the stuff pushed by 'pusha'</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//EDI</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//ESI</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//EBP</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Just an offset, no value</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//EBX</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//EDX</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//ECX</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//EAX</span>
&nbsp;
  <span style="color: #666666; font-style: italic;">//Now these are the data segments pushed by the IRQ handler</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x10</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//DS</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x10</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//ES</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x10</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//FS</span>
  <span style="color: #339933;">*--</span>stack <span style="color: #339933;">=</span> <span style="color: #208080;">0x10</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//GS</span>
  Threads<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">esp0</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>UINT<span style="color: #009900;">&#41;</span>stack<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Update the stack pointer</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>好，已经设好了任务，接着切换它们。不过怎样？恩，还记得Round Robin吧，你已经知道了！我们只有两个任务，所以在PIT IRQ被触发时只需要知道在执行的是哪个人物，把栈切换成另一个的。再保存当前ESP到旧任务的结构体里。如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">//Switch between our two tasks</span>
<span style="color: #666666; font-style: italic;">//Notice how we get the old esp from the ASM code</span>
<span style="color: #666666; font-style: italic;">//It's not a pointer, but we actually get the ESP value</span>
<span style="color: #666666; font-style: italic;">//That way we can save it in our task structure</span>
<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> TaskSwitch<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> OldEsp<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>CurrentTask <span style="color: #339933;">!=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #666666; font-style: italic;">//Were we even running a task?</span>
 Threads<span style="color: #009900;">&#91;</span>CurrenTask<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">esp0</span> <span style="color: #339933;">=</span> OldEsp<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Save the new esp for the thread</span>
&nbsp;
 <span style="color: #666666; font-style: italic;">//Now switch what task we're on</span>
 <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>CurrentTask <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>CurrentTask <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
 <span style="color: #b1b100;">else</span> CurrentTask <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span><span style="color: #009900;">&#123;</span>
 CurrentTask <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//We just started multi-tasking, start with task 0</span>
<span style="color: #009900;">&#125;</span>
 <span style="color: #b1b100;">return</span> Threads<span style="color: #009900;">&#91;</span>CurrentTask<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">esp0</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Return new stack pointer to ASM</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>随后我们需要由PIT来触发切换任务的asm stub，我假定你已经按照常规将IRQ映射到32-47。如果没，就设置一个IRQ0对应的handler。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">extern</span> <span style="color: #993333;">void</span> irq0<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Our ASM stub</span>
<span style="color: #666666; font-style: italic;">//This is a very simple function</span>
<span style="color: #666666; font-style: italic;">//All it does is put us in the IDT</span>
<span style="color: #993333;">void</span> StartMultitasking<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
IdtSetGate<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span>UINT<span style="color: #009900;">&#41;</span>irq0<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Install us in IDT. We multitask NOW!</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>万事俱备，只剩调用这些函数！方便看执行效果起见，在kernel的主文件（可能是main.c）里面添上这两个函数。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">//These are just two simple functions that act as</span>
<span style="color: #666666; font-style: italic;">//'threads' to test our multi-tasker on</span>
<span style="color: #666666; font-style: italic;">//I won't try to explain how they work</span>
<span style="color: #666666; font-style: italic;">//Only that they change colors on two letters of the screen</span>
&nbsp;
<span style="color: #666666; font-style: italic;">//Also - they must NEVER return - just make an infinite loop</span>
&nbsp;
<span style="color: #993333;">void</span> ThreadTest1<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
<span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span> VidMemChar <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #208080;">0xB8001</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">;;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>VidMemChar<span style="color: #339933;">++;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">void</span> ThreadTest2<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
<span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span> VidMemChar <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #208080;">0xB8003</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">;;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>VidMemChar<span style="color: #339933;">++;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>如你所见，执行结果是很容易想象的。不错。加上执行多任务的代码！把几句放到你的主函数（其它函数也行，能执行就好）里面。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">CreateTask<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> ThreadTest1<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Install the first task</span>
CreateTask<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> ThreadTest2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Install the second task</span>
StartMultitasking<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//Start your multitasking OS!</span>
<span style="color: #666666; font-style: italic;">//We're now multitasking. Celebrate!</span></pre></div></div>

<p>该差不多了，你已经可以把你的OS带出单任务的DOS时代了&#8230;多任务多线程的新时代系统！</p>
<p>你可以接着搞搞&#8230;这只是入门而已。简单的Round-Robin算法在很多情况下的效果并不好，不妨看下其他的调度算法，比如Mega Tokyo&#8217;s OS FAQ中列出来的那些。</p>
<p>还有件事情你可能感兴趣，那就是如何将分页整合进来。大多数操作系统都给每个应用程序一个0开始的虚拟地址空间，并一个用户栈。这一来，你就需要给每个任务添加一个页表目录，随后添加一些push和pop以适应CR3中值的改变。</p>
<p>有问题？有建议？Email me at service@cjmovie.net!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2010/09/01/%e5%ae%9e%e7%8e%b0%e5%a4%9a%e4%bb%bb%e5%8a%a1/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>简说写作</title>
		<link>http://www.fleurer-lee.com/2010/08/26/%e7%ae%80%e8%af%b4%e5%86%99%e4%bd%9c/</link>
		<comments>http://www.fleurer-lee.com/2010/08/26/%e7%ae%80%e8%af%b4%e5%86%99%e4%bd%9c/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 09:05:42 +0000</pubDate>
		<dc:creator>fleurer</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[writing]]></category>

		<guid isPermaLink="false">http://www.fleurer-lee.com/?p=645932</guid>
		<description><![CDATA[作者：Paul Graham
翻译：fleurer
原文：http://www.paulgraham.com/writing44.html
（这是在回复一封email时，随笔写下的即兴之作。通常，我写一篇文章都要花几个星期，不过这篇只花了67分钟——23分钟写作，44分钟重写。）

写作的重要性很容易被人们所忽视。写作，不只是交换想法的手段，更是产生想法的原动力。如果你并不熟悉写作、不喜欢写作，很可能会错失由此而生的很多想法。
这里就简谈一下，如何搞好写作：
尽可能快地打出一个草稿；反复重写；删掉所有不必要的部分；以对话的口吻写作；阅读时对不好的文章保持警惕，写作时对自己的文章也如此。
模仿你喜欢的作者；如果感到无从下笔，就找个人把你想表达的先说出来，再把你说的写下来；确保文章中80%的观点是动笔之后推敲的结果，而动笔前50%的观点都是经不住推敲的。
放心大胆的删改；找个信得过的朋友阅读你的文章，告诉你哪里不好理解或者不对劲；不要（总是）列出过于详细的大纲；动笔之前先酝酿几天；随身带个小笔记本；想到开头就别犹豫，马上动笔；要是时间紧，就先写重要的句子；为你喜欢的东西写作；
不要言辞激烈；中途想改变话题就改，不用犹豫；把主题无关的内容放到脚注里；使用排比；大声读出你的文章，找出蹩脚或者无趣的地方；告诉读者新鲜有用的东西；
在时间充裕时写作；决定重写时，从头阅读一遍已经写下的内容；写完了就放松下；没人认真听流行乐，也没人像你这样认真阅读你的文章；
要是说错了什么，马上改正；祸从口出，请朋友找出会让你后悔的句子；回头把激进的语气放缓；发布到网上，读者的存在会激励你继续写作；如果可以，打印出来；使用平实的语言；明辨跑题的迹象；遇到合适的地方就结尾。
update: 翻完之后杯具的看到原文底下有Chinese Translation。 TvT 
]]></description>
			<content:encoded><![CDATA[<p>作者：Paul Graham<br />
翻译：fleurer<br />
原文：<a href="http://www.paulgraham.com/writing44.html">http://www.paulgraham.com/writing44.html</a></p>
<p><em>（这是在回复一封email时，随笔写下的即兴之作。通常，我写一篇文章都要花几个星期，不过这篇只花了67分钟——23分钟写作，44分钟重写。）<br />
</em></p>
<p>写作的重要性很容易被人们所忽视。写作，不只是交换想法的手段，更是产生想法的原动力。如果你并不熟悉写作、不喜欢写作，很可能会错失由此而生的很多想法。</p>
<p>这里就简谈一下，如何搞好写作：</p>
<p>尽可能快地打出一个草稿；反复重写；删掉所有不必要的部分；以对话的口吻写作；阅读时对不好的文章保持警惕，写作时对自己的文章也如此。</p>
<p>模仿你喜欢的作者；如果感到无从下笔，就找个人把你想表达的先说出来，再把你说的写下来；确保文章中80%的观点是动笔之后推敲的结果，而动笔前50%的观点都是经不住推敲的。</p>
<p>放心大胆的删改；找个信得过的朋友阅读你的文章，告诉你哪里不好理解或者不对劲；不要（总是）列出过于详细的大纲；动笔之前先酝酿几天；随身带个小笔记本；想到开头就别犹豫，马上动笔；要是时间紧，就先写重要的句子；为你喜欢的东西写作；</p>
<p>不要言辞激烈；中途想改变话题就改，不用犹豫；把主题无关的内容放到脚注里；使用排比；大声读出你的文章，找出蹩脚或者无趣的地方；告诉读者新鲜有用的东西；</p>
<p>在时间充裕时写作；决定重写时，从头阅读一遍已经写下的内容；写完了就放松下；没人认真听流行乐，也没人像你这样认真阅读你的文章；</p>
<p>要是说错了什么，马上改正；祸从口出，请朋友找出会让你后悔的句子；回头把激进的语气放缓；发布到网上，读者的存在会激励你继续写作；如果可以，打印出来；使用平实的语言；明辨跑题的迹象；遇到合适的地方就结尾。</p>
<p>update: 翻完之后杯具的看到原文底下有<a href="http://cs.unm.edu/~cliu/WritingBriefly_by_pg.htm">Chinese Translation</a>。 TvT </p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2010/08/26/%e7%ae%80%e8%af%b4%e5%86%99%e4%bd%9c/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>抽象代数与计算-Monoid</title>
		<link>http://www.fleurer-lee.com/2010/07/20/%e6%8a%bd%e8%b1%a1%e4%bb%a3%e6%95%b0%e4%b8%8e%e8%ae%a1%e7%ae%97-monoid/</link>
		<comments>http://www.fleurer-lee.com/2010/07/20/%e6%8a%bd%e8%b1%a1%e4%bb%a3%e6%95%b0%e4%b8%8e%e8%ae%a1%e7%ae%97-monoid/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 04:14:15 +0000</pubDate>
		<dc:creator>fleurer</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[Algbera]]></category>
		<category><![CDATA[Math]]></category>
		<category><![CDATA[Monoid]]></category>

		<guid isPermaLink="false">http://www.fleurer-lee.com/?p=645910</guid>
		<description><![CDATA[作者：Mark C. Chu-Carroll
翻译：Fleurer
原文：http://scienceblogs.com/goodmath/2008/02/abstract_algebra_and_computati.php
在前两篇post，我们以范畴的角度观察了群论。范畴论给了我们一个全新的视角来理解对偶。与传统代数不同，范畴论的对偶是从groupoid中出现的，而群则被视为是带对偶的更简单的结构。
看其它的代数结构也是同样。比如环（rings），换个视角，也可以见到一个大不相同的环。
在看范畴论下的环之前，我们不妨先看个简单点的结构。在范畴中，环是通过monoid表示的。不过Monoid绝不仅仅只是环前面的开胃菜，它本身就是好东西。
它们的魅力在什么地方？首先，它们是范畴和代数之间的一座桥梁。我们知道，范畴中的groupoid（广群）使得群论无缝地归入了范畴论。Monoid也可以同样：它们也有范畴中的等价物。而且，Monoid还有地地道道的实际应用——你可以用Monoid表示计算。而且也许不广为人知，计算机科学中很多得力的基础工具其实都是Monoid。
我们先用代数的视角看下，呆会再换到范畴视角。在抽象代数中，Monoid正好跟复合函数的思想一致。开窍了吧，范畴论的基本思路就是复合函数的抽象化！
Monoid跟群类似，是一组支持二元运算的值的集合。Monoid更简单——它只要组合，不要逆元。我们令Monoid里面值的集合为M，二元运算为º，就有三条性质：
1.	封闭性（Closure）: ∀a,m∈M: aºb ∈ M
2.	单位元（Identity）: ∃ i∈M : ∀f∈M : iºf = fºi = f.
3.	结合律（Associativity）: ∀ a,b,c∈M: (aºb)ºc = aº(bºc).
这就是了。有这几条性质，想想函数和复合函数，套进去就满靠谱了。这对大多数函数都适用，于是所有的函数都成了对象，比如定义域和值域都是自然数的函数。封闭性要求两个简单完全函数复合的结果依然是简单完全函数；单位元可以是一个函数f(x)=x；交换律是交换表达式中元素的顺序，不影响最终结果。这都是复合函数天生的性质。
如果拿monoid像群一样，给它加一个操作又会怎样？答案对我们学计科的同学再熟悉不过了：有限自动机！
取一个monoid，(M,º)。我们可以在集合S上定义这monoid的一个操作，也就是：*:M×S→S，取M的一个值和S的一个值，得S的一个值。这个monoid操作显示了两条性质——都是来自monoid。
1.	Identity: ∀s∈S, i*s=s.
2.	Associativity: ∀a,b∈M, ∀s∈S: a*(b*s) = (aºb)*s.
这意味着什么？意味着我们给这个monoid加上了函数应用。这个monoid操作就是M中函数的应用。
这样，有了一个组可组合的函数，一个可以应用到函数的特定集合。可以得到什么？
一台自动机——也就是一种数学上的计算模型。
这个自动机是怎么出来的？
在这个monoid操作里，monoid的每个成员都是函数，也就是值到值的映射；组合符将这些函数链到一起。对自动机来说，monoid中的每个成员都是计算中的一个步骤，组合符将这些步骤连续起来。这还不是完整的计算——但已经靠一个简单的形式，前进了一大步。
多想想。还记得lambda演算？它就是一个表示计算的逻辑工具，里面除了函数什么都没有。想到了吧，我们刚刚只是重新发明了lambda演算的一部分，以抽象代数的思路。
在以后的post里面我会多讲些——不过我得先给你过上一遍，在范畴论里面这些东西都是什么样子——下一步往哪走，在范畴的大陆上清晰无比。
]]></description>
			<content:encoded><![CDATA[<p>作者：Mark C. Chu-Carroll<br />
翻译：Fleurer<br />
原文：<a href="http://scienceblogs.com/goodmath/2008/02/abstract_algebra_and_computati.php">http://scienceblogs.com/goodmath/2008/02/abstract_algebra_and_computati.php</a></p>
<p>在前两篇post，我们以范畴的角度观察了群论。范畴论给了我们一个全新的视角来理解对偶。与传统代数不同，范畴论的对偶是从groupoid中出现的，而群则被视为是带对偶的更简单的结构。</p>
<p>看其它的代数结构也是同样。比如环（rings），换个视角，也可以见到一个大不相同的环。</p>
<p>在看范畴论下的环之前，我们不妨先看个简单点的结构。在范畴中，环是通过monoid表示的。不过Monoid绝不仅仅只是环前面的开胃菜，它本身就是好东西。</p>
<p>它们的魅力在什么地方？首先，它们是范畴和代数之间的一座桥梁。我们知道，范畴中的groupoid（广群）使得群论无缝地归入了范畴论。Monoid也可以同样：它们也有范畴中的等价物。而且，Monoid还有地地道道的实际应用——你可以用Monoid表示计算。而且也许不广为人知，计算机科学中很多得力的基础工具其实都是Monoid。</p>
<p>我们先用代数的视角看下，呆会再换到范畴视角。在抽象代数中，Monoid正好跟复合函数的思想一致。开窍了吧，范畴论的基本思路就是复合函数的抽象化！</p>
<p>Monoid跟群类似，是一组支持二元运算的值的集合。Monoid更简单——它只要组合，不要逆元。我们令Monoid里面值的集合为M，二元运算为º，就有三条性质：</p>
<p>1.	封闭性（Closure）: ∀a,m∈M: aºb ∈ M<br />
2.	单位元（Identity）: ∃ i∈M : ∀f∈M : iºf = fºi = f.<br />
3.	结合律（Associativity）: ∀ a,b,c∈M: (aºb)ºc = aº(bºc).</p>
<p>这就是了。有这几条性质，想想函数和复合函数，套进去就满靠谱了。这对大多数函数都适用，于是所有的函数都成了对象，比如定义域和值域都是自然数的函数。封闭性要求两个简单完全函数复合的结果依然是简单完全函数；单位元可以是一个函数f(x)=x；交换律是交换表达式中元素的顺序，不影响最终结果。这都是复合函数天生的性质。</p>
<p>如果拿monoid像群一样，给它加一个操作又会怎样？答案对我们学计科的同学再熟悉不过了：有限自动机！</p>
<p>取一个monoid，(M,º)。我们可以在集合S上定义这monoid的一个操作，也就是：*:M×S→S，取M的一个值和S的一个值，得S的一个值。这个monoid操作显示了两条性质——都是来自monoid。</p>
<p>1.	Identity: ∀s∈S, i*s=s.<br />
2.	Associativity: ∀a,b∈M, ∀s∈S: a*(b*s) = (aºb)*s.</p>
<p>这意味着什么？意味着我们给这个monoid加上了函数应用。这个monoid操作就是M中函数的应用。</p>
<p>这样，有了一个组可组合的函数，一个可以应用到函数的特定集合。可以得到什么？</p>
<p>一台自动机——也就是一种数学上的计算模型。</p>
<p>这个自动机是怎么出来的？</p>
<p>在这个monoid操作里，monoid的每个成员都是函数，也就是值到值的映射；组合符将这些函数链到一起。对自动机来说，monoid中的每个成员都是计算中的一个步骤，组合符将这些步骤连续起来。这还不是完整的计算——但已经靠一个简单的形式，前进了一大步。</p>
<p>多想想。还记得lambda演算？它就是一个表示计算的逻辑工具，里面除了函数什么都没有。想到了吧，我们刚刚只是重新发明了lambda演算的一部分，以抽象代数的思路。</p>
<p>在以后的post里面我会多讲些——不过我得先给你过上一遍，在范畴论里面这些东西都是什么样子——下一步往哪走，在范畴的大陆上清晰无比。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2010/07/20/%e6%8a%bd%e8%b1%a1%e4%bb%a3%e6%95%b0%e4%b8%8e%e8%ae%a1%e7%ae%97-monoid/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>关于GC的FAQ</title>
		<link>http://www.fleurer-lee.com/2010/03/19/%e5%85%b3%e4%ba%8egc%e7%9a%84faq/</link>
		<comments>http://www.fleurer-lee.com/2010/03/19/%e5%85%b3%e4%ba%8egc%e7%9a%84faq/#comments</comments>
		<pubDate>Fri, 19 Mar 2010 06:13:23 +0000</pubDate>
		<dc:creator>fleurer</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[MM]]></category>

		<guid isPermaLink="false">http://www.fleurer-lee.com/?p=645777</guid>
		<description><![CDATA[翻译：ssword
原文：http://www.iecc.com/gclist/GC-faq.html
这是GC邮件列表的FAQ草稿，欢迎评论、标注或贡献内容。它暂时分为三(?)部分，大约是一般问题，技巧及算法，语言接口和高级论题。以后内容要是多了，这些内容可以重新组织便于查阅。
我们也提供了这些内容的文本文件，即GC-algorithms.txt, GC-lang.txt, and GC-harder.txt。
这里一些内容的学术气可能要差些，而更偏重于传道（或者更确切地说，是在没技术成分有减少的前提下增加点传道的因素）。关于垃圾收集多好多好之类，言辞越简洁越好。
Common questions
常见问题
What is garbage collection?
什么是垃圾收集？
垃圾收集可以是语言运行时的一部分，也可以是个附加库。它们能够与编译器、硬件或操作系统打交道，自动检查出程序中不再使用的内存，并将其回收利用。这也称作“自动存储（内存）回收”。
Why is it good?
它好在哪？
手工内存管理既浪费程序员的生命，也容易引入错误。相当多的程序都难免于内存泄漏，使用了错误处理或线程的程序尤其如此。
未使用过垃圾收集的朋友可能难以察觉它的第二大好处，那就是不必纠结于内存管理的细节（“谁来回收这块内存”），从而简化了程序组件（子程序、库、模块、类）间的接口。
你可以在 Object-Orientation FAQ &#8212; 3.9) Why is Garbage Collection A Good Thing?了解更多内容
Is garbage collection slow?
垃圾收集会不会很慢？
并非如此，现代的垃圾收集器的速度几乎可与手工内存管理(malloc/free或new/delete)相媲美。垃圾收集可能不如特定程序中相应的allocator快，不过为保证手工allocator正常工作而添加的额外代码（如引用计数）往往会使得程序比垃圾收集更慢，共享资源的多线程程序中尤其如此。
正如开始所说，内存已经足够便宜，垃圾收集可以应用到非常大的堆上（比如1GB）。活动内存如果足够的大，暂停时间依然是个问题。不过对于现代的垃圾收集器而言，其暂停时间通常也就0.1秒，对人类的交互几乎可以不计。要是小块活动内存，暂停时间就更少了：0.01秒以下。
Can I use garbage collection with C or C++?
我可以在C\C++中使用垃圾收集吗？
应该可以。对于存在遗留代码的C和C++可能还差些，不过现代的垃圾收集器（测试良好，高效，无暂停）几乎已经支持了一切。了解更多请看GC, C, and C++ 。
Does garbage collection cause my program&#8217;s execution to pause?
垃圾收集会不会让我的程序暂停？
不必。有不一的算法允许垃圾收集并行化、增量化甚至“实时化”。比如C\C++下边就有增量式的垃圾收集。
Where can I get a C or C++ garbage collector?
怎样搞到C\C++的垃圾收集？
Boehm-Weiser collector
http://www.hpl.hp.com/personal/Hans_Boehm/gc/ [...]]]></description>
			<content:encoded><![CDATA[<p>翻译：ssword<br />
原文：http://www.iecc.com/gclist/GC-faq.html</p>
<p>这是GC邮件列表的FAQ草稿，欢迎评论、标注或贡献内容。它暂时分为三(?)部分，大约是一般问题，技巧及算法，语言接口和高级论题。以后内容要是多了，这些内容可以重新组织便于查阅。</p>
<p>我们也提供了这些内容的文本文件，即GC-algorithms.txt, GC-lang.txt, and GC-harder.txt。</p>
<p>这里一些内容的学术气可能要差些，而更偏重于传道（或者更确切地说，是在没技术成分有减少的前提下增加点传道的因素）。关于垃圾收集多好多好之类，言辞越简洁越好。</p>
<p><strong>Common questions<br />
常见问题</strong></p>
<p><strong>What is garbage collection?<br />
什么是垃圾收集？</strong><br />
垃圾收集可以是语言运行时的一部分，也可以是个附加库。它们能够与编译器、硬件或操作系统打交道，自动检查出程序中不再使用的内存，并将其回收利用。这也称作“自动存储（内存）回收”。</p>
<p><strong>Why is it good?<br />
它好在哪？</strong></p>
<p>手工内存管理既浪费程序员的生命，也容易引入错误。相当多的程序都难免于内存泄漏，使用了错误处理或线程的程序尤其如此。</p>
<p>未使用过垃圾收集的朋友可能难以察觉它的第二大好处，那就是不必纠结于内存管理的细节（“谁来回收这块内存”），从而简化了程序组件（子程序、库、模块、类）间的接口。</p>
<p>你可以在 Object-Orientation FAQ &#8212; 3.9) Why is Garbage Collection A Good Thing?了解更多内容</p>
<p><strong>Is garbage collection slow?<br />
垃圾收集会不会很慢？</strong></p>
<p>并非如此，现代的垃圾收集器的速度几乎可与手工内存管理(malloc/free或new/delete)相媲美。垃圾收集可能不如特定程序中相应的allocator快，不过为保证手工allocator正常工作而添加的额外代码（如引用计数）往往会使得程序比垃圾收集更慢，共享资源的多线程程序中尤其如此。</p>
<p>正如开始所说，内存已经足够便宜，垃圾收集可以应用到非常大的堆上（比如1GB）。活动内存如果足够的大，暂停时间依然是个问题。不过对于现代的垃圾收集器而言，其暂停时间通常也就0.1秒，对人类的交互几乎可以不计。要是小块活动内存，暂停时间就更少了：0.01秒以下。</p>
<p><strong>Can I use garbage collection with C or C++?<br />
我可以在C\C++中使用垃圾收集吗？</strong></p>
<p>应该可以。对于存在遗留代码的C和C++可能还差些，不过现代的垃圾收集器（测试良好，高效，无暂停）几乎已经支持了一切。了解更多请看GC, C, and C++ 。</p>
<p><strong>Does garbage collection cause my program&#8217;s execution to pause?<br />
垃圾收集会不会让我的程序暂停？</strong></p>
<p>不必。有不一的算法允许垃圾收集并行化、增量化甚至“实时化”。比如C\C++下边就有增量式的垃圾收集。</p>
<p><strong>Where can I get a C or C++ garbage collector?<br />
怎样搞到C\C++的垃圾收集？</strong></p>
<p>Boehm-Weiser collector<br />
http://www.hpl.hp.com/personal/Hans_Boehm/gc/ or<br />
ftp://parcftp.xerox.com/pub/gc/gc.html<br />
Great Circle from Geodesic Systems  or 800-360-8388 or http://www.geodesic.com<br />
Kevin Warne  or 800-707-7171</p>
<p><strong>Folk myths<br />
坊间传闻</strong></p>
<ul>
<li>GC一定不如手工内存管理快</li>
<li>GC一定会让程序暂停</li>
<li>手工内存管理就不暂停</li>
<li>GC与C\C++水火不容</li>
</ul>
<p><strong>Folk truths<br />
其实…</strong></p>
<ul>
<li>大部分动态创建的对象其实少有与其它对象的关联，通常其引用数就是1。</li>
<li>大部分对象的生存期都很短。</li>
<li>对象大小、生存期呈爆炸式分布，而不是正态分布。</li>
<li>VM很重要</li>
<li>缓存很重要</li>
<li>不成熟的优化乃万恶之源。</li>
</ul>
<p><strong>Tradeoffs<br />
权衡</strong></p>
<ul>
<li>严格式 vs. 保守式</li>
<li>移动/压缩 vs. 无移动</li>
<li>暂停 vs. 增量 vs. 并行</li>
<li>分代 vs. 无分代</li>
</ul>
<p><strong>GC, C, and C++</strong></p>
<p><strong>What do you mean, garbage collection and C?<br />
你说什么，C语言的垃圾收集？</strong></p>
<p>可以引入一个垃圾收集器自动管理内存，从而代替malloc和free的手工申请或释放。通常的做法是将malloc替换为垃圾收集器的allocator，而free则替换为一个空函数。比如X11就是如此。</p>
<p>也可以令free依然生效，不过垃圾收集器就弱化为一个防弹衣的存在，堵住潜在的内存泄漏。这一做法也是有了很多应用，并且工作良好。好处是方便程序员手工管理内存，而程序员顾不到的地方，就交给垃圾收集。这不一定比空free风格的快，不过可能让堆变得小点。</p>
<p><strong>How is this possible?<br />
如何做到的？</strong></p>
<p>C兼容的垃圾收集可以知道指针在什么地方（例如&#8221;bss&#8221;,&#8221;data&#8221;和堆栈里面），保留在堆中的数据结构可以让它们很方便就能找出哪段数据是可能的指针。来个搜索遍历所有可以访问的指针，剩下没被访问的就是垃圾。</p>
<p><strong>This doesn&#8217;t sound very portable. What if I need to port my code and there&#8217;s no garbage collector on the target platform?<br />
这个听起来移植性好像不怎么样。我需要将代码移植到没有垃圾收集器的平台上该怎么办？</strong></p>
<p>有些代码一定是平台相关的，但是大多操作系统都有足够的功能，所以C的垃圾收集其几乎可以在所有平台移植。一般而言，只要有垃圾收集的实现，移植性就不是问题。在Boehm-Weiser的移植还不多的时候，我曾经自己移植过两次，其时我对操作系统的底层接口还不甚熟悉。</p>
<p><strong>Won&#8217;t this leave bugs in my program?<br />
这不会给我程序引入bug吧？</strong></p>
<p>这看你怎么想了。垃圾收集器可以解决程序员的很多问题，从而可以把精力放在其他地方，使得工作完成的更轻松。某种意义上讲，这跟浮点算法和虚拟内存的初衷是一致的。它们被发明出来都是为了解决些折腾程序员的乏味问题（如比例运算、换出页到硬盘）。没有FP和VM支持也可以写代码来实现相应的功能，不过只要有这功能可用，人们就不会自己写。一样的道理。</p>
<p>如果程序中用了垃圾收集风格的代码再扔掉垃圾收集器，内存泄漏的bug是肯定的。同样，如果一个程序用了FP（或VM）相关的代码，再卸掉浮点单元和MMU，bug也是一定的。</p>
<p>实际上，许多使用malloc和free的程序里都有内存泄漏，使用个垃圾收集器反而可以减少程序的bug。这可比手工跟踪内存再手工修复利索多了，要是跟踪发现内存泄漏出在三方库，还根本没办法修复。</p>
<p><strong>Can&#8217;t a devious C programmer break the collector?<br />
程序员可不可以把这收集器玩崩溃?</strong></p>
<p>当然可以。不过大多数人应该更喜欢研究些正事，而不是整天想着把自己的工具玩坏掉。收集器需要正在内存空间中的指针，所以想玩坏就有办法。例如双向链表中的翻转指针技巧就不能用——这指针长得不像指针。如果程序把指针写到文件中，呆会再读出来，没准就崩了，因为这些指针指向的内存很可能已经被回收了。没大有程序会这么玩，所以对大多数程序而言，这不是问题。C中一般的（合法的）指针运算都是没问题的。</p>
<p>某技术团队在考虑使用GC时想到一个问题，那就是使用pointer mangling技巧可能会搞出“不透明”的指针。所谓pointer mangling，就是三方库中的指针与一固定的随机数异或下，这一来三方库中的数据只有按照特定的接口才可以访问。这个不兼容保守式GC，也不兼容Ansi C标准的严格编译&#8230;甚至会迷惑内存泄漏的跟踪器（跟保守式GC用的技术一样）。不过即便如此，它们依然是合法的程序。</p>
<p>Insert more questions here &#8212; send them to</p>
<p><strong>What does a garbage collector do about destructors?<br />
垃圾收集器如何对待析构函数？</strong></p>
<p>析构函数就是对象被释放时候执行的代码，它的用途之一就是来手工回收内存，比如递归地回收对其它对象的引用。垃圾收集器里本没有析构函数的必要：如果一个对象是垃圾，它引用的所有对象就都是垃圾，自然会被收集到。</p>
<p>利用析构函数还可以做点其他事情。有两个应用比较典型：</p>
<p>与外部环境相关的对象的状态。比如文件相关的对象：当一个文件对象被回收时，垃圾收集器应该能保证能够刷新缓冲区、关闭文件，并将文件相关的资源返还给操作系统。</p>
<p>再就是程序需要保留一组在别处引用的对象。程序可能需要知道对象的功能，而不阻止它被收集。</p>
<p>解决这问题有很多方法：</p>
<p>有些系统是“built in”的垃圾收集，它就可以对外部引用的资源有所了解，处理起来也就心里有数<br />
有不少垃圾收集系统有个“弱引用(weak pointer)”的概念，就是不被垃圾收集器认作引用的指针。如果一对象是个弱引用，那么就可以被GC收集。弱引用可以用来实现对象的list之类的数据结构。<br />
再就是，java里可能会这样保护外部资源R：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> ClientR <span style="color: #009900;">&#123;</span>
   CRWeak wr<span style="color: #339933;">;</span>
   <span style="color: #666666; font-style: italic;">// delegate all methods to wr;</span>
   ClientR<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
     wr <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CRWeak<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
   <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> CRWeak <span style="color: #000000; font-weight: bold;">extends</span> <span style="color: #003399;">WeakReference</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #003399;">ReferenceQueue</span> rq <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">ReferenceQueue</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #003399;">Thread</span> th <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CRCleaner<span style="color: #009900;">&#40;</span>rq<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
         th.<span style="color: #006633;">setDaemon</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
         th.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  CRWeak<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> x<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
     <span style="color: #000000; font-weight: bold;">super</span><span style="color: #009900;">&#40;</span>x, rq<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
  ExternalResource r<span style="color: #339933;">;</span>
  <span style="color: #666666; font-style: italic;">// delegated methods from ClientR</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> CRCleaner <span style="color: #000000; font-weight: bold;">extends</span> <span style="color: #003399;">Thread</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #003399;">ReferenceQueue</span> rq<span style="color: #339933;">;</span>
  CRCleaner<span style="color: #009900;">&#40;</span><span style="color: #003399;">ReferenceQueue</span> rq<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000000; font-weight: bold;">this</span>.<span style="color: #006633;">rq</span> <span style="color: #339933;">=</span> rq<span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
         <span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
           CRWeak x <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>CRWeak<span style="color: #009900;">&#41;</span> rq.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
           <span style="color: #666666; font-style: italic;">// Release x.r</span>
         <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>当没有对象引用ClientR时候，这块内存就回收了，而对它的弱引用则移动到了各自的引用队列。清扫线程可以保证这些外部资源的回收。</p>
<p>许多GC系统都有“析构函数”的概念。垃圾收集器在回收一对象的时候，会执行该对象的一个函数来执行必要的清理操作。这会比较复杂，因为有些问题需要考虑：</p>
<ul>
<li>对象是在什么时候才会被收集？它并不像看起来这么简单，这对一些资源吃紧的应用尤为棘手。</li>
<li>析构函数该在哪个线程、资源或者上下文下边执行？</li>
<li>对象的交叉引用该怎么办？</li>
<li>如果一个析构函数使得对象不再是垃圾，又该怎么办？</li>
</ul>
<p><a href="http://www.iecc.com/gclist/GC-faq.html#For%20more%20information"><strong>For more information</strong></a></p>
<p><strong>Contributors (so far)<br />
贡献者（目前）</strong></p>
<p>David Gadbois<br />
Charles Fiterman<br />
David Chase<br />
Marc Shapiro<br />
Kelvin Nilsen<br />
Paul Haahr<br />
Nick Barnes<br />
Pekka P. Pirinen<br />
GC FAQ table of contents<br />
Techniques and algorithms<br />
Language interfaces<br />
Difficult topics<br />
Silly stuff</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2010/03/19/%e5%85%b3%e4%ba%8egc%e7%9a%84faq/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>简介AT&amp;T风格汇编</title>
		<link>http://www.fleurer-lee.com/2010/02/17/%e7%ae%80%e4%bb%8batt%e9%a3%8e%e6%a0%bc%e6%b1%87%e7%bc%96/</link>
		<comments>http://www.fleurer-lee.com/2010/02/17/%e7%ae%80%e4%bb%8batt%e9%a3%8e%e6%a0%bc%e6%b1%87%e7%bc%96/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 04:43:52 +0000</pubDate>
		<dc:creator>fleurer</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[ASM]]></category>
		<category><![CDATA[gas]]></category>

		<guid isPermaLink="false">http://www.fleurer-lee.com/?p=645734</guid>
		<description><![CDATA[作者：vivek
翻译：ssword
原文：http://sig9.com/articles/att-syntax
本文粗谈一下gas(1)的汇编语法，即AT&#038;T风格汇编。初次接触很可能会觉得它别扭，不过若有其他汇编语言的基础，稍事了解即可快速上手。我假设你熟悉INTEL风格汇编——也就是INTEL手册中的那种风格。方便起见，我就用NASM（Netwide Assembler）来做比对。
gas属于GNU Binary Utilities(binutils)，也是GCC的一个后端。对编写较长的汇编程序而言它并非首选，不过对于类Unix系统的内核级hacking，它就无可替代了。选择AT&#038;T风格使得gas饱受争议，人们总说它只是GCC的后端，而对开发者不友好。INTEL风格汇编的教众也认为，它在可读性及编译上几乎是令人窒息。尽管如此，有一点必须了解：很多操作系统都选择了gas作为底层代码的汇编器。
基本形式
AT&#038;T汇编程序的结构与其他汇编大同小异，伪指令、标签、指令—即最多带三个操作数的助记符。要说AT&#038;T汇编的不同，最显眼的地方就是它操作数的顺序。
例如，一个简单的数据移动指令在INTEL风格下边是这个样子：

mnemonic	destination, source

在AT&#038;T风格下边则是这样：

mnemonic	source, destination

一部分人（包括我）觉得这种格式更贴切。接下来说说AT&#038;T风格指令中的操作数。
寄存器
每个IA-32架构寄存器的名字必须以&#8217;%'作前缀，如%al,%bx,%ds,%cr0，等等。

mov	%ax, %bx

如上的mov指令就是把一个16位寄存器ax中的值移动到另一个16位寄存器bx中。
字面量
每个字面量必须以&#8217;$'为前缀。 例如：

mov	$100,	%bx
mov	$A,	%al

第一个指令是把值100移动到寄存器bx中，第二个指令是把一个字节A移动到AL寄存器中。下面这个指令就是错误的：

mov	%bx,	$100

怎么说呢，这条指令是要把寄存器bx的值移动给一个字面量，显然不靠谱。
内存寻址
在AT&#038;T风格中，内存引用起来是这个格式：

segment-override:signed-offset&#40;base,index,scale&#41;

按你寻址的需求，其中的部分可以省略

%es:100&#40;%eax,%ebx,2&#41;

注意下，基地址及偏移中的数不带前缀&#8217;$'。拿几个例子和对应的NASM风格做个比较应该好些：

GAS memory operand			NASM memory operand
------------------			-------------------
&#160;
100					&#91;100&#93;
%es:100					&#91;es:100&#93;
&#40;%eax&#41;					&#91;eax&#93;
&#40;%eax,%ebx&#41;				&#91;eax+ebx&#93;
&#40;%ecx,%ebx,2&#41;				&#91;ecx+ebx*2&#93;
&#40;,%ebx,2&#41;				&#91;ebx*2&#93;
-10&#40;%eax&#41;				&#91;eax-10&#93;
%ds:-10&#40;%ebp&#41;				&#91;ds:ebp-10&#93;

实例：

mov	%ax,	100
mov	%eax,	-100&#40;%eax&#41;

第一个指令是把寄存器AX中的值移动到数据段寄存器（默认）偏移100的内存位置，第二个指令是把寄存器eax中的值移动到[eax-100]。
操作数大小
有时指明操作数的大小是必须的，尤其是移动字面量到内存。例如这个指令：

mov	$10,	100

这里只说了把值10移动到内存偏址100处，而没有说值的大小。在NASM中，这通过给操作数后面跟个byte/word/dword之类的关键词来指明。而在AT&#038;T风格里，是通过指令中b/w/l之类的后缀指明。如：

movb	$10,	%es:&#40;%eax&#41;

把值为10的一个字节移动到内存地址[ex:eax]，另如：

movl	$10,	%es:&#40;%eax&#41;

把值为10的一个长整数移动到同一位置。
再几个例子：

movl	$100, %ebx
pushl	%eax
popw	%ax

控制流程
jmp,call,ret等指令可以转移程序的执行位置。在同一代码段中跳转，是近距跳转(near)。若是跳转到不同的代码段，就是远程跳转(far)。可用的跳转地址可以来自相对偏移（label）、寄存器、内存以及段偏移指针。相对偏移通过label指明，如下：

label1:
	.
	.
  jmp	label1

使用寄存器或者内存的值做地址的操作数必须加个前缀&#8217;*'。若是远程跳转，必须加个&#8217;l'作前缀，如‘ljmp’，‘lcall’等等。例如：

GAS syntax			NASM syntax
==========			===========
&#160;
jmp	*100			jmp  near &#91;100&#93;
call	*100			call near &#91;100&#93;
jmp	*%eax			jmp  near eax
jmp	*%ecx			call near ecx
jmp	*&#40;%eax&#41;			jmp  near &#91;eax&#93;
call	*&#40;%ebx&#41;			call near &#91;ebx&#93;
ljmp	*100			jmp  far  &#91;100&#93;
lcall	*100			call far  &#91;100&#93;
ljmp	*&#40;%eax&#41;			jmp  far  &#91;eax&#93;
lcall	*&#40;%ebx&#41;			call far  &#91;ebx&#93;
ret				retn
lret				retf
lret $0x100			retf 0x100

段偏移指针按下面的格式指明：

jmp	$segment, $offset

例如：

jmp	$0x10, $0x100000

记住这些很快就能上手了。要了解gas的更多细节，不妨参阅这个文档。
]]></description>
			<content:encoded><![CDATA[<p>作者：vivek<br />
翻译：ssword<br />
原文：<a href="http://sig9.com/articles/att-syntax">http://sig9.com/articles/att-syntax</a></p>
<p>本文粗谈一下gas(1)的汇编语法，即AT&#038;T风格汇编。初次接触很可能会觉得它别扭，不过若有其他汇编语言的基础，稍事了解即可快速上手。我假设你熟悉INTEL风格汇编——也就是INTEL手册中的那种风格。方便起见，我就用NASM（Netwide Assembler）来做比对。</p>
<p>gas属于GNU Binary Utilities(binutils)，也是GCC的一个后端。对编写较长的汇编程序而言它并非首选，不过对于类Unix系统的内核级hacking，它就无可替代了。选择AT&#038;T风格使得gas饱受争议，人们总说它只是GCC的后端，而对开发者不友好。INTEL风格汇编的教众也认为，它在可读性及编译上几乎是令人窒息。尽管如此，有一点必须了解：很多操作系统都选择了gas作为底层代码的汇编器。</p>
<p><strong>基本形式</strong></p>
<p>AT&#038;T汇编程序的结构与其他汇编大同小异，伪指令、标签、指令—即最多带三个操作数的助记符。要说AT&#038;T汇编的不同，最显眼的地方就是它操作数的顺序。</p>
<p>例如，一个简单的数据移动指令在INTEL风格下边是这个样子：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">mnemonic	destination<span style="color: #339933;">,</span> source</pre></div></div>

<p>在AT&#038;T风格下边则是这样：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">mnemonic	source<span style="color: #339933;">,</span> destination</pre></div></div>

<p>一部分人（包括我）觉得这种格式更贴切。接下来说说AT&#038;T风格指令中的操作数。</p>
<p><strong>寄存器</strong></p>
<p>每个IA-32架构寄存器的名字必须以&#8217;%'作前缀，如%al,%bx,%ds,%cr0，等等。</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">mov</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">ax</span><span style="color: #339933;">,</span> <span style="color: #339933;">%</span><span style="color: #00007f;">bx</span></pre></div></div>

<p>如上的mov指令就是把一个16位寄存器ax中的值移动到另一个16位寄存器bx中。</p>
<p><strong>字面量</strong></p>
<p>每个字面量必须以&#8217;$'为前缀。 例如：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">mov</span>	$<span style="color: #0000ff;">100</span><span style="color: #339933;">,</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">bx</span>
<span style="color: #00007f; font-weight: bold;">mov</span>	$A<span style="color: #339933;">,</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">al</span></pre></div></div>

<p>第一个指令是把值100移动到寄存器bx中，第二个指令是把一个字节A移动到AL寄存器中。下面这个指令就是错误的：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">mov</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">bx</span><span style="color: #339933;">,</span>	$<span style="color: #0000ff;">100</span></pre></div></div>

<p>怎么说呢，这条指令是要把寄存器bx的值移动给一个字面量，显然不靠谱。</p>
<p><strong>内存寻址</strong></p>
<p>在AT&#038;T风格中，内存引用起来是这个格式：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">segment</span><span style="color: #339933;">-</span>override<span style="color: #339933;">:</span>signed<span style="color: #339933;">-</span><span style="color: #000000; font-weight: bold;">offset</span><span style="color: #009900; font-weight: bold;">&#40;</span>base<span style="color: #339933;">,</span>index<span style="color: #339933;">,</span>scale<span style="color: #009900; font-weight: bold;">&#41;</span></pre></div></div>

<p>按你寻址的需求，其中的部分可以省略</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #339933;">%</span><span style="color: #00007f;">es</span><span style="color: #339933;">:</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #339933;">,%</span><span style="color: #00007f;">ebx</span><span style="color: #339933;">,</span><span style="color: #0000ff;">2</span><span style="color: #009900; font-weight: bold;">&#41;</span></pre></div></div>

<p>注意下，基地址及偏移中的数不带前缀&#8217;$'。拿几个例子和对应的NASM风格做个比较应该好些：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">GAS <span style="color: #000000; font-weight: bold;">memory</span> operand			NASM <span style="color: #000000; font-weight: bold;">memory</span> operand
<span style="color: #339933;">------------------</span>			<span style="color: #339933;">-------------------</span>
&nbsp;
<span style="color: #0000ff;">100</span>					<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #339933;">%</span><span style="color: #00007f;">es</span><span style="color: #339933;">:</span><span style="color: #0000ff;">100</span>					<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">es</span><span style="color: #339933;">:</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span>					<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #339933;">,%</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#41;</span>				<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">eax</span><span style="color: #339933;">+</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">ecx</span><span style="color: #339933;">,%</span><span style="color: #00007f;">ebx</span><span style="color: #339933;">,</span><span style="color: #0000ff;">2</span><span style="color: #009900; font-weight: bold;">&#41;</span>				<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">ecx</span><span style="color: #339933;">+</span><span style="color: #00007f;">ebx</span><span style="color: #339933;">*</span><span style="color: #0000ff;">2</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">,%</span><span style="color: #00007f;">ebx</span><span style="color: #339933;">,</span><span style="color: #0000ff;">2</span><span style="color: #009900; font-weight: bold;">&#41;</span>				<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">ebx</span><span style="color: #339933;">*</span><span style="color: #0000ff;">2</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #339933;">-</span><span style="color: #0000ff;">10</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span>				<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">eax</span><span style="color: #339933;">-</span><span style="color: #0000ff;">10</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #339933;">%</span><span style="color: #00007f;">ds</span><span style="color: #339933;">:-</span><span style="color: #0000ff;">10</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">ebp</span><span style="color: #009900; font-weight: bold;">&#41;</span>				<span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">ds</span><span style="color: #339933;">:</span><span style="color: #00007f;">ebp</span><span style="color: #339933;">-</span><span style="color: #0000ff;">10</span><span style="color: #009900; font-weight: bold;">&#93;</span></pre></div></div>

<p>实例：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">mov</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">ax</span><span style="color: #339933;">,</span>	<span style="color: #0000ff;">100</span>
<span style="color: #00007f; font-weight: bold;">mov</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #339933;">,</span>	<span style="color: #339933;">-</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span></pre></div></div>

<p>第一个指令是把寄存器AX中的值移动到数据段寄存器（默认）偏移100的内存位置，第二个指令是把寄存器eax中的值移动到[eax-100]。</p>
<p><strong>操作数大小</strong></p>
<p>有时指明操作数的大小是必须的，尤其是移动字面量到内存。例如这个指令：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">mov</span>	$<span style="color: #0000ff;">10</span><span style="color: #339933;">,</span>	<span style="color: #0000ff;">100</span></pre></div></div>

<p>这里只说了把值10移动到内存偏址100处，而没有说值的大小。在NASM中，这通过给操作数后面跟个byte/word/dword之类的关键词来指明。而在AT&#038;T风格里，是通过指令中b/w/l之类的后缀指明。如：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">movb	$<span style="color: #0000ff;">10</span><span style="color: #339933;">,</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">es</span><span style="color: #339933;">:</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span></pre></div></div>

<p>把值为10的一个字节移动到内存地址[ex:eax]，另如：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">movl	$<span style="color: #0000ff;">10</span><span style="color: #339933;">,</span>	<span style="color: #339933;">%</span><span style="color: #00007f;">es</span><span style="color: #339933;">:</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span></pre></div></div>

<p>把值为10的一个长整数移动到同一位置。</p>
<p>再几个例子：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">movl	$<span style="color: #0000ff;">100</span><span style="color: #339933;">,</span> <span style="color: #339933;">%</span><span style="color: #00007f;">ebx</span>
pushl	<span style="color: #339933;">%</span><span style="color: #00007f;">eax</span>
popw	<span style="color: #339933;">%</span><span style="color: #00007f;">ax</span></pre></div></div>

<p><strong>控制流程</strong></p>
<p>jmp,call,ret等指令可以转移程序的执行位置。在同一代码段中跳转，是近距跳转(near)。若是跳转到不同的代码段，就是远程跳转(far)。可用的跳转地址可以来自相对偏移（label）、寄存器、内存以及段偏移指针。相对偏移通过label指明，如下：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">label1<span style="color: #339933;">:</span>
	<span style="color: #339933;">.</span>
	<span style="color: #339933;">.</span>
  <span style="color: #00007f; font-weight: bold;">jmp</span>	label1</pre></div></div>

<p>使用寄存器或者内存的值做地址的操作数必须加个前缀&#8217;*'。若是远程跳转，必须加个&#8217;l'作前缀，如‘ljmp’，‘lcall’等等。例如：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;">GAS syntax			NASM syntax
==========			===========
&nbsp;
<span style="color: #00007f; font-weight: bold;">jmp</span>	<span style="color: #339933;">*</span><span style="color: #0000ff;">100</span>			<span style="color: #00007f; font-weight: bold;">jmp</span>  <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #00007f; font-weight: bold;">call</span>	<span style="color: #339933;">*</span><span style="color: #0000ff;">100</span>			<span style="color: #00007f; font-weight: bold;">call</span> <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #00007f; font-weight: bold;">jmp</span>	<span style="color: #339933;">*%</span><span style="color: #00007f;">eax</span>			<span style="color: #00007f; font-weight: bold;">jmp</span>  <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #00007f;">eax</span>
<span style="color: #00007f; font-weight: bold;">jmp</span>	<span style="color: #339933;">*%</span><span style="color: #00007f;">ecx</span>			<span style="color: #00007f; font-weight: bold;">call</span> <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #00007f;">ecx</span>
<span style="color: #00007f; font-weight: bold;">jmp</span>	<span style="color: #339933;">*</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span>			<span style="color: #00007f; font-weight: bold;">jmp</span>  <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #00007f; font-weight: bold;">call</span>	<span style="color: #339933;">*</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#41;</span>			<span style="color: #00007f; font-weight: bold;">call</span> <span style="color: #000000; font-weight: bold;">near</span> <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #000000; font-weight: bold;">ljmp</span>	<span style="color: #339933;">*</span><span style="color: #0000ff;">100</span>			<span style="color: #00007f; font-weight: bold;">jmp</span>  <span style="color: #000000; font-weight: bold;">far</span>  <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
lcall	<span style="color: #339933;">*</span><span style="color: #0000ff;">100</span>			<span style="color: #00007f; font-weight: bold;">call</span> <span style="color: #000000; font-weight: bold;">far</span>  <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #0000ff;">100</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #000000; font-weight: bold;">ljmp</span>	<span style="color: #339933;">*</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#41;</span>			<span style="color: #00007f; font-weight: bold;">jmp</span>  <span style="color: #000000; font-weight: bold;">far</span>  <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">eax</span><span style="color: #009900; font-weight: bold;">&#93;</span>
lcall	<span style="color: #339933;">*</span><span style="color: #009900; font-weight: bold;">&#40;</span><span style="color: #339933;">%</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#41;</span>			<span style="color: #00007f; font-weight: bold;">call</span> <span style="color: #000000; font-weight: bold;">far</span>  <span style="color: #009900; font-weight: bold;">&#91;</span><span style="color: #00007f;">ebx</span><span style="color: #009900; font-weight: bold;">&#93;</span>
<span style="color: #00007f; font-weight: bold;">ret</span>				<span style="color: #00007f; font-weight: bold;">retn</span>
lret				<span style="color: #00007f; font-weight: bold;">retf</span>
lret $<span style="color: #0000ff;">0x100</span>			<span style="color: #00007f; font-weight: bold;">retf</span> <span style="color: #0000ff;">0x100</span></pre></div></div>

<p>段偏移指针按下面的格式指明：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">jmp</span>	$segment<span style="color: #339933;">,</span> $offset</pre></div></div>

<p>例如：</p>

<div class="wp_syntax"><div class="code"><pre class="asm" style="font-family:monospace;"><span style="color: #00007f; font-weight: bold;">jmp</span>	$<span style="color: #0000ff;">0x10</span><span style="color: #339933;">,</span> $<span style="color: #0000ff;">0x100000</span></pre></div></div>

<p>记住这些很快就能上手了。要了解gas的更多细节，不妨参阅这个<a href="http://sourceware.org/binutils/docs-2.16/as/index.html">文档</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2010/02/17/%e7%ae%80%e4%bb%8batt%e9%a3%8e%e6%a0%bc%e6%b1%87%e7%bc%96/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Haskell趣学指南！</title>
		<link>http://www.fleurer-lee.com/2009/12/12/haskell%e8%b6%a3%e5%ad%a6%e6%8c%87%e5%8d%97%ef%bc%81/</link>
		<comments>http://www.fleurer-lee.com/2009/12/12/haskell%e8%b6%a3%e5%ad%a6%e6%8c%87%e5%8d%97%ef%bc%81/#comments</comments>
		<pubDate>Fri, 11 Dec 2009 17:22:02 +0000</pubDate>
		<dc:creator>ssword</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[FP]]></category>
		<category><![CDATA[haskell]]></category>

		<guid isPermaLink="false">http://swdpress.cn/?p=645576</guid>
		<description><![CDATA[译言前几天被杯具了，还好当初嗅到苗头全都抓了来 -v-
新地址见http://fleurer-lee.com/lyah。
用自己写的小工具生成的html，parser的代码不到200行，代码写的非常之perl，不过显然还算够用。
代码高亮用的一个jquery插件Chili，很容易扩展，随便改两个正则就自制了个haskell高亮。
顺便校对了下翻译。

ChinaUnix上的朋友对“柯里函数”等译法的意见比较大，不过在校对中没有做修改。关于人名构成的术语，例如“Fourier transform”还是“傅立叶变换”，译者认为后者更好看。
“Function Application”直译作“函数应用”，在这里译为“函数调用”。相应的“partial application”直译作“部分应用”，在这里译为“不全调用”。
“predicate”在这里译为“限制条件”，因为译者认为“断言”这个词有点吓人。
保留了List，Tuple，List Comprehension等名词，不过将Triple之类译为“三元组”，“function with two parameters”译为“二元函数”。
把原先译文中“在处理数字时是非常有用的”类的句式改为“在处理数字时非常有用”，“的”真的是很别扭的。
有一节的标题“Texas Range”译为“德州区间”，因为译者老家在德州&#8230;囧

修改的比较仓促，bug依然还有很多。呵呵，欢迎提意见！ ^_^
update: 可以svn checkout它的源码：https://lyah-cn.googlecode.com/svn/trunk/
]]></description>
			<content:encoded><![CDATA[<p>译言前几天被杯具了，还好当初嗅到苗头全都抓了来 -v-</p>
<p>新地址见<a href="http://fleurer-lee.com/lyah">http://fleurer-lee.com/lyah</a>。</p>
<p>用自己写的<a href="http://code.google.com/p/fdoc/">小工具</a>生成的html，parser的代码不到200行，代码写的非常之perl，不过显然还算够用。<br />
代码高亮用的一个jquery插件<a href="http://noteslog.com/chili/">Chili</a>，很容易扩展，随便改两个正则就自制了个<a href="http://fleurer-lee.com/lyah/js/chili/code.js">haskell高亮</a>。</p>
<p>顺便校对了下翻译。</p>
<ul>
<li>ChinaUnix上的朋友对“柯里函数”等译法的意见比较大，不过在校对中没有做修改。关于人名构成的术语，例如“Fourier transform”还是“傅立叶变换”，译者认为后者更好看。</li>
<li>“Function Application”直译作“函数应用”，在这里译为“函数调用”。相应的“partial application”直译作“部分应用”，在这里译为“不全调用”。</li>
<li>“predicate”在这里译为“限制条件”，因为译者认为“断言”这个词有点吓人。</li>
<li>保留了List，Tuple，List Comprehension等名词，不过将Triple之类译为“三元组”，“function with two parameters”译为“二元函数”。</li>
<li>把原先译文中“在处理数字时是非常有用的”类的句式改为“在处理数字时非常有用”，“的”真的是很别扭的。</li>
<li>有一节的标题“Texas Range”译为“德州区间”，因为译者老家在德州&#8230;囧</li>
</ul>
<p>修改的比较仓促，bug依然还有很多。呵呵，欢迎提意见！ ^_^</p>
<p>update: 可以svn checkout它的源码：https://lyah-cn.googlecode.com/svn/trunk/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2009/12/12/haskell%e8%b6%a3%e5%ad%a6%e6%8c%87%e5%8d%97%ef%bc%81/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>内核如何管理内存</title>
		<link>http://www.fleurer-lee.com/2009/12/06/%e5%86%85%e6%a0%b8%e5%a6%82%e4%bd%95%e7%ae%a1%e7%90%86%e5%86%85%e5%ad%98/</link>
		<comments>http://www.fleurer-lee.com/2009/12/06/%e5%86%85%e6%a0%b8%e5%a6%82%e4%bd%95%e7%ae%a1%e7%90%86%e5%86%85%e5%ad%98/#comments</comments>
		<pubDate>Sun, 06 Dec 2009 08:08:09 +0000</pubDate>
		<dc:creator>ssword</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MM]]></category>

		<guid isPermaLink="false">http://swdpress.cn/?p=645556</guid>
		<description><![CDATA[作者：Gustavo Duarte
翻译：ssword
原文：http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory

我们已经对类型的虚拟地址空间布局有了一定了解，接下来我们进入内核，了解其内存管理机制。再拿gonzo的图示出来：

Linux内核使用进程描述符task_struct的实例来表示进程。task_struct中的mm字段指向内存描述符mm_struct，它储存了内存中各个段的起始位置（如上图）、进程中使用物理页的数量（rss是Resident Set Size的缩写）、虚拟地址区域的大小以及其他一点细节。在内存描述符中，我们还可以发现其两大心腹：虚拟内存区域和页表。Gonzo的内存区域表示起来如下：

每块虚拟内存区域（VMA）都是一段连续的虚拟地址，其间没有重叠。vm_area_struct的实例就是对一段内存区域的描述，其中包含了内存区域的起始地址、描述行为和访问控制的标志以及表示文件映射的vm_field字段，没有文件映射的VMA就是匿名的（anoymous）。除去内存映射段，如上的每个段（如堆、栈）都单独与一个VMA相关联。x86的机器大都如此组织内存，不过并非如此不可&#8211;VMA本身不关心自己位于哪个段。
对程序员而言，VMA在其内存描述符，既有mmap字段里顺序的一段链表，又有mm_rb字段的一棵红黑树。这棵红黑树使得内核得以快速按照给出的虚拟地址来找出相应的内存区域。像读取文件/proc/pid_of_process/maps的内容，内核就只是简单遍历这个VMA的链表再将其输出。Windows下的EPROCESS差不多就是task_struct和mm_struct的混合体，而其相对于VMA的等价物就是虚拟地址描述符（Virtual Address Descriptor），简称VAD，储存在一颗AVL树中。你说Windows和Linux最有趣的东西是啥？就是那点小差异。
4GB的虚拟地址空间被划分为页。32位的x86处理器允许将页划分为三种大小：4kb、2mb或4mb。Linux和Windows的用户虚拟地址空间的页大小都是4kb。地址中0-4085字节是第一页，4096-8191是第二页，以此类推。VMA的大小必为页大小的整数倍。如下就是按4kb分页的3gb用户空间：

处理器依据页表将虚拟地址转换为物理地址，不同进程的页表各不相同。因此在进程切换时，用户空间的页表也随之切换。Linux将指向进程页表的指针储存于内存描述符的pgd字段中。每个虚页都与一个页表入口（PTE）相关联，在一般的x86分页机制下，它就是一个4字节长的记录：

Linux有专门的函数来读写PTE的属性标志。P位表示次页表是否位于物理内存。若清零，读取这块内存就会触发一个页异常。牢记，就算该位清零，内核依然可以修改其他属性域。R/W表示读/写(read/write)；若清零，该页只读。U/S表示用户/超级用户(user/supervisor)，若清零，该页只允许内核访问。有了这些标志，才可以实现我们前面所见的只读内存和内核空间保护。
D位与A位分别表示dirty和accessed。若一个页曾被写过，它就被标记为dirty；若曾有过读或写，它就会被标记为accessed。这两个标志都挺难搞：进程只管设置它们，清零却只允许内核来做。PTE保存与此页相关联的起始地址，以4kb对齐。这个貌似简单的属性域有一点不足，那就是限制了物理内存最大只能是4GB。物理地址扩展相关的PTE属性域改天再讲。
虚拟页是内存保护的基本单元，其中的所有字节共享同一U/S和R/W标志。不同的虚拟页可能映射自同一物理页，但其保护标志并不一定相同。注意下，PTE中并没有包含执行权的标志。这带来的后果就是，早期的x86系列CPU可能将堆栈段上的代码执行，因而易于遭到堆栈缓冲区溢出的攻击（如今使用return-to-libc或其他技巧，依然可以对堆栈中不可执行的代码搞溢出）。更深一层，PTE中执行权限标志的缺失使得VMA中的保护标志不一定能够应用到硬件的保护机制。内核已尽其全力，然而体系结构的限制尤在。
虚拟内存也不是什么都存。它只是将一个程序的地址空间映射到实在的物理内存，即物理地址空间。虽然在某种意义上有些内存操作也需要经过总线，但我们在此大可忽略之，从而将物理地址看作是从0开头，以字节为单位递增的一段地址。内核将物理地址空间划分为n个页框。处理器对页框的存在并不关心，不过对内核而言页框至关重要，因为页框就是管理物理内存的基本单元。32位的Linux和Windows都是用4kb的页框，如下是个2GB内存机器的例子：

Linux的每个页框都带有一个描述符以及n个属性标志。这些描述符管理了电脑的所有物理内存，使得每个页框的状态都可以明确。物理内存通过伙伴内存分配机制进行管理，对伙伴系统而言，一个页框若可以分配，那它就是free的。分配来的页框可以是匿名，以储存程序数据；也可以是页缓存，以包含来自文件或设备的数据。也有其他类型的页框，在这里先略过就是。Windows下有个类似的页框号（Page Frame Number,PFN）数据库来跟踪物理内存。
现在把虚拟内存区域、页表、页框放到一起，看看其整体是如何工作。如下是个用户堆的例子：

蓝色方形表示了VMA中包含的页，箭头表示页经页表与页框形成的映射关系。某些虚拟页上并没有箭头，因为它们PTE中Present标志都是清零的。这些页可能是从未使用，也可能是已被换出。不过无论怎样，访问这些页都会导致页异常，即便这些页在VMA中也是如此。VMA和页表不能一一对应，可能难以理解，不过确实是经常发生的事情。
VMA就像是程序与内核间的通信员。你先下个什么请求（内存分配，文件映射等等），内核说“行”，它就创建或更新一个合适的VMA。但它并不会马上将其一步到位，而是等到有了页异常再来。内核是个懒惰且狡诈的混蛋，这就是虚拟内存的基本作风。熟悉与否，这一思想随处可见。VMA保存上面分配来的内容，而PTE决定其具体的行为。这两个数据结构共同管理着程序的内存，遇到页异常、释放内存、换页等操作的时候，都有它俩的份。看个这个内存分配的简单例子：

程序使用brk()系统调用来申请更多内存，内核就简单更新下堆的VMA，说“行了”。不过这时并没有页框真正分配出来，页也不在物理内存中。程序一旦要访问这些页，处理器就会触发页异常，并执行do_page_fault()。它使用find_vma()找出异常发生地址对应的VMA，若找到，检查VMA的权限标志以防恶意访问（读或写）；如果没有合适的VMA，就不再管这个内存访问而交给处理器触发一个段异常。
找到相应VMA之后，内核必须检查PTE的内容以及VMA的类型，以处理这个异常。在我们的例子里，PTE显示出这个页不在物理内存中。这里我们PTE为空（全为零），Linux中就表示这段虚拟页从未被映射过。这是个匿名的VMA，因此只能用do_anoymous_page()处理。它会分配一个页框，修改PTE将那发生异常的虚拟页映射到新分配的页框上。
也有例外。例如页已经换出，那它PTE中Present标志就是0，里面则保存了页内容在硬盘上的地址，它使用do_swap_page()读取硬盘并将其装载置页框。
我们内核内存管理之旅大约已行至一半。在下篇post里，我们会讨论上文件及性能的因素，以了解内存管理的整体。

囧，译到四分之三的时候发现已经有人译过了：http://blog.csdn.net/drshenlei/archive/2009/07/15/4350928.aspx
]]></description>
			<content:encoded><![CDATA[<p>作者：Gustavo Duarte<br />
翻译：ssword<br />
原文：http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory</p>
<hr />
<p>我们已经对类型的虚拟地址空间布局有了一定了解，接下来我们进入内核，了解其内存管理机制。再拿gonzo的图示出来：</p>
<p><img class="alignnone size-full wp-image-645562" title="mm_struct" src="http://static.duartes.org/img/blogPosts/mm_struct.png" alt="mm_struct" /></p>
<p>Linux内核使用进程描述符task_struct的实例来表示进程。task_struct中的mm字段指向内存描述符mm_struct，它储存了内存中各个段的起始位置（如上图）、进程中使用物理页的数量（rss是Resident Set Size的缩写）、虚拟地址区域的大小以及其他一点细节。在内存描述符中，我们还可以发现其两大心腹：虚拟内存区域和页表。Gonzo的内存区域表示起来如下：</p>
<p><img class="alignnone size-full wp-image-645565" title="memorydescriptorandmemoryareas" src="http://static.duartes.org/img/blogPosts/memoryDescriptorAndMemoryAreas.png" alt="memorydescriptorandmemoryareas" /></p>
<p>每块虚拟内存区域（VMA）都是一段连续的虚拟地址，其间没有重叠。vm_area_struct的实例就是对一段内存区域的描述，其中包含了内存区域的起始地址、描述行为和访问控制的标志以及表示文件映射的vm_field字段，没有文件映射的VMA就是匿名的（anoymous）。除去内存映射段，如上的每个段（如堆、栈）都单独与一个VMA相关联。x86的机器大都如此组织内存，不过并非如此不可&#8211;VMA本身不关心自己位于哪个段。</p>
<p>对程序员而言，VMA在其内存描述符，既有mmap字段里顺序的一段链表，又有mm_rb字段的一棵红黑树。这棵红黑树使得内核得以快速按照给出的虚拟地址来找出相应的内存区域。像读取文件/proc/pid_of_process/maps的内容，内核就只是简单遍历这个VMA的链表再将其输出。Windows下的EPROCESS差不多就是task_struct和mm_struct的混合体，而其相对于VMA的等价物就是虚拟地址描述符（Virtual Address Descriptor），简称VAD，储存在一颗AVL树中。你说Windows和Linux最有趣的东西是啥？就是那点小差异。</p>
<p>4GB的虚拟地址空间被划分为页。32位的x86处理器允许将页划分为三种大小：4kb、2mb或4mb。Linux和Windows的用户虚拟地址空间的页大小都是4kb。地址中0-4085字节是第一页，4096-8191是第二页，以此类推。VMA的大小必为页大小的整数倍。如下就是按4kb分页的3gb用户空间：</p>
<p><img class="alignnone size-full wp-image-645567" title="pagedvirtualspace" src="http://static.duartes.org/img/blogPosts/pagedVirtualSpace.png" alt="pagedvirtualspace" /></p>
<p>处理器依据页表将虚拟地址转换为物理地址，不同进程的页表各不相同。因此在进程切换时，用户空间的页表也随之切换。Linux将指向进程页表的指针储存于内存描述符的pgd字段中。每个虚页都与一个页表入口（PTE）相关联，在一般的x86分页机制下，它就是一个4字节长的记录：</p>
<p><img class="alignnone size-full wp-image-645569" title="x86pagetableentry4kb" src="http://static.duartes.org/img/blogPosts/x86PageTableEntry4KB.png" alt="x86pagetableentry4kb" /></p>
<p>Linux有专门的函数来读写PTE的属性标志。P位表示次页表是否位于物理内存。若清零，读取这块内存就会触发一个页异常。牢记，就算该位清零，内核依然可以修改其他属性域。R/W表示读/写(read/write)；若清零，该页只读。U/S表示用户/超级用户(user/supervisor)，若清零，该页只允许内核访问。有了这些标志，才可以实现我们前面所见的只读内存和内核空间保护。</p>
<p>D位与A位分别表示dirty和accessed。若一个页曾被写过，它就被标记为dirty；若曾有过读或写，它就会被标记为accessed。这两个标志都挺难搞：进程只管设置它们，清零却只允许内核来做。PTE保存与此页相关联的起始地址，以4kb对齐。这个貌似简单的属性域有一点不足，那就是限制了物理内存最大只能是4GB。物理地址扩展相关的PTE属性域改天再讲。</p>
<p>虚拟页是内存保护的基本单元，其中的所有字节共享同一U/S和R/W标志。不同的虚拟页可能映射自同一物理页，但其保护标志并不一定相同。注意下，PTE中并没有包含执行权的标志。这带来的后果就是，早期的x86系列CPU可能将堆栈段上的代码执行，因而易于遭到堆栈缓冲区溢出的攻击（如今使用return-to-libc或其他技巧，依然可以对堆栈中不可执行的代码搞溢出）。更深一层，PTE中执行权限标志的缺失使得VMA中的保护标志不一定能够应用到硬件的保护机制。内核已尽其全力，然而体系结构的限制尤在。</p>
<p>虚拟内存也不是什么都存。它只是将一个程序的地址空间映射到实在的物理内存，即物理地址空间。虽然在某种意义上有些内存操作也需要经过总线，但我们在此大可忽略之，从而将物理地址看作是从0开头，以字节为单位递增的一段地址。内核将物理地址空间划分为n个页框。处理器对页框的存在并不关心，不过对内核而言页框至关重要，因为页框就是管理物理内存的基本单元。32位的Linux和Windows都是用4kb的页框，如下是个2GB内存机器的例子：</p>
<p><img src="http://static.duartes.org/img/blogPosts/physicalAddressSpace.png" alt="" /></p>
<p>Linux的每个页框都带有一个描述符以及n个属性标志。这些描述符管理了电脑的所有物理内存，使得每个页框的状态都可以明确。物理内存通过伙伴内存分配机制进行管理，对伙伴系统而言，一个页框若可以分配，那它就是free的。分配来的页框可以是匿名，以储存程序数据；也可以是页缓存，以包含来自文件或设备的数据。也有其他类型的页框，在这里先略过就是。Windows下有个类似的页框号（Page Frame Number,PFN）数据库来跟踪物理内存。</p>
<p>现在把虚拟内存区域、页表、页框放到一起，看看其整体是如何工作。如下是个用户堆的例子：</p>
<p><img class="alignnone size-full wp-image-645564" title="heapmapped" src="http://static.duartes.org/img/blogPosts/heapAllocation.png" alt="heapmapped" width="549" height="176" /></p>
<p>蓝色方形表示了VMA中包含的页，箭头表示页经页表与页框形成的映射关系。某些虚拟页上并没有箭头，因为它们PTE中Present标志都是清零的。这些页可能是从未使用，也可能是已被换出。不过无论怎样，访问这些页都会导致页异常，即便这些页在VMA中也是如此。VMA和页表不能一一对应，可能难以理解，不过确实是经常发生的事情。</p>
<p>VMA就像是程序与内核间的通信员。你先下个什么请求（内存分配，文件映射等等），内核说“行”，它就创建或更新一个合适的VMA。但它并不会马上将其一步到位，而是等到有了页异常再来。内核是个懒惰且狡诈的混蛋，这就是虚拟内存的基本作风。熟悉与否，这一思想随处可见。VMA保存上面分配来的内容，而PTE决定其具体的行为。这两个数据结构共同管理着程序的内存，遇到页异常、释放内存、换页等操作的时候，都有它俩的份。看个这个内存分配的简单例子：</p>
<p><img class="alignnone size-full wp-image-645563" title="heapallocation" src="http://static.duartes.org/img/blogPosts/heapAllocation.png" alt="heapallocation" width="678" height="402" /></p>
<p>程序使用brk()系统调用来申请更多内存，内核就简单更新下堆的VMA，说“行了”。不过这时并没有页框真正分配出来，页也不在物理内存中。程序一旦要访问这些页，处理器就会触发页异常，并执行do_page_fault()。它使用find_vma()找出异常发生地址对应的VMA，若找到，检查VMA的权限标志以防恶意访问（读或写）；如果没有合适的VMA，就不再管这个内存访问而交给处理器触发一个段异常。</p>
<p>找到相应VMA之后，内核必须检查PTE的内容以及VMA的类型，以处理这个异常。在我们的例子里，PTE显示出这个页不在物理内存中。这里我们PTE为空（全为零），Linux中就表示这段虚拟页从未被映射过。这是个匿名的VMA，因此只能用do_anoymous_page()处理。它会分配一个页框，修改PTE将那发生异常的虚拟页映射到新分配的页框上。</p>
<p>也有例外。例如页已经换出，那它PTE中Present标志就是0，里面则保存了页内容在硬盘上的地址，它使用do_swap_page()读取硬盘并将其装载置页框。</p>
<p>我们内核内存管理之旅大约已行至一半。在下篇post里，我们会讨论上文件及性能的因素，以了解内存管理的整体。</p>
<hr />
<p>囧，译到四分之三的时候发现已经有人译过了：<a href="http://blog.csdn.net/drshenlei/archive/2009/07/15/4350928.aspx">http://blog.csdn.net/drshenlei/archive/2009/07/15/4350928.aspx</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2009/12/06/%e5%86%85%e6%a0%b8%e5%a6%82%e4%bd%95%e7%ae%a1%e7%90%86%e5%86%85%e5%ad%98/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>柯里生平</title>
		<link>http://www.fleurer-lee.com/2009/10/31/%e6%9f%af%e9%87%8c%e7%94%9f%e5%b9%b3/</link>
		<comments>http://www.fleurer-lee.com/2009/10/31/%e6%9f%af%e9%87%8c%e7%94%9f%e5%b9%b3/#comments</comments>
		<pubDate>Sat, 31 Oct 2009 05:38:37 +0000</pubDate>
		<dc:creator>ssword</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[haskell]]></category>
		<category><![CDATA[传记]]></category>

		<guid isPermaLink="false">http://swdpress.cn/?p=645519</guid>
		<description><![CDATA[翻译：ssword
作者：J J O&#8217;Connor and E F Robertson
原文：http://www.gap-system.org/~history/Biographies/Curry.html



Haskell  Brooks Curry

哈斯克尔•柯里的父亲塞缪尔•塞拉斯•柯里是波士顿Expression学校的校长，他母亲安娜•布里特则是这所学校的一位院长。高中时代的哈斯克尔并没有表现出对数学的特殊爱好，到1916年高中毕业的时侯，他还认为自己会成为一名医生。不久后，他进入了哈弗大学读本科，在医学的课程之余选修了数学。
影响他研究方向的一个重大事件就是1917年春，美国宣布了参与第一次世界大战。欲为祖国效力的柯里认为数学要比学医更有用&#8212;-当然，他也是喜欢数学，而且成绩非常好&#8212;-他就转到了数学系，并于1918年10月18日加入了学生军事训练营。战争不久就结束了（当年11月），12月9日柯里复员回到学校，继续数学专业的学习。1920年，柯里得到了一个A.B学位，顺利毕业。
柯里在通用电气公司找了一份电气工程师的工作，这一来就可以在业余时间中在麻省理工大学进修电气工程了。不过他很快就发现了自己与别人的不同：别人对待学习，都只是“答案正确即可”，而他则关心“答案为什么正确”。意识到自己更适合搞理论研究而非应用科学后，柯里于1922年转专业到物理学。在理论研究上，哈弗要更好，于是柯里作为了P W Bridgeman 在1922-1923学期的助教，回到了哈弗。1924年，柯里得到了物理硕士学位。也正在这时，他意识到自己不属于物理&#8212;-数学才是他的归宿。他开始在哈弗攻读数学博士学位。
转业期的柯里也有其他劳心事。他父亲在1921年不幸去世，其所有不动产都由柯里继承。而其中最大的不动产自然就是波士顿Expression学校。1924年他母亲去世的三年后，这所学校成为了一个股份公司。从创立伊始，柯里就一直担任公司的会计，不过1928年他就卖掉了它。
如果觉得柯里在1924年从此投身数学事业，那你就错了。他的研究方向本是George Birkhoff的微分方程理论，不过与此同时他开始接触了一些逻辑学的书籍，而且发现自己对逻辑更感兴趣。于是他咨询了哈弗的几位教授以及MIT的Norbert Wiener，自己是不是可以转专业到逻辑学，结果遭到了他们的一致反对。在担任1926-27的第一学期兼职讲师的时候，他阅读了罗素和怀特海的《数学原理》。这为他后来的研究奠定了基础，也就是使用组合子来分析复杂的代换规则的设想。他又去咨询哈弗的教授们和Norbert Wiener，问自己可不可以写篇逻辑方面的博士论文。这时他得到的回应却与上次大不相同，其中最典型的当属Wiener&#8212;-他说：如果你有话可说，就别碰逻辑；不过你显然有话可说！
于是，柯里转了最后一次专业。他放弃了微分方程的研究，而准备撰写一篇逻辑方面的博士论文。在Birkhoff的强烈推荐下，他决定在开始新专业的研究之前先去普林斯顿做一年讲师。随后他一边与Velben商讨着他的研究计划，一边翻着普林斯顿大学图书馆里的Mathematische Annalen，其中他们发现1924年M Schönfinkel的一篇论文über die Bausteine der mathematischen Logiküber die Bausteine der mathematischen Logik里有提到一种类似组合子的想法。Velben向柯里保证这是项有意义的研究，不过Alexander告诉他Schönfinkel现在在精神病院里而无法继续这项研究。柯里需要一名最好的博士导师，Velben就向他推荐德国哥廷根的Bernays。为了便于申请经费，柯里便事先公开了他关于组合子的想法，也就是1929年他的第一篇论文《逻辑代换的分析》（An analysis of logical substitution）在《美国数学报》（American Journal of Mathematics）的发布。
动身去哥廷根之前，1928年7月3日，柯里完成了与玛丽•弗吉尼亚•威利的婚事。他们在波士顿Expression学校初识，当时的玛丽还是个学生。随后，两人一起踏上了去德国的旅程。大约一年之后（7月24日），他提交了论文Grundlagen der kombinatorischen Logik。名义上虽是希尔伯特审阅，而天天给予对他帮助的人其实是Bernays。他的论文最终发表于1930年的《美国数学报》。
回到美国之后的1929年9月，柯里被宾夕法尼亚州立学院（即现在的宾夕法尼亚州立大学）聘请。次年7月27日，哈斯克尔和弗吉尼亚的女儿安妮•莱特•柯里出生了，1934年7月6日，他们的儿子罗伯特•威利•柯里出生。在大萧条刚开始时候，柯里能搞到这份工作是很幸运的。随后在大萧条期间，对数理逻辑学家而言就少有工作机会了。柯里在宾夕法尼亚大学担任教员直至1966年退休，他也在其他学校中呆了不少时间。尤其是芝加哥大学，他在1931-1932年间担任其国家研究委员；他也在1938-39年间担任普林斯顿的高级学会会员。其间他发表了一些论文，包括《组合子逻辑的全称量词》（The universal quantifier in combinatory logic，1931），《组合子理论的补遗》（ome additions to the theory of combinators，1932），《显式变量的组合逻辑观点》（Apparent variables from the standpoint of combinatory [...]]]></description>
			<content:encoded><![CDATA[<p>翻译：ssword<br />
作者：J J O&#8217;Connor and E F Robertson<br />
原文：<a href="http://www.gap-system.org/~history/Biographies/Curry.html">http://www.gap-system.org/~history/Biographies/Curry.html</a></p>
<hr />
<div style="float:right; text-align:center;">
<img  src="http://www.gap-system.org/~history/Thumbnails/Curry.jpg"></img></p>
<h3>Haskell  Brooks Curry</h3>
</div>
<p>哈斯克尔•柯里的父亲塞缪尔•塞拉斯•柯里是波士顿Expression学校的校长，他母亲安娜•布里特则是这所学校的一位院长。高中时代的哈斯克尔并没有表现出对数学的特殊爱好，到1916年高中毕业的时侯，他还认为自己会成为一名医生。不久后，他进入了哈弗大学读本科，在医学的课程之余选修了数学。</p>
<p>影响他研究方向的一个重大事件就是1917年春，美国宣布了参与第一次世界大战。欲为祖国效力的柯里认为数学要比学医更有用&#8212;-当然，他也是喜欢数学，而且成绩非常好&#8212;-他就转到了数学系，并于1918年10月18日加入了学生军事训练营。战争不久就结束了（当年11月），12月9日柯里复员回到学校，继续数学专业的学习。1920年，柯里得到了一个A.B学位，顺利毕业。</p>
<p>柯里在通用电气公司找了一份电气工程师的工作，这一来就可以在业余时间中在麻省理工大学进修电气工程了。不过他很快就发现了自己与别人的不同：别人对待学习，都只是“答案正确即可”，而他则关心“答案为什么正确”。意识到自己更适合搞理论研究而非应用科学后，柯里于1922年转专业到物理学。在理论研究上，哈弗要更好，于是柯里作为了P W Bridgeman 在1922-1923学期的助教，回到了哈弗。1924年，柯里得到了物理硕士学位。也正在这时，他意识到自己不属于物理&#8212;-数学才是他的归宿。他开始在哈弗攻读数学博士学位。</p>
<p>转业期的柯里也有其他劳心事。他父亲在1921年不幸去世，其所有不动产都由柯里继承。而其中最大的不动产自然就是波士顿Expression学校。1924年他母亲去世的三年后，这所学校成为了一个股份公司。从创立伊始，柯里就一直担任公司的会计，不过1928年他就卖掉了它。</p>
<p>如果觉得柯里在1924年从此投身数学事业，那你就错了。他的研究方向本是George Birkhoff的微分方程理论，不过与此同时他开始接触了一些逻辑学的书籍，而且发现自己对逻辑更感兴趣。于是他咨询了哈弗的几位教授以及MIT的Norbert Wiener，自己是不是可以转专业到逻辑学，结果遭到了他们的一致反对。在担任1926-27的第一学期兼职讲师的时候，他阅读了罗素和怀特海的《数学原理》。这为他后来的研究奠定了基础，也就是使用组合子来分析复杂的代换规则的设想。他又去咨询哈弗的教授们和Norbert Wiener，问自己可不可以写篇逻辑方面的博士论文。这时他得到的回应却与上次大不相同，其中最典型的当属Wiener&#8212;-他说：如果你有话可说，就别碰逻辑；不过你显然有话可说！</p>
<p>于是，柯里转了最后一次专业。他放弃了微分方程的研究，而准备撰写一篇逻辑方面的博士论文。在Birkhoff的强烈推荐下，他决定在开始新专业的研究之前先去普林斯顿做一年讲师。随后他一边与Velben商讨着他的研究计划，一边翻着普林斯顿大学图书馆里的Mathematische Annalen，其中他们发现1924年M Schönfinkel的一篇论文über die Bausteine der mathematischen Logiküber die Bausteine der mathematischen Logik里有提到一种类似组合子的想法。Velben向柯里保证这是项有意义的研究，不过Alexander告诉他Schönfinkel现在在精神病院里而无法继续这项研究。柯里需要一名最好的博士导师，Velben就向他推荐德国哥廷根的Bernays。为了便于申请经费，柯里便事先公开了他关于组合子的想法，也就是1929年他的第一篇论文《逻辑代换的分析》（An analysis of logical substitution）在《美国数学报》（American Journal of Mathematics）的发布。</p>
<p>动身去哥廷根之前，1928年7月3日，柯里完成了与玛丽•弗吉尼亚•威利的婚事。他们在波士顿Expression学校初识，当时的玛丽还是个学生。随后，两人一起踏上了去德国的旅程。大约一年之后（7月24日），他提交了论文Grundlagen der kombinatorischen Logik。名义上虽是希尔伯特审阅，而天天给予对他帮助的人其实是Bernays。他的论文最终发表于1930年的《美国数学报》。</p>
<p>回到美国之后的1929年9月，柯里被宾夕法尼亚州立学院（即现在的宾夕法尼亚州立大学）聘请。次年7月27日，哈斯克尔和弗吉尼亚的女儿安妮•莱特•柯里出生了，1934年7月6日，他们的儿子罗伯特•威利•柯里出生。在大萧条刚开始时候，柯里能搞到这份工作是很幸运的。随后在大萧条期间，对数理逻辑学家而言就少有工作机会了。柯里在宾夕法尼亚大学担任教员直至1966年退休，他也在其他学校中呆了不少时间。尤其是芝加哥大学，他在1931-1932年间担任其国家研究委员；他也在1938-39年间担任普林斯顿的高级学会会员。其间他发表了一些论文，包括《组合子逻辑的全称量词》（The universal quantifier in combinatory logic，1931），《组合子理论的补遗》（ome additions to the theory of combinators，1932），《显式变量的组合逻辑观点》（Apparent variables from the standpoint of combinatory logic，1933），《组合逻辑中相等性及推导的几个性质》（Some properties of equality and implication in combinatory logic ，1934）。</p>
<p>1936年符号逻辑学会成立。作为创办者之一，柯里在1936-1937的两年里担任学会的发言人，后于1939-1940年间担任会长。1942年，他的离职演说《数理逻辑的组合子基础》发表于《符号逻辑期刊》（Journal of Symbolic Logic）。在里面，柯里先阐明了组合子逻辑的提纲，展示了与Church的lambda演算之间的密切联系，随后开始阐述他的近期工作。他检验了几种从不相容系统中导出悖论（如理查德和罗素悖论）的简便方法。他还为组合子逻辑引入了未定义的概念，如量词、形式推导。这一来，类似Church和Rosser的完备系统就可以推导出来。</p>
<p>40年代的柯里已经成为了世界顶尖的数理逻辑学家之一。此时，他被邀请做一个数学演说来解释下形式主义的基础概念，并提几个新的建议。这次演说的内容被记录在论文《数学严谨性问题的几个方面》（Some aspects of the problem of mathematical rigor）中，后发表于1941年的《美国数学论坛》。其中囊括了对非形式化理论的评论、形式系统的概念、演算的概念、对元理论的讨论、数学的定义、形式系统的可接受性，并讨论了直觉主义与形式主义之争。同在40年代，柯里与波士顿Expression学校重新建立了关系，此时该学校已更名为柯里学院。1940年，他加入了该校校董，并担任了十年。</p>
<p>第二次世界大战期间，柯里开始研究应用数学。1943年，他发表了《Heaviside演算》（the Heaviside operational calculas）。在其中，他阐述了一个简单的代数方法，对着它的不足也有所留意：</p>
<p>…优点，自然就是导出了方程解的约束条件，不过它只对有理数运算，如常项系数的线性微分方程适用。对于更一般的情况而言，如偏微分方程、小数运算等，数值变换理论必然是不可或缺的。</p>
<p>1942年5月到1944年1月，他在Frankford Arsenal工作。离开Frankford Arsenal之后，他在John Hopkins大学的应用物理实验室工作至1945年5月。随后，他去了一个军用武器测试点，即阿伯丁实验场。在那里他接触到了使用ENIAC电脑做的一些研究。1946年，《使用ENIAC的逆向插值法研究》和《使用ENIAC的四阶插值法研究》发布。1946年他回到宾夕法尼亚州立大学后，曾试图说服校领导购置一台电脑，不过失败了。</p>
<p>他的主要著作有《组合逻辑》（同Robert Feys合著）和《数理逻辑基础》（1963）。《组合逻辑》的编写工作始于1950年，当时柯里刚刚获得福尔布莱特奖，从而可以在Louvain与Robert Feys合作。柯里回到美国之后，他们依然保持着此书的合作，最后于1956年完成。E J Cogan论及此书，给了组合逻辑以极高的评价：</p>
<p>组合逻辑解释了数学中一般被认为是直觉而不予考虑的概念。像代换，一般就认为是变量的使用；像系统的类型抽象，通常是通过辅助手段引入，而不被当作系统的一部分。但在组合逻辑中，这些基础性的问题都可以由组合子理论解决。</p>
<p>在《数理逻辑基础》一书中，柯里使用Gentzen方法阐述了一个代数基础的论题。J Tucker说：</p>
<p>这方法最引人注目的地方就是，有循序渐进的规约：先是连词和或，再在后面章节中引入否定和量词。不像传统方法那样在开头将基本连词的含义一一列出，而是使用逻辑的推理将其一一导出。</p>
<p>1966年，他接受了阿姆斯特丹的逻辑学、逻辑史、科学哲学教授的职位。工作四年之后，他回到了宾夕法尼亚州立大学开始自己的离休生活。</p>
<p>[3]的作者高度评价了柯里和他的妻子：</p>
<p>柯里夫妇为人友善，美名远扬。哈斯克尔对同事和学生所做的一切，远多于自己。人若想找他说话，他会随时欢迎，在一同探讨问题的同时给予对方一切能及的鼓励…他办公室的门总是开着。这无疑也对我们研究组合子提供了莫大的鼓舞。不管住在何地，柯里的好客都是很有名的。他经常在家举行派对，而不拘泥形式。我想，弗吉尼亚的厨艺也烘培了我们对组合逻辑的兴趣！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2009/10/31/%e6%9f%af%e9%87%8c%e7%94%9f%e5%b9%b3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>幸为程序员</title>
		<link>http://www.fleurer-lee.com/2009/10/01/%e5%b9%b8%e4%b8%ba%e7%a8%8b%e5%ba%8f%e5%91%98/</link>
		<comments>http://www.fleurer-lee.com/2009/10/01/%e5%b9%b8%e4%b8%ba%e7%a8%8b%e5%ba%8f%e5%91%98/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 04:32:19 +0000</pubDate>
		<dc:creator>ssword</dc:creator>
				<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://swdpress.cn/?p=645494</guid>
		<description><![CDATA[作者：Gustavo Duarte
翻译：ssword
原文：http://duartes.org/gustavo/blog/post/lucky-to-be-a-programmer

前几个星期我们一直都在忙于一个项目。经过大家的一番呕心沥血，这个项目终于得以顺利结束，我们的作息也恢复正常。现在同事们每谈起那段疯狂的时光，总是唉声叹气。我觉得这不大应该，我就很少发牢骚。其实我觉得，只要环境对头，编程这项工作总是有一种难以言传的乐趣。
对这一观点，许多同行都表示赞同，不过其他一些人则不以为然。都怪万恶的制度，它能够把一切事情的乐趣都消灭无形&#8212;-学校可以把生动的知识搞成一堆冷冰冰的公式，企业也可以把挑战性的工作仅当成员工挣钱糊口的手段。
这样很不好。又有什么工作，能好过在一个创作环境里为自己杰作的出世而绞尽脑汁、废寝忘食呢？我这可不是在怂恿人们做工作狂&#8212;-一个稳定的作息是相当必要的&#8212;除却偶尔的狂欢。我要表达的是：编程是解题、写作和工艺的完美结合，在创造中你可以体会到超乎寻常的乐趣。
编程中存在许多诱人的挑战，并为创造提供了充足的空间。一些问题是研究性的：这段代码为什么如此慢？造成这个bug的原因究竟是什么？一些问题是建设性的，比如设计算法和架构。你若喜欢搞研究，这些怪物们都将变得可爱起来：malware、路由、缓存、协议、数据库、图和数。
这些研究就像个策略游戏，是程序员与编程距离最近、也是最有趣的地方。不过对于大部分软件开发而言，“交流”才是重中之重：通过代码与程序员的交流，通过界面与用户的交流。所以比起“解题”来，编程更像是“写作”。它要求你将灵感与设计清晰地表达出来，做到简洁明快。在代码与界面的设计中，创造的乐趣无所不在。
编程的另一个乐趣在于，编程中存在美。这话可能听起来像是狗屎，不过确实是实话。编程可以帮你找到更多的生活乐趣。如欧几里德关于素数无限多的两行证明，我相信很多人可以体会到其中的美感&#8212;-如此简洁、如此惊人！这便是数学的美：严谨、朴素。软件也是如此，这种美弥漫于精妙的算法（像quicksort）、内核和编译器的源码、巧妙的溢出攻击以及日常技巧之中。看到这些方法，不管是大名鼎鼎的算法还是稀松平常的技巧，你都可以会心一笑：“太妙了！”&#8212;-这感觉很棒。
编程中的美感并不仅限于数学。它还存在于良好的设计&#8212;-用少量的代码、整洁的方法来完成功能的实现。一些语言实现起来会比较困难，因而不是所有程序员都能做到游刃有余，但也正因为如此，搞这种代码就有了挑战性；若是用一种表达能力强的语言与同事合作，生活就更加明朗了。
再谈谈工艺。软件是抽象的&#8212;-除了人的思维，还有什么可以搞编程？说是“构造软件”，也是有道理的。程序都是一个特性一个特性地慢慢成型，从模型开始，渐渐增长成架构。同时也得考虑上用户界面，bug的修复以及瓶颈上的优化。它可以给人一种工艺上的满足感：由纯粹的想法出发，一步步“构造”出来，使之可以运行，从而能够给别人提供帮助&#8212;-没准还能彻底改变人类的生活。
就拿医学说吧。虽然经过了400年的科技革命，但今天的医学对病毒性感染或癌症等绝症依然是无能为力。领域内的很多成果都是出自偶然，像抗生素。你在搞肺炎药的临床试验&#8212;-唔&#8212;-病人们都勃起了！伟哥诞生。虽说机遇总是垂青有准备的头脑，但是物理和化学都依靠强大的理论基础取得了长足的进步，而医学却依然被限制到有限的运气上。要治疗癌症？给病人做放疗、化疗，然后祈祷它们先杀死癌细胞吧。这是个很出色的发明，我会乐于接受它们。不过在精度上，它们还是差得很远。
软件正在改变这一切。大约50年前，人类发现了DNA的结构。现在，每个人都可以浏览或下载上百个基因组，或是检索上千个基因（如DLEC1）的核苷酸序列以及对应的氨基酸序列，可以按照名字找到详细的介绍。或许你也可以通过一台廉价的设备检测自己的基因序列，再到基因数据库中检索，得到一份详细的匹配报告。使用标准的串搜索工具&#8212;-BLAST算法，将基因片段从数据库中按照匹配程度评分，因而完全不用怀疑其精确性。这将为药物研发提供巨大的突破。医学正借软件之力，进入了新的纪元。
当然，医学只是一小部分:P，科学、文化以及商业都从程序的进步中获益良多。如今第三世界的孩子都可以到wikipedia浏览资料，这也是我们的成就！是我们定义了RFC和协议栈，开发了浏览器和MediaWiki、操作系统和HTTP服务器。篇幅有限，我就不提wikipedia上的无数条目了。从字节到比特，技术的影响力已经无所不在：是程序员发明了wiki和blog。Henry Mencken说：“媒体的自由受其主人约束。”（freedom of the press is limited to those who own one）。很可惜，他没能见到我们的发明对传统新闻业产生的巨大影响。我们开发的应用使得生产力大为提高并且影响到经济，这在历史上也是少有的。
三年前我本科毕业（之前已编程多年）本打算学医，当时是有两次不爽的经历使得我差点对编程失去热情。很庆幸，我坚持了下来。到今天我依然对医学研究持有兴趣，但若再给我一次机会，我依然会选择编程。其中有太多乐趣。我妈妈则一直以为我的工作就是打字，也罢。
如果你发现自己被困在一个地方，使得自己对编程的热情不停地为之消磨，那就去他妈的，闪人！不要在平庸的地方浪费自己的热情。有热情的人千金难买，而这就是你的资本；靠谱的雇主多的是&#8212;-尤其是创业者。对于拿不准自己是不是喜欢编程的人们，你们的能力或许有高有低，但我强烈希望你们能选择编程。不提工作上的光明前景，更应认识到，软件的应用已经越来越广，身为程序员，可以亲身参与技术带来的变革。毫无疑问，我会乐于参与其中，用我拥有的艺术和工艺。

译者注：请参考国情。
]]></description>
			<content:encoded><![CDATA[<p>作者：Gustavo Duarte<br />
翻译：ssword<br />
原文：<a href="http://duartes.org/gustavo/blog/post/lucky-to-be-a-programmer">http://duartes.org/gustavo/blog/post/lucky-to-be-a-programmer</a></p>
<hr />
<p>前几个星期我们一直都在忙于一个项目。经过大家的一番呕心沥血，这个项目终于得以顺利结束，我们的作息也恢复正常。现在同事们每谈起那段疯狂的时光，总是唉声叹气。我觉得这不大应该，我就很少发牢骚。其实我觉得，只要环境对头，编程这项工作总是有一种难以言传的乐趣。</p>
<p>对这一观点，许多同行都表示赞同，不过其他一些人则不以为然。都怪万恶的制度，它能够把一切事情的乐趣都消灭无形&#8212;-学校可以把生动的知识搞成一堆冷冰冰的公式，企业也可以把挑战性的工作仅当成员工挣钱糊口的手段。</p>
<p>这样很不好。又有什么工作，能好过在一个创作环境里为自己杰作的出世而绞尽脑汁、废寝忘食呢？我这可不是在怂恿人们做工作狂&#8212;-一个稳定的作息是相当必要的&#8212;除却偶尔的狂欢。我要表达的是：编程是解题、写作和工艺的完美结合，在创造中你可以体会到超乎寻常的乐趣。</p>
<p>编程中存在许多诱人的挑战，并为创造提供了充足的空间。一些问题是研究性的：这段代码为什么如此慢？造成这个bug的原因究竟是什么？一些问题是建设性的，比如设计算法和架构。你若喜欢搞研究，这些怪物们都将变得可爱起来：malware、路由、缓存、协议、数据库、图和数。</p>
<p>这些研究就像个策略游戏，是程序员与编程距离最近、也是最有趣的地方。不过对于大部分软件开发而言，“交流”才是重中之重：通过代码与程序员的交流，通过界面与用户的交流。所以比起“解题”来，编程更像是“写作”。它要求你将灵感与设计清晰地表达出来，做到简洁明快。在代码与界面的设计中，创造的乐趣无所不在。</p>
<p>编程的另一个乐趣在于，编程中存在美。这话可能听起来像是狗屎，不过确实是实话。编程可以帮你找到更多的生活乐趣。如欧几里德关于素数无限多的两行证明，我相信很多人可以体会到其中的美感&#8212;-如此简洁、如此惊人！这便是数学的美：严谨、朴素。软件也是如此，这种美弥漫于精妙的算法（像quicksort）、内核和编译器的源码、巧妙的溢出攻击以及日常技巧之中。看到这些方法，不管是大名鼎鼎的算法还是稀松平常的技巧，你都可以会心一笑：“太妙了！”&#8212;-这感觉很棒。</p>
<p>编程中的美感并不仅限于数学。它还存在于良好的设计&#8212;-用少量的代码、整洁的方法来完成功能的实现。一些语言实现起来会比较困难，因而不是所有程序员都能做到游刃有余，但也正因为如此，搞这种代码就有了挑战性；若是用一种表达能力强的语言与同事合作，生活就更加明朗了。</p>
<p>再谈谈工艺。软件是抽象的&#8212;-除了人的思维，还有什么可以搞编程？说是“构造软件”，也是有道理的。程序都是一个特性一个特性地慢慢成型，从模型开始，渐渐增长成架构。同时也得考虑上用户界面，bug的修复以及瓶颈上的优化。它可以给人一种工艺上的满足感：由纯粹的想法出发，一步步“构造”出来，使之可以运行，从而能够给别人提供帮助&#8212;-没准还能彻底改变人类的生活。</p>
<p>就拿医学说吧。虽然经过了400年的科技革命，但今天的医学对病毒性感染或癌症等绝症依然是无能为力。领域内的很多成果都是出自偶然，像抗生素。你在搞肺炎药的临床试验&#8212;-唔&#8212;-病人们都勃起了！伟哥诞生。虽说机遇总是垂青有准备的头脑，但是物理和化学都依靠强大的理论基础取得了长足的进步，而医学却依然被限制到有限的运气上。要治疗癌症？给病人做放疗、化疗，然后祈祷它们先杀死癌细胞吧。这是个很出色的发明，我会乐于接受它们。不过在精度上，它们还是差得很远。</p>
<p>软件正在改变这一切。大约50年前，人类发现了DNA的结构。现在，每个人都可以浏览或下载上百个基因组，或是检索上千个基因（如DLEC1）的核苷酸序列以及对应的氨基酸序列，可以按照名字找到详细的介绍。或许你也可以通过一台廉价的设备检测自己的基因序列，再到基因数据库中检索，得到一份详细的匹配报告。使用标准的串搜索工具&#8212;-BLAST算法，将基因片段从数据库中按照匹配程度评分，因而完全不用怀疑其精确性。这将为药物研发提供巨大的突破。医学正借软件之力，进入了新的纪元。</p>
<p>当然，医学只是一小部分:P，科学、文化以及商业都从程序的进步中获益良多。如今第三世界的孩子都可以到wikipedia浏览资料，这也是我们的成就！是我们定义了RFC和协议栈，开发了浏览器和MediaWiki、操作系统和HTTP服务器。篇幅有限，我就不提wikipedia上的无数条目了。从字节到比特，技术的影响力已经无所不在：是程序员发明了wiki和blog。Henry Mencken说：“媒体的自由受其主人约束。”（freedom of the press is limited to those who own one）。很可惜，他没能见到我们的发明对传统新闻业产生的巨大影响。我们开发的应用使得生产力大为提高并且影响到经济，这在历史上也是少有的。</p>
<p>三年前我本科毕业（之前已编程多年）本打算学医，当时是有两次不爽的经历使得我差点对编程失去热情。很庆幸，我坚持了下来。到今天我依然对医学研究持有兴趣，但若再给我一次机会，我依然会选择编程。其中有太多乐趣。我妈妈则一直以为我的工作就是打字，也罢。</p>
<p>如果你发现自己被困在一个地方，使得自己对编程的热情不停地为之消磨，那就去他妈的，闪人！不要在平庸的地方浪费自己的热情。有热情的人千金难买，而这就是你的资本；靠谱的雇主多的是&#8212;-尤其是创业者。对于拿不准自己是不是喜欢编程的人们，你们的能力或许有高有低，但我强烈希望你们能选择编程。不提工作上的光明前景，更应认识到，软件的应用已经越来越广，身为程序员，可以亲身参与技术带来的变革。毫无疑问，我会乐于参与其中，用我拥有的艺术和工艺。</p>
<hr />
译者注：请参考国情。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2009/10/01/%e5%b9%b8%e4%b8%ba%e7%a8%8b%e5%ba%8f%e5%91%98/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>缪论：C最高效</title>
		<link>http://www.fleurer-lee.com/2009/09/08/%e7%bc%aa%e8%ae%ba%ef%bc%9ac%e6%9c%80%e9%ab%98%e6%95%88/</link>
		<comments>http://www.fleurer-lee.com/2009/09/08/%e7%bc%aa%e8%ae%ba%ef%bc%9ac%e6%9c%80%e9%ab%98%e6%95%88/#comments</comments>
		<pubDate>Tue, 08 Sep 2009 00:54:06 +0000</pubDate>
		<dc:creator>ssword</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[PL]]></category>

		<guid isPermaLink="false">http://swdpress.cn/?p=645455</guid>
		<description><![CDATA[作者：Mark Chu-Carroll (aka MarkCC)
翻译：ssword
原文：http://scienceblogs.com/goodmath/2006/11/the_c_is_efficient_language_fa.php

昨天偶然看到一篇关于程序设计语言的文章，直击我G点，忍不住前来吐槽。这篇文章来自greythumb.org，叫做《程序员的呼喊：C\C++该有什么》。
无非也就是“C\C++是追求高效的首选”之类的老生常谈。他们错了。它们不是。C\C++接近硬件是为了直接控制堆栈、修改寄存器等等，而非为了高效。在科研编程或者数值计算的应用上，它们差劲得很。
贴一段让我崩溃的文字：
首先，那些担忧纯属多余。C\C++永远不会消失。为什么？因为有无数的程序现在是、永远都是吃CPU的。对这些领域的编程而言，根本没有快过C\C++的语言。将来会不会出现这种语言？我非常怀疑。
我说的这些领域就是：科学计算、游戏/物理特效、光线跟踪、实时3D图像、音频处理、编译器、高速路由、进化计算(我的最爱:)，还有高级语言运行时&#8212;毫无疑问。再就是像操作系统、硬件驱动之类“接近底层”，需要很多交互、甚至内嵌汇编的程序。C就是简单版的汇编，这就是为何C总是作为此类程序的首选。
对这些领域而言，在语言及架构层面的过早优化是可以接受、有时甚至是必须的。我敢打赌，在五十年后，这些领域的一部分依然会是C\C++或者相似语言的天下。对于同样一个基于进化计算指令集的实现，C要整整快过java两倍。由此你可以看出C是多么的快。
问题在这里：C\C++在数值计算上的性能相当扯淡。它们不是最快的，而且绝非偶然。实际上，受底层实现的一些限制，使得C\C++根本不可能表现得很高效。这便是为什么到今天依然有Fortan应用于在高精度科研项目，而这些应用往往需要榨干机器的每一滴性能&#8212;&#8212;如流体动力学模拟。[1]
程序要高效离不开编译器优化，现代架构的编译器可以达到人类优化汇编代码的极限。有时交换两条无关指令的顺序就可以得到一个出人意料的性能提升，而机器所做的优化，很多都是人类难以企及的。[2]
因此对于现代的开发而言，程序的高效绝非只凭程序员一人之力。程序员需要做的，是仔细选择合适的算法&#8212;&#8211;这活机器做不了；机器需要做的，是仔细地调整指令、约束流水线、内存延时等等。二者合作才会有高效的程序。二者的工作又是相互影响的：程序员应该用机器能够理解的代码来描述算法，以方便机器进行优化。
这就是C\C++失败的地方。C\C++在语义上过度依赖指针，导致不受约束的指针几乎无处不在。在C\C++中，并没有真正意义上的数组&#8212;&#8212;它们只是指针，下标只是指针运算的简写形式（C\C++里的x[n]与*(x+n)是完全一样的）。
过度依赖指针就意味着，C\C++的编译器会很难辨认两个东西是否独立。由此产生的问题被称作“重名探测”（alias detection），也就是找出可能指向同一个位置的两个变量。若存在不受约束的指针，别名探测几乎就无法实现。举个例子：

for &#40;int i=0; i &#60; 20000&#41; &#123;
   for &#40;int j=0; j &#60; 20000&#41; &#123;
      x&#91;i&#93;&#91;j&#93; = y&#91;i-2&#93;&#91;j+1&#93; * y&#91;i+1&#93;&#91;j-2&#93;;
   &#125;
&#125;

看下这个循环。它可以并行化或向量化[3]，但前提必须是x与y没有重合、完全无关，这在C\C++中这根没办法得到保证。Fortran-77就没问题，你可以轻而易举地检查它俩是否不同。若是Fortran-98，你还可以检查出它们是否为指针，若有需要，程序就可以弄清楚它们之间是否有重复。在C\C++下，这就做不到（Fortran也不是最好的&#8212;&#8212;一门来自Lawrence Livermore labs 的实验语言Sisal，在特定代码上要强过Fortran 20%）。
这个例子是拿并行说事，但“重名”带来的问题可不只并行这么简单，说并行只是因为比较好解释。“重名”带来的问题直接影响了C\C++代码的编写。
再举个具体的例子吧，我就不吐槽了。六年前，我在一个项目里需要实现一个相当复杂的算法来计算两个数组的“最长公共串”（longest comon sequence， LCS）。计算LCS的标准算法被称作“动态规划”，是O*(n^3) 的时间，O(n^2) 的空间。搞计算生物学的人们也有个相似的算法，时间与它相同，不过空间平均起来只有O(n)。
我不知道该用哪门语言，就做了个实验。我用C，C++，ocaml，java和python分别实现了一遍LCS算法，来比对代码的复杂度
以及运行效率。2000个元素的数组来做实验数据，所得的测试结果如下：

C: 0.8 seconds.
C++: 2.3 seconds.
OCaml: 0.6 seconds interpreted, 0.3 seconds fully compiled.
Java: 1 minute [...]]]></description>
			<content:encoded><![CDATA[<p>作者：Mark Chu-Carroll (aka MarkCC)<br />
翻译：ssword<br />
原文：<a href="http://scienceblogs.com/goodmath/2006/11/the_c_is_efficient_language_fa.php">http://scienceblogs.com/goodmath/2006/11/the_c_is_efficient_language_fa.php</a></p>
<hr />
<p>昨天偶然看到一篇关于程序设计语言的文章，直击我G点，忍不住前来吐槽。这篇文章来自greythumb.org，叫做《程序员的呼喊：C\C++该有什么》。</p>
<p>无非也就是“C\C++是追求高效的首选”之类的老生常谈。他们错了。它们不是。C\C++接近硬件是为了直接控制堆栈、修改寄存器等等，而非为了高效。在科研编程或者数值计算的应用上，它们差劲得很。</p>
<p>贴一段让我崩溃的文字：</p>
<blockquote><p>首先，那些担忧纯属多余。C\C++永远不会消失。为什么？因为有无数的程序现在是、永远都是吃CPU的。对这些领域的编程而言，根本没有快过C\C++的语言。将来会不会出现这种语言？我非常怀疑。</p>
<p>我说的这些领域就是：科学计算、游戏/物理特效、光线跟踪、实时3D图像、音频处理、编译器、高速路由、进化计算(我的最爱:)，还有高级语言运行时&#8212;毫无疑问。再就是像操作系统、硬件驱动之类“接近底层”，需要很多交互、甚至内嵌汇编的程序。C就是简单版的汇编，这就是为何C总是作为此类程序的首选。</p>
<p>对这些领域而言，在语言及架构层面的过早优化是可以接受、有时甚至是必须的。我敢打赌，在五十年后，这些领域的一部分依然会是C\C++或者相似语言的天下。对于同样一个基于进化计算指令集的实现，C要整整快过java两倍。由此你可以看出C是多么的快。</p></blockquote>
<p>问题在这里：C\C++在数值计算上的性能相当扯淡。它们不是最快的，而且绝非偶然。实际上，受底层实现的一些限制，使得C\C++根本不可能表现得很高效。这便是为什么到今天依然有Fortan应用于在高精度科研项目，而这些应用往往需要榨干机器的每一滴性能&#8212;&#8212;如流体动力学模拟。[<a href="#q1">1</a>]</p>
<p>程序要高效离不开编译器优化，现代架构的编译器可以达到人类优化汇编代码的极限。有时交换两条无关指令的顺序就可以得到一个出人意料的性能提升，而机器所做的优化，很多都是人类难以企及的。[<a href="#q2">2</a>]</p>
<p>因此对于现代的开发而言，程序的高效绝非只凭程序员一人之力。程序员需要做的，是仔细选择合适的算法&#8212;&#8211;这活机器做不了；机器需要做的，是仔细地调整指令、约束流水线、内存延时等等。二者合作才会有高效的程序。二者的工作又是相互影响的：程序员应该用机器能够理解的代码来描述算法，以方便机器进行优化。</p>
<p>这就是C\C++失败的地方。C\C++在语义上过度依赖指针，导致不受约束的指针几乎无处不在。在C\C++中，并没有真正意义上的数组&#8212;&#8212;它们只是指针，下标只是指针运算的简写形式（C\C++里的x[n]与*(x+n)是完全一样的）。</p>
<p>过度依赖指针就意味着，C\C++的编译器会很难辨认两个东西是否独立。由此产生的问题被称作“重名探测”（alias detection），也就是找出可能指向同一个位置的两个变量。若存在不受约束的指针，别名探测几乎就无法实现。举个例子：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> i<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">20000</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
   <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> j<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> j <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">20000</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      x<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> y<span style="color: #009900;">&#91;</span>i<span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span>j<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">*</span> y<span style="color: #009900;">&#91;</span>i<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span>j<span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
   <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>看下这个循环。它可以并行化或向量化[<a href="#q3">3</a>]，但前提必须是x与y没有重合、完全无关，这在C\C++中这根没办法得到保证。Fortran-77就没问题，你可以轻而易举地检查它俩是否不同。若是Fortran-98，你还可以检查出它们是否为指针，若有需要，程序就可以弄清楚它们之间是否有重复。在C\C++下，这就做不到（Fortran也不是最好的&#8212;&#8212;一门来自Lawrence Livermore labs 的实验语言Sisal，在特定代码上要强过Fortran 20%）。</p>
<p>这个例子是拿并行说事，但“重名”带来的问题可不只并行这么简单，说并行只是因为比较好解释。“重名”带来的问题直接影响了C\C++代码的编写。</p>
<p>再举个具体的例子吧，我就不吐槽了。六年前，我在一个项目里需要实现一个相当复杂的算法来计算两个数组的“最长公共串”（longest comon sequence， LCS）。计算LCS的标准算法被称作“动态规划”，是O*(n^3) 的时间，O(n^2) 的空间。搞计算生物学的人们也有个相似的算法，时间与它相同，不过空间平均起来只有O(n)。</p>
<p>我不知道该用哪门语言，就做了个实验。我用C，C++，ocaml，java和python分别实现了一遍LCS算法，来比对代码的复杂度<br />
以及运行效率。2000个元素的数组来做实验数据，所得的测试结果如下：</p>
<ul>
<li>C: 0.8 seconds.</li>
<li>C++: 2.3 seconds.</li>
<li>OCaml: 0.6 seconds interpreted, 0.3 seconds fully compiled.</li>
<li>Java: 1 minute 20 seconds.</li>
<li>Python: over 5 minutes.</li>
</ul>
<p>一年以后，我用新版本的JIT再次测试，java的时间减少到了0.7秒，外加1秒的JVM初始化。（C\C++以及ocaml的初始化时间几乎可以忽略不计）</p>
<p>Objective-Caml字节码解释器的执行效率比经过仔细优化的C程序还要高！为什么？因为Ocaml编译器可以辨别出两个数组的无关性&#8212;&#8212;这一来就不必担心循环中的一个迭代会影响到另一个迭代中的值。C编译器可做的优化就要少得多了，因为它无法辨认这些优化是否会影响到程序的正常运行。</p>
<p>不只没有赋值的函数式语言，不那么高效的高级语言在一些方面的性能也要比C\C++出色。CMU Common Lisp在数值计算上就比C\C++强。几年前有个论文写了这个：在一台Sun Sparc工作站上，如果你带上类型声明，用vector（Lisp的数组）和赋值来实现的科学计算/数值计算Lisp代码，要比经过Solaris C或gcc最大优化的C算法实现更加高效。</p>
<hr />
<p>译者注：<br />
<a name="q1"></a>[1] 从一开始Fortran就被设计成可以进行高度优化的语言…Fortran的设计者关注的是科学计算中代码的运行时性能…在这样想法的指导下，只有当Fortran编译器生成的代码性能是有经验的程序员手写的并经过性能调整的汇编代码性能的两倍时，Fortran语言才会被用户接受。 &#8212;&#8212;J.Backus《the history of Fortran I , II, III》<br />
C最初被设计成有类型的汇编语言，重点针对可用性，而非优化。 &#8212;&#8212;《现代体系结构的优化编译器》 p147</p>
<p><a name="q2"></a>[2] 为此就需要对指令重新排列，或成为调度，以使相互冲突或依赖的指令在时间上分离，这种需要也就是为现代处理器做编译器特别困难的主要原因之一。 &#8212;&#8212;-《程序设计语言：实践之路》 p207</p>
<p><a name="q3"></a>[3] 向量化就是将一个循环中的迭代并行执行。<br />
例如：<br />
For I  in  1 to 100<br />
    A[i]=i*2<br />
End<br />
其中的a[1]和a[2]可以同时执行，前提就是循环中的每个迭代不会相互影响。古代有种机器貌似叫做向量计算机，里面的循环都是并行执行的，就是比较早的并行计算的大型机了。</p>
<hr />
<p>ps:<br />
作者原文上的评论是相当的长，应了那句“凡语言贴，必火”。 毕竟没有银弹，要了解一个东西也应该了解它的弱点，不然就容易为你爱的东西所束缚。</p>
<p>不过作者貌似回避了一点，那就是所谓“科学计算、游戏/物理特效、光线跟踪、实时3D图像、音频处理、编译器、高速路由、进化计算，还有高级语言运行时”等等，在实际中还是C用的多。“C是接近硬件，而不是为了高效”，不过游戏、音频等方面还有个东西叫做硬件加速哇。对于C在优化上的限制，可以参考《现代体系结构的优化编译器》 418页，上面举的例子可能要更好，说明也更详细，也有一整章C编译器在优化上的解决方法。</p>
<p>pps: 很明显，第一段翻译的很不妥..求正解</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fleurer-lee.com/2009/09/08/%e7%bc%aa%e8%ae%ba%ef%bc%9ac%e6%9c%80%e9%ab%98%e6%95%88/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
