2011年7月31日星期日

阮一峰的网络日志

阮一峰的网络日志


理解Linux系统负荷

Posted: 30 Jul 2011 10:19 AM PDT

一、查看系统负荷

如果你的电脑很慢,你或许想查看一下,它的工作量是否太大了。

在Linux系统中,我们一般使用uptime命令查看(w命令和top命令也行)。(另外,它们在苹果公司的Mac电脑上也适用。)

你在终端窗口键入uptime,系统会返回一行信息。

这行信息的后半部分,显示"load average",它的意思是"系统的平均负荷",里面有三个数字,我们可以从中判断系统负荷是大还是小。

为什么会有三个数字呢?你从手册中查到,它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。

如果你继续看手册,它还会告诉你,当CPU完全空闲的时候,平均负荷为0;当CPU工作量饱和的时候,平均负荷为1。

那么很显然,"load average"的值越低,比如等于0.2或0.3,就说明电脑的工作量越小,系统负荷比较轻。

但是,什么时候能看出系统负荷比较重呢?等于1的时候,还是等于0.5或等于1.5的时候?如果1分钟、5分钟、15分钟三个值不一样,怎么办?

二、一个类比

判断系统负荷是否过重,必须理解load average的真正含义。下面,我根据Understanding Linux CPU Load这篇文章,尝试用最通俗的语言,解释这个问题。

首先,假设最简单的情况,你的电脑只有一个CPU,所有的运算都必须由这个CPU来完成。

那么,我们不妨把这个CPU想象成一座大桥,桥上只有一根车道,所有车辆都必须从这根车道上通过。(很显然,这座桥只能单向通行。)

系统负荷为0,意味着大桥上一辆车也没有。

系统负荷为0.5,意味着大桥一半的路段有车。

系统负荷为1.0,意味着大桥的所有路段都有车,也就是说大桥已经"满"了。但是必须注意的是,直到此时大桥还是能顺畅通行的。

系统负荷为1.7,意味着车辆太多了,大桥已经被占满了(100%),后面等着上桥的车辆为桥面车辆的70%。以此类推,系统负荷2.0,意味着等待上桥的车辆与桥面的车辆一样多;系统负荷3.0,意味着等待上桥的车辆是桥面车辆的2倍。总之,当系统负荷大于1,后面的车辆就必须等待了;系统负荷越大,过桥就必须等得越久。

CPU的系统负荷,基本上等同于上面的类比。大桥的通行能力,就是CPU的最大工作量;桥梁上的车辆,就是一个个等待CPU处理的进程(process)。

如果CPU每分钟最多处理100个进程,那么系统负荷0.2,意味着CPU在这1分钟里只处理20个进程;系统负荷1.0,意味着CPU在这1分钟里正好处理100个进程;系统负荷1.7,意味着除了CPU正在处理的100个进程以外,还有70个进程正排队等着CPU处理。

为了电脑顺畅运行,系统负荷最好不要超过1.0,这样就没有进程需要等待了,所有进程都能第一时间得到处理。很显然,1.0是一个关键值,超过这个值,系统就不在最佳状态了,你要动手干预了。

三、系统负荷的经验法则

1.0是系统负荷的理想值吗?

不一定,系统管理员往往会留一点余地,当这个值达到0.7,就应当引起注意了。经验法则是这样的:

当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。

当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。

当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。

四、多处理器

上面,我们假设你的电脑只有1个CPU。如果你的电脑装了2个CPU,会发生什么情况呢?

2个CPU,意味着电脑的处理能力翻了一倍,能够同时处理的进程数量也翻了一倍。

还是用大桥来类比,两个CPU就意味着大桥有两根车道了,通车能力翻倍了。

所以,2个CPU表明系统负荷可以达到2.0,此时每个CPU都达到100%的工作量。推广开来,n个CPU的电脑,可接受的系统负荷最大为n.0。

五、多核处理器

芯片厂商往往在一个CPU内部,包含多个CPU核心,这被称为多核CPU。

在系统负荷方面,多核CPU与多CPU效果类似,所以考虑系统负荷的时候,必须考虑这台电脑有几个CPU、每个CPU有几个核心。然后,把系统负荷除以总的核心数,只要每个核心的负荷不超过1.0,就表明电脑正常运行。

