Let's Go!

发布新日志

  • 浏览器是如何工作的?(工作原理)(二)

    2011-12-06 17:26:29


    浏览器容错 Browsers error tolerance

    你从来不会在一个html页面上看到“无效语法”这样的错误,浏览器修复了无效内容并继续工作。

    以下面这段html为例:

    <html>

    <mytag>

    </mytag>

    <div>

    <p>

    </div>

    Really lousy HTML

    </p>

    </html>

    这段html违反了很多规则(mytag不是合法的标签,p及div错误的嵌套等等),但是浏览器仍然可以没有任何怨言的继续显示,它在解析的过程中修复了html作者的错误。

    浏览器都具有错误处理的能力,但是,另人惊讶的是,这并不是html最新规范的内容,就像书签及前进后退按钮一样,它只是浏览器长期发展的结果。一些比较知名的非法html结构,在许多站点中出现过,浏览器都试着以一种和其他浏览器一致的方式去修复。

    Html5规范定义了这方面的需求,webkit在html解析类开始部分的注释中做了很好的总结。

    解析器将符号化的输入解析为文档并创建文档,但不幸的是,我们必须处理很多没有很好格式化的html文档,至少要小心下面几种错误情况。

    1. 在未闭合的标签中添加明确禁止的元素。这种情况下,应该先将前一标签闭合

    2. 不能直接添加元素。有些人在写文档的时候会忘了中间一些标签(或者中间标签是可选的),比如HTML HEAD BODY TR TD LI等

    3. 想在一个行内元素中添加块状元素。关闭所有的行内元素,直到下一个更高的块状元素

    4. 如果这些都不行,就闭合当前标签直到可以添加该元素。

    下面来看一些webkit容错的例子:

    </br>替代<br>

    一些网站使用</br>替代<br>,为了兼容IE和Firefox,webkit将其看作<br>。

    代码:

    if (t->isCloseTag(brTag) && m_document->inCompatMode()) {

    reportError(MalformedBRError);

    t->beginTag = true;

    }

    Note-这里的错误处理在内部进行,用户看不到。

    迷路的表格

    这指一个表格嵌套在另一个表格中,但不在它的某个单元格内。

    比如下面这个例子:

    <table>

    <table>

    <tr><td>inner table</td></tr>

    </table>

    <tr><td>outer table</td></tr>

    </table>

    webkit将会将嵌套的表格变为两个兄弟表格:

    <table>

    <tr><td>outer table</td></tr>

    </table>

    <table>

    <tr><td>inner table</td></tr>

    </table>

    代码:

    if (m_inStrayTableContent && localName == tableTag)

    popBlock(tableTag);

    webkit使用堆栈存放当前的元素内容,它将从外部表格的堆栈中弹出内部的表格,则它们变为了兄弟表格。

    嵌套的表单元素

    用户将一个表单嵌套到另一个表单中,则第二个表单将被忽略。

    代码:

    if (!m_currentFormElement) {

    m_currentFormElement = new HTMLFormElement(formTag, m_document);

    }

    太深的标签继承

    www.liceo.edu.mx是一个由嵌套层次的站点的例子,最多只允许20个相同类型的标签嵌套,多出来的将被忽略。

    代码:

    bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)

    {

    unsigned i = 0;

    for (HTMLStackElem* curr = m_blockStack;

    i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;

    curr = curr->next, i++) { }

    return i != cMaxRedundantTagDepth;

    }

    放错了地方的html、body闭合标签

    又一次不言自明。

    支持不完整的html。我们从来不闭合body,因为一些愚蠢的网页总是在还未真正结束时就闭合它。我们依赖调用end方法去执行关闭的处理。

    代码:

    if (t->tagName == htmlTag || t->tagName == bodyTag )

    return;

    所以,web开发者要小心了,除非你想成为webkit容错代码的范例,否则还是写格式良好的html吧。

    CSS解析 CSS parsing

    还记得简介中提到的解析的概念吗,不同于html,css属于上下文无关文法,可以用前面所描述的解析器来解析。Css规范定义了css的词法及语法文法。

    看一些例子:

    每个符号都由正则表达式定义了词法文法(词汇表):

    comment ///*[^*]*/*+([^/*][^*]*/*+)*//

    num [0-9]+|[0-9]*”.”[0-9]+

    nonascii [/200-/377]

    nmstart [_a-z]|{nonascii}|{escape}

    nmchar [_a-z0-9-]|{nonascii}|{escape}

    name {nmchar}+

    ident {nmstart}{nmchar}*

    “ident”是识别器的缩写,相当于一个class名,“name”是一个元素id(用“#”引用)。

    语法用BNF进行描述:

    ruleset

    : selector [ ',' S* selector ]*

    ‘{’ S* declaration [ ';' S* declaration ]* ‘}’ S*

    ;

    selector

    : simple_selector [ combinator selector | S+ [ combinator selector ] ]

    ;

    simple_selector

    : element_name [ HASH | class | attrib | pseudo ]*

    | [ HASH | class | attrib | pseudo ]+

    ;

    class

    : ‘.’ IDENT

    ;

    element_name

    : IDENT | ‘*’

    ;

    attrib

    : ‘[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*

    [ IDENT | STRING ] S* ] ‘]’

    ;

    pseudo

    : ‘:’ [ IDENT | FUNCTION S* [IDENT S*] ‘)’ ]

    ;

    说明:一个规则集合有这样的结构

    div.error , a.error {

    color:red;

    font-weight:bold;

    }

    div.error和a.error时选择器,大括号中的内容包含了这条规则集合中的规则,这个结构在下面的定义中正式的定义了:

    ruleset

    : selector [ ',' S* selector ]*

    ‘{’ S* declaration [ ';' S* declaration ]* ‘}’ S*

    ;

    这说明,一个规则集合具有一个或是可选个数的多个选择器,这些选择器以逗号和空格(S表示空格)进行分隔。每个规则集合包含大括号及大括号中的一条或多条以分号隔开的声明。声明和选择器在后面进行定义。

    Webkit CSS 解析器 Webkit CSS parser

    Webkit使用Flex和Bison解析生成器从CSS语法文件中自动生成解析器。回忆一下解析器的介绍,Bison创建一个自底向上的解析器,Firefox使用自顶向下解析器。它们都是将每个css文件解析为样式表对象,每个对象包含css规则,css规则对象包含选择器和声明对象,以及其他一些符合css语法的对象。

    图12:解析css

    脚本解析 Parsing scripts

    本章将介绍Javascript。

    处理脚本及样式表的顺序 The order of processing scripts and style. sheets

    脚本

    web的模式是同步的,开发者希望解析到一个script标签时立即解析执行脚本,并阻塞文档的解析直到脚本执行完。如果脚本是外引的,则网络必须先请求到这个资源——这个过程也是同步的,会阻塞文档的解析直到资源被请求到。这个模式保持了很多年,并且在html4及html5中都特别指定了。开发者可以将脚本标识为defer,以使其不阻塞文档解析,并在文档解析结束后执行。Html5增加了标记脚本为异步的选项,以使脚本的解析执行使用另一个线程。

    预解析 Speculative parsing

    Webkit和Firefox都做了这个优化,当执行脚本时,另一个线程解析剩下的文档,并加载后面需要通过网络加载的资源。这种方式可以使资源并行加载从而使整体速度更快。需要注意的是,预解析并不改变Dom树,它将这个工作留给主解析过程,自己只解析外部资源的引用,比如外部脚本、样式表及图片。

    样式表 Style. sheets

    样式表采用另一种不同的模式。理论上,既然样式表不改变Dom树,也就没有必要停下文档的解析等待它们,然而,存在一个问题,脚本可能在文档的解析过程中请求样式信息,如果样式还没有加载和解析,脚本将得到错误的值,显然这将会导致很多问题,这看起来是个边缘情况,但确实很常见。Firefox在存在样式表还在加载和解析时阻塞所有的脚本,而chrome只在当脚本试图访问某些可能被未加载的样式表所影响的特定的样式属性时才阻塞这些脚本。

    渲染树的构造 Render tree construction

    当Dom树构建完成时,浏览器开始构建另一棵树——渲染树。渲染树由元素显示序列中的可见元素组成,它是文档的可视化表示,构建这棵树是为了以正确的顺序绘制文档内容。

    Firefox将渲染树中的元素称为frames,webkit则用renderer或渲染对象来描述这些元素。

    一个渲染对象直到怎么布局及绘制自己及它的children。

    RenderObject是Webkit的渲染对象基类,它的定义如下:

    class RenderObject{

    virtual void layout();

    virtual void paint(PaintInfo);

    virtual void rect repaintRect();

    Node* node; //the DOM node

    RenderStyle* style; // the computed style

    RenderLayer* containgLayer; //the containing z-index layer

    }

    每个渲染对象用一个和该节点的css盒模型相对应的矩形区域来表示,正如css2所描述的那样,它包含诸如宽、高和位置之类的几何信息。盒模型的类型受该节点相关的display样式属性的影响(参考样式计算章节)。下面的webkit代码说明了如何根据display属性决定某个节点创建何种类型的渲染对象。

    RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)

    {

    Document* doc = node->document();

    RenderArena* arena = doc->renderArena();

    RenderObject* o = 0;

    switch (style->display()) {

    case NONE:

    break;

    case INLINE:

    o = new (arena) RenderInline(node);

    break;

    case BLOCK:

    o = new (arena) RenderBlock(node);

    break;

    case INLINE_BLOCK:

    o = new (arena) RenderBlock(node);

    break;

    case LIST_ITEM:

    o = new (arena) RenderListItem(node);

    break;

    }

    return o;

    }

    元素的类型也需要考虑,例如,表单控件和表格带有特殊的框架。

    在webkit中,如果一个元素想创建一个特殊的渲染对象,它需要复写“createRenderer”方法,使渲染对象指向不包含几何信息的样式对象。

    渲染树和Dom树的关系 The render tree relation to the DOM tree

    渲染对象和Dom元素相对应,但这种对应关系不是一对一的,不可见的Dom元素不会被插入渲染树,例如head元素。另外,display属性为none的元素也不会在渲染树中出现(visibility属性为hidden的元素将出现在渲染树中)。

    还有一些Dom元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。例如,select元素有三个渲染对象——一个显示区域、一个下拉列表及一个按钮。同样,当文本因为宽度不够而折行时,新行将作为额外的渲染元素被添加。另一个多个渲染对象的例子是不规范的html,根据css规范,一个行内元素只能仅包含行内元素或仅包含块状元素,在存在混合内容时,将会创建匿名的块状渲染对象包裹住行内元素。

    一些渲染对象和所对应的Dom节点不在树上相同的位置,例如,浮动和绝对定位的元素在文本流之外,在两棵树上的位置不同,渲染树上标识出真实的结构,并用一个占位结构标识出它们原来的位置。

    图12:渲染树及对应的Dom树

    创建树的流程 The flow of constructing the tree

    Firefox中,表述为一个监听Dom更新的监听器,将frame的创建委派给Frame. Constructor,这个构建器计算样式(参看样式计算)并创建一个frame。

    Webkit中,计算样式并生成渲染对象的过程称为attachment,每个Dom节点有一个attach方法,attachment的过程是同步的,调用新节点的attach方法将节点插入到Dom树中。

    处理html和body标签将构建渲染树的根,这个根渲染对象对应被css规范称为containing block的元素——包含了其他所有块元素的顶级块元素。它的大小就是viewport——浏览器窗口的显示区域,Firefox称它为viewPortFrame,webkit称为RenderView,这个就是文档所指向的渲染对象,树中其他的部分都将作为一个插入的Dom节点被创建。

    样式计算 Style. Computation

    创建渲染树需要计算出每个渲染对象的可视属性,这可以通过计算每个元素的样式属性得到。

    样式包括各种来源的样式表,行内样式元素及html中的可视化属性(例如bgcolor),可视化属性转化为css样式属性。

    样式表来源于浏览器默认样式表,及页面作者和用户提供的样式表——有些样式是浏览器用户提供的(浏览器允许用户定义喜欢的样式,例如,在Firefox中,可以通过在Firefox Profile目录下放置样式表实现)。

    计算样式的一些困难:

    1. 样式数据是非常大的结构,保存大量的样式属性会带来内存问题

    2. 如果不进行优化,找到每个元素匹配的规则会导致性能问题,为每个元素查找匹配的规则都需要遍历整个规则表,这个过程有很大的工作量。选择符可能有复杂的结构,匹配过程如果沿着一条开始看似正确,后来却被证明是无用的路径,则必须去尝试另一条路径。

    例如,下面这个复杂选择符

    div div div div{…}

    这意味着规则应用到三个div的后代div元素,选择树上一条特定的路径去检查,这可能需要遍历节点树,最后却发现它只是两个div的后代,并不使用该规则,然后则需要沿着另一条路径去尝试

    3. 应用规则涉及非常复杂的级联,它们定义了规则的层次

    我们来看一下浏览器如何处理这些问题:

    共享样式数据

    webkit节点引用样式对象(渲染样式),某些情况下,这些对象可以被节点间共享,这些节点需要是兄弟或是表兄弟节点,并且:

    1. 这些元素必须处于相同的鼠标状态(比如不能一个处于hover,而另一个不是)
    2. 不能有元素具有id
    3. 标签名必须匹配
    4. class属性必须匹配
    5. 对应的属性必须相同
    6. 链接状态必须匹配
    7. 焦点状态必须匹配
    8. 不能有元素被属性选择器影响
    9. 元素不能有行内样式属性
    10. 不能有生效的兄弟选择器,webcore在任何兄弟选择器相遇时只是简单的抛出一个全局转换,并且在它们显示时使整个文档的样式共享失效,这些包括+选择器和类似:first-child和:last-child这样的选择器。

    Firefox规则树 Firefox rule tree

    Firefox用两个树用来简化样式计算-规则树和样式上下文树,webkit也有样式对象,但它们并没有存储在类似样式上下文树这样的树中,只是由Dom节点指向其相关的样式。

    图14:Firefox样式上下文树

    样式上下文包含最终值,这些值是通过以正确顺序应用所有匹配的规则,并将它们由逻辑值转换为具体的值,例如,如果逻辑值为屏幕的百分比,则通过计算将其转化为绝对单位。样式树的使用确实很巧妙,它使得在节点中共享的这些值不需要被多次计算,同时也节省了存储空间。

    所有匹配的规则都存储在规则树中,一条路径中的底层节点拥有最高的优先级,这棵树包含了所找到的 所有规则匹配的路径(译注:可以取巧理解为每条路径对应一个节点,路径上包含了该节点所匹配的所有规则)。规则树并不是一开始就为所有节点进行计算,而是 在某个节点需要计算样式时,才进行相应的计算并将计算后的路径添加到树中。

    我们将树上的路径看成辞典中的单词,假如已经计算出了如下的规则树:

    假如需要为内容树中的另一个节点匹配规则,现在知道匹配的规则(以正确的顺序)为B-E-I,因为我们已经计算出了路径A-B-E-I-L,所以树上已经存在了这条路径,剩下的工作就很少了。

    现在来看一下树如何保存。

    结构化

    样式上下文按结构划分,这些结构包括类似border或color这样的特定分类的样式信息。一个结构中的所有特性不是继承的就是非继承的,对继承的特性,除非元素自身有定义,否则就从它的parent继承。非继承的特性(称为reset特性)如果没有定义,则使用默认的值。

    样式上下文树缓存完整的结构(包括计算后的值),这样,如果底层节点没有为一个结构提供定义,则使用上层节点缓存的结构。

    使用规则树计算样式上下文

    当为一个特定的元素计算样式时,首先计算出规则树中的一条路径,或是使用已经存在的一条,然后使 用路径中的规则去填充新的样式上下文,从样式的底层节点开始,它具有最高优先级(通常是最特定的选择器),遍历规则树,直到填满结构。如果在那个规则节点 没有定义所需的结构规则,则沿着路径向上,直到找到该结构规则。

    如果最终没有找到该结构的任何规则定义,那么如果这个结构是继承型的,则找到其在内容树中的parent的结构,这种情况下,我们也成功的共享了结构;如果这个结构是reset型的,则使用默认的值。

    如果特定的节点添加了值,那么需要做一些额外的计算以将其转换为实际值,然后在树上的节点缓存该值,使它的children可以使用。

    当一个元素和它的一个兄弟元素指向同一个树节点时,完整的样式上下文可以被它们共享。

    来看一个例子:假设有下面这段html

    <html>

    <body>

    <div class=”err” id=”div1″>

    <p>this is a

    <span class=”big”> big error </span>

    this is also a

    <span class=”big”> very big error</span>

    error

    </p>

    </div>

    <div class=”err” id=”div2″>another error</div>

    </body>

    </html>

    以及下面这些规则

    1. div {margin:5px;color:black}

    2. .err {color:red}

    3. .big {margin-top:3px}

    4. div span {margin-bottom:4px}

    5. #div1 {color:blue}

    6. #div2 {color:green}

    简化下问题,我们只填充两个结构——color和margin,color结构只包含一个成员-颜色,margin结构包含四边。

    生成的规则树如下(节点名:指向的规则)

    上下文树如下(节点名:指向的规则节点)

    假设我们解析html,遇到第二个div标签,我们需要为这个节点创建样式上下文,并填充它的样式结构。

    我们进行规则匹配,找到这个div匹配的规则为1、2、6,我们发现规则树上已经存在了一条我们可以使用的路径1、2,我们只需为规则6新增一个节点添加到下面(就是规则树中的F)。

    然后创建一个样式上下文并将其放到上下文树中,新的样式上下文将指向规则树中的节点F。

    现在我们需要填充这个样式上下文,先从填充margin结构开始,既然最后一个规则节点没有添加margin结构,沿着路径向上,直到找到缓存的前面插入节点计算出的结构,我们发现B是最近的指定margin值的节点。因为已经有了color结构的定义,所以不能使用缓存的结构,既然color只有一个属性,也就不需要沿着路径向上填充其他属性。计算出最终值(将字符串转换为RGB等),并缓存计算后的结构。

    第二个span元素更简单,进行规则匹配后发现它指向规则G,和前一个span一样,既然有兄弟节点指向同一个节点,就可以共享完整的样式上下文,只需指向前一个span的上下文。

    因为结构中包含继承自parent的规则,上下文树做了缓存(color特性是继承来的,但Firefox将其视为reset并在规则树中缓存)。

    例如,如果我们为一个paragraph的文字添加规则:

    p {font-family:Verdana;font size:10px;font-weight:bold}

    那么这个p在内容树中的子节点div,会共享和它parent一样的font结构,这种情况发生在没有为这个div指定font规则时。

    Webkit中,并没有规则树,匹配的声明会被遍历四次,先是应用非important的高优先级属性(之所以先应用这些属性,是因为其他的依赖于它们-比如display),其次是高优先级important的,接着是一般优先级非important的,最后是一般优先级important的规则。这样,出现多次的属性将被按照正确的级联顺序进行处理,最后一个生效。

    总结一下,共享样式对象(结构中完整或部分内容)解决了问题1和3,Firefox的规则树帮助以正确的顺序应用规则。

    对规则进行处理以简化匹配过程

    ----->见下文

  • 浏览器是如何工作的?(工作原理)(一)

    2011-12-06 17:24:50

    简介

    浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工 作原理,我们将看到,从你在地址栏输入google.com到你看到google主页过程中都发生了什么。

    将讨论的浏览器

    今天,有五种主流浏览器——IE、Firefox、Safari、Chrome及Opera。

    本文将基于一些开源浏览器的例子——Firefox、 Chrome及Safari,Safari是部分开源的。

    根据W3C(World Wide Web Consortium 万维网联盟)的浏览器统计数据,当前(2011年9月),Firefox、Safari及Chrome的市场占有率综合已快接近50%。(原文为2009年10月,数据没有太大变化)因此,可以说开源浏览器将近占据了浏览器市场的半壁江山。

    浏览器的主要功能

    浏览器的主要功能是将用户选择得web资源呈现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常是HTML,也包括PDF、image及其他格式。用户用URI(Uniform. Resource Identifier 统一资源标识符)来指定所请求资源的位置,在网络一章有更多讨论。

    HTML和CSS规范中规定了浏览器解释html文档的方式,由 W3C组织对这些规范进行维护,W3C是负责制定web标准的组织。

    HTML规范的最新版本是HTML4(http://www.w3.org/TR/html401/),HTML5还在制定中(译注:两年前),最新的CSS规范版本是2(http://www.w3.org/TR/CSS2),CSS3也还正在制定中(译注:同样两年前)。

    这些年来,浏览器厂商纷纷开发自己的扩展,对规范的遵循并不完善,这为web开发者带来了严重的兼容性问题。

    但是,浏览器的用户界面则差不多,常见的用户界面元素包括:

    • 用来输入URI的地址栏
    • 前进、后退按钮
    • 书签选项
    • 用于刷新及暂停当前加载文档的刷新、暂停按钮
    • 用于到达主页的主页按钮

    奇怪的是,并没有哪个正式公布的规范对用户界面做出规定,这些是多年来各浏览器厂商之间相互模仿和不断改进得结果。

    HTML5并没有规定浏览器必须具有的UI元素,但列出了一些常用元素,包括地址栏、状态栏及工具栏。还有一些浏览器有自己专有得功能,比如Firefox得下载管理。更多相关内容将在后面讨论用户界面时介绍。

    浏览器的主要构成High Level Structure

    浏览器的主要组件包括:

    1. 用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
    2. 浏览器引擎- 用来查询及操作渲染引擎的接口
    3. 渲染引擎- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来
    4. 网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
    5. UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
    6. JS解释器- 用来解释执行JS代码
    7. 数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术

    图1:浏览器主要组件

    需要注意的是,不同于大部分浏览器,Chrome为每个Tab分配了各自的渲染引擎实例,每个Tab就是一个独立的进程。

    对于构成浏览器的这些组件,后面会逐一详细讨论。

    组件间的通信 Communication between the components

    Firefox和Chrome都开发了一个特殊的通信结构,后面将有专门的一章进行讨论。

    渲染引擎 The rendering engine

    渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。

    默认情况下,渲染引擎可以显示html、xml文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用PDF阅读器插件,可以显示PDF格式,将由专门一章讲解插件及扩展,这里只讨论渲染引擎最主要的用途——显示应用了CSS之后的html及图片。

    渲染引擎 Rendering engines

    本文所讨论得浏览器——Firefox、Chrome和Safari是基于两种渲染引擎构建的,Firefox使用Geoko——Mozilla自主研发的渲染引擎,Safari和Chrome都使用webkit。

    Webkit是一款开源渲染引擎,它本来是为linux平台研发的,后来由Apple移植到Mac及Windows上,相关内容请参考http://webkit.org。

    主流程 The main flow

    渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。

    下面是渲染引擎在取得内容之后的基本流程:

    解析html以构建dom树->构建render树->布局render树->绘制render树

    图2:渲染引擎基本流程

    渲染引擎开始解析html,并将标签转化为内容树中的dom节点。接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树。

    Render树由一些包含有颜色和大小等属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。

    Render树构建好了之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标。再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。

    值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

    图3:webkit主流程

    图4:Mozilla的Geoko 渲染引擎主流程

    从图3和4中可以看出,尽管webkit和Gecko使用的术语稍有不同,他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为frame树,每个元素都是一个frame,webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局,而Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment,Gecko在html和dom树之间附加了一层,这层称为内容接收器,相当制造dom元素的工厂。下面将讨论流程中的各个阶段。

    解析 Parsing-general

    既然解析是渲染引擎中一个非常重要的过程,我们将稍微深入的研究它。首先简要介绍一下解析。

    解析一个文档即将其转换为具有一定意义的结构——编码可以理解和使用的东西。解析的结果通常是表达文档结构的节点树,称为解析树或语法树。

    例如,解析“2+3-1”这个表达式,可能返回这样一棵树。

    图5:数学表达式树节点

    文法 Grammars

    解析基于文档依据的语法规则——文档的语言或格式。每种可被解析的格式必须具有由词汇及语法规则组成的特定的文法,称为上下文无关文法。人类语言不具有这一特性,因此不能被一般的解析技术所解析。

    解析器-词法分析器 Parser-Lexer combination

    解析可以分为两个子过程——语法分析及词法分析

    词法分析就是将输入分解为符号,符号是语言的词汇表——基本有效单元的集合。对于人类语言来说,它相当于我们字典中出现的所有单词。

    语法分析指对语言应用语法规则。

    解析器一般将工作分配给两个组件——词法分析器(有时也叫分词器)负责将输入分解为合法的符号,解析器则根据语言的语法规则分析文档结构,从而构建解析树,词法分析器知道怎么跳过空白和换行之类的无关字符。

    图6:从源文档到解析树

    解析过程是迭代的,解析器从词法分析器处取道一个新的符号,并试着用这个符号匹配一条语法规则, 如果匹配了一条规则,这个符号对应的节点将被添加到解析树上,然后解析器请求另一个符号。如果没有匹配到规则,解析器将在内部保存该符号,并从词法分析器 取下一个符号,直到所有内部保存的符号能够匹配一项语法规则。如果最终没有找到匹配的规则,解析器将抛出一个异常,这意味着文档无效或是包含语法错误。

    转换 Translation

    很多时候,解析树并不是最终结果。解析一般在转换中使用——将输入文档转换为另一种格式。编译就是个例子,编译器在将一段源码编译为机器码的时候,先将源码解析为解析树,然后将该树转换为一个机器码文档。

    图7:编译流程

    解析实例 Parsing example

    图5中,我们从一个数学表达式构建了一个解析树,这里定义一个简单的数学语言来看下解析过程。

    词汇表:我们的语言包括整数、加号及减号。

    语法:

    1. 该语言的语法基本单元包括表达式、term及操作符

    2. 该语言可以包括多个表达式

    3. 一个表达式定义为两个term通过一个操作符连接

    4. 操作符可以是加号或减号

    5. term可以是一个整数或一个表达式

    现在来分析一下“2+3-1”这个输入

    第一个匹配规则的子字符串是“2”,根据规则5,它是一个term,第二个匹配的是“2+3”,它符合第2条规则——一个操作符连接两个term,下一次匹配发生在输入的结束处。“2+3-1”是一个表达式,因为我们已经知道“2+3”是一个term,所以我们有了一个term紧跟着一个操作符及另一个term。“2++”将不会匹配任何规则,因此是一个无效输入。

    词汇表及语法的定义

    词汇表通常利用正则表达式来定义。

    例如上面的语言可以定义为:

    INTEGER:0|[1-9][0-9]*

    PLUS:+

    MINUS:-

    正如看到的,这里用正则表达式定义整数。

    语法通常用BNF格式定义,我们的语言可以定义为:

    expression := term operation term

    operation := PLUS | MINUS

    term := INTEGER | expression

    如果一个语言的文法是上下文无关的,则它可以用正则解析器来解析。对上下文无关文法的一个直观的定义是,该文法可以用BNF来完整的表达。可查看http://en.wikipedia.org/wiki/Context-free_grammar。

    解析器类型 Types of parsers

    有两种基本的解析器——自顶向下解析及自底向上解析。比较直观的解释是,自顶向下解析,查看语法的最高层结构并试着匹配其中一个;自底向上解析则从输入开始,逐步将其转换为语法规则,从底层规则开始直到匹配高层规则。

    来看一下这两种解析器如何解析上面的例子:

    自顶向下解析器从最高层规则开始——它先识别出“2+3“,将其视为一个表达式,然后识别出”2+3-1“为一个表达式(识别表达式的过程中匹配了其他规则,但出发点是最高层规则)。

    自底向上解析会扫描输入直到匹配了一条规则,然后用该规则取代匹配的输入,直到解析完所有输入。部分匹配的表达式被放置在解析堆栈中。

    Stack

    Input

    2 + 3 – 1
    term + 3 – 1
    term operation 3 – 1
    expression - 1
    expression operation 1
    expression

    自底向上解析器称为shift reduce 解析器,因为输入向右移动(想象一个指针首先指向输入开始处,并向右移动),并逐渐简化为语法规则。

    自动化解析 Generating parse

    解析器生成器这个工具可以自动生成解析器,只需要指定语言的文法——词汇表及语法规则,它就可以生成一个解析器。创建一个解析器需要对解析有深入的理解,而且手动的创建一个由较好性能的解析器并不容易,所以解析生成器很有用。Webkit使用两个知名的解析生成器——用于创建语法分析器的Flex及创建解析器的Bison(你可能接触过Lex和Yacc)。Flex的输入是一个包含了符号定义的正则表达式,Bison的输入是用BNF格式表示的语法规则。rs automatically

    HTML解析器 HTML Parser

    HTML解析器的工作是将html标识解析为解析树。

    HTML文法定义 The HTML grammar definition

    W3C组织制定规范定义了HTML的词汇表和语法。

    非上下文无关文法 Not a context free grammar

    正如在解析简介中提到的,上下文无关文法的语法可以用类似BNF的格式来定义。

    不幸的是,所有的传统解析方式都不适用于html(当然我提出它们并不只是因为好玩,它们将用来解析css和js),html不能简单的用解析所需的上下文无关文法来定义。

    Html 有一个正式的格式定义——DTD(Document Type Definition 文档类型定义)——但它并不是上下文无关文法,html更接近于xml,现在有很多可用的xml解析器,html有个xml的变体——xhtml,它们间的不同在于,html更宽容,它允许忽略一些特定标签,有时可以省略开始或结束标签。总的来说,它是一种soft语法,不像xml呆板、固执。

    显然,这个看起来很小的差异却带来了很大的不同。一方面,这是html流行的原因——它的宽容使web开发人员的工作更加轻松,但另一方面,这也使很难去写一个格式化的文法。所以,html的解析并不简单,它既不能用传统的解析器解析,也不能用xml解析器解析。

    HTML DTD

    Html适用DTD格式进行定义,这一格式是用于定义SGML家族的语言,包括了对所有允许元素及它们的属性和层次关系的定义。正如前面提到的,html DTD并没有生成一种上下文无关文法。

    DTD有一些变种,标准模式只遵守规范,而其他模式则包含了对浏览器过去所使用标签的支持,这么做是为了兼容以前内容。最新的标准DTD在http://www.w3.org/TR/html4/strict.dtd

    DOM

    输出的树,也就是解析树,是由DOM元素及属性节点组成的。DOM是文档对象模型的缩写,它是html文档的对象表示,作为html元素的外部接口供js等调用。

    树的根是“document”对象。

    DOM和标签基本是一一对应的关系,例如,如下的标签:

    <html>

    <body>

    <p>

    Hello DOM

    </p>

    <div><img src=”example.png” /></div>

    </body>

    </html>

    将会被转换为下面的DOM树:

    图8:示例标签对应的DOM树

    和html一样,DOM的规范也是由W3C组织制定的。访问http://www.w3.org/DOM/DOMTR,这是使用文档的一般规范。一个模型描述一种特定的html元素,可以在http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm 查看html定义。

    这里所谓的树包含了DOM节点是说树是由实现了DOM接口的元素构建而成的,浏览器使用已被浏览器内部使用的其他属性的具体实现。

    解析算法 The parsing algorithm

    正如前面章节中讨论的,hmtl不能被一般的自顶向下或自底向上的解析器所解析。

    原因是:

    1. 这门语言本身的宽容特性

    2. 浏览器对一些常见的非法html有容错机制

    3. 解析过程是往复的,通常源码不会在解析过程中发生改变,但在html中,脚本标签包含的“document.write ”可能添加标签,这说明在解析过程中实际上修改了输入

    不能使用正则解析技术,浏览器为html定制了专属的解析器。

    Html5规范中描述了这个解析算法,算法包括两个阶段——符号化及构建树。

    符号化是词法分析的过程,将输入解析为符号,html的符号包括开始标签、结束标签、属性名及属性值。

    符号识别器识别出符号后,将其传递给树构建器,并读取下一个字符,以识别下一个符号,这样直到处理完所有输入。

    图9:HTML解析流程

    符号识别算法 The tokenization algorithm

    算法输出html符号,该算法用状态机表示。每次读取输入流中的一个或多个字符,并根据这些字符转移到下一个状态,当前的符号状态及构建树状态共同影响结果,这意味着,读取同样的字符,可能因为当前状态的不同,得到不同的结果以进入下一个正确的状态。

    这个算法很复杂,这里用一个简单的例子来解释这个原理。

    基本示例——符号化下面的html:

    <html>

    <body>

    Hello world

    </body>

    </html>

    初始状态为“Data State”,当遇到“<”字符,状态变为“Tag open state”,读取一个a-z的字符将产生一个开始标签符号,状态相应变为“Tag name state”,一直保持这个状态直到读取到“>”,每个字符都附加到这个符号名上,例子中创建的是一个html符号。

    当读取到“>”,当前的符号就完成了,此时,状态回到“Data state”,“<body>”重复这一处理过程。到这里,html和body标签都识别出来了。现在,回到“Data state”,读取“Hello world”中的字符“H”将创建并识别出一个字符符号,这里会为“Hello world”中的每个字符生成一个字符符号。

    这样直到遇到“</body>”中的“<”。现在,又回到了“Tag open state”,读取下一个字符“/”将创建一个闭合标签符号,并且状态转移到“Tag name state”,还是保持这一状态,直到遇到“>”。然后,产生一个新的标签符号并回到“Data state”。后面的“</html>”将和“</body>”一样处理。

    图10:符号化示例输入

    树的构建算法 Tree construction algorithm

    在树的构建阶段,将修改以Document为根的DOM树,将元素附加到树上。每个由符号识别器识别生成的节点将会被树构造器进行处理,规范中定义了每个符号相对应的Dom元素,对应的Dom元素将会被创建。这些元素除了会被添加到Dom树上,还将被添加到开放元素堆栈中。这个堆栈用来纠正嵌套的未匹配和未闭合标签,这个算法也是用状态机来描述,所有的状态采用插入模式。

    来看一下示例中树的创建过程:

    <html>

    <body>

    Hello world

    </body>

    </html>

    构建树这一阶段的输入是符号识别阶段生成的符号序列。

    首先是“initial mode”,接收到html符号后将转换为“before html”模式,在这个模式中对这个符号进行再处理。此时,创建了一个HTMLHtmlElement元素,并将其附加到根Document对象上。

    状态此时变为“before head”,接收到body符号时,即使这里没有head符号,也将自动创建一个HTMLHeadElement元素并附加到树上。

    现在,转到“in head”模式,然后是“after head”。到这里,body符号会被再次处理,将创建一个HTMLBodyElement并插入到树中,同时,转移到“in body”模式。

    然后,接收到字符串“Hello world”的字符符号,第一个字符将导致创建并插入一个text节点,其他字符将附加到该节点。

    接收到body结束符号时,转移到“after body”模式,接着接收到html结束符号,这个符号意味着转移到了“after after body”模式,当接收到文件结束符时,整个解析过程结束。

    图11:示例html树的构建过程

    解析结束时的处理 Action when the parsing is finished

    在这个阶段,浏览器将文档标记为可交互的,并开始解析处于延时模式中的脚本——这些脚本在文档解析后执行。

    文档状态将被设置为完成,同时触发一个load事件。

    Html5规范中有符号化及构建树的完整算法(http://www.w3.org/TR/html5/syntax.html#html-parser)。

    浏览器容错 Browsers error tolerance

    ---->见下文

  • Loadrunner Response time in controller and analysis 不一致

    2011-12-01 18:46:48




    在分析性能测试结果时,有时会发现controller 中的average response time 和 analysis transaction summary中的 response time不一致。

    可能原因有2:
    1. 在controller中运行场景时,选择了replay thinking time.在analysis transaction summary中默认是ignore thinking time的。
     
      Solution: controller->run time setting->think time-> ignore think time.

    2. controller 中的采样时间间隔和analysis中的采样时间间隔不一致。
       查看controller的采样时间间隔:right click graphc->configure->refresh rate (sec)
       查看analysis的采样时间间隔:analysis->view->set granularity
       solution: 将analysis的采样时间设置为controller一致


    over!!

    转自:
    http://blog.sina.com.cn/s/blog_43ac2b030100vx16.html




    你会发现还不一样,见讨论帖:
    http://bbs.51testing.com/viewthread.php?tid=75250&extra=&page=1


    关键词:
    1. 图表刷新频率或者说数据展现/采样频率:
        controller:默认5s,analysis:默认4s。
    2. 待选集合,总体:不详
    3. 被采集到的样本,即被用来计算数值的
       controller, analysis :是否全部?  ;  Diagnostics:只选取部分Vuser

    4.计算方法

  • 浏览器加载和渲染html的顺序(Yahoo对网页设计性能的建议) (转)

    2011-11-30 18:54:07

    浏览器加载和渲染html的顺序 


    前阵子,在组内给大家做了一次关于“浏览器加载和渲染HTML的顺序”的分享,这里再总结一下吧。
    1.浏览器加载和渲染html的顺序

    1、IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。
    2、在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)
    3、如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。
    4、并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。阻塞加载
    5、样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。
    6、JS、CSS中如有重定义,后定义函数将覆盖前定义函数
    2. JS的加载
    2.1 不能并行下载和解析(阻塞下载)
    2.2 当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现.
    3.如何加快HTML页面加载速度
    1,页面减肥
    页面的肥瘦是影响加载速度最重要的因素
    删除不必要的空格、注释
    将inline的script和css移到外部文件
    可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥
    2,减少文件数量
    减少页面上引用的文件数量可以减少HTTP连接数
    许多JavaScript、CSS文件可以合并最好合并,人家财帮子都把自己的JavaScript. functions和Prototype.js合并到一个base.js文件里去了
    3,减少域名查询
    DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好
    4,缓存重用数据
    使用缓存吧
    5,优化页面元素加载顺序
    首先加载页面最初显示的内容和与之相关的JavaScript和CSS
    然后加载DHTML相关的东西
    像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载
    6,减少inline JavaScript的数量
    浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大
    不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容
    7,使用现代CSS和合法的标签
    使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片
    使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥
    8,Chunk your content
    不要使用嵌套tables
    <table>
      <table>
        <table>
          ..
        <table>
      <table>
    <table>
    而使用非嵌套tables或者divs
    <table>...</table>
    <table>...</table>
    <table>...</table>
    将基于大块嵌套的tables的layout分解成小tables,这样显示时不用加载整个页面(或大table)的内容
    9,指定图像和tables的大小
    如果浏览器可以立即决定图像或tables的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作
    这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变
    image使用height和width
    table使用table-layout: fixed并使用col和colgroup标签指定columns的width
    10,根据用户浏览器明智的选择策略
    IE、Firefox、Safari等等等等
    11,页面结构的例子
    · HTML

        · HEAD

            · LINK ...
            CSS files required for page appearance. Minimize the number of files for performance while keeping unrelated CSS in separate files for maintenance.

            · SCRIPT. ...
            JavaScript. files for functions required during the loading of the page, but not any DHTML that can only run after page loads.
            Minimize the number of files for performance while keeping unrelated JavaScript. in separate files for maintenance.

        · BODY
        · User visible page content in small chunks (tables / divs) that can be displayed without waiting for the full page to download.

            · SCRIPT. ...
            Any scripts which will be used to perform. DHTML. DHTML script. typically can only run after the page has completely loaded and all necessary objects have been initialized. There is no need to load these scripts before the page content. That only slows down the initial appearance of the page load.
            Minimize the number of files for performance while keeping unrelated JavaScript. in separate files for maintenance.
            If any images are used for rollover effects, you should preload them here after the page content has downloaded.



    4.HTML页面加载和解析流程
    1.用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件; 
    2.浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件; 
    3.浏览器又发出CSS文件的请求,服务器返回这个CSS文件; 
    4.浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了; 
    5.浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码; 
    6.服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码; 
    7.浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它; 
    8.Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码; 
    9.终于等到了</html>的到来,浏览器泪流满面…… 
    10.等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径; 
    11.浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

    5.Yahoo对网页设计性能的建议,个人感觉是说得非常好的。
      英文版:http://developer.yahoo.com/performance/rules.html
      中文翻译:http://www.cnblogs.com/smjack/archive/2009/02/24/1396895.html

    参考资料:
    http://hideto.javaeye.com/blog/133384
    http://blog.chinaacc.com/liuzhantao/blog/20100430-3015241029081.html

    ---------------------------------------------

    转自: http://renyongjie668.blog.163.com/blog/static/1600531201097062789/

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

    提高网站访问速度的34条军规
    http://www.cnblogs.com/smjack/archive/2009/02/24/1396895.html


    这是雅虎上的一篇文章,最近正在研究提高网页响应速度和用户体验的方法,拿来翻译一下,加深理解。

    原文:best practices for speeding up your web site

    • 提高网站访问速度的34条军规 1-3

      • 减少HTTP请求数量

      • 使用内容分布式网络

      • 给头部添加一个失效期或者cache-control

    • 提高网站访问速度的34条军规 4-6

      • 压缩组件

      • 把样式表放于前面

      • 把脚本放在最后

    • 提高网站访问速度的34条军规 7-10

      • 不使用CSS表达式

      • 使用外部的Javascript和CSS

      • 减少DNS的查询

      • 缩小Javascript和CSS

    • 提高网站访问速度的34条军规 11-13

      • 避免重定向

      • 移除重复的脚本

      • 设定ETags

    • 提高网站访问速度的34条军规 14-17

      • 让Ajax可以缓存

      • 更早的刷新缓冲区

      • 在Ajax请求中使用GET方法

      • 后加载组件

    • 提高网站访问速度的34条军规 18-19

      • 预先加载组件

      • 减小DOM元素的数量

    • 提高网站访问速度的34条军规 20-25

    • 提高网站访问速度的34条军规 26-30

    • 提高网站访问速度的34条军规 31-34

  • awk与shell之间的变量传递方法 (转)

    2011-11-30 18:52:38


    这个博客还是可以滴:http://renyongjie668.blog.163.com/blog/#m=0

    我认为在linuxawk是个好东东啊,处理一些文本文件会非常方便。而在Linux下嘛,经常会和shell打交道,所以awkshell之间的变量相互传递,有时还是很有必要的,所以简单总结一下吧。

     

    awk中使用shell中的变量

    : "'$var'"

    这种写法大家无需改变用'括起awk程序的习惯,是老外常用的写法.:

    var="test"
    awk 'BEGIN{print "'$var'"}'

    这种写法其实际是双括号变为单括号的常量,传递给了awk.

    如果var中含空格,为了shell不把空格作为分格符,便应该如下使用:

    var="this is a test"
    awk 'BEGIN{print "'"$var"'"}'
    : '"$var"'

    这种写法与上一种类似.如果变量含空格,则变为'""$var""'较为可靠.
    : export 变量,使用ENVIRON["var"]形式,获取环境变量的值

    例如:
    var="this is a test"; export var;

    awk 'BEGIN{print ENVIRON["var"]}'
    : 可以使用awk-v选项  (如果变量个数不多,个人偏向于这种写法)

    例如:
    var="this is a test"
    awk -v awk_var="$var" 'BEGIN {print awk_var}'

    这样便把系统变量var传递给了awk变量awk_var.

     

     awkshell变量传递值

    awkshell传递变量,其思想无非是用awk(sed/perl等也是一样)输出若干条shell命令,然后再用shell去执行这些命令。

    eval $(awk 'BEGIN{print "var1='str1';var2='str2'"}')

    或者eval $(awk '{printf("var1=%s; var2=%s; var3=%s;",$1,$2,$3)}' abc.txt)

    之后可以在当前shell中使用var1,var2等变量了。

    echo "var1=$var1 ----- var2=$var2"

     

     参考资料:

    http://developer.51cto.com/art/200509/3781.htm

    http://www.ixpub.net/thread-1457438-1-1.html


    转自:http://renyongjie668.blog.163.com/blog/static/16005312011829102025222/



  • Linux下vmstat调优工具的深入分析 (*****)

    2011-11-30 13:03:31

    一)概述:

    vmstat
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 310596  24796 143780    0    0   933    89 1044  119  3 21 72  4  0
     
    1)procs:
    r:代表正在运行的进程数.
    b:代表处在不可屏蔽中断的进程数.

    2)memory:
    swpd:已使用swap的空间大小.
    free:空闲的内存.
    buff:内存中buffer的大小.
    cache:内存中cache的大小.

    3)swap
    si:从swap读出的内存,也就是主缺页,这个值是一个比率,即每秒读了多少KB.
    so:置换到swap的内存,这个值是一个比率,即每秒写了多少KB.

    4)io
    bi:从磁盘读数据,这个值是一个比率,即每秒读了多少BLOCK.
    bo:写数据到磁盘,这个值是一个比率,即每秒写了多少BLOCK.

    5)system
    in:每秒中断的次数.
    cs:每秒转换上下文的次数.

    6)
    us:用户态进程使用CPU的百分比.
    sy:内核态进程使用CPU的百分比.
    id:处于空闲的CPU百分比.
    wa:系统等待IO的CPU时间百分比.
    st:来自于一个虚拟机偷取的CPU时间的百分比.

     

    二)系统监控的实验:

    实例一,大量的算术运算
    本程序会进入一个死循环,不断的进行求平方根的操作,模拟大量的算术运算的环境.
    测试源程序如下:
    #include <stdio.h>
    #include <math.h>
    #include <unistd.h>
    #include <stdlib.h>

    void
    run_status(void)
    {
    double pi = M_PI;
    double pisqrt;
    long i;
    while(1){
    pisqrt = sqrt(pi);
    }
    }

    int
    main (void)
    {
    run_status();

    exit(EXIT_SUCCESS);
    }

    gcc run.c -o run -lm
    ./run&

    运行:
    vmstat 1
    注:我们的程序不断的进行算术运算,r的值由0变成了1,而cpu的用户态利用率也达到了100%.如下:
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 304592  25244 147800    0    0     0     0 1010   31  0  1 99  0  0
     0  0      0 304592  25244 147800    0    0     0     0 1020   37  0  0 100  0  0
     1  0      0 304592  25244 147800    0    0     0    36 1016   46 69  1 30  0  0
     1  0      0 304592  25244 147800    0    0     0     0 1067   56 100  0  0  0  0
     1  0      0 304592  25244 147800    0    0     0     0 1010   31 100  0  0  0  0


    实例二,大量的系统调用
    本脚本会进入一个死循环,不断的执行cd命令,从而模拟大量系统调用的环境
    测试脚本如下:
    #!/bin/bash

    while (true)
    do
    cd ;
    done

    chmod +x loop.sh
    ./loop.sh

    运行:
    vmstat 1
    注:程序不断的调用cd命令,此时进程不断的进行上下文切换,所以cs的值会骤然提高,而cpu的内核态利用率也会达到98%左右.如下:
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 304592  25248 147804    0    0   111    15 1025  179 20 20 59  0  0
     0  0      0 304592  25248 147804    0    0     0     0 1013   36  0  0 100  0  0
     1  0      0 304592  25248 147816    0    0     0     0 1015  422  1 72 27  0  0
     2  0      0 304592  25248 147816    0    0     0     0 1012  544  3 97  0  0  0
     1  0      0 304592  25248 147816    0    0     0     0 1007  522  3 97  0  0  0
     1  0      0 304592  25248 147816    0    0     0    64 1015  523  3 97  0  0  0
     2  0      0 304592  25248 147816    0    0     0     0 1003  572  2 98  0  0  0
     1  0      0 304592  25248 147816    0    0     0     0 1012 1263  2 98  0  0  0
     1  0      0 304592  25248 147816    0    0     0     0 1006 1264  3 97  0  0  0
     1  0      0 304592  25248 147816    0    0     0     0 1015 1249  3 97  0  0  0


    实例三,大量的io操作

    我们用dd命令,从/dev/zero读数据,写入到/tmp/data文件中,如下:
    dd if=/dev/zero f=/tmp/data bs=1M count=1000

    运行:
    vmstat 1
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 302160  25868 149004    0    0    77   116 1027  151 14 17 69  0  0
     1  0      0 302160  25868 149004    0    0     0     0 1018   35  0  1 99  0  0
     3  0      0 134884  26032 311628    0    0     0 109872 1423  102  0 100  0  0  0
     1  0      0  14596  26148 428808    0    0     0 117208 1372  120  0 100  0  0  0
     1  0      0   6224  22908 440592    0    0     4 64944 1305  322  0 98  0  2  0
     1  0      0   5976  21836 441016    0    0     4 79072 1447  162  0 51  0 49  0
     0  2      0   5716  21956 439672    0    0     4 79016 1431  374  0 81  0 19  0
     2  2      0   6180  22044 438064    0    0     0 61432 1392  285  0 61  0 39  0
     2  2      0   6912  22104 436828    0    0     4 73980 1486  253  1 59  0 40  0
     0  4      0   5876  14132 448856    0    0     8 63784 1378  313  0 69  0 31  0
     0  2      4   5980   4140 457860    0    0     0 46756 1399  274  0 65  0 35  0
     1  3      4   6060   3892 457580    0    0     8 69876 1398  214  0 46  0 54  0
     1  4      4   6120   2872 457348    0    0     0 59920 1364  327  0 71  0 29  0
     
    注:dd不断的向磁盘写入数据,所以bo的值会骤然提高,而cpu的wait数值也变高,说明由于大量的IO操作,系统的瓶径出现在低速的设备上.
    由于对文件系统的写入操作,cache也从149004KB提高到了457348KB,又由于大量的写中断调用,in的值也从1018提高到1364.


    接下来我们还用dd命令,这回从/tmp/data文件读,写到/dev/null文件中,如下:
    dd if=/tmp/test1 f=/dev/null bs=1M

    运行:
    vmstat 1
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0     60   7056   2492 464560    0    0   177   517 1028  116 10 12 78  1  0
     0  0     60   7056   2492 464560    0    0     0     0 1006   32  0  0 100  0  0
     0  1     60   5768   2296 465032    0    4 94340     4 1514  252  0 65 17 18  0
     1  1     60   5876   2220 466032    0    0 150148    56 1770  306  0 93  0  7  0
     0  1     60   5792   2180 467152    0    0 98872     0 1598  281  0 81  0 19  0
     0  1     60   6308    988 469816    0   52 89556    52 1722  303  0 88  0 12  0
     2  1     60   5620   1004 470488    0    0 79052     0 1671  690  0 72  0 28  0
     0  1     60   6548   1028 469540    0    0 67392     4 1535  657  1 66  0 33  0
     1  1     60   5648   1060 470588    0    0 47408    16 1400  482  0 44  0 56  0
     0  1     60   6368   1088 469836    0    0 70212     0 1561  666  0 66  0 34  0

    注:dd不断的从/tmp/data磁盘文件中读取数据,所以bi的值会骤然变高,最后我们看到b(不可中断进程数)也由0变成了1.

    接下来我们继续用dd命令,把数据写到/dev/ram1里,如下:
    dd if=/dev/zero f=/dev/ram1 bs=1M count=16
    16+0 records in
    16+0 records out
    16777216 bytes (17 MB) copied, 0.0635522 seconds, 264 MB/s

    运行:
    vmstat 1
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0     60   6156   6256 466280    0    0   366   480 1029  111  9 11 79  1  0
     0  0     60   6156   6256 466280    0    0     0     0 1011   32  0  0 100  0  0
     0  0     60   6156   6256 466292    0    0    12     0 1031   65  0  3 96  1  0
     0  0     60   6156   6264 466284    0    0     0    48 1022   48  0  1 99  0  0
     0  0     60   6148  17920 454652    0    0     0     4 1021   81  0  8 92  0  0
     0  0     60   6148  17920 454652    0    0     0     0 1013   32  1  0 99  0  0
     0  0     60   6148  17920 454652    0    0     0     0 1016   36  0  1 99  0  0
     0  0     60   6148  17920 454652    0    0     0     0 1006   31  0  0 100  0  0
     0  0     60   6148  17920 454652    0    0     0     0 1026   42  0  0 100  0  0
     
    注:dd从/dev/zero读取数据,写入到/dev/ram1里面,由于/dev/ram1是设备节点,所以buff会增加.


    实例四:大量的占用内存

    本程序会不断分配内存,直到系统崩溃.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>

    int main (int argc, char *argv[])
    {
    void *ptr;
    int n = 0;
    while (1){
    ptr = malloc(0x100000);

    if (ptr == NULL)
    break;

    memset(ptr, 1, 0x100000);
    printf("malloced %d MB\n", ++n);
    }

    pause();
    }

    gcc callmem.c -o callmem
    ./callmem


    运行:
    vmstat 1
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0   7512  18796 451064    0    0   346   442 1028  105  8 10 81  1  0
     0  0      0   7512  18796 451064    0    0     0     0 1012   36  0  1 99  0  0
     2  0      0   6096  18704 417676    0    0     0     0 1042  169  0 88 12  0  0
     1  0      0   5896  18616 379356    0    0     0     0 1048  183  1 99  0  0  0
     1  0      0   6324  18532 340796    0    0     0    36 1043  215  1 99  0  0  0
     1  0      0   6376  18444 302372    0    0     0     0 1048  195  0 100  0  0  0
     1  0      0   6036  18384 264904    0    0     0     0 1040  187  1 99  0  0  0
     1  0      0   5784  18384 228560    0    0     0     0 1046  175  1 99  0  0  0
     1  0      0   6532  18372 190316    0    0     0     0 1041  188  1 99  0  0  0
     1  0      0   6084  18364 153804    0    0     0     0 1047  187  0 100  0  0  0
     1  0      0   6256  18368 115992    0    0     0    12 1041  195  1 99  0  0  0
     1  0      0   6580  17852  77868    0    0     0     0 1050  196  1 99  0  0  0
     1  1      0   4892  17304  39556    0    0     0     0 1044  157  0 100  0  0  0
     1  1      0   5252  17304  18676    0    0     0     0 1037   81  1 99  0  0  0
     1  0   9236   6156  16456   7580    0 9172     0  9196 1092  131  1 74  0 25  0
     1  0  56008   6860  16456   7620    0 46772     0 46772 1394  399  1 96  0  3  0
     2  1 103600   6488  16464   7600    0 47652     0 47668 1416  395  2 96  0  2  0
     1  1 162644   6580  16464   7536    0 59044     0 59044 1300  333  2 92  0  6  0
     1  0 197980   6408  16464   5876    0 35340     0 35340 1330  259  3 89  0  8  0
     2  4 230372   4920  16468   2876    0 32392     4 32392 1299  293  2 96  0  2  0
     2  2 269352   5824  16472   1304    0 38980   252 38980 1306  285  2 90  0  8  0
     2  2 305656   4936  16468   1392   88 36304   216 36304 1310  317  1 87  0 12  0
     3  5 361100   5136  16472   1352   96 55444    96 55456 1301  297  2 91  0  7  0
     5  3 404796   6820  16488   1760   96 43696   524 43696 1282  186  2 82  0 16  0

    注:我们看到cache迅速减少,而swpd迅速增加,这是因为系统为了分配给新的程序,而从cache(文件系统缓存)回收空间,当空间依然不足时,会用到swap空间.
    而于此同时,si/so也会增加,尤其是so,而swap属于磁盘空间,所以bo也会增加.

    最后:st这个值来自一个虚拟机偷到的时间,在Linux 2.6.11内核之前,没有这个值.

     

    实例五:又一个大量分配内存例子

    我们这个例子为了说明active/inactivte的作用,
    源程序如下:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>

    int
    main (int argc, char *argv[])
    {
            if (argc != 2)
                    exit (0);

            size_t mb = strtoul(argv[1],NULL,0);

            size_t nbytes = mb * 0x100000;
            char *ptr = (char *) malloc(nbytes);
            if (ptr == NULL){
                    perror("malloc");
                    exit (EXIT_FAILURE);
            }

            size_t i;
            const size_t stride = sysconf(_SC_PAGE_SIZE);
            for (i = 0;i < nbytes; i+= stride) {
                    ptr[i] = 0;
            }

            printf("allocated %d mb\n", mb);
            pause();
            return 0;
    }
    gcc act.c -o act -lrt
    ./act 100

    运行:
    vmstat -a 1
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 363868  79488  47724    5  160   291   504 1032   95  6  9 83  1  0
     0  0      0 363868  79488  47728    0    0     0     0 1010   32  0  0 100  0  0
     0  0      0 363868  79488  47728    0    0     0     0 1014   34  0  0 100  0  0
     1  0      0 333924  79492  77736    0    0     8    36 1014   44  0 75 24  1  0
     1  0      0 293252  79500 118388    0    0     0     0 1011   34  0 100  0  0  0
     0  0      0 261384  79500 150140    0    0     0     0 1009   36  0 78 22  0  0
     0  0      0 261384  79500 150192    0    0     0     0 1015   33  0  1 99  0  0
     0  0      0 261384  79500 150192    0    0     0     0 1008   30  0  0 100  0  0
     0  0      0 261384  79500 150192    0    0     0     0 1015   32  0  0 100  0  0
     0  0      0 363436  79500  47732    0    0     0    12 1018   45  0  2 98  0  0
     0  0      0 363436  79508  47732    0    0     0     0 1011   55  0  1 99  0  0
     0  0      0 363436  79508  47732    0    0     0     0 1008   29  0  0 100  0  0

    注:程序运行时系统给它分配了100MB的内存,所以此时的active从47728kb变到了150192kb.

     

    三)vmstat用法:

    查看系统已经fork了多少次
    vmstat -f
    processes 114688
    注:这个数据是从/proc/stat中的processes字段里取得的.

    查看内存的active和inactive
    vmstat -a
    procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
     r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 361952  80536  48396    5  140   255   441 1031   87  6  8 85  1  0
    注:inact和active的数据来自于/proc/meminfo.

    查看内存使用的详细信息
    vmstat -s
           515600  total memory
           154020  used memory
            48396  active memory
            80600  inactive memory
           361580  free memory
            46376  buffer memory
            57352  swap cache
          1052248  total swap
                0  used swap
          1052248  free swap
            31352 non-nice user cpu ticks
              167 nice user cpu ticks
            43234 system cpu ticks
           489636 idle cpu ticks
             5509 IO-wait cpu ticks
             1243 IRQ cpu ticks
             1751 softirq cpu ticks
                0 stolen cpu ticks
          1451094 pages paged in
          2514952 pages paged out
             6822 pages swapped in
           198826 pages swapped out
          5905614 interrupts
           498241 CPU context switches
       1300715746 boot time
           114723 forks
    注:这些信息的分别来自于/proc/meminfo,/proc/stat和/proc/vmstat.


    查看磁盘的读/写
    vmstat -d
    disk- ------------reads------------ ------------writes----------- -----IO------
           total merged sectors      ms  total merged sectors      ms    cur    sec
    ram0       0      0       0       0      0      0       0       0      0      0
    ram1       0      0       0       0      0      0       0       0      0      0
    ram2       0      0       0       0      0      0       0       0      0      0
    ram3       0      0       0       0      0      0       0       0      0      0
    ram4       0      0       0       0      0      0       0       0      0      0
    ram5       0      0       0       0      0      0       0       0      0      0
    ram6       0      0       0       0      0      0       0       0      0      0
    ram7       0      0       0       0      0      0       0       0      0      0
    ram8       0      0       0       0      0      0       0       0      0      0
    ram9       0      0       0       0      0      0       0       0      0      0
    ram10      0      0       0       0      0      0       0       0      0      0
    ram11      0      0       0       0      0      0       0       0      0      0
    ram12      0      0       0       0      0      0       0       0      0      0
    ram13      0      0       0       0      0      0       0       0      0      0
    ram14      0      0       0       0      0      0       0       0      0      0
    ram15      0      0       0       0      0      0       0       0      0      0
    sda    26411  18657 2900913  144728  22982 605889 5030968 3128488      0    117
    hdc      143     94    1276     357      0      0       0       0      0      0
    fd0        0      0       0       0      0      0       0       0      0      0
    md0        0      0       0       0      0      0       0       0      0      0

    注:这些信息主要来自于/proc/diskstats.
    merged:表示一次来自于合并的写/读请求,一般系统会把多个连接/邻近的读/写请求合并到一起来操作.

    查看/dev/sda1磁盘的读/写
    vmstat -p /dev/sda1
    sda1          reads   read sectors  writes    requested writes
                   35215    2842778     430114    3440912
                  
    注:这些信息主要来自于/proc/diskstats
    reads:来自于这个分区的读的次数.
    read sectors:来自于这个分区的读扇区的次数.
    writes:来自于这个分区的写的次数.
    requested writes:来自于这个分区的写请求次数.


    查看系统的slab信息
    vmstat -m
    Cache                       Num  Total   Size  Pages
    rpc_buffers                   8      8   2048      2
    rpc_tasks                     8     20    192     20
    rpc_inode_cache               6      9    448      9
    ip_fib_alias                 14    113     32    113
    ip_fib_hash                  14    113     32    113
    fib6_nodes                   24    113     32    113
    ip6_dst_cache                16     30    256     15
    ndisc_cache                   1     20    192     20
    RAWv6                         4     11    704     11
    UDPv6                         2      6    640      6
    tw_sock_TCPv6                 0      0    128     30
    request_sock_TCPv6            0      0    128     30
    TCPv6                         3      3   1280      3
    dm_tio                        0      0     16    203
    dm_io                         0      0     20    169
    jbd_4k                        0      0   4096      1
    scsi_cmd_cache                1     12    320     12
    sgpool-128                   32     32   2048      2
    sgpool-64                    32     32   1024      4
    sgpool-32                    32     32    512      8
    sgpool-16                    32     45    256     15
    sgpool-8                     34     60    128     30
    scsi_io_context               0      0    104     37
    ext3_inode_cache          21829  21832    492      8
    ext3_xattr                   36     78     48     78
    journal_handle                2    169     20    169
    journal_head                 63     72     52     72
    revoke_table                  2    254     12    254
    revoke_record                 0      0     16    203
    uhci_urb_priv                 0      0     28    127
    UNIX                         56     63    448      9
    flow_cache                    0      0    128     30
    cfq_ioc_pool                  0      0     92     42
    cfq_pool                      0      0     96     40
    crq_pool                      0      0     44     84
    deadline_drq                 63    252     44     84
    as_arq                        0      0     56     67
    mqueue_inode_cache            1      7    576      7
    isofs_inode_cache             2     20    368     10
    hugetlbfs_inode_cache         1     11    340     11
    ext2_inode_cache              0      0    476      8
    ext2_xattr                    0      0     48     78
    dnotify_cache                 2    169     20    169
    Cache                       Num  Total   Size  Pages
    dquot                         0      0    128     30
    eventpoll_pwq                 1    101     36    101
    eventpoll_epi                 1     30    128     30
    inotify_event_cache           0      0     28    127
    inotify_watch_cache           1     92     40     92
    kioctx                        0      0    192     20
    kiocb                         0      0    128     30
    fasync_cache                  0      0     16    203
    shmem_inode_cache           239    243    436      9
    posix_timers_cache            0      0     88     44
    uid_cache                     5     59     64     59
    ip_mrt_cache                  0      0    128     30
    tcp_bind_bucket               7    203     16    203
    inet_peer_cache               0      0     64     59
    secpath_cache                 0      0     32    113
    xfrm_dst_cache                0      0    320     12
    ip_dst_cache                  5     15    256     15
    arp_cache                     2     20    192     20
    RAW                           2      7    512      7
    UDP                           8     14    512      7
    tw_sock_TCP                   0      0    128     30
    request_sock_TCP              0      0    128     30
    TCP                           6      7   1152      7
    blkdev_ioc                    6    127     28    127
    blkdev_queue                 20     20    956      4
    blkdev_requests             100    184    172     23
    biovec-256                    7      8   3072      2
    biovec-128                    7     10   1536      5
    biovec-64                     7     10    768      5
    biovec-16                     7     20    192     20
    biovec-4                      7     59     64     59
    biovec-1                     71    406     16    203
    bio                         342    390    128     30
    utrace_engine_cache           0      0     32    113
    utrace_cache                  0      0     32    113
    sock_inode_cache            104    110    384     10
    skbuff_fclone_cache          10     10    384     10
    skbuff_head_cache           120    120    192     20
    file_lock_cache               3     40     96     40
    Acpi-Operand                448    552     40     92
    Acpi-ParseExt                 0      0     44     84
    Acpi-Parse                    0      0     28    127
    Acpi-State                    0      0     44     84
    Cache                       Num  Total   Size  Pages
    Acpi-Namespace              337    338     20    169
    delayacct_cache              78     78     48     78
    taskstats_cache              19     53     72     53
    proc_inode_cache             19     99    356     11
    sigqueue                     17     27    144     27
    radix_tree_node            2319   2422    276     14
    bdev_cache                   23     28    512      7
    sysfs_dir_cache            2928   2940     44     84
    mnt_cache                    28     30    128     30
    inode_cache                 692    792    340     11
    dentry_cache              24563  24563    136     29
    filp                        830    980    192     20
    names_cache                   3      3   4096      1
    avc_node                     14     72     52     72
    selinux_inode_security      636    780     48     78
    key_jar                      11     30    128     30
    idr_layer_cache             100    116    136     29
    buffer_head                8996   9000     52     72
    mm_struct                    61     63    448      9
    vm_area_struct             1788   1932     84     46
    fs_cache                     59     59     64     59
    files_cache                  60     60    384     10
    signal_cache                 81     81    448      9
    sighand_cache                72     72   1344      3
    task_struct                  78     78   1360      3
    anon_vma                    794   1016     12    254
    pgd                          47     47   4096      1
    pid                          92    101     36    101
    size-131072(DMA)              0      0 131072      1
    size-131072                   0      0 131072      1
    size-65536(DMA)               0      0  65536      1
    size-65536                    1      1  65536      1
    size-32768(DMA)               0      0  32768      1
    size-32768                    1      1  32768      1
    size-16384(DMA)               0      0  16384      1
    size-16384                    1      1  16384      1
    size-8192(DMA)                0      0   8192      1
    size-8192                     0      0   8192      1
    size-4096(DMA)                0      0   4096      1
    size-4096                    92     92   4096      1
    size-2048(DMA)                0      0   2048      2
    size-2048                   234    234   2048      2
    size-1024(DMA)                0      0   1024      4
    Cache                       Num  Total   Size  Pages
    size-1024                   176    176   1024      4
    size-512(DMA)                 0      0    512      8
    size-512                    264    264    512      8
    size-256(DMA)                 0      0    256     15
    size-256                    300    300    256     15
    size-128(DMA)                 0      0    128     30
    size-128                   1200   1200    128     30
    size-64(DMA)                  0      0     64     59
    size-32(DMA)                  0      0     32    113
    size-64                    1013   1180     64     59
    size-32                    1856   1921  &nbs

  • 诊断 CPU、内存或磁盘瓶颈的流程图(zt)

    2011-11-30 12:54:47


    诊断 CPU、内存或磁盘瓶颈的流程图(zt)


     

    从步骤1开始,首先查看CPU使用情况,按照诊断CPU、内存或磁盘瓶颈的指导进行操作。对于下面的每个步骤,查找一端时间内的趋势,从中收集系统运行性能较差时的数据。另外,只有将这些数据与系统正常运行时收集的数据进行比较时才能进行准确的诊断。

    步骤1

    # sar -u [interval] [iterations]
    (示例
    : sar -u 5 30)
    %idle是否很低?这是CPU未在运行任何进程的时间百分比。在一端时间内%idle为零可能是CPU瓶颈的第一个指示。

    不是->系统未发生CPU瓶颈。转至步骤3。
    是->系统可能发生了CPU、内存或I/O瓶颈。转至步骤2。

    步骤2

    %usr是否较高?很多系统正常情况下花费80%的CPU时间用于用户,20%用于系统。其他系统通常会使用80%左右的用户时间。

    不是->系统可能遇到CPU、内存或I/O瓶颈。转至步骤3。
    是->系统可能由于用户进程遇到CPU瓶颈。转至部分3,部分A, 调整系统的CPU瓶颈。

    步骤3

    %wio的值是否大于15?

    是->以后记住这个值。它可能表示磁盘或磁带瓶颈。转至步骤4。
    不是->转至步骤4。

    步骤4

    # sar -d [interval] [iterations]
    用于任何磁盘的%busy是否都大于50? (请记住,50%指示一个大概的 指南,它可能远远高于您系统的正常值。在某些系统上,甚至%busy值为20可能就表示发生了磁盘瓶颈,而其他系统正常情况下可能就为50% busy。)对于同一个磁盘上,avwait是否大于avserv?

    不是->很可能不是磁盘瓶颈,转至步骤6。
    是->此设备上好像发生了IO瓶颈。
    转至步骤5。

    步骤5

    系统上存在磁盘瓶颈,发生瓶颈的磁盘上有哪些内容?

    原始分区,
    文件系统->转至部分3,部分B,调整发生磁盘IO瓶颈的系统。
    Swap ->可能是由于内存瓶颈导致的。
    转至步骤6。

    步骤6

    # vmstat [interval] [iterations]
    在很长的一端时间内,po是否总是大于0
    ?
    对于一个s800系统(free * 4k)是否小于2 MB,

    (对于s700系统free * 4k是否小于1 MB)?
    (值2 MB和1 MB指示大概的指南,真正的LOTSFREE值,即系统开始发生paging的值是在系统引导时计算的,它是基于系统内存的大小的。)

    不是->如果步骤1中的%idle较低,系统则很可能发生了CPU瓶颈。
    转至部分3,部分A,调整发生了CPU瓶颈的系统。
    如果%idle不是很低,则可能不是CPU、磁盘IO或者内存瓶颈。
    请转至部分4,其他瓶颈。
    是->系统上存在内存瓶颈,转至部分3部分C,调整发生内存瓶颈的系统。



    转自:http://blog.csdn.net/macky0668/article/details/6720105

  • Linux TCP 连接数修改 使用支持高并发网络I/O的编程技术

    2011-11-30 12:47:37



    3、使用支持高并发网络I/O的编程技术
     
       在Linux上编写高并发TCP连接应用程序时,必须使用合适的网络I/O技术和I/O事件分派机制。
     
       可用的I/O技术有同步I/O,非阻塞式同步I/O(也称反应式I/O),以及异步I/O。在高TCP并发的情形下,如果使用同步I/O,这会严重阻塞程序的运转,除非为每个TCP连接的I/O创建一个线程。但是,过多的线程又会因系统对线程的调度造成巨大开销。因此,在高TCP并发的情形下使用同步I/O是不可取的,这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select(),poll(),epoll等机制。异步I/O的技术就是使用AIO
     
       从I/O事件分派机制来看,使用select()是不合适的,因为它所支持的并发连接数有限(通常在1024个以内)。如果考虑性能,poll()也是不合适的,尽管它可以支持的较高的TCP并发数,但是由于其采用“轮询”机制,当并发数较高时,其运行效率相当低,并可能存在I/O事件分派不均,导致部分TCP连接上的I/O出现“饥饿”现象。而如果使用epoll或AIO,则没有上述问题(早期Linux内核的AIO技术实现是通过在内核中为每个I/O请求创建一个线程来实现的,这种实现机制在高并发TCP连接的情形下使用其实也有严重的性能问题。但在最新的Linux内核中,AIO的实现已经得到改进)
     
       综上所述,在开发支持高并发TCP连接的Linux应用程序时,应尽量使用epoll或AIO技术来实现并发的TCP连接上的I/O控制,这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。


    Linux  TCP 连接数修改
     
    一、           文件数限制修改
    (1)   vi /etc/security/limits.conf
    *  soft nofile 10240
       *  hard nofile 10240
     
    (2) vi /etc/pam.d/login
    session required /lib/security/pam_limits.so
     
    二、           网络端口限制修改
    (1) vi /etc/rc.d/rc.local
    /sbin/modprobe ip_conntrack  # 加载 ip_contrack 模块
    # /sbin/sysctl –p              # 使 /etc/sysctl.conf 的配置生效,根据实际情况来决定是否添加此命令
     
    [root@AS4U8 ~]# sysctl -a | grep "net.ipv4.ip"
    net.ipv4.ip_conntrack_max = 16384
    这表明将系统对最大跟踪的TCP连接数限制默认为16384。请注意,此限制值要尽量小,以节省对内核内存的占用
     
    (2) vi /etc/sysctl.conf
    net.ipv4.ip_local_port_range = 1024 65000
    net.ipv4.ip_conntrack_max = 10240
    此限制值要尽量小,以节省对内核内存的占用。
     
     
    详细解说:
    1、修改用户进程可打开文件数限制
       在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的文件数限制:
       [speng@as4 ~]$ ulimit -n
       1024
       这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件中还得除去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听socket,进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数就只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许同时1014个TCP并发连接。
     
       对于想支持更高数量的TCP并发连接的通讯处理程序,就必须修改Linux对当前用户的进程同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中软限制是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数;硬限制则是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。通常软限制小于或等于硬限制。
     
       修改上述限制的最简单的办法就是使用ulimit命令:
       [speng@as4 ~]$ ulimit -n <file_num>
       上述命令中,在<file_num>中指定要设置的单一进程允许打开的最大文件数。如果系统回显类似于“Operation notpermitted”之类的话,说明上述限制修改失败,实际上是因为在<file_num>中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。因此,就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。
     
       第一步,修改/etc/security/limits.conf文件,在文件中添加如下行:
       speng soft nofile 10240
       speng hard nofile 10240
       其中speng指定了要修改哪个用户的打开文件数限制,可用'*'号表示修改所有用户的限制;soft或hard指定要修改软限制还是硬限制;10240则指定了想要修改的新的限制值,即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。
     
       第二步,修改/etc/pam.d/login文件,在文件中添加如下行:
       session required /lib/security/pam_limits.so
       这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制),而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。修改完后保存此文件。
     
       第三步,查看Linux系统级的最大打开文件数限制,使用如下命令:
       [speng@as4 ~]$ cat /proc/sys/fs/file-max
       12158
       这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)12158个文件,是Linux系统级硬限制,所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制,如果没有特殊需要,不应该修改此限制,除非想为用户级打开文件数限制设置超过此限制的值。修改此硬限制的方法是修改/etc/rc.local脚本,在脚本中添加如下行:
       echo 22158 > /proc/sys/fs/file-max
       这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为22158。修改完后保存此文件。
     
       完成上述步骤后重启系统,一般情况下就可以将Linux系统对指定用户的单一进程允许同时打开的最大文件数限制设为指定的数值。如果重启后用ulimit-n命令查看用户可打开文件数限制仍然低于上述步骤中设置的最大值,这可能是因为在用户登录脚本/etc/profile中使用ulimit-n命令已经将用户可同时打开的文件数做了限制。由于通过ulimit-n修改系统对用户可同时打开文件的最大数限制时,新修改的值只能小于或等于上次ulimit-n设置的值,因此想用此命令增大这个限制值是不可能的。所以,如果有上述问题存在,就只能去打开/etc/profile脚本文件,在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量,如果找到,则删除这行命令,或者将其设置的值改为合适的值,然后保存文件,用户退出并重新登录系统即可。
       通过上述步骤,就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。
     
    2、修改网络内核对TCP连接的有关限制
     
       在Linux上编写支持高并发TCP连接的客户端通讯处理程序时,有时会发现尽管已经解除了系统对用户同时打开文件数的限制,但仍会出现并发TCP连接数增加到一定数量时,再也无法成功建立新的TCP连接的现象。出现这种现在的原因有多种。
     
       第一种原因可能是因为Linux网络内核对本地端口号范围有限制。此时,进一步分析为什么无法建立TCP连接,会发现问题出在connect()调用返回失败,查看系统错误提示消息是“Can't assign requested address”。同时,如果在此时用tcpdump工具监视网络,会发现根本没有TCP连接时客户端发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。其实,问题的根本原因在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围进行了限制(例如,内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时存在太多的TCP客户端连接时,由于每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中),如果现有的TCP客户端连接已将所有的本地端口号占满,则此时就无法为新的TCP客户端连接分配一个本地端口号了,因此系统会在这种情况下在connect()调用中返回失败,并将错误提示消息设为“Can't assign requested address”。有关这些控制逻辑可以查看Linux内核源代码,以linux2.6内核为例,可以查看tcp_ipv4.c文件中如下函数:
       static int tcp_v4_hash_connect(struct sock *sk)
       请注意上述函数中对变量sysctl_local_port_range的访问控制。变量sysctl_local_port_range的初始化则是在tcp.c文件中的如下函数中设置:
       void __init tcp_init(void)
       内核编译时默认设置的本地端口号范围可能太小,因此需要修改此本地端口范围限制。
       第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
       net.ipv4.ip_local_port_range = 1024 65000
       这表明将系统对本地端口范围限制设置为1024~65000之间。请注意,本地端口范围的最小值必须大于或等于1024;而端口范围的最大值则应小于或等于65535。修改完后保存此文件。
       第二步,执行sysctl命令:
       [speng@as4 ~]$ sysctl -p
       如果系统没有错误提示,就表明新的本地端口范围设置成功。如果按上述端口范围进行设置,则理论上单独一个进程最多可以同时建立60000多个TCP客户端连接。
     
       第二种无法建立TCP连接的原因可能是因为Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP连接数有限制。此时程序会表现为在connect()调用中阻塞,如同死机,如果用tcpdump工具监视网络,也会发现根本没有TCP连接时客户端发SYN包的网络流量。由于IP_TABLE防火墙在内核中会对每个TCP连接的状态进行跟踪,跟踪信息将会放在位于内核内存中的conntrackdatabase中,这个数据库的大小有限,当系统中存在过多的TCP连接时,数据库容量不足,IP_TABLE无法为新的TCP连接建立跟踪信息,于是表现为在connect()调用中阻塞。此时就必须修改内核对最大跟踪的TCP连接数的限制,方法同修改内核对本地端口号范围的限制是类似的:
       第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
       net.ipv4.ip_conntrack_max = 10240
       这表明将系统对最大跟踪的TCP连接数限制设置为10240。请注意,此限制值要尽量小,以节省对内核内存的占用。
       第二步,执行sysctl命令:
       [speng@as4 ~]$ sysctl -p
       如果系统没有错误提示,就表明系统对新的最大跟踪的TCP连接数限制修改成功。如果按上述参数进行设置,则理论上单独一个进程最多可以同时建立10000多个TCP客户端连接。



    转自:http://blog.csdn.net/macky0668/article/details/6802329
  • 设置mysql最大连接数的方法

    2011-11-30 10:48:46

    MYSQL数据库安装完成后,默认最大连接数是100,一般流量稍微大一点的论坛或网站这个连接数是远远不够的,增加默认MYSQL连接数的方法有两个

    方法一:进入MYSQL安装目录 打开MYSQL配置文件 my.ini 或 my.cnf查找 max_connections=100   修改为 max_connections=1000 服务里重起MYSQL即可

    方法二:MySQL的最大连接数默认是100客户端登录:mysql -uusername -ppassword

    设置新的最大连接数为200:mysql> set GLOBAL max_connections=200

    显示当前运行的Query:mysql> show processlist

    显示当前状态:mysql> show status

    退出客户端:mysql> exit

    查看当前最大连接数:mysqladmin -uusername -ppassword variables

     

    方法三:以centos 4.4 下面的mysql 5.0.33 手工编译版本为例说明:

      vi /usr/local/mysql/bin/mysqld_safe

      找到safe_mysqld编辑它,找到mysqld启动的那两行,在后面加上参数 :

      -O max_connections=1500

      具体一点就是下面的位置:

      用红字特别说明:

      then $NOHUP_NICENESS $ledir/$MYSQLD

      $defaults --basedir=$MY_BASEDIR_VERSION

      --datadir=$DATADIR $USER_OPTION

      --pid-file=$pid_file

      --skip-external-locking

      -O max_connections=1500

      >> $err_log 2>&1 else

      eval "$NOHUP_NICENESS $ledir/$MYSQLD

      $defaults --basedir=$MY_BASEDIR_VERSION

      --datadir=$DATADIR $USER_OPTION

      --pid-file=$pid_file

      --skip-external-locking $args

      -O max_connections=1500 >>

      $err_log 2>&1"

      保存。

      # service mysqld restart

      # /usr/local/mysql/bin/mysqladmin -uroot -p variables

      输入root数据库账号的密码后可看到

      max_connections 1500 即新改动已经生效。

      还有一种方法,

      修改原代码:

      解开MySQL的原代码,进入里面的sql目录修改mysqld.cc找到下面一行:

      {"max_connections", OPT_MAX_CONNECTIONS,

      "The number of simultaneous clients allowed.", (gptr*) &max_connections,

      (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1,

      0},

      把它改为:

      {"max_connections", OPT_MAX_CONNECTIONS,

      "The number of simultaneous clients allowed.", (gptr*) &max_connections,

      (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 1500, 1, 16384, 0, 1,

      0},

      存盘退出,然后./configure ;make;make install可以获得同样的效果。



    http://www.cnblogs.com/stable/archive/2010/12/02/1894295.html

  • nginx+php 502 bad gateway解决方法

    2011-11-29 18:18:53

    Nginx的502错误

    重启服务器后发现Nginx在我的WordPress报502 Bad Gateway错误。
    查了好久,以为是nginx.conf或者php-fpm.conf的错,突然想起来,好像哪里看到说php.ini中memory_limit设低了会出错,
    修改了php.ini的memory_limit为64M,重启nginx,发现真好了~
    原来是PHP的内存不足了。
    360M内存的服务器,扛这点应用应该OK~

    ------------------------------------------------------------------------------------------

    nginx+php 502 bad gateway解决方法

    打开 /usr/local/php/etc/php-fpm.conf

          调大以下两个参数(根据服务器实际情况,过大也不行)

        <value name=”max_children”>5120</value>

          <value name=”max_requests”>600</value>

    修改后速度快了,好象mysql占用cpu也下降了

    -----------------------------------------------------------------------------------------

    nginx 502 bad故障原因及解决方法收集

    如题,最近网站频繁出现502错误,简直无法正常运转,出现这种情况大多是php-cgi超时没有返回信息,或进程僵死等情况造成的,参考张宴的这 篇关于502错误的解决办法(http://blog.s135.com/read.php?361),并咨询系统管理员高手,我们的nginx已经配置到极致这些都已经老早做过修改了,但现在又出然出现。

    经过分析将nginx的error log打开,发现”pstream sent too big header while reading response header from upstream”这样的错误提示,查阅了一下资料,大意是nginx缓 冲区有一个bug造成的,我们网站的页面消耗占用缓冲区可能过大。参考老外写的修改办法增加了缓冲区容量大小设置,502问题彻底解决,后来系统管理员又 对参数做了调整只保留了2个设置参数:client head buffer,fastcgi buffer size。

    参考:

    http://www.sudone.com/nginx/nginx_400_bad_request.html

    http://blog.rackcorp.com/?p=14

    二、昨天装上nginx后在高负载的时候,论坛上传图片或者执行较长时间脚本的时候就不停的出现502 Bad Gateway ,网上搜了,大多数都是张大师的那篇解决方案,他的解决方案是

    http
    {
    ……
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    ……
    }

    增加了fastcgi的相应请求时间。但是我在实际中碰到了这个问题,设置到500,还是会出现,只是比我设置120的时候要少一些。后来发现主要是在一些post或者数据库操作的时候出现这种情况,静态页面是不会出现的。

    反复的查问题,调试,也加大了CGI的进程数。

    128

    256再加上去可能会变得很慢。占用内存大了。

    在php-fpm.conf设置中还有一项,可能当时没注意到,无意中改了这个值。

    request_terminate_timeout

    这个值是max_execution_time,就是fast-cgi的执行脚本时间。

    0s

    0s为关闭,就是无限执行下去。(当时装的时候没仔细看就改了一个数字)

    发现,问题解决了,执行很长时间也不会出错了。

    优化fastcgi中,还可以改改这个值5s 。看看效果

    终于发现502的错误其实不是nginx的问题,哈哈

    php-cgi进程数不够用、php执行时间长、或者是php-cgi进程死掉,都会出现502错误

    三、

    一台服务器上运行着nginx php(fpm) xcache,访问量日均 300W pv左右
    最近经常会出现这样的情况: php页面打开很慢,cpu使用率突然降至很低,系统负载突然升至很高,查看网卡的流量,也会发现突然降到了很低。这种情况只持续数秒钟就恢复了
    检查php-fpm的日志文件发现了一些线索
    Sep 30 08:32:23.289973 [NOTICE] fpm_unix_init_main(), line 271: getrlimit(nofile): max:51200, cur:51200
    Sep 30 08:32:23.290212 [NOTICE] fpm_sockets_init_main(), line 371: using inherited socket fd=10, “127.0.0.1:9000″
    Sep 30 08:32:23.290342 [NOTICE] fpm_event_init_main(), line 109: libevent: using epoll
    Sep 30 08:32:23.296426 [NOTICE] fpm_init(), line 47: fpm is running, pid 30587
    在这几句的前面,是1000多行的关闭children和开启children的日志
    原来,php-fpm有一个参数 max_requests ,该参数指明了,每个children最多处理多少个请求后便会被关闭,默认的设置是500。因为php是把请求轮询给每个children,在大流量 下,每个childre到达max_requests所用的时间都差不多,这样就造成所有的children基本上在同一时间被关闭。
    在这期间,nginx无法将php文件转交给php-fpm处理,所以cpu会降至很低(不用处理php,更不用执行sql),而负载会升至很高(关闭和开启children、nginx等待php-fpm),网卡流量也降至很低(nginx无法生成数据传输给客户端)

    解决问题很简单,增加children的数量,并且将 max_requests 设置未 0 或者一个比较大的值,重启php-fpm

    四、

    nginx 502错 误的原因比较多,是因为在代理模式下后端服务器出现问题引起的。这些错误一般都不是nginx本身的问题,一定要从后端找原因!但nginx把这些出错都 揽在自己身上了,着实让nginx的推广者备受置疑,毕竟从字眼上理解,bad gateway?不就是bad nginx吗?让不了解的人看到,会直接把责任推在nginx身上,希望nginx下一个版本会把出错提示写稍微友好一些,至少不会是现在简单的一句 502 Bad Gateway,另外还不忘附上自己的大名。

    502错误最通常的出现情况就是后端主机当机,当然还有。在upstream配置里有这么一项配置:proxy_next_upstream,这个 配置指定了nginx在从一个后端主机取数据遇到何种错误时会转到下一个后端主机,里头写上的就是会出现502的所有情况拉,默认是error timeout,error就是当机、断线之类的,timeout就是读取堵塞超时,比较容易理解。我一般是全写上的:

    proxy_next_upstream error timeout invalid_header http_500 http_503;

    不过现在可能我要去掉http_500这一项了,http_500指定后端返回500错误时会转一个主机,后端的jsp出错的话,本来会打印一堆 stacktrace的错误信息,现在被502取代了。但公司的程序员可不这么认为,他们认定是nginx出现了错误,我实在没空跟他们解释502的原理 了……

    invalid_header我也没认真查清到底指的什么,我也很想先把它弄下来。

    503错误就可以保留,因为后端通常是apache resin,如果apache死机就是error,但resin死机,仅仅是503,所以还是有必要保留的。

    对nginx fastcgi使用的情况,我现在用得不多,不熟就不乱说了。


    转自:http://apps.hi.baidu.com/share/detail/17351945

  • 长连接和Keepalive

    2011-11-29 18:02:45




    TCP协议中有长连接和短连接之分。短连接在数据包发送完成后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即我们通常所说的Keepalive(存活定时器)功能。
    默认的Keepalive超时需要7,200,000 milliseconds,即2小时,探测次数为5次。它的功效和用户自己实现的心跳机制是一样的。开启Keepalive功能需要消耗额外的宽带和流量,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,Keepalive设置不合理时可能会因为短暂的网络波动而断开健康的TCP连接。

    keepalive并不是TCP规范的一部分。在Host Requirements RFC罗列有不使用它的三个理由:(1)在短暂的故障期间,它们可能引起一个良好连接(good connection)被释放(dropped),(2)它们消费了不必要的宽带,(3)在以数据包计费的互联网上它们(额外)花费金钱。然而,在许多的实现中提供了存活定时器。

    一些服务器应用程序可能代表客户端占用资源,它们需要知道客户端主机是否崩溃。存活定时器可以为这些应用程序提供探测服务。Telnet服务器和Rlogin服务器的许多版本都默认提供存活选项。

    个人计算机用户使用TCP/IP协议通过Telnet登录一台主机,这是能够说明需要使用存活定时器的一个常用例子。如果某个用户在使用结束时只是关掉了电源,而没有注销(log off),那么他就留下了一个半打开(half-open)的连接。如果客户端消失,留给了服务器端半打开的连接,并且服务器又在等待客户端的数据,那么等待将永远持续下去。存活特征的目的就是在服务器端检测这种半打开连接。

    也可以在客户端设置存活器选项,且没有不允许这样做的理由,但通常设置在服务器。如果连接两端都需要探测对方是否消失,那么就可以在两端同时设置(比如NFS)。



    keepalive工作原理:

    若在一个给定连接上,两小时之内无任何活动,服务器便向客户端发送一个探测段。(我们将在下面的例子中看到探测段的样子。)客户端主机必须是下列四种状态之一:

    1) 客户端主机依旧活跃(up)运行,并且从服务器可到达。从客户端TCP的正常响应,服务器知道对方仍然活跃。服务器的TCP为接下来的两小时复位存活定时器,如果在这两个小时到期之前,连接上发生应用程序的通信,则定时器重新为往下的两小时复位,并且接着交换数据。

    2) 客户端已经崩溃,或者已经关闭(down),或者正在重启过程中。在这两种情况下,它的TCP都不会响应。服务器没有收到对其发出探测的响应,并且在75秒之后超时。服务器将总共发送10个这样的探测,每个探测75秒。如果没有收到一个响应,它就认为客户端主机已经关闭并终止连接。

    3) 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。

    4) 客户端主机活跃运行,但从服务器不可到达。这与状态2类似,因为TCP无法区别它们两个。它所能表明的仅是未收到对其探测的回复。

     

    服务器不必担心客户端主机被关闭然后重启的情况(这里指的是操作员执行的正常关闭,而不是主机的崩溃)。当系统被操作员关闭时,所有的应用程序进程(也就是客户端进程)都将被终止,客户端TCP会在连接上发送一个FIN。收到这个FIN后,服务器TCP向服务器进程报告一个文件结束,以允许服务器检测这种状态。

    在第一种状态下,服务器应用程序不知道存活探测是否发生。凡事都是由TCP层处理的,存活探测对应用程序透明,直到后面234三种状态发生。在这三种状态下,通过服务器的TCP,返回给服务器应用程序错误信息。(通常服务器向网络发出一个读请求,等待客户端的数据。如果存活特征返回一个错误信息,则将该信息作为读操作的返回值返回给服务器。)在状态2,错误信息类似于“连接超时”。状态3则为“连接被对方复位”。第四种状态看起来像连接超时,或者根据是否收到与该连接相关的ICMP错误信息,而可能返回其它的错误信息。

    linux内核包含对keepalive的支持。其中使用了三个参数:tcp_keepalive_time(开启keepalive的闲置时 长)tcp_keepalive_intvlkeepalive探测包的发送间隔)和tcp_keepalive_probes (如果对方不予应答,探测包的发送次数);在liunx中,keepalive是一个开关选项,可以通过函数来使能。具体地说,可以使用以下代码:
    setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));

    tcp检测到对端socket不再可用时(不能发出探测包,或探测包没有收到ACK的响应包),select会返回socket可读,并且在recv时返回-1,同时置上errnoETIMEDOUT。此时TCP的状态是断开的。



    keepalive参数设置代码如下:
    // 开启KeepAlive
    BOOL bKeepAlive = TRUE;
    int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
    if (nRet == SOCKET_ERROR)
    {
    return FALSE;
    }

    // 设置KeepAlive参数
    tcp_keepalive alive_in                = {0};
    tcp_keepalive alive_out                
    = {0};
    alive_in.keepalivetime                
    = 5000;                // 开始首次KeepAlive探测前的TCP空闭时间
    alive_in.keepaliveinterval        = 1000;                // 两次KeepAlive探测间的时间间隔
    alive_in.onoff                                = TRUE;
    unsigned 
    long ulBytesReturn = 0;
    nRet 
    = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
    &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
    if (nRet == SOCKET_ERROR)
    {
    return FALSE;
    }

    开启Keepalive选项之后,对于使用IOCP模型的服务器端程序来说,一旦检测到连接断 开,GetQueuedCompletionStatus函数将立即返回FALSE,使得服务器端能及时清除该连接、释放该连接相关的资源。对于使用 select模型的客户端来说,连接断开被探测到时,以recv目的阻塞在socket上的select方法将立即返回SOCKET_ERROR,从而得 知连接已失效,客户端程序便有机会及时执行清除工作、提醒用户或重新连接。

    TCP连接非正常断开的检测(KeepAlive探测)

    此处的”非正常断开”指TCP连接不是以优雅的方式断开,如网线故障等物理链路的原因,还有突然主机断电等原因

    有两种方法可以检测:1.TCP连接双方定时发握手消息 2.利用TCP协议栈中的KeepAlive探测

    第二种方法简单可靠,只需对TCP连接两个Socket设定KeepAlive探测。


    在windows下使用,要包含MSTcpIP.h的头文件。点击下面的链接即可下载这个文件
      MSTcpIP

    备注:长连接虽好,但是比较好用但是占用系统资源比较大。个人建议如无特殊需要,用自己的心跳包机制最好


    评论

    # re: 长连接和Keepalive 2011-08-26 17:45 
    你好, 请问一下 我现在 通过web访问 apache 然后 apache去交互另外一个server(可能是脚本).需要登录验证 然后返回DataInfo.这样apache接受到信息后 就与另一个server断开了链接。这个server有时候会返回一些必要的信息到apache,但是开始交互的TCP协议交互完就断开了。
    我先用keepalive 绑定 apache与 交互的另一个server(可能是接口)一直长连接 来实现。
    不知道这个可以实现吗?


    http://www.cppblog.com/zhangyq/archive/2010/02/28/108615.html




    Nginx设置Keep-Alive为close




    nginx不能在响应头部添加Keep-Alive,详见:http://wiki.nginx.org/HttpCoreModule#keepalive_timeout

    http1.1 中默认的keep-alive为connection(使用持久化连接),在http1.0中则默认为close,在大并发量的情况下可能需要将客户端的 连接close掉,以保障服务器的正常运转。(因为每一台服务器它所能建立的最大连接数是有上限的,lnux下ulimit n xxx)

    以腾讯首页为例,就有很多是请求是在客户端发生请求后,服务器响应完就立即关闭了。

    image

    nginx不像apache,直接有指令keep-alive off/on;它使用的是keepalive_timeout [time],默认的时长为75,可以在http、server、location使用此指令。

     

    在本机进行的模拟测试:

    nginx.conf指定的VHOST中添加了规则:

    location /gtj/ {
        alias C:/phpApp/gtj/;
        keepalive_timeout  0;
        expires 5m;
    }

    客户端请求后,可以用httpwatch抓取返回的头部信息:

    image








    转自:
    http://www.20ju.com/content/V170118.htm




  • 解决Nginx + PHP(FastCGI)遇到的502 Bad Gateway错误(转载)

    2011-11-29 17:54:04

    [文章作者:张宴 本文版本:v1.0 最后修改:2008.07.30 转载请注明原文链接:http://blog.s135.com/read.php/361/]

      昨日,有朋友问我,他将Web服务器换成Nginx 0.6.31  + PHP 4.4.7(FastCGI)后,有时候访问会出现“502 Bad Gateway”错误,如何解决。

      我让按照以下两个步骤去解决,最后在第2步中将FastCGI的timeout时间增加为300,问题解决:

      PS:比较羡慕迅雷的Web服务器,16G内存。



      1、查看当前的PHP FastCGI进程数是否够用:
    netstat -anpo | grep "php-cgi" | wc -l

      如果实际使用的“FastCGI进程数”接近预设的“FastCGI进程数”,那么,说明“FastCGI进程数”不够用,需要增大。



      2、部分PHP程序的执行时间超过了Nginx的等待时间,可以适当增加nginx.conf配置文件中FastCGI的timeout时间,例如:
    ......
    http
    {
    ......
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
    ......
    }
    ......





    acky
    2008-7-30 09:02
    不太理解:
    netstat -an | grep "php-cgi" | wc -l

    netstat -an 不是显示端口, grep php-cgi 能得到什么?
    # netstat -an | grep "php-cgi" | wc -l
    0
    张宴 回复于 2008-7-30 09:25
    不要意思,写错了,是:
    netstat -anpo | grep "php-cgi" | wc -l
    lhwmzy
    2008-7-30 09:08
    命令写错了吧
    gusingchen
    2008-7-30 09:18
    1、502是php执行时间过长造成,有问题的话加大进程数也没用.  2、web  服务器没有必要这么大的内存吧。cpu消耗多些。
    张宴 回复于 2008-7-30 09:27
    php-cgi进程数不够用、php执行时间长、或者是php-cgi进程死掉,都会出现502错误。
    wayne Email Homepage
    2008-7-30 11:06
    预先设置的FastCGI数,是不是就是php-fpm.conf里的max_children值
    willko
    2008-7-31 22:44
    方便面没打开也会出现这个错误。。。
    JulyClyde
    2008-8-1 11:49
    504 Gateway Timeout才需要增加等待时间吧?
    502是Bad Gateway,是sock、端口没被监听造成的
    ww
    2008-8-1 20:06
    老师有2个问题。。
    1.按照你的方法配置,,但是修改php.in 的display_errors = on。。用phpinfo()看还是off的。。。已经重启并且没改错php.ini文件
    2.请问下nginx怎么才能支持index.php/id/2这样的路径呢?
    我发现只支持index.php?/id/2

    谢谢
    blankyao Email Homepage
    2008-8-3 23:44
    刚注册,我试下头像是否显示正常
    Dewei Email
    2008-8-10 17:07
    large_client_header_buffers 4 32k;
    请问上次我问的时候,你回答的这样的,要不要再加了?
    张宴 回复于 2008-8-11 10:21
    如果报的是400错误,就增大header_buffers值为32k(默认为4k)
    large_client_header_buffers 4 32k;
    wayne
    2008-8-14 16:46
    nginx里的错误日志是不是不记录 502错误的,我没有发现呀
    dodoup
    2008-8-15 17:19
    10309 daemon    15   0 7570m 2.7g  812 S    0  0.6   0:02.63 nginx                                                                  
    10310 daemon    15   0 7552m 2.7g  812 S    0  0.6   0:01.26 nginx                                                                  
    10313 daemon    15   0 75464  25m  812 S    0  0.6   0:01.23 nginx                                                                  
    10318 daemon    15   0 7543m 2.7g  812 S    0  0.6   0:01.31 nginx  

    有谁遇到过这样的问题米?
    wayne
    2008-8-20 22:37
    兄弟,帮我看一下,nginx的日志,出现好多400的错误,这样是什么原因呢?

    220.255.7.194 - - [20/Aug/2008:22:38:30 +0800] "-" 400 0 "-" "-" -
    60.190.0.78 - - [20/Aug/2008:22:38:31 +0800] "-" 400 0 "-" "-" -
    220.202.4.12 - - [20/Aug/2008:22:38:31 +0800] "-" 400 0 "-" "-" -
    220.202.4.12 - - [20/Aug/2008:22:38:31 +0800] "-" 400 0 "-" "-" -
    58.220.236.250 - - [20/Aug/2008:22:38:33 +0800] "-" 400 0 "-" "-" -
    117.40.118.93 - - [20/Aug/2008:22:38:33 +0800] "-" 400 0 "-" "-" -
    222.242.65.94 - - [20/Aug/2008:22:38:34 +0800] "-" 400 0 "-" "-" -
    58.220.236.250 - - [20/Aug/2008:22:38:34 +0800] "-" 400 0 "-" "-" -
    219.152.214.89 - - [20/Aug/2008:22:38:34 +0800] "-" 400 0 "-" "-" -
    58.33.12.209 - - [20/Aug/2008:22:38:35 +0800] "-" 400 0 "-" "-" -
    60.50.171.133 - - [20/Aug/2008:22:38:36 +0800] "-" 400 0 "-" "-" -
    jspshiwo
    2008-8-27 23:15
    我刚新安装的 ngnix 就提示 502 Bad Gateway 错误 请问为什么啊 ?

     我用 netstat -anpo | grep "php-cgi" | wc -l  查看你 显示 1
    uddtm
    2008-9-23 19:46
    还是不行呀,我的/usr/local/webserver/php/bin/里边就没有php-cgi这个文件呀,我这样该怎样启动php-cgi进程

    安装过程都正常,启动nginx后显示的也是502 Bad Gateway .
    weiwei Email Homepage
    2008-10-24 17:04
    502/504错误,改那个不顶用,而且PHP-CGI经常大片小时,网站很卡,一会又好了,感觉
    php跑PHP-CGI很不稳定,快的时候真的很快,但慢起来就是 502、504

    http://bbs.woyool.com
    hanliuyj
    2008-10-31 17:39
    我这里是这样的设置的,并发请求一多,还是出现502的错误哦
       fastcgi_connect_timeout 300;
       fastcgi_send_timeout 300;
       fastcgi_read_timeout 300;
    amazergling
    2008-11-28 13:09
    我这边用跑在按张sir手顺配置的环境下,不知为何也很不稳定,即便是测试期间只有一两个节点的访问量也会莫名其妙的出现空白页,有时候多点几下又好了,再点又空白,急煞我也 :(

    现在只好改回apache暂时将就着,但对于nginx的高负载能力很是垂涎三尺,希望尽早能够构建出稳定的nginx下的php环境,即便放弃fcgi也在所不惜......:-P
    zhifang
    2008-12-22 12:19
    张老师,我想请教个问题。
    我原来的环境是:freebsd 6.3  apache-2.2.6_2  php5.2.5
    改用nginx后是:freebsd 6.3  nginx-devel-0.6.17   php5.2.5   spawn-fcgi-1.4.18

    在换的时候我先把php5   make deinstall  
    然后make config 里把[ ] APACHE     Build Apache module  模块去掉再make install
    fast-cgi以前都是有安装的,扩展也没变。
    启动/usr/local/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/local/bin/php-cgi 时超慢,2分钟左右才启动起来,restart也一样要2分钟。(能启动起来)
    但新装的系统,再安装nginx的这些环境就没有这种现象。

    请张老师或高手们指点一下,谢谢!
    zhifang
    2008-12-23 18:40
    张老师,我又碰到新问题了。
    服务器:DELL 2950   4G内存
    系统是刚装的:freebsd 7.0 amd64  nginx-devel-0.6.17   php5.2.5   spawn-fcgi-1.4.18
    /usr/local/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www -g www -f /usr/local/bin/php-cgi -C 250
    fastcgi启动正常,但到生产环境上时报错:zend_mm_heap corrupted
    而且PHP-CGI的CPU占用超高,不加-C 250或调小负载还是一样很重。
    我的配置如下:
           server {
                   listen          80;
                   server_name    www.XX.com *.XX.com;
           location / {
               root   /usr/local/www/www;
               index  index.php index.html index.htm;
           }
           location ~ \.php$ {
               fastcgi_pass   127.0.0.1:9000;
               fastcgi_index  index.php;
               fastcgi_param  SCRIPT_FILENAME  /usr/local/www$fastcgi_script_name;
               include        fastcgi_params;
            }
           }
    请指点一下!谢谢!
    cfans Email Homepage
    2008-12-25 17:04
    增加fastcgi_connect_timeout 等三个参数值的方法效果也不好
    经过长时间反复测试,发现静态页面不会出现该错误,只有在运行动态页面或者长时间操作数据库时才会出现这个错误,重启Nginx+FastCGI后即可解决,但是几分钟到几个小时后又会出现该错误。
    经过测试,发现修改php-fpm.conf文件中 request_terminate_timeout即FastCGI脚本运行时间可以有效改善该问题,增加CGI进程数也可以改善该问题,但占资源太多效率太低。
    还可以修改
    <value name=\"request_terminate_timeout\">0s</value>
    <value name=\"process_control_timeout\">5s</value>
    等值对FastCGI进行优化,所以出现502的错误其实不是nginx的问题
    php-cgi进程数不够用、php执行时间长、或者是php-cgi进程死掉,都会出现502错误




    转自:http://blog.s135.com/post/361/
  • linux和windows下TIME_WAIT过多的解决办法

    2011-11-29 17:52:47


    linux和windows下TIME_WAIT过多的解决办法 http://yuxu9710108.blog.163.com/blog/static/23751534201101244616211/

    如果使用了nginx代理,那么系统TIME_WAIT的数量会变得比较多,这是由于nginx代理使用了短链接的方式和后端交互的原因,使得 nginx和后端的ESTABLISHED变得很少而TIME_WAIT很多。这不但发生在安装nginx的代理服务器上,而且也会使后端的app服务器 上有大量的TIME_WAIT。查阅TIME_WAIT资料,发现这个状态很多也没什么大问题,但可能因为它占用了系统过多的端口,导致后续的请求无法获 取端口而造成障碍。

    虽然TIME_WAIT会造成一些问题,但是要完全枪毙掉它也是不正当的,虽然看起来这么做没什么错。具体可看这篇文档:

    http://hi.baidu.com/tim_bi/blog/item/35b005d784ca91d5a044df1d.html

    所以目前看来最好的办法是让每个TIME_WAIT早点过期。

    在linux上可以这么配置:

    #让TIME_WAIT状态可以重用,这样即使TIME_WAIT占满了所有端口,也不会拒绝新的请求造成障碍
    echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
    #让TIME_WAIT尽快回收,我也不知是多久,观察大概是一秒钟
    echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

    很多文档都会建议两个参数都配置上,但是我发现只用修改tcp_tw_recycle就可以解决问题的了,TIME_WAIT重用TCP协议本身就是不建议打开的。

    不能重用端口可能会造成系统的某些服务无法启动,比如要重启一个系统监控的软件,它用了40000端口,而这个端口在软件重启过程中刚好被使用了,就可能会重启失败的。linux默认考虑到了这个问题,有这么个设定:

    #查看系统本地可用端口极限值
    cat /proc/sys/net/ipv4/ip_local_port_range

    用这条命令会返回两个数字,默认是:32768 61000,说明这台机器本地能向外连接61000-32768=28232个连接,注意是本地向外连接,不是这台机器的所有连接,不会影响这台机器的 80端口的对外连接数。但这个数字会影响到代理服务器(nginx)对app服务器的最大连接数,因为nginx对app是用的异步传输,所以这个环节的 连接速度很快,所以堆积的连接就很少。假如nginx对app服务器之间的带宽出了问题或是app服务器有问题,那么可能使连接堆积起来,这时可以通过设 定nginx的代理超时时间,来使连接尽快释放掉,一般来说极少能用到28232个连接。

    因为有软件使用了40000端口监听,常常出错的话,可以通过设定ip_local_port_range的最小值来解决:

    echo "40001 61000" > /proc/sys/net/ipv4/ip_local_port_range

    但是这么做很显然把系统可用端口数减少了,这时可以把ip_local_port_range的最大值往上调,但是好习惯是使用不超过32768的端口来侦听服务,另外也不必要去修改ip_local_port_range数值成1024 65535之类的,意义不大。

    因为使用了nginx代理,在windows下也会造成大量TIME_WAIT,当然windows也可以调整:

    在注册表(regedit)的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,值就是秒数,即可。

    windows默认是重用TIME_WAIT,我现在还不知道怎么改成不重用的,本地端口也没查到是什么值,但这些都关系不大,都可以按系统默认运作。

    ------------------------------------------------------------------------------------------------------------------------

    TIME_WAIT状态

    根据TCP协议,主动发起关闭的一方,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime),缺省为240秒,在这个post中简洁的介绍了为什么需要这个状态。

    值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知, 对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。

    HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个request/response,一个主要 原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少,因为240秒的时间实在是忒长了点,对于 Windows,修改注册表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。

    对于大型的服务,一台server搞不定,需要一个LB(Load Balancer)把流量分配到若干后端服务器上,如果这个LB是以NAT方式工作的话,可能会带来问题。假如所有从LB到后端Server的IP包的 source address都是一样的(LB的对内地址),那么LB到后端Server的TCP连接会受限制,因为频繁的TCP连接建立和关闭,会在server上留 下TIME_WAIT状态,而且这些状态对应的remote address都是LB的,LB的source port撑死也就60000多个(2^16=65536,1~1023是保留端口,还有一些其他端口缺省也不会用),每个LB上的端口一旦进入 Server的TIME_WAIT黑名单,就有240秒不能再用来建立和Server的连接,这样LB和Server最多也就能支持300个左右的连接。 如果没有LB,不会有这个问题,因为这样server看到的remote address是internet上广阔无垠的集合,对每个address,60000多个port实在是够用了。

    一开始我觉得用上LB会很大程度上限制TCP的连接数,但是实验表明没这回事,LB后面的一台Windows Server 2003每秒处理请求数照样达到了600个,难道TIME_WAIT状态没起作用?用Net Monitor和netstat观察后发现,Server和LB的XXXX端口之间的连接进入TIME_WAIT状态后,再来一个LB的XXXX端口的 SYN包,Server照样接收处理了,而是想像的那样被drop掉了。翻书,从书堆里面找出覆满尘土的大学时代买的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中间提到一句,对于BSD-derived实现,只要SYN的sequence number比上一次关闭时的最大sequence number还要大,那么TIME_WAIT状态一样接受这个SYN,难不成Windows也算BSD-derived?有了这点线索和关键字 (BSD),找到这个post,在NT4.0的时候,还是和BSD-derived不一样的,不过Windows Server 2003已经是NT5.2了,也许有点差别了。

    做个试验,用Socket API编一个Client端,每次都Bind到本地一个端口比如2345,重复的建立TCP连接往一个Server发送Keep-Alive=false 的HTTP请求,Windows的实现让sequence number不断的增长,所以虽然Server对于Client的2345端口连接保持TIME_WAIT状态,但是总是能够接受新的请求,不会拒绝。那 如果SYN的Sequence Number变小会怎么样呢?同样用Socket API,不过这次用Raw IP,发送一个小sequence number的SYN包过去,Net Monitor里面看到,这个SYN被Server接收后如泥牛如海,一点反应没有,被drop掉了。

    按照书上的说法,BSD-derived和Windows Server 2003的做法有安全隐患,不过至少这样至少不会出现TIME_WAIT阻止TCP请求的问题,当然,客户端要配合,保证不同TCP连接的sequence number要上涨不要下降。

    ----------------------------------------------------------------------------------------------------------------------------

    Socket中的TIME_WAIT状态

    在高并发短连接的server端,当server处理完client的请求后立刻closesocket此时会出现 time_wait状态然后如果client再并发2000个连接,此时部分连接就连接不上了,用linger强制关闭可以解决此问题,但是linger 会导致数据丢失,linger值为0时是强制关闭,无论并发多少多能正常连接上,如果非0会发生部分连接不上的情况!(可调用setsockopt设置套接字的linger延时标志,同时将延时时间设置为0。
    TCP/IP的RFC文档。TIME_WAIT是TCP连接断开时必定会出现的状态。
    是无法避免掉的,这是TCP协议实现的一部分。
    在WINDOWS下,可以修改注册表让这个时间变短一些

    time_wait的时间为2msl,默认为4min.
    你可以通过改变这个变量:
    TcpTimedWaitDelay
    把它缩短到30s
    TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭一个socket时,主动关闭一端的socket将进入 TIME_WAIT状态,而被动关闭一方则转入CLOSED状态,这的确能够保证所有的数据都被传输。当一个socket关闭的时候,是通过两端互发信息 的四次握手过程完成的,当一端调用close()时,就说明本端没有数据再要发送了。这好似看来在握手完成以后,socket就都应该处于关闭 CLOSED状态了。但这有两个问题,首先,我们没有任何机制保证最后的一个ACK能够正常传输,第二,网络上仍然有可能有残余的数据包 (wandering duplicates),我们也必须能够正常处理。
    通过正确的状态机,我们知道双方的关闭过程如下
    linux和windows下TIME_WAIT过多的解决办法 - 笨笨猫 - 我的博客

    假设最后一个ACK丢失了,服务器会重发它发送的最后一个FIN,所以客户端必须维持一个状态信息,以便能够重发ACK;如果不维持这种状态,客户 端在接收到FIN后将会响应一个RST,服务器端接收到RST后会认为这是一个错误。如果TCP协议能够正常完成必要的操作而终止双方的数据流传输,就必 须完全正确的传输四次握手的四个节,不能有任何的丢失。这就是为什么socket在关闭后,仍然处于 TIME_WAIT状态,因为他要等待以便重发ACK。

    如果目前连接的通信双方都已经调用了close(),假定双方都到达CLOSED状态,而没有TIME_WAIT状态时,就会出现如下的情况。现在 有一个新的连接被建立起来,使用的IP地址与端口与先前的完全相同,后建立的连接又称作是原先连接的一个化身。还假定原先的连接中有数据报残存于网络之 中,这样新的连接收到的数据报中有可能是先前连接的数据报。为了防止这一点,TCP不允许从处于TIME_WAIT状态的socket建立一个连接。处于 TIME_WAIT状态的socket在等待两倍的MSL时间以后(之所以是两倍的MSL,是由于MSL是一个数据报在网络中单向发出到认定丢失的时间, 一个数据报有可能在发送图中或是其响应过程中成为残余数据报,确认一个数据报及其响应的丢弃的需要两倍的MSL),将会转变为CLOSED状态。这就意味 着,一个成功建立的连接,必然使得先前网络中残余的数据报都丢失了。

    由于TIME_WAIT状态所带来的相关问题,我们可以通过设置SO_LINGER标志来避免socket进入TIME_WAIT状态,这可以通过发送RST而取代正常的TCP四次握手的终止方式。但这并不是一个很好的主意,TIME_WAIT对于我们来说往往是有利的。

    客户端与服务器端建立TCP/IP连接后关闭SOCKET后,服务器端连接的端口
    状态为TIME_WAIT
    是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?
    有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?
    主动关闭的一方在发送最后一个 ack 后
    就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间
    这个是TCP/IP必不可少的,也就是“解决”不了的。

    也就是TCP/IP设计者本来是这么设计的
    主要有两个原因
    1。防止上一次连接中的包,迷路后重新出现,影响新连接
       (经过2MSL,上一次连接中所有的重复包都会消失)
    2。可靠的关闭TCP连接
       在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发
       fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以
       主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

    TIME_WAIT 并不会占用很大资源的,除非受到攻击。

    还有,如果一方 send 或 recv 超时,就会直接进入 CLOSED 状态

    socket-faq中的这一段讲的也很好,摘录如下:
    2.7. Please explain the TIME_WAIT state.

    Remember that TCP guarantees all data transmitted will be delivered,
    if at all possible. When you close a socket, the server goes into a
    TIME_WAIT state, just to be really really sure that all the data has
    gone through. When a socket is closed, both sides agree by sending
    messages to each other that they will send no more data. This, it
    seemed to me was good enough, and after the handshaking is done, the
    socket should be closed. The problem is two-fold. First, there is no
    way to be sure that the last ack was communicated successfully.
    Second, there may be "wandering duplicates" left on the net that must
    be dealt with if they are delivered.

    Andrew Gierth (andrew@erlenstar.demon.co.uk) helped to explain the
    closing sequence in the following usenet posting:

    Assume that a connection is in ESTABLISHED state, and the client is
    about to do an orderly release. The client's sequence no. is Sc, and
    the server's is Ss. Client Server
    ====== ======
    ESTABLISHED ESTABLISHED
    (client closes)
    ESTABLISHED ESTABLISHED
    ------->>
    FIN_WAIT_1
    <<--------
    FIN_WAIT_2 CLOSE_WAIT
    <<-------- (server closes)
    LAST_ACK
    , ------->>
    TIME_WAIT CLOSED
    (2*msl elapses...)
    CLOSED

    Note: the +1 on the sequence numbers is because the FIN counts as one
    byte of data. (The above diagram is equivalent to fig. 13 from RFC
    793).

    Now consider what happens if the last of those packets is dropped in
    the network. The client has done with the connection; it has no more
    data or control info to send, and never will have. But the server does
    not know whether the client received all the data correctly; that's
    what the last ACK segment is for. Now the server may or may not care
    whether the client got the data, but that is not an issue for TCP; TCP
    is a reliable rotocol, and must distinguish between an orderly
    connection close where all data is transferred, and a connection abort
    where data may or may not have been lost.

    So, if that last packet is dropped, the server will retransmit it (it
    is, after all, an unacknowledged segment) and will expect to see a
    suitable ACK segment in reply. If the client went straight to CLOSED,
    the only possible response to that retransmit would be a RST, which
    would indicate to the server that data had been lost, when in fact it
    had not been.

    (Bear in mind that the server's FIN segment may, additionally, contain
    data.)

    DISCLAIMER: This is my interpretation of the RFCs (I have read all the
    TCP-related ones I could find), but I have not attempted to examine
    implementation source code or trace actual connections in order to
    verify it. I am satisfied that the logic is correct, though.

    More commentarty from Vic:

    The second issue was addressed by Richard Stevens (rstevens@noao.edu,
    author of "Unix Network Programming", see ``1.5 Where can I get source
    code for the book [book title]?''). I have put together quotes from
    some of his postings and email which explain this. I have brought
    together paragraphs from different postings, and have made as few
    changes as possible.

    From Richard Stevens (rstevens@noao.edu):

    If the duration of the TIME_WAIT state were just to handle TCP's full-
    duplex close, then the time would be much smaller, and it would be
    some function of the current RTO (retransmission timeout), not the MSL
    (the packet lifetime).

    A couple of points about the TIME_WAIT state.

    o The end that sends the first FIN goes into the TIME_WAIT state,
    because that is the end that sends the final ACK. If the other
    end's FIN is lost, or if the final ACK is lost, having the end that
    sends the first FIN maintain state about the connection guarantees
    that it has enough information to retransmit the final ACK.

    o Realize that TCP sequence numbers wrap around after 2**32 bytes
    have been transferred. Assume a connection between A.1500 (host A,
    port 1500) and B.2000. During the connection one segment is lost
    and retransmitted. But the segment is not really lost, it is held
    by some intermediate router and then re-injected into the network.
    (This is called a "wandering duplicate".) But in the time between
    the packet being lost & retransmitted, and then reappearing, the
    connection is closed (without any problems) and then another
    connection is established between the same host, same port (that
    is, A.1500 and B.2000; this is called another "incarnation" of the
    connection). But the sequence numbers chosen for the new
    incarnation just happen to overlap with the sequence number of the
    wandering duplicate that is about to reappear. (This is indeed
    possible, given the way sequence numbers are chosen for TCP
    connections.) Bingo, you are about to deliver the data from the
    wandering duplicate (the previous incarnation of the connection) to
    the new incarnation of the connection. To avoid this, you do not
    allow the same incarnation of the connection to be reestablished
    until the TIME_WAIT state terminates.

    Even the TIME_WAIT state doesn't complete solve the second problem,
    given what is called TIME_WAIT assassination. RFC 1337 has more
    details.

    o The reason that the duration of the TIME_WAIT state is 2*MSL is
    that the maximum amount of time a packet can wander around a
    network is assumed to be MSL seconds. The factor of 2 is for the
    round-trip. The recommended value for MSL is 120 seconds, but
    Berkeley-derived implementations normally use 30 seconds instead.
    This means a TIME_WAIT delay between 1 and 4 minutes. Solaris 2.x
    does indeed use the recommended MSL of 120 seconds.

    A wandering duplicate is a packet that appeared to be lost and was
    retransmitted. But it wasn't really lost ... some router had
    problems, held on to the packet for a while (order of seconds, could
    be a minute if the TTL is large enough) and then re-injects the packet
    back into the network. But by the time it reappears, the application
    that sent it originally has already retransmitted the data contained
    in that packet.

    Because of these potential problems with TIME_WAIT assassinations, one
    should not avoid the TIME_WAIT state by setting the SO_LINGER option
    to send an RST instead of the normal TCP connection termination
    (FIN/ACK/FIN/ACK). The TIME_WAIT state is there for a reason; it's
    your friend and it's there to help you :-)

    I have a long discussion of just this topic in my just-released
    "TCP/IP Illustrated, Volume 3". The TIME_WAIT state is indeed, one of
    the most misunderstood features of TCP.

    I'm currently rewriting "Unix Network Programming" (see ``1.5 Where
    can I get source code for the book [book title]?''). and will include
    lots more on this topic, as it is often confusing and misunderstood.

    An additional note from Andrew:

    Closing a socket: if SO_LINGER has not been called on a socket, then
    close() is not supposed to discard data. This is true on SVR4.2 (and,
    apparently, on all non-SVR4 systems) but apparently not on SVR4; the
    use of either shutdown() or SO_LINGER seems to be required to
    guarantee delivery of all data.

    -------------------------------------------------------------------------------------------

    讨厌的 Socket TIME_WAIT 问题

    netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'

    会得到类似下面的结果,具体数字会有所不同:

    LAST_ACK 1
    SYN_RECV 14
    ESTABLISHED 79
    FIN_WAIT1 28
    FIN_WAIT2 3
    CLOSING 5
    TIME_WAIT 1669

    状态:描述
    CLOSED:无连接是活动的或正在进行
    LISTEN:服务器在等待进入呼叫
    SYN_RECV:一个连接请求已经到达,等待确认
    SYN_SENT:应用已经开始,打开一个连接
    ESTABLISHED:正常数据传输状态
    FIN_WAIT1:应用说它已经完成
    FIN_WAIT2:另一边已同意释放
    ITMED_WAIT:等待所有分组死掉
    CLOSING:两边同时尝试关闭
    TIME_WAIT:另一边已初始化一个释放
    LAST_ACK:等待所有分组死掉

    也就是说,这条命令可以把当前系统的网络连接状态分类汇总。

    下面解释一下为啥要这样写:

    一个简单的管道符连接了netstat和awk命令。

    ------------------------------------------------------------------

    每个TCP报文在网络内的最长时间,就称为MSL(Maximum Segment Lifetime),它的作用和IP数据包的TTL类似。

    RFC793指出,MSL的值是2分钟,但是在实际的实现中,常用的值有以下三种:30秒,1分钟,2分钟。

    注意一个问题,进入TIME_WAIT状态的一般情况下是客户端,大多数服务器端一般执行被动关闭,不会进入TIME_WAIT状态,当在服务

    器端关闭某个服务再重新启动时,它是会进入TIME_WAIT状态的。

    举例:
    1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的

    80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。
    2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连

    接还处于TIME_WAIT状态。


    检查net.ipv4.tcp_tw当前值,将当前的值更改为1分钟:
    [root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
    net.ipv4.tcp_tw_reuse = 0
    net.ipv4.tcp_tw_recycle = 0
    [root@aaa1 ~]#

    vi /etc/sysctl
    增加或修改net.ipv4.tcp_tw值:
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1

    使内核参数生效:
    [root@aaa1 ~]# sysctl -p

    [root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1

    用netstat再观察正常


    这里解决问题的关键是如何能够重复利用time_wait的值,我们可以设置时检查一下time和wait的值
    #sysctl -a | grep time | grep wait
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
    net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120

    问一下TIME_WAIT有什么问题,是闲置而且内存不回收吗?

    是的,这样的现象实际是正常的,有时和访问量大有关,设置这两个参数: reuse是表示是否允许重新应用处于TIME-WAIT状态的

    socket用于新的TCP连接; recyse是加速TIME-WAIT sockets回收

    Q: 我正在写一个unix server程序,不是daemon,经常需要在命令行上重启它,绝大
    多数时候工作正常,但是某些时候会报告"bind: address in use",于是重启失
    败。

    A: Andrew Gierth
    server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。至于
    TIME_WAIT状态,你无法避免,那是TCP协议的一部分。

    Q: 如何避免等待60秒之后才能重启服务

    A: Erik Max Francis

    使用setsockopt,比如

    --------------------------------------------------------------------------
    int ption = 1;

    if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option,
    sizeof( option ) ) < 0 )
    {
    die( "setsockopt" );
    }
    --------------------------------------------------------------------------

    Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?

    A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用
    端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,
    指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧
    使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期
    望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不
    可能。

    一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端
    口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组
    还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使
    用 SO_REUSEADDR 选项。

    Q: 在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解TCP自动机 TIME_WAIT 状
    态?

    A: W. Richard Stevens <1999年逝世,享年49岁>

    下面我来解释一下 TIME_WAIT 状态,这些在<>
    中2.6节解释很清楚了。

    MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现
    都必须选择一个确定的MSL值。RFC 1122建议是2分钟,但BSD传统实现采用了30秒。

    TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟。

    IP头部有一个TTL,最大值255。尽管TTL的单位不是秒(根本和时间无关),我们仍需
    假设,TTL为255的TCP报文在Internet上生存时间不能超过MSL。

    TCP报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果
    发送方TCP机制开始超时重传。前一个TCP报文可以称为"漫游TCP重复报文",后一个
    TCP报文可以称为"超时重传TCP重复报文",作为面向连接的可靠协议,TCP实现必须
    正确处理这种重复报文,因为二者可能最终都到达。

    一个通常的TCP连接终止可以用图描述如下:

    client server
    FIN M
    close -----------------> (被动关闭)
    ACK M+1
    <-----------------
    FIN N
    <----------------- close
    ACK N+1
    ----------------->

    为什么需要 TIME_WAIT 状态?

    假设最终的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发
    最终的ACK,否则会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连
    接的两个方向(全双工关闭),client必须进入 TIME_WAIT 状态,因为client可能面
    临重发最终ACK的情形。

    {
    scz 2001-08-31 13:28

    先调用close()的一方会进入TIME_WAIT状态
    }

    此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连
    接处在 TIME_WAIT 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理
    TCP报文时,两个连接互相干扰。使用 SO_REUSEADDR 选项就需要考虑这种情况。

    为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间?

    如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。
    第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二
    个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT
    状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被
    丢弃。建立第二个连接的时候,不会混淆。

    A: 小四

    在Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间

    # ndd -get /dev/tcp tcp_time_wait_interval
    240000
    # ndd -set /dev/tcp tcp_time_wait_interval 1000

    缺省设置是240000ms,也就是4分钟。如果用ndd修改这个值,最小只能设置到1000ms,
    也就是1秒。显然内核做了限制,需要Kernel Hacking。

    # echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem
    physmem 3b72
    tcp_param_arr: 0x3e8 = 0x0
    # ndd -set /dev/tcp tcp_time_wait_interval 0

    我不知道这样做有什么灾难性后果,参看<>的声明。

    Q: TIME_WAIT 状态保持时间为0会有什么灾难性后果?在普遍的现实应用中,好象也
    就是服务器不稳定点,不见得有什么灾难性后果吧?

    D: rain@bbs.whnet.edu.cn

    Linux 内核源码 /usr/src/linux/include/net/tcp.h 中

    #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
    * close the socket, about 60 seconds */

    最好不要改为0,改成1。端口分配是从上一次分配的端口号+1开始分配的,所以一般
    不会有什么问题。端口分配算法在tcp_ipv4.c中tcp_v4_get_port中。


    转自:http://blog.csdn.net/ajian005/article/details/6724814



  • 有关nginx的worker_processes参数

    2011-11-23 15:23:58

    有关nginx的worker_processes参数


    分享一:

    搜索到原作者的话:

    As a general rule you need the only worker with large number of
    worker_connections, say 10,000 or 20,000.

    However, if nginx does CPU-intensive work as SSL or gzipping and
    you have 2 or more CPU, then you may set worker_processes to be equal
    to CPU number.

    Besides, if you serve many static files and the total size of the files
    is bigger than memory, then you may increase worker_processes to
    utilize a full disk bandwidth.

    Igor Sysoev

    一般一个进程足够了,你可以把连接数设得很大。如果有SSL、gzip这些比较消耗CPU的工作,而且是多核CPU的话,可以设为和CPU的数量一样。或 者要处理很多很多的小文件,而且文件总大小比内存大很多的时候,也可以把进程数增加,以充分利用IO带宽(主要似乎是IO操作有block)。

    根据我配置实践,服务器是“多个CPU+gzip+网站总文件大小大于内存”的环境,worker_processes设置为CPU个数的两倍比较好。



    分享二:

    最近PPC经常出现502错误,网页经常无法打开,所以本人决定对Nginx进行深入折腾!

    Nginx本身没有挂掉,否则不会出现502的错误信息,所以原因一定在Nginx的设置上。

    经过我查阅资料和测试,发现有可能是worker_processes的参数设置不当引起的。

    worker_processes默认情况下为1,一般情况下不用修改,但考虑到实际情况,可以修改这个数值,以提高性能,官方的建议是修改成CPU的内核数,这里引用一段翻译过的文章: 
    worker_processes指明了nginx要开启的进程数,据官方说法,一般开一个就够了,多开几个,可以减少机器io带来的影响。据实践表明,nginx的这个参数在一般情况下开4个或8个就可以了,再往上开的话优化不太大。据另一种说法是,nginx开启太多的进程,会影响主进程调度,所以占用的cpu会增高。
    经过我测试发现,这个数字是不能乱设置的,如果网站没有出现io性能问题,最好不要修改,采用默认的1即可,如果非要设置,必须要和CPU的内核数匹配,否则要么就假死(主要是Windows),要么就出现502的错误(主要是Linux)。

    我的电脑是双核的,按理说应该是2,但是实际上应该是4,因为是双线程的。测试结果如下: 
    1、worker_processes为1,线程打开2个,有一个是主线程,运行很稳定。
    2、worker_processes为2,线程打开3个,有一个是主线程,1分钟左右挂掉(假死,无法打开网页,浏览器一直处于载入中)。
    3、worker_processes为4,线程打开5个,有一个是主线程,运行很稳定。

    4、worker_processes为8,线程打开9个,有一个是主线程,和2一样,1分钟左右挂掉。


    配置参考

    配置1:4 CPU (4 Core) + 4 worker_processes (每个worker_processes 使用1个CPU)

    [reistlin@reistlin.com ~]$ cat /proc/cpuinfo | grep processor
    processor       : 0
    processor       : 1
    processor       : 2
    processor       : 3
    worker_processes 4;
    worker_cpu_affinity 0001 0010 0100 1000;

    配置2:8 CPU (8 Core) + 8 worker_processes (每个worker_processes 使用1个CPU)

    [reistlin@reistlin.com ~]$ cat /proc/cpuinfo | grep processor
    processor       : 0
    processor       : 1
    processor       : 2
    processor       : 3
    processor       : 4
    processor       : 5
    processor       : 6
    processor       : 7
    worker_processes 8;
    worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

    配置3:16 CPU (16 Core) + 16 worker_processes (每个worker_processes 使用1个CPU)

    [reistlin@reistlin.com ~]$ cat /proc/cpuinfo | grep processor
    processor       : 0
    processor       : 1
    processor       : 2
    processor       : 3
    processor       : 4
    processor       : 5
    processor       : 6
    processor       : 7
    processor       : 8
    processor       : 9
    processor       : 10
    processor       : 11
    processor       : 12
    processor       : 13
    processor       : 14
    processor       : 15
    worker_processes 16;
    worker_cpu_affinity 0000000000000001 0000000000000010 0000000000000100 0000000000001000 0000000000010000 0000000000100000 0000000001000000 0000000010000000 0000000100000000 0000001000000000 0000010000000000 0000100000000000 0001000000000000 0010000000000000 0100000000000000 1000000000000000;

    标签: linuxnginx

    Nginx worker_cpu_affinity 设置

    根据 Nginx Wiki 上的资料显示:

    worker_cpu_affinity

    Syntax: worker_cpu_affinity cpumask [cpumask...]

    Default: none

    Linux only.

    With this option you can bind the worker process to a CPU, it calls sched_setaffinity().

    For example,

    worker_processes     4;
    worker_cpu_affinity 0001 0010 0100 1000;

    Bind each worker process to one CPU only.

    worker_processes     2;
    worker_cpu_affinity 0101 1010;

    Bind the first worker to CPU0/CPU2, bind the second worker to CPU1/CPU3. This is suitable for HTT.

    worker_cpu_affinity 默认是没有开启的,根据例子我们可以看得出,0001 0010 0100 1000 分别代表第1、2、3、4个逻辑CPU,所以我们可以设置0010 0100 1000来将3个进程分别绑定到第2、3、4个逻辑CPU上:
    worker_processes 3;
    worker_cpu_affinity 0010 0100 1000;

    同时根据例子我们也可以看出,worker_cpu_affinity 可以将同1个进程绑定在2个逻辑CPU上:
    worker_processes 2;
    worker_cpu_affinity 0101 1010;

    0101也就是第1、3个逻辑CPU上,1010就是第2、4个逻辑CPU上。 

     

     

    分享三:

    worker_processes指明了nginx要开启的进程数,据官方说法,一般开一个就够了,多开几个,可以减少机器io带来的影响。


    据实践表明,nginx的这个参数在一般情况下开4个或8个就可以了,再往上开的话优化不太大。据另一种说法是,nginx开启太多的进程,会影响主进程调度,所以占用的cpu会增高,这个说法我个人没有证实,估计他们是开了一两百个进程来对比的吧。

    worker_processes配置的一些注意事项:

    1、worker_cpu_affinity配置最好是能写上

    我这里服务器多数是双核超线程,相当于4cpu,我一般开8进程,所以这个配置就是这样:

    worker_cpu_affinity 0001 0100 1000 0010 0001 0100 1000 0010;

    另,worker_cpu_affinity不是什么时候都能用的,我没有认真研究并罗列所有情况,只知道2.4内核的机器用不了,如果用不了的话,那么最好是加大worker_processes进程数,这样分配cpu就会平均一点啦,如果不平均只好多重启几下。

    2、worker_rlimit_nofile配置要和系统的单进程打开文件数一致,千万不要再画蛇添足地除以worker_processes。

    我现在在linux 2.6内核下开启文件打开数为65535,worker_rlimit_nofile就相应应该填写65535。

    这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。



    转自:
    http://note.sdo.com/u/1188867330/n/6TaQ2~jwaIr0LX00c000-r

    来源: http://bbs.linuxtone.org/thread-1062-1-1.html

  • nginx1.0安装配置详解

    2011-11-23 15:19:38

    nginx1.0安装配置详解

    本文转自:twenty_four的博文,自己稍作精简修改。
    http://twentyfour.blog.51cto.com/945260/568906


    1、选择Nginx的理由
    1.1 支持高并发连接
    通过top命令可以查看系统负载和CPU使用率
    由于apache采用的是select网络I/O模型,处理大量连续的读写很低效
    1.2 内存消耗少
    在服务器3W并发连接下,开启10个Nginx进程消耗150MB内存(15MB*10),开启64个php-cgi进程消耗128MB内存(20MB*64),共消耗不到2GB内存,可以使用webbench做压力测试,运行依旧很快。
    1.3 成本低廉
    购买F5 BIG-IP、NetScaler等负载均衡交换机需要几十万RMB,而开源Nginx替代这些商业设备。
    1.4 其他理由
    网络配置简单,对比apache就知道了
    支持rewrite重写规则,能够根据域名、URL的不同、将HTTP请求分到不同的后端服务器群组
    内置的健康检查功能
    节省带宽,支持GZIP压缩,可以添加浏览器本地缓存的Header头
    支持热部署,能够在不间断服务的情况下、对软件版本进行升级

    结论:Nginx在反向代理、rewrite规则、稳定性、静态文件处理、内存消耗等方面都表现出很强的优势。

    二、Nginx安装与启用
    Nginx服务器的安装与配置
    http://www.nginx.net/
    最新稳定版nginx-1.0.0

    首先linux系统得安装有GCC编译器,再者还有autoconf和automake工具用于自动创建功能完善的makefile,因为Nginx也是用这一工具生成makefile的。
    用yum命令安装GCC和autoconf和automake工具:
    yum install gcc gcc-c++ autoconf automake -y

    Nginx的一些模块需要其他第三方库的支持,例如gzip模块需要zlib库、rewrite模块需要pcre库 、ssl功能需要openssl库等。同样可以使用Yum安装这些模块依赖的库:
    yum install zlib zlib-devel openssl openssl-devel pcre pcre-devel
    (当然最好使用最新版本源码包安装,像系统ISO文件里头自带的pcre库就相对旧些)

    1、安装Nginx
    1.1 安装Nginx所需要的pcre库、才得以支持rewrite
      tar zxvf pcre-8.02.tar.gz
      cd pcre-8.02
      ./configure
      make && make install
    1.2 安装Nginx
      tar zxvf nginx-1.0.0.tar.gz
      cd nginx-1.0.0
      ./configure --prefix=/usr/local/nginx --with-http_ssl_module
      make && make install
    --->
    Configuration summary
      + using system PCRE library
      + using system OpenSSL library
      + md5: using OpenSSL library
      + using system zlib library


    2、启动Nginx
    # /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    查看进程:
    # ps -ef |grep  nginx
    root     11659     1  0 14:13 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    nobody   11660 11659  0 14:13 ?        00:00:00 nginx: worker process
    查看端口号:            
    # netstat -antup |grep nginx
    tcp     0    0 0.0.0.0:80      0.0.0.0:*    LISTEN      11659/nginx.conf
    从上面可以看到Nginx启用一个主进程和一个子进程,我们可以修改nginx.conf里头的
    worker_processes使得运行多个子进程。

    3、停止Nginx
    当Nginx服务启动起来后,就会在原本空目录/usr/local/nginx/logs/下自动生成3个文件:
    access.log  error.log  nginx.pid
    这里包含nginx的主进程号:nginx.pid ,我们可以通过如上的ps命令查看、也可以直接cat出来
    3.1 从容停止Nginx
    kill -QUIT Nginx主进程号
    # kill -QUIT `cat /usr/local/nginx/logs/nginx.pid`
    3.2 快速停止Nginx
    kill -TERM Nginx主进程号
    # kill -TERM `cat /usr/local/nginx/logs/nginx.pid`

    4、重启Nginx
    如果改变了Nginx的主配置文件,想重启Nginx,可以先检查Nginx的配置文件是否正确后才发送系统信号给Nginx主进程的方式进行,检查方法如下:
    # /usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
    nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
    nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
    只有出现如上OK信息才会加载新的配置文件。
    4.1 平滑重启Nginx
    kill -HUP Nginx主进程号
    # kill -HUP `cat /usr/local/nginx/logs/nginx.pid`
    如果Nginx本身都还没启动,平滑重启可是会报错的。

    5、Nginx的信号控制
    TERM,INT  快速关闭
    QUIT       从容关闭
    HUP        平滑重启
    USR1       重新打开日志文件、在切割日志时用途大
    USR2       平滑升级可执行程序
    WINCH      从容关闭工作进程

    三、Nginx的基本配置与优化

    首先看Nginx的主配置文件,可以发现原本的配置文件非常简练

    # grep -v "#" nginx.conf  worker_processes  1;  events {      worker_connections  1024;  }  http {      include       mime.types;      default_type  application/octet-stream;      sendfile        on;      keepalive_timeout  65;      server {          listen       80;          server_name  localhost;          location / {              root   html;              index  index.html index.htm;          }          error_page   500 502 503 504  /50x.html;          location = /50x.html {              root   html;          }      }  }
    1、配置虚拟主机
    1.1、配置基于IP的虚拟主机
    首先添加虚拟网卡:

      ifconfig eth0:1 110.65.99.209 broadcast 110.65.99.255 netmask 255.255.252.0 up
      route add -host 110.65.99.209 dev eth0:1
      ifconfig eth0:2 110.65.99.210 broadcast 110.65.99.255 netmask 255.255.252.0 up
      route add -host 110.65.99.210 dev eth0:2
    如上命令最好写进/etc/rc.local中使得系统开机自动创建。
    或者直接cp ifcfg-eth0 ifcfg-eth0:1

    修改Nginx的主配置文件: /usr/local/nginx/conf/nginx.conf
    在原本定义的一个server之后添加如下两个虚拟主机


    ###############server2################################          server {          listen  110.65.99.209:80;          server_name     server2;          access_log      logs/server2.access.log combined;          location / {          index   index.html index.htm;          root    /usr/local/nginx/server2;                    }          }  ###############server3################################          server {          listen  110.65.99.210:80;          server_name     server3;          access_log      logs/server3.access.log combined;          location / {          index   index.html index.htm;          root    /usr/local/nginx/server3;                    }          }
    然后创建目录server2和server3,并且在各自目录下创建index.html测试文件
    检查配置文件是否写正确,然后才平滑重启Nginx
    # /usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
    # kill -HUP `cat /usr/local/nginx/logs/nginx.pid`
    测试:
    #  links http://110.65.99.209
    #  links http://110.65.99.210
    测试后在/usr/local/nginx/logs目录下会自动创建Log日志文件
    server2.access.log  server3.access.log
    # cat server2.access.log
    110.65.99.209 - - [21/Apr/2011:22:34:11 +0800] "GET / HTTP/1.1" 200 8 "-" "ELinks/0.11.1 (textmode; Linux; 142x38-2)"
    # cat server3.access.log
    110.65.99.210 - - [21/Apr/2011:22:34:24 +0800] "GET / HTTP/1.1" 200 8 "-" "ELinks/0.11.1 (textmode; Linux; 142x38-2)"


    1.2、配置基于域名的虚拟主机
    接着如上的配置在server3下面添加如下两个虚拟主机


    ###############server4################################          server {          listen  80;          server_name     server4.com;          access_log      logs/server4.access.log combined;          location / {          index   index.html index.htm;          root    /usr/local/nginx/server4;                    }          }  ###############server5################################          server {          listen  80;          server_name     server5.com;          access_log      logs/server5.access.log combined;          location / {          index   index.html index.htm;          root    /usr/local/nginx/server5;                    }          }
    然后创建目录server4和server5,并且在各自目录下创建index.html测试文件
    检查配置文件是否写正确,然后才平滑重启Nginx
    # /usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
    # kill -HUP `cat /usr/local/nginx/logs/nginx.pid`
    为了让系统直接缓存域名解析,在/etc/hosts后面追加如下内容(110.65.99.208是eth0-IP):
    110.65.99.208   server5.com
    110.65.99.208   server4.com
    测试:
    # links http://server4.com
    # links http://server5.com
    测试后在/usr/local/nginx/logs目录下会自动创建Log日志文件
    # cat server4.access.log
    110.65.99.208 - - [21/Apr/2011:22:41:46 +0800] "GET / HTTP/1.1" 200 8 "-" "ELinks/0.11.1 (textmode; Linux; 142x38-2)"
    # cat server5.access.log
    110.65.99.208 - - [21/Apr/2011:22:40:56 +0800] "GET / HTTP/1.1" 200 8 "-" "ELinks/0.11.1 (textmode; Linux; 142x38-2)"

     

    2、Nginx的日志文件配置与切割
    如上的日志文件内容是有一定的指令定义的,在Nginx的主配置文件中是通过
    log_format指令来设置日志格式:

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

    如上指令的解析:
    $remote_addr用户获取客户端的IP地址,但如果前端使用了反向代理,则获取到的是反向代理服务器的IP地址
    $http_x_forwarded_for"则可以使得反向代理服务器转发请求的HTTP头信息来记录客户端IP
    $request用于记录请求的URL和HTTP协议
    $time_local用于记录访问时间与时区
    $status用于记录请求状态,成功时状态为200,页面找到时状态为404
    $body_bytes_sent用于记录发送给客户端的文件主体内容大小
    $http_user_agent用于记录客户端浏览器的相关信息
    $http_referer用于记录是从哪个页面链接访问过来的

    上面指定好了日志格式后就要定义日志存放的路径了,access_log指令用于指定日志存放的路径
    如果说不想记录日志,则直接
    access_log off;
    如果使用如下定义的日志格式存储日志,则可以通过
    access_log  logs/access.log main;
    表示采用main的日志格式,记录在/usr/local/nginx/logs/access.log文件中
    (如果把日志存储路径定义在其他地方,要确保Nginx进程设置的用户和组对该目录有读写权限,否则该日志文件就无法生成了)

    对于生产系统下的server,由于巨大的访问量会导致日志文件的增加非常快,日志太大会影响server的效率,同时为了方便对日志进行分析计算,所以需要对日志文件做定时切割,这里使用按天计算切割。
    先前的文档提及过kill -USR1的作用时重新打开日志文件,这样我们就可以先对旧的日志文件重命名,通过kill -USR1重新生成access.log文件。最后借助crontab进行按天切割的计划任务。
    如下通过一个shell脚本进行日志切割:


    #!/bin/bash  logs_path1="/usr/local/nginx/oldlog" logs_path2="/usr/local/nginx/logs" mkdir -p $logs_path1  time=`date +%Y%m%d`  mv ${logs_path2}/access.log ${logs_path1}/$time.access.log  kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`  通过crontab计划任务实现每天凌晨3点做一次日志切割  # chmod +x /usr/local/nginx/log.sh  # crontab -l  * 3 * * * /usr/local/nginx/log.sh

    3、Nginx的压缩输出配置与自动列目录配置
    3.1、Nginx的压缩输出配置
    gzip(GNU- ZIP)是一种压缩技术,通过gzip压缩后的页面可以变为原来的30%,使得用户浏览页面的时候速度会快很多,gzip的压缩需要browser和 server都支持。其过程是server端进行gzip压缩,browser端解压并解析。目前大多数browser都支持gzip过的页面,如IE、 firefox、chrome。
    Nginx的压缩输出由一组gzip指令来实现,默认的Nginx配置文件仅仅为
    gzip  on;
    这么个简单的指令,所以我们需要参考gzip模块相关介绍,实现gzip的具体压缩功能
    如下:
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 2;
    gzip_vary on;
    3.2、 Nginx的自动列目录配置
    当指定目录下不存在index指令设置的默认首页文件,当访问该页面的时候会出现403 Forbidden
    的信息,所以可以通过在Nginx的主配置文件中的location / {......}目录控制中配置自动列目录,如下代码:
    location / {
     autoindex on;
    }
    这样就可以访问页面显示出该目录下的文件或目录,而不是403 Forbidden信息。
    3.3、 Nginx的浏览器本地缓存设置
    browser caching是为了加速浏览,在用户磁盘上对最近请求过的文档进行存储。当访问者再次请求这个页面的时候,browser就可以直接从本地磁盘显示文档,从而加速页面的阅览,最终到达节约网络资源。
    browser caching通过expires指令输出header头来实现,该指令语法如下:
    语法:expires [time|epoch|max|off]
    默认值: expires off
    作用域: http ,server ,location
    假设一个html页面会引用到一些javascript文件、图片文件,而这些格式的文件很少会被修改,则可以通过expires设置browser caching
    eg:对常见格式的图片、flash文件在浏览器本地缓存30天,对Js、css文件在本地缓存1个小时:
    location ~ .*/.(gif|jpg|jpeg|png|bmp|swf)$
    {
     expires 30d;
    }
    location ~ .*/.(js|css)?$
    {
     expires 1h;
    }

    四、Nginx与PHP(FastCGI)的安装、配置与优化

    CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。
    FastCGI 是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知,CGI解释器的反复加载是 CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over特性等等。

    FastCGI的工作原理(摘自百度百科)
    1、web server(Nginx)启动时载入FastCGI进程管理器
    2、FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自web server的连接
    3、当客户端请求到达web server时,FastCGI进程管理器选择并连接到一个CGI解释器。web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi
    4、FastCGI子进程完成处理后将标准输出和错误信息从同一个连接返回web server,当FastCGI子进程关闭连接时,请求便高处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器的下一个连接。

    所以,如果是使用CGI技术的话,每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。然而不足之处在于FastCGI比CGI消耗更多的系统内存,因为FastCGI是多进程的,CGI是多线程的。

    1、安装PHP所需库(FastCGI模式)
    1.1、 编译安装PHP所需的支持库

    1>libiconv
    GNU的libiconv是为nicode和其他的传统编码两种应用设计的编码转换库
    http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz
      tar zxvf libiconv-1.13.1.tar.gz
      cd libiconv-1.13.1
      ./configure --prefix=/usr/local/
      make && make install

    2>libmcrypt
    Libmcrypt is a library which provides a uniform. interface to several symmetric encryption algorithms. It is intended to have a simple interface to access encryption algorithms in ofb, cbc, cfb, and ecb modes. The algorithms it supports are DES, 3DES, RIJNDAEL, Twofish, IDEA, GOST, CAST-256, ARCFOUR, SERPENT, SAFER+, and more. The algorithms and modes are also modular so you can add and remove them on the fly without recompiling the library.
    主要功能用于加密解密
    http://down1.chinaunix.net/distfiles/libmcrypt-2.5.7.tar.gz
      tar zxvf libmcrypt-2.5.7.tar.gz
      cd libmcrypt-2.5.7
      ./configure
      make && make install
      /sbin/ldconfig     #ldconfig通常在系统启动时运行,当用户安装了一个新的动态链接库时,就需要手工运行这个命令
      cd libltdl/
      ./configure --enable-ltdl-install
      make && make install

    3>mhash
    mhash是哈希函数库,比如用来计算消息的校验码之类的
    http://sourceforge.net/projects/mhash/files/mhash/0.9.9/mhash-0.9.9.tar.gz/download
      tar jxvf mhash-0.9.9.9.tar.bz2
      cd mhash-0.9.9.9
      ./configure
      make && make install

    4>mcrypt
    Mcrypt扩展库可以对数据进行加密和解密
    http://code.google.com/p/lcmp/downloads/detail?name=mcrypt-2.6.8.tar.gz
      tar zxvf mcrypt-2.6.8.tar.gz.gz
      cd mcrypt-2.6.8
      /sbin/ldconfig
      ./configure
      make && make install

    这里我出现了个小报错:configure: error: *** libmcrypt was not found

    是因为环境变量的问题,gcc编译的时候根据自身定义的变量寻找相关函数库等文件,libmcrypt也是刚安装的,在变量中没有定义出来,所以手动添加:

    #export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH


     

     

    5>建立软连接
      ln -s /usr/local/lib/libmcrypt.la /usr/lib/libmcrypt.la
      ln -s /usr/local/lib/libmcrypt.so /usr/lib/libmcrypt.so
      ln -s /usr/local/lib/libmcrypt.so.4 /usr/lib/libmcrypt.so.4
      ln -s /usr/local/lib/libmcrypt.so.4.4.7 /usr/lib/libmcrypt.so.4.4.7
      ln -s /usr/local/lib/libmhash.* /usr/lib/

     

    2、安装mysql
    http://www.mysql.com/downloads/mysql/5.1.html#downloads
    选择下拉“source code”最后一个
    官网要注册才能下载的,去台湾一个网站可以直接下载:
    http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.57.tar.gz/from/http://mysql.cs.pu.edu.tw/

    或者:
    http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.57.tar.gz/from/http://mysql.ntu.edu.tw/

    rpm版mysql需要安装三个包:

    MySQL-server-community-5.1.57-1.rhel5.x86_64.rpm
    MySQL-client-community-5.1.57-1.rhel5.x86_64.rpm
    MySQL-devel-community-5.1.57-1.rhel5.x86_64.rpm


    http://dev.mysql.com/get/Downloads/MySQL-5.1/MySQL-server-community-5.1.57-1.rhel5.x86_64.rpm/from/http://mysql.ntu.edu.tw/

    http://dev.mysql.com/get/Downloads/MySQL-5.1/MySQL-client-community-5.1.57-1.rhel5.x86_64.rpm/from/http://mysql.cs.pu.edu.tw/


    http://dev.mysql.com/get/Downloads/MySQL-5.1/MySQL-devel-community-5.1.57-1.rhel5.x86_64.rpm/from/http://mysql.cs.pu.edu.tw/

     

    useradd mysql
      tar zxvf mysql-5.1.56.tar.gz
      cd mysql-5.1.56
      ./configure --prefix=/usr/local/mysql --enable-assembler --with-extra-charsets=complex --enable-thread-safe-client --with-big-tables --with-readline --with-ssl --with-embedded-server --enable-local-infile --with-plugins=innobase
      make && make install
      cp support-files/my-medium.cnf /etc/my.cnf
      cd /usr/local/mysql/
      bin/mysql_install_db --user=mysql
      chown -R root .
      chown -R mysql var/
      chgrp -R mysql .
      bin/mysqld_safe --user=mysql &
      ln -s /usr/local/mysql/bin/* /usr/bin/
      mysqladmin -uroot password 123456
      mysql -uroot -p123456
    mysql> GRANT ALL PRIVILEGES ON *.* TO
    'admin'@'localhost' IDENTIFIED BY '123456';


    3、安装PHP
    1、编译安装PHP(FastCGI模式)
      tar zxvf php-5.2.17.tar.gz
      gzip -cd php-5.2.17-fpm-0.5.14.diff.gz | patch -d php-5.2.17 -p1 //打补丁
      cd php-5.2.17
      ./configure --prefix=/usr/local/php --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr/ --enable-xml --disable-rpath --enable-discard-path --enable-safe-mode --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --with-curlwrappers --enable-mbregex --enable-fastcgi --enable-fpm --enable-force-cgi-redirect --enable-mbstring --with-mcrypt --with-gd --enable-gd-native-ttf --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-ldap --with-ldap-sasl --with-xmlrpc --enable-zip --enable-soap --without-pear --with-mysql=/usr/local/mysql/

    注意:如果是RPM版MYSQL可能会出现报错
    configure: error: Cannot find libmysqlclient under /usr.
    解决方法是将 /usr/lib64/mysql/libmysqlclient.a libmysqlclient.la 拷贝到/usr/lib/下,另外编译参数 --with-mysql=/usr/local/mysql/
    需改为:--with-mysql=/usr

    make ZEND_EXTRA_LTBS='-liconv'
      make install
      cp php.ini-dist /usr/local/php/etc/php.ini
      cp php.ini-dist /usr/local/lib/php.ini


    4、编译安装php5扩展模块
    1>memcache
    Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。
    它可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个HashTable,Memcached自管理这些HashTable。
    php中的所讲的memcache是PHP的一个扩展,是一个客户端,用于连接memcached。
    http://pecl.php.net/get/memcache-2.2.6.tgz
      tar zxvf memcache-2.2.6.tgz
      cd memcache-2.2.6
      /usr/local/php/bin/phpize
      ./configure --with-php-config=/usr/local/php/bin/php-config
      make && make install

    2>eaccelerator
    eAccelerator是一个自由开放源码php加速器,优化和动态内容缓存,提高了php脚本的缓存性能,使得PHP脚本在编译的状态下,对服务器的开销几乎完全消除。 它还有对脚本起优化作用,以加快其执行效率。使PHP程序代码执效率能提高1-10倍;
    http://bart.eaccelerator.net/source/0.9.6.1/eaccelerator-0.9.6.1.tar.bz2
      tar jxvf eaccelerator-0.9.6.1.tar.bz2
      cd eaccelerator-0.9.6.1
      /usr/local/php/bin/phpize
      ./configure --enable-eaccelerator=shared --with-php-config=/usr/local/php/bin/php-config
      make && make install

    3>PDO_MYSQL
    PDO_MYSQL is a driver that implements the PHP Data Objects (PDO) interface to enable access from PHP to MySQL 3.x, 4.x and 5.x databases.
    http://pecl.php.net/get/PDO_MYSQL-1.0.2.tgz
      tar zxvf PDO_MYSQL-1.0.2.tgz
      cd PDO_MYSQL-1.0.2
      /usr/local/php/bin/phpize
      ./configure --with-php-config=/usr/local/php/bin/php-config --with-pdo-mysql=/usr/local/mysql/
      make && make install

    4>ImageMagick
    ImageMagick 是一套功能强大、稳定而且免费的工具集和开发包,可以用来读、写和处理超过89种基本格式的图片文件,包括流行的TIFF, JPEG, GIF, PNG, PDF以及PhotoCD等格式。利用ImageMagick,你可以根据web应用程序的需要动态生成图片, 还可以对一个(或一组)图片进行改变大小、旋转、锐化、减色或增加特效等操作,并将操作的结果以相同格式或其它格式保存。
    ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
      tar zxvf ImageMagick-6.6.5-10.tar.gz
      cd ImageMagick-6.6.5-10
      ./configure
      make && make install

    5>imagick
    imagick 是PHP的一个扩展程序,它是默认的GD图象函数库的绝佳替代方案。面向对象的PHP编程使用非常方便。
    imagick 默认并没有在PHP的安装包里,需要去PHP网站上下载,并在PHP.INI中打开此扩展。
    http://pecl.php.net/get/imagick-3.0.1.tgz 或者
    http://pecl.php.net/get/imagick-3.0.0.tgz
      tar zxvf imagick-3.0.0.tgz
      cd imagick-3.0.0
      /usr/local/php/bin/phpize
      ./configure --with-php-config=/usr/local/php/bin/php-config
      make && make install

    6>修改php.ini文件,配置eaccelerator加速PHP
    1、查找/usr/local/php/etc/php.ini中的extension_dir = "./"
    修改extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/"
    并在此行后添加如下几行用于调用上面的扩展模块
    extension = "memcache.so"
    extension = "pdo_mysql.so"
    extension = "imagick.so"
    最后查找output_buffering = Off,修改成output_buffering = On
    2、创建目录/usr/local/eaccelerator_cache
    mkdir /usr/local/eaccelerator_cache
    修改/usr/local/php/etc/php.ini在最后添加如下:
    [eaccelerator]
    zend_extension="/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/eaccelerator.so"
    eaccelerator.shm_size="64"
    eaccelerator.cache_dir=/usr/local/eaccelerator_cache
    eaccelerator.enable="1"
    eaccelerator.optimizer="1"
    eaccelerator.check_mtime="1"
    eaccelerator.debug="0"
    eaccelerator.filter=""
    eaccelerator.shm_max="0"
    eaccelerator.shm_ttl="3600"
    eaccelerator.shm_prune_period="3600"
    eaccelerator.shm_only="0"
    eaccelerator.compress="1"
    eaccelerator.compress_level="9"


    5、配置启用php-cgi
    /usr/sbin/groupadd www
    /usr/sbin/useradd -g www www
    修改php-fpm配置文件(php-fpm是为PHP打的一个FastCGI管理补丁),可以实现平滑变更php.ini配置而无需重启php-cgi
    vim /usr/local/php/etc/php-fpm.conf
    把两处nobody修改成www,并且去掉<--!-->这样的注释,表示使用www启用进程php-cgi
    然后通过 /usr/local/php/sbin/php-fpm start 来启用php-cgi。
    ps -ef |grep php-cgi 可以查看php-cgi进程


    6、配置Nginx支持PHP
    修改Nginx的主配置文件/usr/local/nginx/conf/nginx.conf
    去掉下面代码前面的#号:
    location ~ /.php$ {
                root           html;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /usr/local/nginx/html$fastcgi_script_name;
                include        fastcgi_params;
            }
    然后在/usr/local/nginx/html目录下创建index.php文件测试即可

    *******************************************

    对于Nginx还有很多东西可以整理出来,后续在跟大伙分享分享。








    转自:http://www.th7.cn/db/mysql/2011/08/17/12403.shtml


    应用服务器Nginx安装及配置.rar(1.5 MB)
  • 从PROC文件系统与SYSFS系统获得系统运行时数据

    2011-11-23 15:11:10

    从PROC文件系统与SYSFS系统获得系统运行时数据




    ===================
    - PROC文件系统介绍
    ===================

    ; 显示内存信息,对对应命令: vmstat/free
    $ cat /proc/meminfo
    $ cat /proc/vmstat
    $ cat /proc/vmmemctl

    ; 显示设备相关信息
    $ cat /proc/devices

    ; 显示磁盘相关信息,第二条命令显示了分区名称,主要设备号与次要设备号,对应命令: fdisk -l
    $ cat /proc/diskstats
    $ cat /proc/partitions

    ; 显示启动时间
    $ cat /proc/uptime
    $ cat /proc/version

    ; 显示系统中断以及相关信息,用于查看设备所请求的IRQ中断,其中'I8042'为键盘
    ; 第二个命令显示系统IO所占用的地址范围
    ; 第三个命令查看系统设备读写的地址空间,编程会用得着
    $ cat /proc/interrupts
    $ cat /proc/ioports
    $ cat /proc/iomem

    ; 查看CPU信息,查看CPU对指令集的支持
    $ cat /proc/cpuinfo | awk '/flags/'
    $ cat /proc/cpuinfo | awk '/vendor/'
    $ cat /proc/cpuinfo | awk '/MHz/'

    ; 查看内核所编译的文件系统,即,查看内核所支持的文件系统
    $ cat /proc/filesystems

    ; 查看系统装载的模块,对应命令: lsmod
    $ cat /proc/modules

    ; 查看系统平均负载
    $ cat /proc/loadavg
    ; 查看系统启动命令行,传送到内核的命令行参数
    $ cat /proc/cmdline

    ; 系统调用列表
    $ cat /proc/kallsyms


    + 网络
    ; 显示系统ARP表,"HWADDR"全0表示IP地址未被占用
    $ cat /proc/net/arp
    ; 显示SNMP相关信息,上面一行表示各个字段,下面一行表示值,以下同
    $ cat /proc/net/snmp
    ; 显示接口总流量
    $ cat /proc/net/dev
    ; 显示路由表,注意,这里显示的不是点分10进制,而10完全16进制
    $ cat /proc/net/route
    ; 显示无线相关信息,包含接收到的AP,信号质量等
    $ cat /proc/net/wireless

    + 进程<CPU/IO/Fd>
    ; 基准目录<vw>: /proc/#Pid/
    ; 查看内存映射表,包括其对动态链接库的引用,堆栈的位置,可以用于故障排除
    $ cat $vw/maps
    $ cat $vw/smaps
    ; 查看进程运作状况,如SLEEP参数表明了进程的繁忙程度
    $ cat $vw/status
    ; 查看进程的IO情况,也可以通过此查看进程是否为IO密集型进程
    $ cat $vw/io
    ; 查看进程打开的文件,对对应命令: lsof
    $ ls $vw/fd
    ; 查看进程工作目录
    $ ls -l $vw/cwd
    ; 查看进程可执行文件
    $ ls -l $vw/exe
    ; 查看传入进程的环境变量
    $ cat $vw/environ
    ; 查看传入进程的命令行
    $ cat $vw/cmdline | tr "\000" "\n"
    ; 查看对进程的限制,如打开文件数,运行作业数等
    $ cat $vw/limits
    ; 查看进程锁定的元素
    $ cat $vw/locks

    + 设备
    ; 查看光驱相关信息,注意DRIVENAME,进行挂载时用得着
    $ cat /proc/sys/dev/cdrom/info

    + 总线
    ; 查看PCI总线,对应命令: lspci/setpci
    $ cat /proc/bus/pci/devices
    ; 查看USB总线,对应命令: lsusb
    $ cat /proc/bus/usb/devices
    ; 查看键盘,鼠标,扬声器
    $ cat /proc/bus/input/devices
    $ cat /proc/bus/input/handler
    ; 查看SCSI总线
    $ cat /proc/scsi/scsi
    ; 查看SCSI设备
    $ cat /proc/scsi/device_info
    =====================
    * SYS 文件系统
    =====================

    ; 查看网卡原始MAC
    $ cat /sys/class/net/ethX/address
    ; 查看网卡统计数据
    $ cat /sys/class/net/ethX/statistics/*
    ; 查看接口状态
    $ cat /sys/class/net/ethX/operstate
     
    举个小例子
    这段代码统计在10秒内ETH0接口接收的网络流量,单位是BPS
    #!/bin/bash
    cd /sys/class/net/eth0/statistics
    startRX=`cat rx_bytes`
    sleep 10
    endRX=`cat rx_bytes`
    bytesTotal=`expr $endRX - $startRX`
    bps=`expr $bytesTotal '/' 10`
    echo "The average traffic is RX: ${bps}Bytes/sec"
      后记:从上文可以看到,LINUX性能数据收集工具均是从/PROC文件系统读取性能数据,另外,既然已经知道系统性能数据来源,在系统中没有安装这些工 具的情况下,我们也可以自行编写脚本进行数据采集了,另外,在对PROC文件系统不了解的情况下,不可对PROC文件系统中的任何文件进行写操作,特别是 线上的系统,否则本人概不负责
     
    作者 “NOTHING IS SERIOUS!”

    转自:http://www.2cto.com/os/201109/102448.html







  • 淘宝开源代码: http://code.taobao.org/

    2011-11-23 15:09:26


    淘宝开源代码:
    http://code.taobao.org/

    监控工具:tsar
    http://tsar.taobao.org/

  • 百度搜索研发部官方博客(http://www.baidu-tech.com)

    2011-11-23 11:43:32



    http://stblog.baidu-tech.com/


    http://stblog.baidu-tech.com/?p=755

  • Linux服务器性能评估与优化

    2011-11-22 16:15:25

    Linux服务器性能评估与优化【转载】

    转载:http://www.517sou.net/Article/104.aspx
    http://blog.csdn.net/macky0668/article/details/6720772



    一、影响Linux服务器性能的因素 

    1. 操作系统级

    Ø       CPU

    Ø       内存

    Ø       磁盘I/O带宽

    Ø       网络I/O带宽

    2.        程序应用级

     

    二、系统性能评估标准

     

    影响性能因素

    评判标准

    糟糕

    CPU

    user% + sys%< 70%

    user% + sys%= 85%

    user% + sys% >=90%

    内存

    Swap In(si)=0

    Swap Out(so)=0

    Per CPU with 10 page/s

    More Swap In & Swap Out

    磁盘

    iowait % < 20%

    iowait % =35%

    iowait % >= 50%

     


     

    其中:

           %user:表示CPU处在用户模式下的时间百分比。

           %sys:表示CPU处在系统模式下的时间百分比。

           %iowait:表示CPU等待输入输出完成时间的百分比。

           swap in:即si,表示虚拟内存的页导入,即从SWAP DISK交换到RAM

           swap out:即so,表示虚拟内存的页导出,即从RAM交换到SWAP DISK。

     

    三、系统性能分析工具

    1.常用系统命令 

    Vmstat、sar、iostat、netstat、free、ps、top等

    2.常用组合方式 

    •           用vmstat、sar、iostat检测是否是CPU瓶颈

    •           用free、vmstat检测是否是内存瓶颈

    •           用iostat检测是否是磁盘I/O瓶颈

    •           用netstat检测是否是网络带宽瓶颈

    四、Linux性能评估与优化

     

    1. 系统整体性能评估(uptime命令)

     

    [root@web1 ~]# uptime

    16:38:00 up 118 days,  3:01,  5 users,  load average: 1.22, 1.02, 0.91

    这里需要注意的是:load average这个输出值,这三个值的大小一般不能大于系统CPU的个数,例如,本输出中系统有8个CPU,如果load average的三个值长期大于8时,说明CPU很繁忙,负载很高,可能会影响系统性能,但是偶尔大于8时,倒不用担心,一般不会影响系统性能。相反,如果load average的输出值小于CPU的个数,则表示CPU还有空闲的时间片,比如本例中的输出,CPU是非常空闲的。

    2. CPU性能评估

     

    (1)利用vmstat命令监控系统CPU

       该命令可以显示关于系统各种资源之间相关性能的简要信息,这里我们主要用它来看CPU一个负载情况。

       下面是vmstat命令在某个系统的输出结果:

    [root@node1 ~]# vmstat 2 3

    procs -----------memory----------  ---swap--  -----io---- --system--  -----cpu------

     r  b   swpd   free      buff  cache   si   so    bi    bo       in     cs     us sy  id   wa st

     0  0    0    162240   8304  67032   0    0    13    21   1007   23     0  1   98   0   0

     0  0    0    162240   8304  67032   0    0     1     0     1010   20     0  1   100 0   0

     0  0    0    162240   8304  67032   0    0     1     1     1009   18     0  1    99  0   0

    l        Procs

         r列表示运行和等待cpu时间片的进程数,这个值如果长期大于系统CPU的个数,说明CPU不足,需要增加CPU。

         b列表示在等待资源的进程数,比如正在等待I/O、或者内存交换等。

    l        Cpu

        us列显示了用户进程消耗的CPU 时间百分比。us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期大于50%,就需要考虑优化程序或算法。

         sy列显示了内核进程消耗的CPU时间百分比。Sy的值较高时,说明内核消耗的CPU资源很多。

        根据经验,us+sy的参考值为80%,如果us+sy大于 80%说明可能存在CPU资源不足。

     

    (2)利用sar命令监控系统CPU

     

    sar功能很强大,可以对系统的每个方面进行单独的统计,但是使用sar命令会增加系统开销,不过这些开销是可以评估的,对系统的统计结果不会有很大影响。

     下面是sar命令对某个系统的CPU统计输出:

    [root@webserver ~]# sar -u 3 5

    Linux 2.6.9-42.ELsmp (webserver)        11/28/2008      _i686_  (8 CPU)

    11:41:24 AM     CPU     %user     %nice   %system   %iowait    %steal     %idle

    11:41:27 AM     all      0.88      0.00      0.29      0.00      0.00     98.83

    11:41:30 AM     all      0.13      0.00      0.17      0.21      0.00     99.50

    11:41:33 AM     all      0.04      0.00      0.04      0.00      0.00     99.92

    11:41:36 AM     all      90.08     0.00      0.13      0.16      0.00     9.63

    11:41:39 AM     all      0.38      0.00      0.17      0.04      0.00     99.41

    Average:        all      0.34      0.00      0.16      0.05      0.00     99.45

                               

    对上面每项的输出解释如下:

    l        %user列显示了用户进程消耗的CPU 时间百分比。

    l        %nice列显示了运行正常进程所消耗的CPU 时间百分比。

    l        %system列显示了系统进程消耗的CPU时间百分比。

    l        %iowait列显示了IO等待所占用的CPU时间百分比

    l        %steal列显示了在内存相对紧张的环境下pagein强制对不同的页面进行的steal操作 。

    l        %idle列显示了CPU处在空闲状态的时间百分比。

     

    问题

    1.你是否遇到过系统CPU整体利用率不高,而应用缓慢的现象?

           在一个多CPU的系统中,如果程序使用了单线程,会出现这么一个现象,CPU的整体使用率不高,但是系统应用却响应缓慢,这可能是由于程序使用单线程的原因,单线程只使用一个CPU,导致这个CPU占用率为100%,无法处理其它请求,而其它的CPU却闲置,这就导致了整体CPU使用率不高,而应用缓慢现象的发生。

    3. 内存性能评估

    (1)利用free指令监控内存

    free是监控linux内存使用状况最常用的指令,看下面的一个输出:

    [root@webserver ~]# free  -m

                    total         used       free     shared    buffers     cached

    Mem:       8111       7185        926          0        243           6299

    -/+ buffers/cache:     643       7468

    Swap:       8189          0         8189

         一般有这样一个经验公式:应用程序可用内存/系统物理内存>70%时,表示系统内存资源非常充足,不影响系统性能,应用程序可用内存/系统物理内存<20%时,表示系统内存资源紧缺,需要增加系统内存,20%<应用程序可用内存/系统物理内存<70%时,表示系统内存资源基本能满足应用需求,暂时不影响系统性能。

    3.内存性能评估

     

    (1)利用free指令监控内存

    free是监控linux内存使用状况最常用的指令,看下面的一个输出:

    [root@webserver ~]# free  -m

                    total         used       free     shared    buffers     cached

    Mem:       8111       7185        926          0        243           6299

    -/+ buffers/cache:     643       7468

    Swap:       8189          0         8189

         一般有这样一个经验公式:应用程序可用内存/系统物理内存>70%时,表示系统内存资源非常充足,不影响系统性能,应用程序可用内存/系统物理内存<20%时,表示系统内存资源紧缺,需要增加系统内存,20%<应用程序可用内存/系统物理内存<70%时,表示系统内存资源基本能满足应用需求,暂时不影响系统性能。

      

    (2)利用vmstat命令监控内存

    [root@node1 ~]# vmstat 2 3

    procs -----------memory----------  ---swap--  -----io---- --system--  -----cpu------

     r  b   swpd   free      buff  cache   si   so    bi    bo       in     cs     us sy  id  wa st

     0  0    0    162240   8304  67032   0    0    13    21   1007   23     0  1  98   0  0

     0  0    0    162240   8304  67032   0    0     1     0     1010   20     0  1  100 0  0

     0  0    0    162240   8304  67032   0    0     1     1     1009   18     0  1  99   0  0

    l        memory

             swpd列表示切换到内存交换区的内存数量(以k为单位)。如果swpd的值不为0,或者比较大,只要si、so的值长期为0,这种情况下一般不用担心,不会影响系统性能。

             free列表示当前空闲的物理内存数量(以k为单位)

             buff列表示buffers cache的内存数量,一般对块设备的读写才需要缓冲。

             cache列表示page cached的内存数量,一般作为文件系统cached,频繁访问的文件都会被cached,如果cache值较大,说明cached的文件数较多,如果此时IO中bi比较小,说明文件系统效率比较好。

    l        swap

    si列表示由磁盘调入内存,也就是内存进入内存交换区的数量。

    so列表示由内存调入磁盘,也就是内存交换区进入内存的数量。

    一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足。需要增加系统内存。

    4.磁盘I/O性能评估 

    (1)磁盘存储基础

    l             熟悉RAID存储方式,可以根据应用的不同,选择不同的RAID方式。

    l             尽可能用内存的读写代替直接磁盘I/O,使频繁访问的文件或数据放入内存中进行操作处理,因为内存读写操作比直接磁盘读写的效率要高千倍。

    l             将经常进行读写的文件与长期不变的文件独立出来,分别放置到不同的磁盘设备上。

    l              对于写操作频繁的数据,可以考虑使用裸设备代替文件系统。

            

           使用裸设备的优点有:

    ü           数据可以直接读写,不需要经过操作系统级的缓存,节省了内存资源,避免了内存资源争用。

    ü           避免了文件系统级的维护开销,比如文件系统需要维护超级块、I-node等。

    ü           避免了操作系统的cache预读功能,减少了I/O请求。

           使用裸设备的缺点是:

    ü            数据管理、空间管理不灵活,需要很专业的人来操作。

    (2)利用iostat评估磁盘性能

    [root@webserver ~]#   iostat -d 2 3

    Linux 2.6.9-42.ELsmp (webserver)        12/01/2008      _i686_  (8 CPU)

     

    Device:         tps   Blk_read/s   Blk_wrtn/s   Blk_read      Blk_wrtn

    sda               1.87         2.58       114.12        6479462     286537372

     

    Device:         tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn

    sda               0.00         0.00         0.00              0                0

     

    Device:         tps   Blk_read/s   Blk_wrtn/s   Blk_read    Blk_wrtn

    sda               1.00         0.00        12.00             0                24

    对上面每项的输出解释如下:

    Blk_read/s表示每秒读取的数据块数。

    Blk_wrtn/s表示每秒写入的数据块数。

    Blk_read表示读取的所有块数。

    Blk_wrtn表示写入的所有块数。

    Ø            可以通过Blk_read/s和Blk_wrtn/s的值对磁盘的读写性能有一个基本的了解,如果Blk_wrtn/s值很大,表示磁盘的写操作很频繁,可以考虑优化磁盘或者优化程序,如果Blk_read/s值很大,表示磁盘直接读取操作很多,可以将读取的数据放入内存中进行操作。

    Ø            对于这两个选项的值没有一个固定的大小,根据系统应用的不同,会有不同的值,但是有一个规则还是可以遵循的:长期的、超大的数据读写,肯定是不正常的,这种情况一定会影响系统性能。

     

     

    (3)利用sar评估磁盘性能

             通过“sar –d”组合,可以对系统的磁盘IO做一个基本的统计,请看下面的一个输出:

    [root@webserver ~]# sar -d 2 3

    Linux 2.6.9-42.ELsmp (webserver)        11/30/2008      _i686_  (8 CPU)

     

    11:09:33 PM  DEV     tps   rd_sec/s   wr_sec/s  avgrq-sz  avgqu-sz   await  svctm   %util

    11:09:35 PM dev8-0  0.00  0.00            0.00        0.00          0.00         0.00   0.00     0.00

     

    11:09:35 PM  DEV     tps  rd_sec/s    wr_sec/s  avgrq-sz  avgqu-sz  await   svctm   %util

    11:09:37 PM dev8-0  1.00  0.00         12.00        12.00         0.00        0.00    0.00     0.00

     

    11:09:37 PM   DEV    tps    rd_sec/s  wr_sec/s   avgrq-sz  avgqu-sz  await  svctm   %util

    11:09:39 PM dev8-0  1.99   0.00         47.76         24.00       0.00        0.50    0.25     0.05

     

    Average:  DEV          tps    rd_sec/s   wr_sec/s  avgrq-sz  avgqu-sz    await  svctm   %util

    Average:  dev8-0      1.00   0.00          19.97         20.00       0.00         0.33    0.17     0.02

          需要关注的几个参数含义:

         await表示平均每次设备I/O操作的等待时间(以毫秒为单位)。

         svctm表示平均每次设备I/O操作的服务时间(以毫秒为单位)。

         %util表示一秒中有百分之几的时间用于I/O操作。

     

     

    对以磁盘IO性能,一般有如下评判标准:

         正常情况下svctm应该是小于await值的,而svctm的大小和磁盘性能有关,CPU、内存的负荷也会对svctm值造成影响,过多的请求也会间接的导致svctm值的增加。

         await值的大小一般取决与svctm的值和I/O队列长度以及I/O请求模式,如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢,此时可以通过更换更快的硬盘来解决问题。

         %util项的值也是衡量磁盘I/O的一个重要指标,如果%util接近100%,表示磁盘产生的I/O请求太多,I/O系统已经满负荷的在工作,该磁盘可能存在瓶颈。长期下去,势必影响系统的性能,可以通过优化程序或者通过更换更高、更快的磁盘来解决此问题。

     

    5. 网络性能评估

     

    (1)通过ping命令检测网络的连通性

    (2)通过netstat –i组合检测网络接口状况

    (3)通过netstat –r组合检测系统的路由表信息

    (4)通过sar –n组合显示系统的网络运行状态

    五、Oracle在Linux下的性能优化

     

    Oracle数据库内存参数的优化

    Ø       与oracle相关的系统内核参数

    Ø       SGA、PGA参数设置

    Oracle下磁盘存储性能优化

    Ø       文件系统的选择(ext2/ext3、xfs、ocfs2)

    Ø       Oracle  ASM存储

     1.优化oracle性能参数之前要了解的情况

    1)物理内存有多大

    2)操作系统估计要使用多大内存

    3)数据库是使用文件系统还是裸设备

    4)有多少并发连接

    5)应用是OLTP类型还是OLAP类型

    2.oracle数据库内存参数的优化

    (1)系统内核参数

    修改 /etc/sysctl.conf 这个文件,加入以下的语句:

    kernel.shmmax = 2147483648

    kernel.shmmni = 4096

    kernel.shmall = 2097152

    kernel.sem = 250 32000 100 128

    fs.file-max = 65536

    net.ipv4.ip_local_port_range = 1024 65000

    参数依次为:

    Kernel.shmmax:共享内存段的最大尺寸(以字节为单位)。

    Kernel.shmmni系统中共享内存段的最大数量。

    Kernel.shmall:共享内存总量,以页为单位。

    fs.file-max:文件句柄数,表示在Linux系统中可以打开的文件数量。

    net.ipv4.ip_local_port_range:应用程序可使用的IPv4端口范围。

     

    需要注意的几个问题

    关于Kernel.shmmax

         Oracle SGA 由共享内存组成,如果错误设置 SHMMAX可能会限制SGA 的大小,SHMMAX设置不足可能会导致以下问题:ORA-27123:unable to attach to shared memory segment,如果该参数设置小于Oracle SGA设置,那么SGA就会被分配多个共享内存段。这在繁忙的系统中可能成为性能负担,带来系统问题。

         Oracle建议Kernel.shmmax最好大于sga,以让oracle共享内存区SGA在一个共享内存段中,从而提高性能。

    关于Kernel.shmall

         表示系统共享内存总大小,以页为单位。

         一个32位的Linux系统,8G的内存,可以设置kernel.shmall = 2097152,即为: 2097152*4k/1024/1024 = 8G就是说可用共享内存一共8G,这里的4K是32位操作系统一页的大小,即4096字节。

    关于Kernel.shmmni

         表示系统中共享内存段的最大数量。系统默认是4096,一般无需修改,在SUN OS下还有Kernel.shmmin参数,表示共享内存段最小尺寸,勿要混肴!
    (2)SGA、PAG参数的设置

     

    A Oracle在内存管理方面的改进

         Oracle 9i通过参数PGA_AGGREGATE_TARGET参数实现PGA自动管理  Oracle 10g通过参数SGA_TARGET参数实现了SGA的自动管理,

         Oracle 11g实现了数据库所有内存块的全自动化管理,使得动态管理SGA和PGA成为现实。

     

    自动内存管理的两个参数:

         MEMORY_TARGET:表示整个ORACLE实例所能使用的内存大小,包括PGA和SGA的整体大小,即这个参数是动态的,可以动态控制SGA和PGA的大小。

         MEMORY_MAX_TARGET:这个参数定义了MEMORY_TARGET最大可以达到而不用重启实例的值,如果没有设置MEMORY_MAX_TARGET值,默认等于MEMORY_TARGET的值。

         使用动态内存管理时,SGA_TARGET和PGA_AGGREGATE_TARGET代表它们各自内存区域的最小设置,要让Oracle完全控制内存管理,这两个参数应该设置为0。

    B Oracle五种内存管理方式

    Ø         自动内存管理,即AMM (Automatic Memory Management)

    Ø         自动共享内存管理,即ASMM(Automatic Shared Memory Management)

    Ø         手动共享内存管理

    Ø         自动PGA管理

    Ø         手动PGA管理

    自动内存管理(AMM)

    默认安装oracle11g的实例就是AMM方式。通过如下查看:

    示例如下:

    SQL> show parameters target
    NAME                                       TYPE                  VALUE
    ------------ ---------------------      ------------------    ---------------------- archive_lag_target                     integer                      0
    db_flashback_retention_target   integer                    1860
    fast_start_io_target                    integer                      0
    fast_start_mttr_target                 integer                      0
    memory_max_target                  big integer              1400M
    memory_target                          big integer              1400M

    pga_aggregate_target                big integer                0
    sga_target                                  big integer                0

    注意:如果初始化参数 LOCK_SGA = true ,则 AMM 是不可用的。

    自动共享内存管理

    自动共享内存管理是oracle10g引进的,如果要使用自动共享内存管理,只需设置MEMORY_TARGET=0,然后显式指定SGA_TARGET即可。

    示例如下:

    SQL> alter system set memory_target=0 scope=both;
    System altered.
    SQL> alter system set sga_target=1024m scope=both;
    System altered.
    SQL>

    手工共享内存管理

    Oracle9i以及以前版本,只能手工设置共享内存管理,如果要使用手动共享内存管理,首先需要设置SGA_TARGET 与 MEMORY_TARGET为0。

    SGA包含主要参数有:

    share_pool_size:共享池大小,建议300-500M之间。

    Log_buffer:日志缓冲区大小,建议1-3M之间。

    Large_pool_size:大缓冲池大小,非MTS系统,建议在20-30M之间。

    Java_pool_size:java池大小,没有java应用时,建议10-20M之间。

    db_cache_size:数据缓冲区大小,根据可使用内存大小,尽可能大。

     

    自动PAG管理

    Oracle9i版本引入了自动PGA管理,如果使用的是AMM管理方式,则无需担心PGA的配置,但是如果对对AMM管理不放心的话,可以设置自动PGA管理,设置

         WORKAREA_SIZE_POLICY = AUTO

    然后指定PGA_AGGREGATE_TARGET大小即可。,

     

    手工PAG管理

    如果要做到精确的控制PGA,还可以设置手动管理PGA,设置

    WORKAREA_SIZE_POLICY = manual

    然后分别指定PGA相关参数即可:

    PGA相关参数有:

    SORT_AREA_SIZE

    SORT_AREA_RETAINED_SIZE,

     

    3.Oracle下磁盘存储性能优化

     

    ①      选择文件系统存取数据

    文件系统的选择

         单一文件系统(ext2、ext3、xfs等)

         集群文件系统(gfs、ocfs2)

    文件系统存储优缺点:

         优点:管理维护方便。

         缺点:数据读写要经过操作系统级的缓存,效率不是很高。

    ②      ASM(Automatic Storage Management)

    ASM优点:

         数据可直接读写,无需经过操作系统存取效率很高,读写效率与直接的原始设备基本相同。

         Oracle提供了专门的管理和维护工具

    关于作者

     

     高俊峰,网名:南非蚂蚁

    IXPUB “存储设备与容灾技术”及“ Linux与开源世界”版主。

     喜欢oracle和 Unix/Linux技术,平时主要活动在ITPUB.net﹑IXPUB.net﹑ChinaUnix.net等大型技术社区,一直致力与oracle 数据库﹑Unix/Linux操作系统管理﹑优化领域,现在主要从事oracle数据库管理和项目规划设计工作,擅长oracle数据库的备份恢复,性能调优,对Unix/Linux集群应用也有一定的研究。

  • strace命令用法——strace_truss_ltrace

    2011-11-22 16:09:00

    linux的strace命令(详解)
    本文详细讲述linux下的strace命令的用法。

    strace 命令是一种强大的工具,它能够显示所有由用户空间程序发出的系统调用。
      strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。
      下面记录几个常用 option .
      1 -f -F选项告诉strace同时跟踪fork和vfork出来的进程
      2 -o xxx.txt 输出到某个文件。
      3 -e execve 只记录 execve 这类系统调用
      —————————————————
      进程无法启动,软件运行速度突然变慢,程序的”SegmentFault”等等都是让每个Unix系统用户头痛的问题,
      本文通过三个实际案例演示如何使用truss、strace和ltrace这三个常用的调试工具来快速诊断软件的”疑难杂症”。
      
      
      truss和strace用来跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;
      而strace最初是为SunOS系统编写的,ltrace最早出现在GNU/DebianLinux中。
      这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。
      
      你不仅可以从命令行调试一个新开始的程序,也可以把truss、strace或ltrace绑定到一个已有的PID上来调试一个正在运行的程序。三个调试工具的基本使用方法大体相同,下面仅介绍三者共有,而且是最常用的三个命令行参数:
      
      -f :除了跟踪当前进程外,还跟踪其子进程。
      -o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。
      -p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。
      
       使用上述三个参数基本上就可以完成大多数调试任务了,下面举几个命令行例子:
      truss -o ls.truss ls -al: 跟踪ls -al的运行,将输出信息写到文件/tmp/ls.truss中。
      strace -f -o vim.strace vim: 跟踪vim及其子进程的运行,将输出信息写到文件vim.strace。
      ltrace -p 234: 跟踪一个pid为234的已经在运行的进程。
      
       三个调试工具的输出结果格式也很相似,以strace为例:
      
      brk(0) = 0×8062aa8
      brk(0×8063000) = 0×8063000
      mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0×92f) = 0×40016000
      
      每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 truss、strace和ltrace的工作原理大同小异,都是使用ptrace系统调用跟踪调试运行中的进程,详细原理不在本文讨论范围内,有兴趣可以参考它们的源代码。



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

    http://blog.linuxmine.com/i554

    strace命令用法
     
    调用:
    strace [ -dffhiqrtttTvxx ] [ -acolumn ] [ -eexpr ] ...
    [ -ofile ] [ -ppid ] ... [ -sstrsize ] [ -uusername ] [ command [ arg ... ] ]

    strace -c [ -eexpr ] ... [ -Ooverhead ] [ -Ssortby ] [ command [ arg ... ] ]
    功能:
    跟踪程式执行时的系统调用和所接收的信号.通常的用法是strace执行一直到commande结束.
    并且将所调用的系统调用的名称、参数和返回值输出到标准输出或者输出到-o指定的文件.
    strace是一个功能强大的调试,分析诊断工具.你将发现他是一个极好的帮手在你要调试一个无法看到源码或者源码无法在编译的程序.
    你将轻松的学习到一个软件是如何通过系统调用来实现他的功能的.而且作为一个程序设计师,你可以了解到在用户态和内核态是如何通过系统调用和信号来实现程序的功能的.
    strace的每一行输出包括系统调用名称,然后是参数和返回值.这个例子:
    strace cat /dev/null
    他的输出会有:
    open(\"/dev/null\",O_RDONLY) = 3
    有错误产生时,一般会返回-1.所以会有错误标志和描述:
    open(\"/foor/bar\",)_RDONLY) = -1 ENOENT (no such file or directory)
    信号将输出喂信号标志和信号的描述.跟踪并中断这个命令\"sleep 600\":
    sigsuspend({}
    --- SIGINT (Interrupt) ---
    +++ killed by SIGINT +++
    参数的输出有些不一致.如shell命令中的 \">>tmp\",将输出:
    open(\"tmp\",O_WRONLY|O_APPEND|A_CREAT,0666) = 3
    对于结构指针,将进行适当的显示.如:\"ls -l /dev/null\":
    lstat(\"/dev/null\",{st_mode=S_IFCHR|0666},st_rdev=makdev[1,3],...}) = 0
    请注意\"struct stat\" 的声明和这里的输出.lstat的第一个参数是输入参数,而第二个参数是向外传值.
    当你尝试\"ls -l\" 一个不存在的文件时,会有:
    lstat(/foot/ball\",0xb004) = -1 ENOENT (no such file or directory)
    char*将作为C的字符串类型输出.没有字符串输出时一般是char* 是一个转义字符,只输出字符串的长度.
    当字符串过长是会使用\"...\"省略.如在\"ls -l\"会有一个gepwuid调用读取password文件:
    read(3,\"root::0:0:System Administrator:/\"...,1024) = 422
    当参数是结构数组时,将按照简单的指针和数组输出如:
    getgroups(4,[0,2,4,5]) = 4
    关于bit作为参数的情形,也是使用方括号,并且用空格将每一项参数隔开.如:
    sigprocmask(SIG_BLOCK,[CHLD TTOU],[]) = 0
    这里第二个参数代表两个信号SIGCHLD 和 SIGTTOU.如果bit型参数全部置位,则有如下的输出:
    sigprocmask(SIG_UNBLOCK,~[],NULL) = 0
    这里第二个参数全部置位.

    参数说明:
    -c 统计每一系统调用的所执行的时间,次数和出错的次数等.
    -d 输出strace关于标准错误的调试信息.
    -f 跟踪由fork调用所产生的子进程.
    -ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
    -F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
    -h 输出简要的帮助信息.
    -i 输出系统调用的入口指针.
    -q 禁止输出关于脱离的消息.
    -r 打印出相对时间关于,,每一个系统调用.
    -t 在输出中的每一行前加上时间信息.
    -tt 在输出中的每一行前加上时间信息,微秒级.
    -ttt 微秒级输出,以秒了表示时间.
    -T 显示每一调用所耗的时间.
    -v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
    -V 输出strace的版本信息.
    -x 以十六进制形式输出非标准字符串
    -xx 所有字符串以十六进制形式输出.
    -a column
    设置返回值的输出位置.默认为40.
    -e expr
    指定一个表达式,用来控制如何跟踪.格式如下:
    [qualifier=][!]value1[,value2]...
    qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的qualifier是 trace.感叹号是否定符号.例如:
    -eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
    注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
    -e trace=set
    只跟踪指定的系统调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
    -e trace=file
    只跟踪有关文件操作的系统调用.
    -e trace=process
    只跟踪有关进程控制的系统调用.
    -e trace=network
    跟踪与网络有关的所有系统调用.
    -e strace=signal
    跟踪所有与系统信号有关的系统调用
    -e trace=ipc
    跟踪所有与进程通讯有关的系统调用
    -e abbrev=set
    设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
    -e raw=set
    将指定的系统调用的参数以十六进制显示.
    -e signal=set
    指定跟踪的系统信号.默认为all.如signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
    -e read=set
    输出从指定文件中读出的数据.例如:
    -e read=3,5
    -e write=set
    输出写入到指定文件中的数据.
    -o filename
    将strace的输出写入文件filename
    -p pid
    跟踪指定的进程pid.
    -s strsize
    指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
    -u username
    以username的UID和GID执行被跟踪的命令.

     

     

    用strace调试程序

           在理想世界里,每当一个程序不能正常执行一个功能时,它就会给出一个有用的错误提示,告诉你在足够的改正错误的线索。但遗憾的是,我们不是生活在理想世界里,起码不总是生活在理想世界里。有时候一个程序出现了问题,你无法找到原因。

      这就是调试程序出现的原因。strace是一个必不可少的调试工具,strace用来监视系统调用。你不仅可以调试一个新开始的程序,也可以调试一个已经在运行的程序(把strace绑定到一个已有的PID上面)。

      首先让我们看一个真实的例子:

      [BOLD]启动KDE时出现问题[/BOLD]

      前一段时间,我在启动KDE的时候出了问题,KDE的错误信息无法给我任何有帮助的线索。

      代码:

      _KDE_IceTransSocketCreateListener: failed to bind listener
      _KDE_IceTransSocketUNIXCreateListener: ...SocketCreateListener() failed
      _KDE_IceTransMakeAllCOTSServerListeners: failed to create listener for local

      Cannot establish any listening sockets DCOPServer self-test failed.


      对我来说这个错误信息没有太多意义,只是一个对KDE来说至关重要的负责进程间通信的程序无法启动。我还可以知道这个错误和ICE协议(Inter Client Exchange)有关,除此之外,我不知道什么是KDE启动出错的原因。

      我决定采用strace看一下在启动dcopserver时到底程序做了什么:

      代码:

      strace -f -F -o ~/dcop-strace.txt dcopserver


      这里 -f -F选项告诉strace同时跟踪fork和vfork出来的进程,-o选项把所有strace输出写到~/dcop-strace.txt里面,dcopserver是要启动和调试的程序。

      再次出现错误之后,我检查了错误输出文件dcop-strace.txt,文件里有很多系统调用的记录。在程序运行出错前的有关记录如下:

      代码:

      27207 mkdir("/tmp/.ICE-unix", 0777) = -1 EEXIST (File exists)
      27207 lstat64("/tmp/.ICE-unix", {st_mode=S_IFDIR|S_ISVTX|0755, st_size=4096, ...}) = 0
      27207 unlink("/tmp/.ICE-unix/dcop27207-1066844596") = -1 ENOENT (No such file or directory)
      27207 bind(3, {sin_family=AF_UNIX, path="/tmp/.ICE-unix/dcop27207-1066844596"}, 38) = -1 EACCES (Permission denied)
      27207 write(2, "_KDE_IceTrans", 13) = 13
      27207 write(2, "SocketCreateListener: failed to "..., 46) = 46
      27207 close(3) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
      27207 write(2, "SocketUNIXCreateListener: ...Soc"..., 59) = 59
      27207 umask(0) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
      27207 write(2, "MakeAllCOTSServerListeners: fail"..., 64) = 64
      27207 write(2, "Cannot establish any listening s"..., 39) = 39


      其 中第一行显示程序试图创建/tmp/.ICE-unix目录,权限为0777,这个操作因为目录已经存在而失败了。第二个系统调用(lstat64)检查 了目录状态,并显示这个目录的权限是0755,这里出现了第一个程序运行错误的线索:程序试图创建属性为0777的目录,但是已经存在了一个属性为 0755的目录。第三个系统调用(unlink)试图删除一个文件,但是这个文件并不存在。这并不奇怪,因为这个操作只是试图删掉可能存在的老文件。

      但 是,第四行确认了错误所在。他试图绑定到/tmp/.ICE-unix/dcop27207-1066844596,但是出现了拒绝访问错误。. ICE_unix目录的用户和组都是root,并且只有所有者具有写权限。一个非root用户无法在这个目录下面建立文件,如果把目录属性改成0777, 则前面的操作有可能可以执行,而这正是第一步错误出现时进行过的操作。

      所以我运行了chmod 0777 /tmp/.ICE-unix之后KDE就可以正常启动了,问题解决了,用strace进行跟踪调试只需要花很短的几分钟时间跟踪程序运行,然后检查并分析输出文件。

      说 明:运行chmod 0777只是一个测试,一般不要把一个目录设置成所有用户可读写,同时不设置粘滞位(sticky bit)。给目录设置粘滞位可以阻止一个用户随意删除可写目录下面其他人的文件。一般你会发现/tmp目录因为这个原因设置了粘滞位。KDE可以正常启动 之后,运行chmod +t /tmp/.ICE-unix给.ICE_unix设置粘滞位。

      [BOLD]解决库依赖问题[/BOLD]

      starce 的另一个用处是解决和动态库相关的问题。当对一个可执行文件运行ldd时,它会告诉你程序使用的动态库和找到动态库的位置。但是如果你正在使用一个比较老 的glibc版本(2.2或更早),你可能会有一个有bug的ldd程序,它可能会报告在一个目录下发现一个动态库,但是真正运行程序时动态连接程序 (/lib/ld-linux.so.2)却可能到另外一个目录去找动态连接库。这通常因为/etc/ld.so.conf和 /etc/ld.so.cache文件不一致,或者/etc/ld.so.cache被破坏。在glibc 2.3.2版本上这个错误不会出现,可能ld-linux的这个bug已经被解决了。

      尽管这样,ldd并不能把所有程序依赖的动态库列出 来,系统调用dlopen可以在需要的时候自动调入需要的动态库,而这些库可能不会被ldd列出来。作为glibc的一部分的NSS(Name Server Switch)库就是一个典型的例子,NSS的一个作用就是告诉应用程序到哪里去寻找系统帐号数据库。应用程序不会直接连接到NSS库,glibc则会通 过dlopen自动调入NSS库。如果这样的库偶然丢失,你不会被告知存在库依赖问题,但这样的程序就无法通过用户名解析得到用户ID了。让我们看一个例 子:

      whoami程序会给出你自己的用户名,这个程序在一些需要知道运行程序的真正用户的脚本程序里面非常有用,whoami的一个示例输出如下:
      代码:

      # whoami
      root


      假设因为某种原因在升级glibc的过程中负责用户名和用户ID转换的库NSS丢失,我们可以通过把nss库改名来模拟这个环境:
      代码:

      # mv /lib/libnss_files.so.2 /lib/libnss_files.so.2.backup
      # whoami
      whoami: cannot find username for UID 0


      这里你可以看到,运行whoami时出现了错误,ldd程序的输出不会提供有用的帮助:
      代码:

      # ldd /usr/bin/whoami
      libc.so.6 => /lib/libc.so.6 (0x4001f000)
      /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)


      你只会看到whoami依赖Libc.so.6和ld-linux.so.2,它没有给出运行whoami所必须的其他库。这里时用strace跟踪whoami时的输出:
      代码:

      strace -o whoami-strace.txt whoami

      open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      open("/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      stat64("/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
      open("/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      stat64("/lib/i686", 0xbffff190) = -1 ENOENT (No such file or directory)
      open("/lib/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      stat64("/lib/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
      open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      stat64("/lib", {st_mode=S_IFDIR|0755, st_size=2352, ...}) = 0
      open("/usr/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
      stat64("/usr/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
      open("/usr/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)


      你可以发现在不同目录下面查找libnss.so.2的尝试,但是都失败了。如果没有strace这样的工具,很难发现这个错误是由于缺少动态库造成的。现在只需要找到libnss.so.2并把它放回到正确的位置就可以了。

      [BOLD]限制strace只跟踪特定的系统调用[/BOLD]

      如果你已经知道你要找什么,你可以让strace只跟踪一些类型的系统调用。例如,你需要看看在configure脚本里面执行的程序,你需要监视的系统调用就是execve。让strace只记录execve的调用用这个命令:

      代码:

      strace -f -o configure-strace.txt -e execve ./configure


      部分输出结果为:
      代码:

      2720 execve("/usr/bin/expr", ["expr", "a", ":", "(a)"], [/* 31 vars */]) = 0
      2725 execve("/bin/basename", ["basename", "./configure"], [/* 31 vars */]) = 0
      2726 execve("/bin/chmod", ["chmod", "+x", "conftest.sh"], [/* 31 vars */]) = 0
      2729 execve("/bin/rm", ["rm", "-f", "conftest.sh"], [/* 31 vars */]) = 0
      2731 execve("/usr/bin/expr", ["expr", "99", "+", "1"], [/* 31 vars */]) = 0
      2736 execve("/bin/ln", ["ln", "-s", "conf2693.file", "conf2693"], [/* 31 vars */]) = 0


      你 已经看到了,strace不仅可以被程序员使用,普通系统管理员和用户也可以使用strace来调试系统错误。必须承认,strace的输出不总是容易理 解,但是很多输出对大多数人来说是不重要的。你会慢慢学会从大量输出中找到你可能需要的信息,像权限错误,文件未找到之类的,那时strace就会成为一 个有力的工具了。

    转自: http://www.yuanma.org/data/2007/0201/article_2213.htm





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

    linux的strace命令(详解)
    本文详细讲述linux下的strace命令的用法。

    strace 命令是一种强大的工具,它能够显示所有由用户空间程序发出的系统调用。
      strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。
      下面记录几个常用 option .
      1 -f -F选项告诉strace同时跟踪fork和vfork出来的进程
      2 -o xxx.txt 输出到某个文件。
      3 -e execve 只记录 execve 这类系统调用
      —————————————————
      进程无法启动,软件运行速度突然变慢,程序的”SegmentFault”等等都是让每个Unix系统用户头痛的问题,
      本文通过三个实际案例演示如何使用truss、strace和ltrace这三个常用的调试工具来快速诊断软件的”疑难杂症”。
      
      
      truss和strace用来跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;
      而strace最初是为SunOS系统编写的,ltrace最早出现在GNU/DebianLinux中。
      这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。
      
      你不仅可以从命令行调试一个新开始的程序,也可以把truss、strace或ltrace绑定到一个已有的PID上来调试一个正在运行的程序。三个调试工具的基本使用方法大体相同,下面仅介绍三者共有,而且是最常用的三个命令行参数:
      
      -f :除了跟踪当前进程外,还跟踪其子进程。
      -o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。
      -p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。
      
       使用上述三个参数基本上就可以完成大多数调试任务了,下面举几个命令行例子:
      truss -o ls.truss ls -al: 跟踪ls -al的运行,将输出信息写到文件/tmp/ls.truss中。
      strace -f -o vim.strace vim: 跟踪vim及其子进程的运行,将输出信息写到文件vim.strace。
      ltrace -p 234: 跟踪一个pid为234的已经在运行的进程。
      
       三个调试工具的输出结果格式也很相似,以strace为例:
      
      brk(0) = 0×8062aa8
      brk(0×8063000) = 0×8063000
      mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0×92f) = 0×40016000
      
      每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 truss、strace和ltrace的工作原理大同小异,都是使用ptrace系统调用跟踪调试运行中的进程,详细原理不在本文讨论范围内,有兴趣可以参考它们的源代码。
      举两个实例演示如何利用这三个调试工具诊断软件的”疑难杂症”:
      
      案例一:运行clint出现Segment Fault错误
      
      操作系统:FreeBSD-5.2.1-release
      clint是一个C++静态源代码分析工具,通过Ports安装好之后,运行:
      
      # clint foo.cpp
      Segmentation fault (core dumped)
       在Unix系统中遇见”Segmentation Fault”就像在MS Windows中弹出”非法操作”对话框一样令人讨厌。OK,我们用truss给clint”把把脉”:
      
      # truss -f -o clint.truss clint
      Segmentation fault (core dumped)
      # tail clint.truss
       739: read(0×6,0×806f000,0×1000) = 4096 (0×1000)
       739: fstat(6,0xbfbfe4d0) = 0 (0×0)
       739: fcntl(0×6,0×3,0×0) = 4 (0×4)
       739: fcntl(0×6,0×4,0×0) = 0 (0×0)
       739: close(6) = 0 (0×0)
       739: stat(”/root/.clint/plugins”,0xbfbfe680) ERR#2 ‘No such file or directory’
      SIGNAL 11
      SIGNAL 11
      Process stopped because of: 16
      process exit, rval = 139
      我们用truss跟踪clint的系统调用执行情况,并把结果输出到文件clint.truss,然后用tail查看最后几行。
      注意看clint执行的最后一条系统调用(倒数第五行):stat(”/root/.clint/plugins”,0xbfbfe680) ERR#2 ‘No such file or directory’,问题就出在这里:clint找不到目录”/root/.clint/plugins”,从而引发了段错误。怎样解决?很简单: mkdir -p /root/.clint/plugins,不过这次运行clint还是会”Segmentation Fault”9。继续用truss跟踪,发现clint还需要这个目录”/root/.clint/plugins/python”,建好这个目录后 clint终于能够正常运行了。
      
      案例二:vim启动速度明显变慢
      
      操作系统:FreeBSD-5.2.1-release
      vim版本为6.2.154,从命令行运行vim后,要等待近半分钟才能进入编辑界面,而且没有任何错误输出。仔细检查了.vimrc和所有的vim脚本都没有错误配置,在网上也找不到类似问题的解决办法,难不成要hacking source code?没有必要,用truss就能找到问题所在:
      
      # truss -f -D -o vim.truss vim
      
      这里-D参数的作用是:在每行输出前加上相对时间戳,即每执行一条系统调用所耗费的时间。我们只要关注哪些系统调用耗费的时间比较长就可以了,用less仔细查看输出文件vim.truss,很快就找到了疑点:
      
      735: 0.000021511 socket(0×2,0×1,0×0) = 4 (0×4)
      735: 0.000014248 setsockopt(0×4,0×6,0×1,0xbfbfe3c8,0×4) = 0 (0×0)
      735: 0.000013688 setsockopt(0×4,0xffff,0×8,0xbfbfe2ec,0×4) = 0 (0×0)
      735: 0.000203657 connect(0×4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 ‘Connection refused’
      735: 0.000017042 close(4) = 0 (0×0)
      735: 1.009366553 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0×0)
      735: 0.000019556 socket(0×2,0×1,0×0) = 4 (0×4)
      735: 0.000013409 setsockopt(0×4,0×6,0×1,0xbfbfe3c8,0×4) = 0 (0×0)
      735: 0.000013130 setsockopt(0×4,0xffff,0×8,0xbfbfe2ec,0×4) = 0 (0×0)
      735: 0.000272102 connect(0×4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 ‘Connection refused’
      735: 0.000015924 close(4) = 0 (0×0)
      735: 1.009338338 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0×0)
      
      vim试图连接10.57.18.27这台主机的6000端口(第四行的connect()),连接失败后,睡眠一秒钟继续重试(第6行的 nanosleep())。以上片断循环出现了十几次,每次都要耗费一秒多钟的时间,这就是vim明显变慢的原因。可是,你肯定会纳闷:”vim怎么会无缘无故连接其它计算机的6000端口呢?”。问得好,那么请你回想一下6000是什么服务的端口?没错,就是X Server。看来vim是要把输出定向到一个远程X Server,那么Shell中肯定定义了DISPLAY变量,查看.cshrc,果然有这么一行:setenv DISPLAY ${REMOTEHOST}:0,把它注释掉,再重新登录,问题就解决了。
      
      
      案例三:用调试工具掌握软件的工作原理
      
      操作系统:Red Hat Linux 9.0
      用调试工具实时跟踪软件的运行情况不仅是诊断软件”疑难杂症”的有效的手段,也可帮助我们理清软件的”脉络”,即快速掌握软件的运行流程和工作原理,不失为一种学习源代码的辅助方法。下面这个案例展现了如何使用strace通过跟踪别的软件来”触发灵感”,从而解决软件开发中的难题的。
      大家都知道,在进程内打开一个文件,都有唯一一个文件描述符(fd:file descriptor)与这个文件对应。而本人在开发一个软件过程中遇到这样一个问题:
      已知一个fd,如何获取这个fd所对应文件的完整路径?不管是Linux、FreeBSD或是其它Unix系统都没有提供这样的API,怎么办呢?我们换个角度思考:Unix下有没有什么软件可以获取进程打开了哪些文件?如果你经验足够丰富,很容易想到lsof,使用它既可以知道进程打开了哪些文件,也可以了解一个文件被哪个进程打开。好,我们用一个小程序来试验一下lsof,看它是如何获取进程打开了哪些文件。lsof: 显示进程打开的文件。
      
      /* testlsof.c */
      #include #include #include #include #include
      int main(void)
      {
       open(”/tmp/foo”, O_CREAT|O_RDONLY); /* 打开文件/tmp/foo */
       sleep(1200); /* 睡眠1200秒,以便进行后续操作 */
       return 0;
      }
      
      将testlsof放入后台运行,其pid为3125。命令lsof -p 3125查看进程3125打开了哪些文件,我们用strace跟踪lsof的运行,输出结果保存在lsof.strace中:
      
      # gcc testlsof.c -o testlsof
      # ./testlsof &
      [1] 3125
      # strace -o lsof.strace lsof -p 3125
      
      我们以”/tmp/foo”为关键字搜索输出文件lsof.strace,结果只有一条:
      
      
      # grep ‘/tmp/foo’ lsof.strace
      readlink(”/proc/3125/fd/3″, “/tmp/foo”, 4096) = 8
      
      原来lsof巧妙的利用了/proc/nnnn/fd/目录(nnnn为pid):Linux内核会为每一个进程在/proc/建立一个以其pid为名的目录用来保存进程的相关信息,而其子目录fd保存的是该进程打开的所有文件的fd。目标离我们很近了。好,我们到/proc/3125/fd/看个究竟:
      
      # cd /proc/3125/fd/
      # ls -l
      total 0
      lrwx—— 1 root root 64 Nov 5 09:50 0 -> /dev/pts/0
      lrwx—— 1 root root 64 Nov 5 09:50 1 -> /dev/pts/0
      lrwx—— 1 root root 64 Nov 5 09:50 2 -> /dev/pts/0
      lr-x—— 1 root root 64 Nov 5 09:50 3 -> /tmp/foo
      # readlink /proc/3125/fd/3
      /tmp/foo
      
      答案已经很明显了:/proc/nnnn/fd/目录下的每一个fd文件都是符号链接,而此链接就指向被该进程打开的一个文件。我们只要用readlink()系统调用就可以获取某个fd对应的文件了,代码如下:
      
      
      #include #include #include #include #include #include
      int get_pathname_from_fd(int fd, char pathname[], int n)
      {
       char buf[1024];
       pid_t pid;
       bzero(buf, 1024);
       pid = getpid();
       snprintf(buf, 1024, “/proc/%i/fd/%i”, pid, fd);
       return readlink(buf, pathname, n);
      }
      int main(void)
      {
       int fd;
       char pathname[4096];
       bzero(pathname, 4096);
       fd = open(”/tmp/foo”, O_CREAT|O_RDONLY);
       get_pathname_from_fd(fd, pathname, 4096);
       printf(”fd=%d; pathname=%sn”, fd, pathname);
       return 0;
      }
      
      出于安全方面的考虑,在FreeBSD 5 之后系统默认已经不再自动装载proc文件系统,因此,要想使用truss或strace跟踪程序,你必须手工装载proc文件系统:mount -t procfs proc /proc;或者在/etc/fstab中加上一行:
      
      proc /proc procfs rw 0 0

Open Toolbar