怎么知道电脑有多少个CPU核心呢?

"cat /proc/cpuinfo"命令,可以查看CPU信息。"grep -c 'model name' /proc/cpuinfo"命令,直接返回CPU的总核心数。

六、最佳观察时长

最后一个问题,"load average"一共返回三个平均值----1分钟系统负荷、5分钟系统负荷,15分钟系统负荷,----应该参考哪个值?

如果只有1分钟的系统负荷大于1.0,其他两个时间段都小于1.0,这表明只是暂时现象,问题不大。

如果15分钟内,平均系统负荷大于1.0(调整CPU核心数之后),表明问题持续存在,不是暂时现象。所以,你应该主要观察"15分钟系统负荷",将它作为电脑正常运行的指标。

==========================================

[参考文献]

1. Understanding Linux CPU Load

2. Wikipedia - Load (computing)

(完)

文档信息

2011年7月29日星期五

阮一峰的网络日志

阮一峰的网络日志


DOS的历史

Posted: 28 Jul 2011 07:19 AM PDT

昨日(7月27日),微软公司的DOS操作系统迎来了30岁生日。

DOS是历史上一个划时代的产品,标识着PC(个人电脑)的崛起和普及,对计算机行业影响深远。

只有了解DOS的历史,才能理解今天的计算机工业从何而来。下面就是我对这一段历史的介绍。

==========================================

DOS的历史

作者:阮一峰

1.

1974年4月,Intel推出8位芯片8080。这块芯片的体积和性能,已经能够满足开发微型电脑的需要,标志微机时代即将来临。

2.

1975年初,MITS电脑公司推出了基于8080芯片的Altair 8800微机,这是人类历史上第一台PC(个人电脑)。

3.

1975年1月,Popular Electronics杂志以封面报道的形式,介绍了Altair 8800。这是历史上第一篇关于微机的新闻报道。

4.

22岁的西雅图程序员Paul Allen看到了这一期杂志,深感震动,就把它拿给好友20岁的Bill Gates看。

两人决定为Altair 8800开发一套BASIC解释器,卖给MITS公司。1975年7月,他们用这个产品成立了微软公司。

5.

1975年,另一家公司Digital Research为Altair 8800开发了操作系统CP/M。它很快成为Intel 8080芯片的标准操作系统。(上图为CP/M的运行界面。)

6.

1978年,Intel公司推出历史上第一块16位芯片8086。

7.

1979年,一家名叫Seattle Computer Products(SCP)的公司,决定开发基于8086芯片的个人电脑。

它原计划采用CP/M作为操作系统,但是此时CP/M还未完成针对16位芯片的升级。

8.

1980年4月,足足一年之后,CP/M还是没有推出16位的版本。SCP决定不等了,自己开发16位操作系统。

24岁的程序员Tim Paterson负责这个任务。

9.

1980年8月,Tim Paterson完成了原始的操作系统,取名为QDOS,意思是"简易的操作系统"(Quick and Dirty Operating System)。

在设计上,他充分借鉴了CP/M,用户界面和编程接口几乎完全一样,这使得CP/M上的应用程序,可以直接在QDOS上运行。他做出的最大改变,就是为QDOS引入了微软公司BASIC解释器的FAT文件系统。

10.

1980年10月,IBM公司决定推出基于Intel 8086芯片的PC。

它找到Digital Research公司,要求获得授权使用CP/M系统。但是,协议没有谈成。于是,IBM又去找微软公司,要求微软为它提供操作系统。

当时,微软没有操作系统产品,但是Bill Gates知道SCP公司正在开发QDOS。微软支付2.5万美元给SCP,获得了QDOS的使用许可。(上图为DOS的运行界面。)

11.

1981年7月,微软对IBM PC的整个设计已经相当了解,Bill Gates意识到未来PC市场的巨大规模,决定不再使用许可证模式,而是直接把QDOS买下来。这又花费了微软公司5-7万美元。

与此同时,Tim Paterson也从SCP辞职了,微软雇用了他。

12.

1981年7月27日,协议达成。QDOS成了微软的财产,名称正式改为MS-DOS。微软对DOS的解释是"磁盘操作系统"(Disk Operating System)。

这一天,就是微软公司DOS操作系统的诞生纪念日。

13.

1981年8月12日,IBM公司正式推出个人电脑产品IBM PC,使用的操作系统是MS-DOS 1.14版。

14.

1983年3月8日,IBM又推出增强版IBM PC/XT,第一次在PC上配备了硬盘,使用的操作系统是MS-DOS 2.0版。

15.

1984年,IBM推出了下一代个人电脑IBM PC/AT,操作系统是MS-DOS 3.0版。

16.

1989年,MS-DOS 4.0版发布,开始支持鼠标和图形界面。

此时,微软已经准备终结DOS这个产品了。微软公开表态,用户可以考虑放弃DOS,转而使用由IBM和微软共同开发的OS/2操作系统。

但是不久以后,Windows 3.0获得巨大成功,微软也就不再考虑OS/2了。

17.

1991年,MS-DOS 5.0版发布,内置QBasic编程环境。这是MS-DOS最后一次作为单独产品出现。

18.

1993年,MS-DOS 6.0版发布,具备了磁盘压缩技术。

19.

1995年,MS-DOS 7.0版支持FAT32文件系统,它随同Windows 95一起发布。

20.

2000年9月14日,MS-DOS的最后一个版本8.0版发布,只用于Windows XP系统的启动盘。至此,微软公司的DOS开发正式宣告全部结束。

==========================================

[参考文献]

* 维基百科,Timeline Of x86 DOS operating systems

* Tony Smith, Microsoft's MS-DOS is 30 today

* Sebastian Anthony, MS-DOS is 30 years old today

(完)

文档信息

2011年7月26日星期二

阮一峰的网络日志

阮一峰的网络日志


jQuery设计思想

Posted: 25 Jul 2011 11:04 PM PDT

jQuery是目前使用最广泛的javascript函数库。

统计,全世界排名前100万的网站,有46%使用jQuery,远远超过其他库。微软公司甚至把jQuery作为他们的官方库。

对于网页开发者来说,学会jQuery是必要的。因为它让你了解业界最通用的技术,为将来学习更高级的库打下基础,并且确实可以很轻松地做出许多复杂的效果。

虽然jQuery上手简单,比其他库容易学会,但是要全面掌握,却不轻松。因为它涉及到网页开发的方方面面,提供的各种方法和内部变化有上千种之多。初学者常常感到,入门很方便,提高很困难。

目前,互联网上最好的jQuery入门教材,是Rebecca Murphey写的《jQuery基础》(jQuery Fundamentals)。在Google里搜索"jQuery 培训",此书排在第一位。jQuery官方团队已经同意,把此书作为官方教程的基础。

这本书虽然是入门教材,但也足足有100多页。我对它做了一个详细的笔记,试图理清jQuery的设计思想,找出学习的脉络。我的目标是全面掌握jQuery,遇到问题的时候,心里有底,基本知道使用它的哪一个功能,然后可以迅速从手册中找到具体的写法。

下面就是我的笔记,它应该是目前网上不多的jQuery中文教程之一。你只需要一点javascript语言的基本知识,就能看懂它,在最短的时间里,掌握jQuery的所有主要方面(除了ajax插件开发)。

===========================================

jQuery设计思想

原文网址:http://jqfundamentals.com/book/

阮一峰 翻译整理

【目录】

  一、选择网页元素

  二、改变结果集

  三、链式操作

  四、元素的操作:取值和赋值

  五、元素的操作:移动

  六、元素的操作:复制、删除和创建

  七、工具方法

  八、事件操作

  九、特殊效果

【正文】

一、选择网页元素

jQuery的基本设计和主要用法,就是"选择某个网页元素,然后对其进行某种操作"。这是它区别于其他函数库的根本特点。

使用jQuery的第一步,往往就是将一个选择表达式,放进构造函数jQuery()(简写为$),然后得到被选中的元素。

选择表达式可以是CSS选择器

  $(document) //选择整个文档对象

  $('#myId') //选择ID为myId的网页元素

  $('div.myClass') // 选择class为myClass的div元素

  $('input[name=first]') // 选择name属性等于first的input元素

也可以是jQuery特有的表达式

  $('a:first') //选择网页中第一个a元素

  $('tr:odd') //选择表格的奇数行

  $('#myForm :input') // 选择表单中的input元素

  $('div:visible') //选择可见的div元素

  $('div:gt(2)') // 选择所有的div元素,除了前三个

  $('div:animated') // 选择当前处于动画状态的div元素

二、改变结果集

如果选中多个元素,jQuery提供过滤器,可以缩小结果集:

  $('div').has('p'); // 选择包含p元素的div元素

  $('div').not('.myClass'); //选择class不等于myClass的div元素

  $('div').filter('.myClass'); //选择class等于myClass的div元素

  $('div').first(); //选择第1个div元素

  $('div').eq(5); //选择第6个div元素

有时候,我们需要从结果集出发,移动到附近的相关元素,jQuery也提供了在DOM树上的移动方法

  $('div').next('p'); //选择div元素后面的第一个p元素

  $('div').parent(); //选择div元素的父元素

  $('div').closest('form'); //选择离div最近的那个form父元素

  $('div').children(); //选择div的所有子元素

  $('div').siblings(); //选择div的同级元素

三、链式操作

选中网页元素以后,就可以对它进行某种操作。

jQuery允许将所有操作连接在一起,以链条的形式写出来,比如:

  $('div').find('h3').eq(2).html('Hello');

分解开来,就是下面这样:

  $('div') //找到div元素

   .find('h3') //选择其中的h3元素

   .eq(2) //选择第3个h3元素

   .html('Hello'); //将它的内容改为Hello

这是jQuery最令人称道、最方便的特点。它的原理在于每一步的jQuery操作,返回的都是一个jQuery对象,所以不同操作可以连在一起。

jQuery还提供了.end()方法,使得结果集可以后退一步:

  $('div')

   .find('h3')

   .eq(2)

   .html('Hello')

   .end() //退回到选中所有的h3元素的那一步

   .eq(0) //选中第一个h3元素

   .html('World'); //将它的内容改为World

四、元素的操作:取值和赋值

操作网页元素,最常见的需求是取得它们的值,或者对它们进行赋值。

jQuery使用同一个函数,来完成取值(getter)和赋值(setter)。到底是取值还是赋值,由函数的参数决定。

  $('h1').html(); //html()没有参数,表示取出h1的值

  $('h1').html('Hello'); //html()有参数Hello,表示对h1进行赋值

常见的取值和赋值函数如下:

  .html() 取出或设置html内容

  .text() 取出或设置text内容

  .attr() 取出或设置某个属性的值

  .width() 取出或设置某个元素的宽度

  .height() 取出或设置某个元素的高度

  .val() 取出某个表单元素的值

需要注意的是,如果结果集包含多个元素,那么赋值的时候,将对其中所有的元素赋值;取值的时候,则是只取出第一个元素的值(.text()例外,它取出所有元素的text内容)。

五、元素的操作:移动

如果要移动选中的元素,有两种方法:一种是直接移动该元素,另一种是移动其他元素,使得目标元素达到我们想要的位置。

假定我们选中了一个div元素,需要把它移动到p元素后面。

第一种方法是使用.insertAfter(),把div元素移动p元素后面:

  $('div').insertAfter('p');

第二种方法是使用.after(),把p元素加到div元素前面:

  $('p').after('div');

表面上看,这两种方法的效果是一样的,唯一的不同似乎只是操作视角的不同。但是实际上,它们有一个重大差别,那就是返回的元素不一样。第一种方法返回div元素,第二种方法返回p元素。你可以根据需要,选择到底使用哪一种方法。

使用这种模式的操作方法,一共有四对:

  .insertAfter().after():在现存元素的外部,从后面插入元素

  .insertBefore().before():在现存元素的外部,从前面插入元素

  .appendTo().append():在现存元素的内部,从后面插入元素

  .prependTo().prepend():在现存元素的内部,从前面插入元素

六、元素的操作:复制、删除和创建

复制元素使用.clone()

删除元素使用.remove().detach()。两者的区别在于,前者不保留被删除元素的事件,后者保留,有利于重新插入文档时使用。

清空元素内容(但是不删除该元素)使用.empty()

创建新元素的方法非常简单,只要把新元素直接传入jQuery的构造函数就行了:

  $('<p>Hello</p>');

  $('<li class="new">new list item</li>');

  $('ul').append('<li>list item</li>');

七、工具方法

除了对选中的元素进行操作以外,jQuery还提供一些工具方法(utility),不必选中元素,就可以直接使用。

如果你懂得Javascript语言的继承原理,那么就能理解工具方法的实质。它是定义在jQuery构造函数上的方法,即jQuery.method(),所以可以直接使用。而那些操作元素的方法,是定义在构造函数的prototype对象上的方法,即jQuery.prototype.method(),所以必须生成实例(即选中元素)后使用。如果不理解这种区别,问题也不大,只要把工具方法理解成,是像javascript原生函数那样,可以直接使用的方法就行了。

常用的工具方法有以下几种:

  $.trim() 去除字符串两端的空格。

  $.each() 遍历一个数组或对象。

  $.inArray() 返回一个值在数组中的索引位置。如果该值不在数组中,则返回-1。

  $.grep() 返回数组中符合某种标准的元素。

  $.extend() 将多个对象,合并到第一个对象。

  $.makeArray() 将对象转化为数组。

  $.type() 判断对象的类别(函数对象、日期对象、数组对象、正则对象等等)。

  $.isArray() 判断某个参数是否为数组。

  $.isEmptyObject() 判断某个对象是否为空(不含有任何属性)。

  $.isFunction() 判断某个参数是否为函数。

  $.isPlainObject() 判断某个参数是否为用"{}"或"new Object"建立的对象。

  $.support() 判断浏览器是否支持某个特性。

八、事件操作

jQuery可以对网页元素绑定事件。根据不同的事件,运行相应的函数。

  $('p').click(function(){

    alert('Hello');

  });

目前,jQuery主要支持以下事件:

  .blur() 表单元素失去焦点。

  .change() 表单元素的值发生变化

  .click() 鼠标单击

  .dblclick() 鼠标双击

  .focus() 表单元素获得焦点

  .focusin() 子元素获得焦点

  .focusout() 子元素失去焦点

  .hover() 同时为mouseenter和mouseleave事件指定处理函数

  .keydown() 按下键盘(长时间按键,只返回一个事件)

  .keypress() 按下键盘(长时间按键,将返回多个事件)

  .keyup() 松开键盘

  .load() 元素加载完毕

  .mousedown() 按下鼠标

  .mouseenter() 鼠标进入(进入子元素不触发)

  .mouseleave() 鼠标离开(离开子元素不触发)

  .mousemove() 鼠标在元素内部移动

  .mouseout() 鼠标离开(离开子元素也触发)

  .mouseover() 鼠标进入(进入子元素也触发)

  .mouseup() 松开鼠标

  .ready() DOM加载完成

  .resize() 浏览器窗口的大小发生改变

  .scroll() 滚动条的位置发生变化

  .select() 用户选中文本框中的内容

  .submit() 用户递交表单

  .toggle() 根据鼠标点击的次数,依次运行多个函数

  .unload() 用户离开页面

以上这些事件在jQuery内部,都是.bind()的便捷方式。使用.bind()可以更灵活地控制事件,比如为多个事件绑定同一个函数:

  $('input').bind(

    'click change', //同时绑定click和change事件

    function() {

      alert('Hello');

    }

  );

有时,你只想让事件运行一次,这时可以使用.one()方法。

  $("p").one("click", function() {

    alert("Hello"); //只运行一次,以后的点击不会运行

  });

.unbind()用来解除事件绑定。

  $('p').unbind('click');

所有的事件处理函数,都可以接受一个事件对象(event object)作为参数,比如下面例子中的e:

  $("p").click(function(e) {

    alert(e.type); // "click"

  });

这个事件对象有一些很有用的属性和方法:

  event.pageX 事件发生时,鼠标距离网页左上角的水平距离

  event.pageY 事件发生时,鼠标距离网页左上角的垂直距离

  event.type 事件的类型(比如click)

  event.which 按下了哪一个键

  event.data 在事件对象上绑定数据,然后传入事件处理函数

  event.target 事件针对的网页元素

  event.preventDefault() 阻止事件的默认行为(比如点击链接,会自动打开新页面)

  event.stopPropagation() 停止事件向上层元素冒泡

在事件处理函数中,可以用this关键字,返回事件针对的DOM元素:

  $('a').click(function() {

    if ($(this).attr('href').match('evil')) { //如果确认为有害链接

      e.preventDefault(); //阻止打开

      $(this).addClass('evil'); //加上表示有害的class

    }

  });

有两种方法,可以自动触发一个事件。一种是直接使用事件函数,另一种是使用.trigger().triggerHandler()

  $('a').click();

  $('a').trigger('click');

九、特殊效果

jQuery允许对象呈现某些特殊效果

  $('h1').show(); //展现一个h1标题

常用的特殊效果如下:

  .fadeIn() 淡入

  .fadeOut() 淡出

  .fadeTo() 调整透明度

  .hide() 隐藏元素

  .show() 显示元素

  .slideDown() 向下展开

  .slideUp() 向上卷起

  .slideToggle() 依次展开或卷起某个元素

  .toggle() 依次展示或隐藏某个元素

除了.show().hide(),所有其他特效的默认执行时间都是400ms(毫秒),但是你可以改变这个设置。

  $('h1').fadeIn(300); // 300毫秒内淡入

  $('h1').fadeOut('slow'); // 缓慢地淡出

在特效结束后,可以指定执行某个函数。

  $('p').fadeOut(300, function() { $(this).remove(); });

更复杂的特效,可以用.animate()自定义。

  $('div').animate(

    {

      left : "+=50", //不断右移

      opacity : 0.25 //指定透明度

    },

    300, // 持续时间

    function() { alert('done!'); } //回调函数

  );

.stop().delay()用来停止或延缓特效的执行。

$.fx.off如果设置为true,则关闭所有网页特效。

(完)

文档信息

2011年7月23日星期六

阮一峰的网络日志

阮一峰的网络日志


日本刀的制造过程

Posted: 22 Jul 2011 07:13 PM PDT

日本刀是日本的象征,以优美锋利而著称。

它是钢制的,历史可以追溯到13世纪。也就是说,至少在900年前,日本人已经掌握了炼钢技术,可以制造出一流的钢铁兵器。

但是那个时候,日本还是一个非常落后的国家,冶炼技术怎么可能达到这样的水平呢?

美国的日本刀爱好者Jesus Hernandez,对这个问题很好奇。他决定在自己家里,使用古代日本人的方法,打造一把日本刀,看看能达到怎样的水平。

他把整个过程拍成了照片,放到网上。图文并茂,读来非常增长见识。下面就是我的翻译。

============================================

日本刀的制造过程

作者:Jesus Hernandez

翻译:阮一峰


1.

首先,准备一些木炭。它们是炼钢过程中的炉内燃料,以及碳元素的来源。

2.

原料还有红色的氧化铁矿砂。

3.

熔炉的示意图。为了装卸方便,分成了三截。

4.

熔炉采用水泥和沙子制造。

5.

下部是进风口,用来输送空气。

6.

组装完成后的熔炉实物。

7.

炼钢时,首先用煤气进行加热。

8.

为了提高热效率,熔炉外面裹一层防热毯。一个小时之后,测温计显示炉内温度已经达到了1000华氏度,并在继续上升。

9.

不久,炉口冒出了熊熊火焰。

10.

两个小时之后,炉内开始爆出火星。

11.

大约四个小时之后,熄灭熔炉。冷却后打开,炉内堆满矿渣。

12.

去除矿渣后,紧贴炉壁的一层就是炼钢得到的钢渣。

13.

将钢渣取下。

14.

剖开后就可以看到钢了。

15.

钢截面的特写。

16.

将所有的钢渣收集起来。

17.

送到火炉内,让它们融合成一块。

18.

将融合在一起的钢,压制成钢块。

19.

压制的过程。

20.

压制成型的钢胚。

21.

将钢胚进一步做成钢条。

22.

钢条加热。

23.

取出烧红的钢条。

24.

对钢条进行锻造。

25.

初步做出刀的形状。

26.

查看钢条的表面。

27.

再一次送入炉内。

28.

打造出刀锋。

29.

初步完成的成品。

30.

刀的表面抹上特制的泥浆。

31.

做出花纹。

32.

进行高温烘烤。

33.

刀身上就出现了花纹。

34.

对刀进行磨光。

35.

磨光后的刀身。

36.

用竹子制作刀鞘。

37.

做好的刀鞘。

38.

对刀进行抛光。

39.

刀身的花纹依稀可见。

40.

最后的成品。

(完)

文档信息