﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Tinysun-随笔分类-GNU Linux/Unix</title><link>http://www.blogjava.net/tinysun/category/37798.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 23 Aug 2012 06:43:12 GMT</lastBuildDate><pubDate>Thu, 23 Aug 2012 06:43:12 GMT</pubDate><ttl>60</ttl><item><title>svn修订版和最后修改的修订版[转]</title><link>http://www.blogjava.net/tinysun/archive/2012/08/23/386100.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 23 Aug 2012 05:13:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2012/08/23/386100.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/386100.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2012/08/23/386100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/386100.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/386100.html</trackback:ping><description><![CDATA[<div style="display: inline-block; "><h1 class="xspace-title" style="word-break: break-all; font-size: 22px; background-image: url(http://www.51testing.com/attachments/2009/09/94816_200909231633415aj7m.gif); background-color: #ffffff; padding-left: 0.2em; line-height: 30px; color: #467ca2; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; text-align: left; background-position: 0% 100%; background-repeat: repeat no-repeat; "></h1></div><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">花了点时间把svnbook看了遍，对于svn有了个比较好的认识。svn info时，修订版和最后修改的修订版总是让我感觉很困惑。要搞明白这个需要对下面几个关键字有所了解。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">HEAD：版本库中的最新版本。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">COMMITED：文件最后提交生成的版本号。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">PREV：文件倒数第二次提交生成的版本号。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">BASE：目录签出或者签入生成的版本号。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">HEAD、COMMITED和PREV比较好理解，BASE比较难于理解。假设一个目录下有两个文件configure.ac和Makefile.am，第一次将它们check out出来时，会生成一个新的revision，这个便是BASE了。此时使用svn info configure.ac/Makefile.am可以发现它们的修订版是一样的，但是最后修改的修订版不同。这里的修订版对应其实就是BASE，而最后修改的修订版则是COMMITED。插一句，很多人很容易误解为啥修订版号和最后修改的修订版号不一致。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">若将configure.ac修改并check in，这个时候会生成一个新的revision，configure.ac的BASE和COMMITED的值相当。而svn info Makefile.am，发现它的BASE和COMMITED没有改变。svn up一下，发现Makefile.am的BASE会变成最新的，和configure.ac相同。</p><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">签出代码库。</p><div id="highlighter_324879"  plain"="" style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; "><div style="word-break: break-all; line-height: normal !important; margin: 10px 0px; "><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">1</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn]$ svn co file:///Users/henshao/svn/dogg/learn_svn/ learn_svn2</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">2</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">A&nbsp;&nbsp;&nbsp; learn_svn2/trunk</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">3</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">A&nbsp;&nbsp;&nbsp; learn_svn2/trunk/configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">4</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">A&nbsp;&nbsp;&nbsp; learn_svn2/trunk/Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">5</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Checked out revision 17.</code></td></tr></tbody></table></div></div></div><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">显示修订版（BASE）和最后修改的修订版（COMMITED）。</p><div id="highlighter_440758"  plain"="" style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; "><div style="word-break: break-all; line-height: normal !important; margin: 10px 0px; "><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">01</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn st -v</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">02</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">03</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">04</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">05</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;</code>&nbsp;</td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">06</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">07</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Path: Makefile.am</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">08</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Name: Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">09</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/Makefile.am</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">10</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository Root: file:///Users/henshao/svn/dogg</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">11</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">12</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Revision: 17</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">13</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Node Kind: file</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">14</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Schedule: normal</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">15</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Author: henshao</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">16</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Rev: 15</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">17</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Date: 2011-06-23 17:03:08 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">18</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">19</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Checksum: 5b211a202b8ae001a86a557108d4989c</code></td></tr></tbody></table></div></div></div><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">修改Makefile.am并签入看看。</p><div id="highlighter_10862"  plain"="" style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; "><div style="word-break: break-all; line-height: normal !important; margin: 10px 0px; "><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">01</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn ci Makefile.am -m "LD_ADD add ssl library"</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">02</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Sending&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">03</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Transmitting file data .</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">04</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Committed revision 18.</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">05</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;</code>&nbsp;</td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">06</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">07</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Path: Makefile.am</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">08</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Name: Makefile.am</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">09</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/Makefile.am</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">10</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository Root: file:///Users/henshao/svn/dogg</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">11</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">12</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Revision: 18</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">13</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Node Kind: file</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">14</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Schedule: normal</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">15</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Author: henshao</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">16</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Rev: 18</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">17</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Date: 2011-06-23 18:41:41 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">18</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Text Last Updated: 2011-06-23 18:41:31 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">19</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Checksum: e4cc7bf424ff911c9619060a5f1c1030</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">20</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;</code>&nbsp;</td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">21</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">22</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Path: configure.ac</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">23</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Name: configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">24</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/configure.ac</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">25</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository Root: file:///Users/henshao/svn/dogg</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">26</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">27</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Revision: 17</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">28</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Node Kind: file</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">29</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Schedule: normal</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">30</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Author: henshao</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">31</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Rev: 17</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">32</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Date: 2011-06-23 17:44:51 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">33</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">34</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Checksum: 6b49ae8f3346120311e11843c23b0b00</code></td></tr></tbody></table></div></div></div><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">svn update一下看看。</p><div id="highlighter_909821"  plain"="" style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; "><div style="word-break: break-all; line-height: normal !important; margin: 10px 0px; "><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">01</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn up</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">02</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">At revision 18.</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">03</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;</code>&nbsp;</td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">04</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn info configure.ac</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">05</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Path: configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">06</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Name: configure.ac</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">07</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">URL: file:///Users/henshao/svn/dogg/learn_svn/trunk/configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">08</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository Root: file:///Users/henshao/svn/dogg</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">09</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Repository UUID: 7ee338c4-a6e3-468b-b576-d1b767dd90e2</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">10</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Revision: 18</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">11</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Node Kind: file</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">12</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Schedule: normal</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">13</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Author: henshao</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">14</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Rev: 17</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">15</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Last Changed Date: 2011-06-23 17:44:51 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">16</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Text Last Updated: 2011-06-23 18:37:50 +0800 (四, 23&nbsp; 6 2011)</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">17</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">Checksum: 6b49ae8f3346120311e11843c23b0b00</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">18</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;</code>&nbsp;</td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">19</code></td><td style="word-break: break-all; "><code plain"="" style="word-break: break-all; ">[henshao@henshao ~/svn/learn_svn2/trunk]$ svn st -v</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">20</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .</code></td></tr></tbody></table></div><div alt1"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">21</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; configure.ac</code></td></tr></tbody></table></div><div alt2"="" style="word-break: break-all; margin: 10px 0px; "><table style="word-break: break-all; "><tbody style="word-break: break-all; "><tr style="word-break: break-all; "><td style="word-break: break-all; "><code style="word-break: break-all; ">22</code></td><td style="word-break: break-all; "><code style="word-break: break-all; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code plain"="" style="word-break: break-all; ">18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18 henshao&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Makefile.am</code></td></tr></tbody></table></div></div></div><p style="word-break: break-all; margin: 10px 0px; color: #333333; font-family: Georgia, Arial, Helvetica, sans-serif, 微软雅黑; font-size: 12px; text-align: left; background-color: #ffffff; ">svn一个版本库的revision是全局的，不管是在trunk还是branch，也不管使用merge合并代码还是消除修改，签入和签出都会生成一个新的revision。当项目中一个文件签入时会导致别的文件的BASE暂时低于HEAD，但是一旦update，二者将保持一致。</p><img src ="http://www.blogjava.net/tinysun/aggbug/386100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2012-08-23 13:13 <a href="http://www.blogjava.net/tinysun/archive/2012/08/23/386100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux分布式编译distcc和ccache的部署 </title><link>http://www.blogjava.net/tinysun/archive/2012/02/24/370682.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 24 Feb 2012 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2012/02/24/370682.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/370682.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2012/02/24/370682.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/370682.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/370682.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body">
<p>unset LANGUAGE<br />export LANG="en"<br />cd /home/kingsoft<br />mkdir distcc<br />cd distcc</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>rpm包用：rpm -ivh ...<br />bz2包用：tar -xvf ...<br />进入distcc解压后的目录<br />./configure &amp;&amp; make &amp;&amp; make install<br />mkdir /usr/lib/distcc<br />mkdir /usr/lib/distcc/bin<br />cd /usr/lib/distcc/bin<br />ln -s /usr/local/bin/distcc gcc<br />ln -s /usr/local/bin/distcc cc<br />ln -s /usr/local/bin/distcc g++<br />ln -s /usr/local/bin/distcc c++</p>
<p>&nbsp;</p>
<p><br />进入ccache解压目录<br />./configure &amp;&amp; make &amp;&amp; make install</p>
<p>mkdir /Data<br />mkdir /Data/Cache<br />mkdir /Data/Cache/CCache<br />cd /Data/Cache<br />touch /var/log/distccd.log</p>
<p>vim ~/.bash_profile<br />把 /usr/lib/distcc/bin 加到PATH<br />并添加下面内容<br />## ----- Distcc -----<br /># <br />DISTCC_HOSTS="localhost 192.168.1.1"<br />DISTCC_VERBOSE=1<br />DISTCC_LOG="/var/log/distcc.log"<br />export DISTCC_HOSTS PATH DISTCC_VERBOSE DISTCC_LOG<br />#<br />## ----- End -----</p>
<p>## ----- Ccache -----<br />#<br /># export CCACHE_DISABLE=1<br />CCACHE_DIR=/Data/Cache/CCache<br />CCACHE_LOGFILE=/Data/Cache/CCache.log<br />CCACHE_PREFIX="distcc"<br />CC="ccache gcc"<br />CXX="ccache g++"<br />export CCACHE_DIR CCACHE_LOGFILE CCACHE_PREFIX CC CXX<br />#<br />## ----- End -----</p>
<p><br />vim /etc/rc.local<br />distccd --daemon --allow 10.20.0.0/16</p>
<p><br />==========================================<br />启动监控：distccd --daemon --allow 10.20.0.0/16<br />查看监控：distccmon-text 1</p></div><script type="text/javascript">
if ($ != jQuery) {
	$ = jQuery.noConflict();
}
var isLogined = false;
var cb_blogId = 20515;
var cb_entryId = 1821490;
var cb_blogApp = "linn";
var cb_blogUserGuid = "665c360b-63cf-dd11-9e4d-001cf0cd104b";
var cb_entryCreatedDate = '2010/9/8 15:24:00';
</script><img src ="http://www.blogjava.net/tinysun/aggbug/370682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2012-02-24 14:49 <a href="http://www.blogjava.net/tinysun/archive/2012/02/24/370682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 网络编程之nagle算法和TCP_NODELAY </title><link>http://www.blogjava.net/tinysun/archive/2011/05/20/350659.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 20 May 2011 01:27:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2011/05/20/350659.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/350659.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2011/05/20/350659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/350659.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/350659.html</trackback:ping><description><![CDATA[<p>TCP/IP协议中，无论发送多少数据，总是要在数据前面加上协议头，同时，对方接收到数据，也需要发送ACK表示确认。为了尽可能的利用网络带宽，TCP总是希望尽可能的发送足够大的数据。（一个连接会设置MSS参数，因此，TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据）。<br />Nagle算法就是为了尽可能发送大块数据，避免网络中充斥着许多小数据块。</p>
<p>Nagle算法的基本定义是任意时刻，最多只能有一个未被确认的小段。 所谓&#8220;小段&#8221;，指的是小于MSS尺寸的数据块，所谓&#8220;未被确认&#8221;，是指一个数据块发送出去后，没有收到对方发送的ACK确认该数据已收到。</p>
<p>举个例子，比如之前的blog中的实验，一开始client端调用socket的write操作将一个int型数据(称为A块)写入到网络中，由于此时连接是空闲的（也就是说还没有未被确认的小段），因此这个int型数据会被马上发送到server端，接着，client端又调用write操作写入&#8216;\r\n&#8217;（简称B块），这个时候，A块的ACK没有返回，所以可以认为已经存在了一个未被确认的小段，所以B块没有立即被发送，一直等待A块的ACK收到（大概40ms之后），B块才被发送。整个过程如图所示：</p>
<p>&nbsp;</p>
<p>这里还隐藏了一个问题，就是A块数据的ACK为什么40ms之后才收到？这是因为TCP/IP中不仅仅有nagle算法，还有一个ACK延迟机制 。当Server端收到数据之后，它并不会马上向client端发送ACK，而是会将ACK的发送延迟一段时间（假设为t），它希望在t时间内server端会向client端发送应答数据，这样ACK就能够和应答数据一起发送，就像是应答数据捎带着ACK过去。在我之前的时间中，t大概就是40ms。这就解释了为什么'\r\n'(B块)总是在A块之后40ms才发出。</p>
<p>如果你觉着nagle算法太捣乱了，那么可以通过设置TCP_NODELAY将其禁用 。当然，更合理的方案还是应该使用一次大数据的写操作，而不是多次小数据的写操作。</p>
<p><br />本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/historyasamirror/archive/2011/05/15/6423235.aspx">http://blog.csdn.net/historyasamirror/archive/2011/05/15/6423235.aspx</a></p><img src ="http://www.blogjava.net/tinysun/aggbug/350659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2011-05-20 09:27 <a href="http://www.blogjava.net/tinysun/archive/2011/05/20/350659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解可执行程序的各种神器 </title><link>http://www.blogjava.net/tinysun/archive/2011/01/19/343184.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Tue, 18 Jan 2011 16:05:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2011/01/19/343184.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/343184.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2011/01/19/343184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/343184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/343184.html</trackback:ping><description><![CDATA[<div id="detail" style="line-height: 1.3">
<p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">ldd</span></strong><span style="color: black; font-family: 宋体">查看应用程序链接了哪些动态库。 </span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">nm</span></strong><span style="color: black; font-family: 宋体">列出目标文件中包含的符号信息。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">size</span></strong><span style="color: black; font-family: 宋体">列出各个段的大小及总的大小。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">strings</span></strong><span style="color: black; font-family: 宋体">列出文件中的字符串。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">readelf</span></strong><span style="color: black; font-family: 宋体">读取elf文件的完整结构。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">objdump</span></strong><span style="color: black; font-family: 宋体">导出目标文件的相关信息（elf文件相关工具的源头）。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">gdb</span></strong><span style="color: black; font-family: 宋体">对文件的执行过程进行调试分析，设置断点(b)、单步执行(n)、函数调用追踪(bt)、反汇编(disassemble)。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">strace</span></strong><span style="color: black; font-family: 宋体">跟踪程序中的系统调用及信号处理信息。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">LD_DEBUG</span></strong><span style="color: black; font-family: 宋体">通过设置这个环境变量，可以方便的看到 loader 的加载过程（包括库的加载，符号解析等过程），使用【LD_DEBUG=help 可执行文件路径】可查看使用帮助。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">LD_PRELOAD</span></strong><span style="color: black; font-family: 宋体">环境变量指定的共享库会被预先加载，如果出现重名的函数，预先加载的函数将会被调用，如在预先加载的库中包含自定义的puts函数，则在执行程序时将使用自定义版本的puts函数，而不是libc库中的puts函数。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">proc</span></strong><strong><span style="color: black; font-family: 宋体">文件系统</span></strong><span style="color: black; font-family: 宋体">中包含进程的地址空间映射关系，具体查看/proc/进程id/maps文件的内容。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">valgrind</span></strong><span style="color: black; font-family: 宋体">工具对可执行程序文件进行内存检查（还有cache模拟、调用过程跟踪等功能），以避免内存泄露等问题。</span></p>
<p style="margin: 0cm 0cm 0pt"><strong><span style="color: black; font-family: 宋体">addrline</span></strong><span style="color: black; font-family: 宋体">将可执行文件中的地址转换为其在源文件中对应的位置（文件名：行号）。</span></p>
</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/343184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2011-01-19 00:05 <a href="http://www.blogjava.net/tinysun/archive/2011/01/19/343184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程上下文和中断上下文</title><link>http://www.blogjava.net/tinysun/archive/2010/11/07/337492.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sun, 07 Nov 2010 15:59:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/11/07/337492.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/337492.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/11/07/337492.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/337492.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/337492.html</trackback:ping><description><![CDATA[<p><font style="background-color: #cde9d1">进程上下文和中断上下文是操作系统中很重要的两个概念，这两个概念在操作系统课程中不断被提及，是最经常接触、看上去很懂但又说不清楚到底怎么回事。造成这种局面的原因，可能是原来接触到的操作系统课程的教学总停留在一种浅层次的理论层面上，没有深入去研究。</font></p>
<p><font style="background-color: #cde9d1">处理器总处于以下状态中的一种：<br />
１、内核态，运行于进程上下文，内核代表进程运行于内核空间；<br />
２、内核态，运行于中断上下文，内核代表硬件运行于内核空间；<br />
３、用户态，运行于用户空间。</font></p>
<p><font style="background-color: #cde9d1">用户空间的应用程序，通过系统调用，进入内核空间。这个时候用户空间的进程要传递很多变量、参数的值给内核，内核态运行的时候也要保存用户进程的一些寄存器值、变量等。所谓的&#8220;进程上下文&#8221;，可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。</font></p>
<p><font style="background-color: #cde9d1">硬件通过触发信号，导致内核调用中断处理程序，进入内核空间。这个过程中，硬件的一些变量和参数也要传递给内核，内核通过这些参数进行中断处理。所谓的&#8220;中断上下文&#8221;，其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境（主要是当前被打断执行的进程环境）。</font></p>
<font style="background-color: #cde9d1">
<p><br />
关于进程上下文LINUX完全注释中的一段话： </p>
<p>&nbsp;&nbsp; 当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。当内核需要切换到另一个进程时，它需要保存当前进程的所有状态，即保存当前进程的上下文，以便在再次执行该进程时，能够必得到切换时的状态执行下去。在LINUX中，当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中，在内核态下执行中断服务例程。但同时会保留所有需要用到的资源，以便中继服务结束时能恢复被中断进程的执行。</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/eroswang/archive/2007/11/28/1905830.aspx</font></p>
<img src ="http://www.blogjava.net/tinysun/aggbug/337492.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-11-07 23:59 <a href="http://www.blogjava.net/tinysun/archive/2010/11/07/337492.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Socket send函数和recv函数详解</title><link>http://www.blogjava.net/tinysun/archive/2010/10/20/335699.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Wed, 20 Oct 2010 08:21:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/10/20/335699.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/335699.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/10/20/335699.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/335699.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/335699.html</trackback:ping><description><![CDATA[<meta http-equiv="content-type" content="text/html; charset=utf-8" /><span  style="border-collapse: collapse; ">
<p><font size="3">i<font size="2">nt send( SOCKET s,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char FAR *buf,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int len,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int flags );&nbsp;&nbsp;</font></font></p>
<p><font size="2">不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。</font></p>
<p><font size="2">客户程序一般用send函数向服务器发送请求，而服务器则通常用send函数来向客户程序发送应答。</font></p>
<p><font size="2">该函数的第一个参数指定发送端套接字描述符；</font></p>
<p><font size="2">第二个参数指明一个存放应用程序要发送数据的缓冲区；</font></p>
<p><font size="2">第三个参数指明实际要发送的数据的字节数；</font></p>
<p><font size="2">第四个参数一般置0。</font></p>
<p><font size="2">这里只描述同步Socket的send函数的执行流程。当调用该函数时，send先比较待发送数据的长度len和套接字s的<strong><strong>发送缓冲</strong>的 长度</strong>，如果len大于s的发送缓冲区的长度，该函数返回SOCKET_ERROR；如果len小于或者等于s的发送缓冲区的长度，那么send先检查协议是否正在发送s的发送缓冲中的数据，如果是就等待协议把数据发送完，如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据，那么 send就比较s的发送缓冲区的剩余空间和len，如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完，如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里（<strong><font color="#000000">注意并不是send把s的发送缓冲中的数据传到连接的另一端的，而是协议传的，</font></strong><font color="#ff0000"><strong><font color="#000000"><strong>send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里</strong></font></strong><font color="#000000">）。</font></font>如果send函数copy数据成功，就返回实际copy的字节数，如果send在copy数据时出现错误，那么send就返回SOCKET_ERROR；如果send在等待协议传送数据时网络断开的话，那么send函数也返回SOCKET_ERROR。</font></p>
<p><font size="2"><font color="#000000"><strong>要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了，但是此时这些数据并不一定马上被传到连接的另一端</strong>。</font>如果协议在后续的传送过程中出现网络错误的话，那么下一个Socket函数就会返回SOCKET_ERROR。（每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续，如果在等待时出现网络错误，那么该Socket函数就返回 SOCKET_ERROR）</font></p>
<p><font size="2">注意：在Unix系统下，如果send在等待协议传送数据时网络断开的话，调用send的进程会接收到一个SIGPIPE信号，进程对该信号的默认处理是进程终止。</font></p>
<p><font color="#ff0000">通过测试发现，异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数，同时使用select检测也是可写的，但是过几秒钟之后，再send就会出错了，返回-1。select也不能检测出可写了。</font></p>
<p><font size="3"><font color="#000000" size="3"><strong>recv函数</strong></font><br />
</font></p>
<p><font size="3">i<font size="2">nt recv( SOCKET s,&nbsp;&nbsp;&nbsp;&nbsp; char FAR *buf,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int len,&nbsp;&nbsp;&nbsp;&nbsp; int flags&nbsp;&nbsp;&nbsp;&nbsp; );&nbsp;&nbsp;&nbsp;</font></font></p>
<p><font size="2">不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。</font></p>
<p><font size="2">该函数的第一个参数指定接收端套接字描述符；</font></p>
<p><font size="2">第二个参数指明一个缓冲区，该缓冲区用来存放recv函数接收到的数据；</font></p>
<p><font size="2">第三个参数指明buf的长度；</font></p>
<p><font size="2">第四个参数一般置0。</font></p>
<p><font size="2">这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时，recv先等待s的发送缓冲中的数据被协议传送完毕，如果协议在传送s的发送缓冲中的数据时出现网络错误，那么recv函数返回SOCKET_ERROR，如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后，recv先检查套接字s的接收缓冲区，如果s接收缓冲区中没有数据或者协议正在接收数据，那么recv就一直等待，只到协议把数据接收完毕。当协议把数据接收完毕，recv函数就把s的接收缓冲中的数据copy到buf中（<strong><font color="#000000">注意协议接收到的数据可能大于buf的长度，所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。<strong>recv函数仅仅是copy数据，真正的接收数据是协议来完成的</strong></font></strong>），recv函数返回其实际copy的字节数。如果recv在copy时出错，那么它返回SOCKET_ERROR；如果recv函数在等待协议接收数据时网络中断了，那么它返回0。</font></p>
<p><font size="2">注意：在Unix系统下，如果recv函数在等待协议接收数据时网络断开了，那么调用recv的进程会接收到一个SIGPIPE信号，进程对该信号的默认处理是进程终止。</font></p>
</span>
<img src ="http://www.blogjava.net/tinysun/aggbug/335699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-10-20 16:21 <a href="http://www.blogjava.net/tinysun/archive/2010/10/20/335699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>。。。。</title><link>http://www.blogjava.net/tinysun/archive/2010/10/11/334455.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Mon, 11 Oct 2010 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/10/11/334455.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/334455.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/10/11/334455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/334455.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/334455.html</trackback:ping><description><![CDATA[http://blog.csdn.net/absurd/archive/2006/07/25/976820.aspx
<div>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /><a href="http://blog.csdn.net/eroswang/category/322381.aspx?PageNumber=2">http://blog.csdn.net/eroswang/category/322381.aspx?PageNumber=2</a></div>
<img src ="http://www.blogjava.net/tinysun/aggbug/334455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-10-11 17:20 <a href="http://www.blogjava.net/tinysun/archive/2010/10/11/334455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>信号“未决”与“阻塞”</title><link>http://www.blogjava.net/tinysun/archive/2010/10/11/334368.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Mon, 11 Oct 2010 07:47:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/10/11/334368.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/334368.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/10/11/334368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/334368.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/334368.html</trackback:ping><description><![CDATA[<span style="font-size: x-small;"><span style="font-size: small;"><span style="font-weight: bold;">信号状态：</span></span><br />
&nbsp;&nbsp;&nbsp; <span style="color: #ff0102;">信号的&#8221;未决&#8220;是一种状态</span>，指的是从信号的产生到信号被处理前的这一段时间；<span style="color: #ff0102;">信号的&#8221;阻塞&#8220;是一个开关动作</span>，指的是阻止信号被处理，但不是阻止信号产生。 <br />
&nbsp;&nbsp;&nbsp;
APUE例题在sleep前用sigprocmask阻塞了退出信号，然后sleep,然后在sleep的过程中产生一个退出信号，但是此时退出信号被阻
塞过，（中文的&#8221;阻塞&#8221;在这里容易被误解为一种状态，实际上是一种类似于开关的动作，所以说&#8220;被阻塞过&#8221;，而不是&#8220;被阻塞&#8221;）所以处于&#8220;未决&#8221;状态，在
sleep后又用sigprocmask关掉退出信号的阻塞开关，因为之前产生的退出信号一直处于未决状态，当<span style="color: #ff0102;">关上阻塞开关后，马上退出&#8220;未决&#8221;状态，得到处理，这一切发生在sigprocmask返回之前。</span> <br />
<br />
<span style="font-size: small;"><span style="font-weight: bold;">信号生命周期:</span></span><br />
&nbsp;&nbsp;&nbsp;
对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说，可以分为三个重要的阶段，这三个阶段由四个重要事件来刻画：1.信号诞生；2.
信号在进程中注册完毕；3.信号在进程中的注销完毕；4.信号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的一个阶段。<br />
&nbsp;&nbsp;&nbsp; 下面阐述四个事件的实际意义：<br />
1.信号"诞生"。信号的诞生指的是触发信号的事件发生（如检测到硬件异常、定时器超时以及调用信号发送函数kill()或sigqueue()等）。 <br />
<br />
2.信号在目标进程中"注册"；<br />
&nbsp;&nbsp;&nbsp; 进程的task_struct结构中有关于本进程中未决信号的数据成员： <br />
struct sigpending pending;<br />
struct sigpending<br />
{<br />
&nbsp;&nbsp;&nbsp; struct sigqueue *head, **tail;<br />
&nbsp;&nbsp;&nbsp; sigset_t signal;<br />
};<br />
第一、第二个成员分别指向一个sigqueue类型的结构链（称之为"未决信号信息链"）的首尾，</span><span style="font-size: x-small;">第三个成员是进程中所有未决信号集，</span><span style="font-size: x-small;">信息链中的每个sigqueue结构体刻画一个特定信号所携带的信息，并指向下一个sigqueue结构: <br />
struct sigqueue<br />
{<br />
&nbsp;&nbsp;&nbsp; struct sigqueue *next;<br />
&nbsp;&nbsp;&nbsp; siginfo_t info;<br />
};<br />
&nbsp;&nbsp;&nbsp;
信号在进程中注册指的就是信号值加入到进程的未决信号集中（sigpending结构的第二个成员sigset_t
signal），并且信号所携带的信息被保留到未决信号信息链的某个sigqueue结构中。只要信号在进程的未决信号集中，表明进程已经知道这些信号的
存在，但还没来得及处理，或者该信号被进程阻塞。 <br />
<span style="font-weight: bold;">注：</span> <br />
&nbsp;&nbsp;&nbsp; 当一个实时信号发送给一个进程时，不管该信号是否已经在进程中注册，都会被再注册一次，因此，信号不会丢失，因此，<span style="color: #ff0102;">实时信号又叫做"可靠信号"</span>。这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个sigqueue结构（进程每收到一个实时信号，都会为它分配一个结构来登记该信号信息，并把该结构添加在未决信号链尾，即所有诞生的实时信号都会在目标进程中注册）； <br />
当一个非实时信号发送给一个进程时，如果该信号已经在进程中注册，则该信号将被丢弃，造成信号丢失。因此，<span style="color: #ff0102;">非实时信号又叫做"不可靠信号"</span>。
这意味着同一个非实时信号在进程的未决信号信息链中，至多占有一个sigqueue结构（一个非实时信号诞生后，（1）、如果发现相同的信号已经在目标结
构中注册，则不再注册，对于进程来说，相当于不知道本次信号发生，信号丢失；（2）、如果进程的未决信号中没有相同信号，则在进程中注册自己）。 <br />
<br />
3.
信号在进程中的注销。在目标进程执行过程中，会检测是否有信号等待处理（每次从系统空间返回到用户空间时都做这样的检查）。如果存在未决信号等待处理且该
信号没有被进程阻塞，则在运行相应的信号处理函数前，进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号
是不同的。对于非实时信号来说，由于在未决信号信息链中最多只占用一个sigqueue结构，因此该结构被释放后，应该把信号在进程未决信号集中删除（信
号注销完毕）；而对于实时信号来说，可能在未决信号信息链中占用多个sigqueue结构，因此应该针对占用gqueue结构的数目区别对待：如果只占用
一个sigqueue结构（进程只收到该信号一次），则应该把信号在进程的未决信号集中删除（信号注销完毕）。否则，不在进程的未决信号集中删除该信号
（信号注销完毕）。<span style="color: #ff0102;">进程在执行信号相应处理函数之前，首先要把信号在进程中注销。</span> <br />
<br />
4.信号生命终止。进程注销信号后，立即执行相应的信号处理函数，执行完毕后，信号的本次发送对进程的影响彻底结束。 <br />
<span style="font-weight: bold;">注： </span><br />
1）
信号注册与否，与发送信号的函数（如kill()或sigqueue()等）以及信号安装函数（signal()及sigaction()）无关，只与信
号值有关（信号值小于SIGRTMIN的信号最多只注册一次，信号值在SIGRTMIN及SIGRTMAX之间的信号，只要被进程接收到就被注册）。 <br />
2）在信号被注销到相应的信号处理函数执行完毕这段时间内，如果进程又收到同一信号多次，则对实时信号来说，每一次都会在进程中注册；而对于非实时信号来说，无论收到多少次信号，都会视为只收到一个信号，只在进程中注册一次。</span>
<img src ="http://www.blogjava.net/tinysun/aggbug/334368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-10-11 15:47 <a href="http://www.blogjava.net/tinysun/archive/2010/10/11/334368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux的内存管理FAQ</title><link>http://www.blogjava.net/tinysun/archive/2010/09/30/333559.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 30 Sep 2010 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/30/333559.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333559.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/30/333559.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333559.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333559.html</trackback:ping><description><![CDATA[<p><strong>一、</strong><strong>Linux</strong><strong>内存管理到底是分段还是分页？</strong></p>
<p align="left">在实模式下没有引入段式内存管理模式前,程序都是使用绝对地址来进行内存操作的,这样造成了程序的可移植性差等问题。于
是在8086时代开始引入段式内存管理,段式管理引入了地址映射的概念,不但解决了可移植性问题，也解决了16位ALU和段寄存器的可寻址限制,即支持更
大的内存寻址.每个段的大小是64KB.</p>
<p align="left">在实模式下,由于系统程序和用户程序在访问内存等资源时权限没有区分,所以很可能造成操作系统出现异常.于是Intel在80286时代开发出来了保护模式.</p>
<p align="left">而Intel为了兼容80386前的处理器就规定：在IA(Intel
Arch)32保护模式下，不能禁止分段，但是分页是Optional的。/*When operating in protected mode,
some form of segmentation must be used. There is no mode bit to disable
segmentation. The use of paging, however, is optional.*/.</p>
<p align="left">既然linux在内存管理上不能禁止分段,那么linux又认为分段会造成大量的内存碎片,所以就通过将逻辑地址中的段基址置为0,加上偏移量形成线性地址,来简化段式管理内存而使用分页管理内存的。</p>
<p>在IA32下,每页是4K.</p>
<p><strong>二、</strong><strong>linux</strong><strong>的三种类型内存地址及关系是什么样的？</strong></p>
<p>分段&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分页</p>
<p>逻辑地址————&gt; 线性地址 —————-&gt;物理地址</p>
<p>在 x86 架构中，内存被划分成 3 种类型的地址：</p>
<pre>逻辑地址 (logical address) 由16位段选择符:32位偏移量来表示，它有可能直接对应于一个物理位置(Intel实模式下)。</pre>
<pre>线性地址 (linear address)或虚拟地址 是由段描述符中的32位段基址和逻辑地址中的32位偏移量相加构成的。例如:0x08048394,由于linux把所有的段基址都置为0x00000000.所以线性地址=0x00000000+偏移量=逻辑地址,在不分页的情况下：线性地址就直接是物理地址;</pre>
<p>物理地址 (physical address) 是使用物理地址总线中的位表示的地址。内存管理单元可以将逻辑地址转换成物理地址。</p>
<p><strong>三、为什么在</strong><strong>32</strong><strong>位的操作系统下每个进程会有</strong><strong>4G</strong><strong>的内存使用限制</strong><strong>?</strong></p>
<p>由于32位操作系统下,由于虚拟地址空间是2的32次方=4&nbsp;294&nbsp;967&nbsp;296,通常的数据对象都以字节为单位,所以一个进程的可寻址范围就是4G内存</p>
<p><strong>四、为什么</strong><strong>linux</strong><strong>服务器是</strong><strong>32</strong><strong>位的还可以认出来多于</strong><strong>4G</strong><strong>的内存</strong><strong>?</strong></p>
<p>本来在IA32下,机器只能认出4G的内存,但Intel为了让32位的OS能支持更多的内存。采取了PAE(Physical Address
Extension, 物理地址扩展)技术.PAE的支持,不仅需要处理器的支持,OS的内核也需要支持才可行(我们通常采用的RHEL3/4/5
kernel都支持PAE),也就是处理器内部增加了PAE寄存器,用于记录多出的4条地址总线，所以系统就是2^36=64G.</p>
<p><strong>五、在</strong><strong>32</strong><strong>位的</strong><strong>linux</strong><strong>操作系统下</strong><strong>,</strong><strong>为什么</strong><strong>linux</strong><strong>系统认出来的内存大于</strong><strong>4G,</strong><strong>而我的程序每个进程却只能分配</strong><strong>4G</strong><strong>以下的内存</strong><strong>?</strong></p>
<p>虽然Intel为其处理器为支持PAE增加了4条地址线，但因为虚拟地址是32位的,所以单个进程还是只能分配4G以下的内存</p>
<p>Note: Linux can use up to 64 Gigabytes of physical memory on x86
systems. However, the address space of 32-bit x86 processors is only 4
Gigabytes large. Thus means that, if you have a large amount of physical
memory, not all of it can be &#8220;permanently mapped&#8221; by the kernel. The
physical memory that&#8217;s not permanently mapped is called &#8220;high memory&#8221;.</p>
<p>Redhat网站http://www.redhat.com/rhel/compare/</p>
<p><strong>六、</strong><strong>32</strong><strong>位的</strong><strong>RedHat linux</strong><strong>在</strong><strong>&#8221;smp&#8221; kernel</strong><strong>和</strong><strong>&#8221;hugemem&#8221; kernel</strong><strong>中内存支持方面有什么建议</strong><strong>?</strong></p>
<p>Smp kernel和hugemem
kernel都支持PAE支持,也就意味着最大能支持64G的内存。而RedHat建议当你的物理内存在16GB之内,用SMP&#8221;
kernel,在16GB-64GB之间使用&#8221;Hugemem&#8221;
kernel.这是因为虚拟地址空间里有1G用于内核空间，而3G用于用户空间。而关健的一些数据结构是存放在1G内核空间的，在管理32G内存当中，需
要用到0.5G来用于管理这些物理内存(容易触发OOM
killer).虽然32位OS下,内核和用户空间的比例都是1：3，但Hugemem打了一个补丁,使比例成为4G:4G,即使内核空间和用户空间相互
独立，所以也会有性能上的损失,因为应用程序的运行,通常会有内核和用户空间的切换。所以如果你的内存大于16G,建议你使用64位的OS。</p>
<p>/* The &#8220;SMP&#8221; kernel supports a maximum of 16GB of main memory.
Systems with more than 16GB of main memory use the &#8220;Hugemem&#8221; kernel. In
certain workload scenarios it may be advantageous to use the &#8220;Hugemem&#8221;
kernel on systems with more than 12GB of main memory. */</p>
<p><strong>七、我们通常</strong><strong>malloc</strong><strong>了</strong><strong>4K</strong><strong>内存</strong><strong>,</strong><strong>到底是怎么对应到物理内存的？</strong></p>
<p>在linux下,处理器在得到内存的线性地址进行内存寻址时，不是直接在内存的物理地址里查找的，而是通过线性地址转换到主内存的物理地
址，TLB(Translation lookaside
buffer,可以简单的理解为:一种存储线性地址和物理地址的硬件高速缓冲器)就是负责将虚拟内存地址翻译成实际的物理内存地址，而CPU寻址时会优先
在TLB中进行寻址。如果TLB里没有,则从页表里进行线性地址到物理地址的转换.hugepage能增加TLB的命中率，所以会在某些方面能大大提高系
统性能)。</p>
<p align="left"><strong>1</strong><strong>、逻辑地址转线性地址</strong></p>
<p><img size-full="" wp-image-541="" title="LL.jpg" src="http://www.opensolution.org.cn/wp-content/uploads/2009/12/LL.jpg.gif" alt="LL.jpg" width="960" height="720" /></p>
<p>a、首先根据指令的性质来确定该使用哪一个段寄存器。<br />
b、根据段寄存器的内容，到GDT中找到相应的&#8220;段描述结构&#8221;</p>
<p>c、根据linux把所有的段基址都置为0&#215;00000000.所以0&#215;00000000+偏移量就是线性地址了.在不分页的情况下：线性地址就直接是物理地址;<br />
同时，在上面过程中，由于有对访问权限的检查，就实现了保护。</p>
<p align="left"><strong>2</strong><strong>、线性地址转物理地址</strong></p>
<p>在保护模式下，控制寄存器CR0的最高位PG位(PE位控制是否为保护模式)控制着分页管理机制是否生效，如果PG=1，分页机制生效，需通过页表查找才能把线性地址转换物理地址。如果PG=0，则分页机制无效，线性地址就直接做为物理地址。</p>
<p>页式内存管理中，32位的线性地址划分为三个部分：10位的页目录表下标、10位的页面表下标、12位的页内偏移量。CPU增加了一个CR3寄存器存放指向当前页目录表的指针。寻址方式就改为：<br />
a、从CR3取得页目录表的基地址；<br />
b、根据10位页目录表下标和从CR3取得的基地址，得到相应页表的基地址；<br />
c、根据10位页面表下标和b中得到的页表基地址，从页面表中取得相应的页面描述项；<br />
d、将页面描述项中的页面基地址和线性地址中的12位页内地址偏移相加，得到物理地址。<br />
同时，在地址转换的过程中也有越界和权限的检查，就不赘述了。</p>
<p><img size-full="" wp-image-542="" title="LP" src="http://www.opensolution.org.cn/wp-content/uploads/2009/12/LP.gif" alt="LP" width="646" height="539" /></p>
<p>注:传统的32位操作系统采用的是两级分页模型</p>
<p>64位和PAE支持都采用了三级分页模型</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/333559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-30 16:22 <a href="http://www.blogjava.net/tinysun/archive/2010/09/30/333559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux slab 分配器剖析</title><link>http://www.blogjava.net/tinysun/archive/2010/09/30/333537.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 30 Sep 2010 05:46:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/30/333537.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333537.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/30/333537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333537.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333537.html</trackback:ping><description><![CDATA[http://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
<img src ="http://www.blogjava.net/tinysun/aggbug/333537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-30 13:46 <a href="http://www.blogjava.net/tinysun/archive/2010/09/30/333537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>频繁分配释放内存导致的性能问题的分析</title><link>http://www.blogjava.net/tinysun/archive/2010/09/30/333536.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 30 Sep 2010 05:35:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/30/333536.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333536.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/30/333536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333536.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333536.html</trackback:ping><description><![CDATA[<p><strong>现象<br />
</strong>1 压力测试过程中，发现被测对象性能不够理想，具体表现为： <br />
进程的系统态CPU消耗20，用户态CPU消耗10，系统idle大约70 <br />
2 用ps -o majflt,minflt -C program命令查看，发现majflt每秒增量为0，而minflt每秒增量大于10000。<br />
<br />
<strong>初步分析</strong><br />
majflt代表major fault，中文名叫大错误，minflt代表minor fault，中文名叫小错误。<br />
这两个数值表示一个进程自启动以来所发生的缺页中断的次数。<br />
当一个进程发生缺页中断的时候，进程会陷入内核态，执行以下操作： <br />
检查要访问的虚拟地址是否合法 <br />
查找/分配一个物理页 <br />
填充物理页内容（读取磁盘，或者直接置0，或者啥也不干） <br />
建立映射关系（虚拟地址到物理地址） <br />
重新执行发生缺页中断的那条指令 <br />
如果第3步，需要读取磁盘，那么这次缺页中断就是majflt，否则就是minflt。 <br />
此进程minflt如此之高，一秒10000多次，不得不怀疑它跟进程内核态cpu消耗大有很大关系。<br />
<br />
<strong>分析代码</strong><br />
查看代码，发现是这么写的：一个请求来，用malloc分配2M内存，请求结束后free这块内存。看日志，发现分配内存语句耗时10us，平均一条请求处理耗时1000us 。 原因已找到！ <br />
虽然分配内存语句的耗时在一条处理请求中耗时比重不大，但是这条语句严重影响了性能。要解释清楚原因，需要先了解一下内存分配的原理。 <br />
<br />
<strong>内存分配的原理</strong><br />
从
操作系统角度来看，进程分配内存有两种方式，分别由两个系统调用完成：brk和mmap（不考虑共享内存）。brk是将数据段(.data)的最高地址指
针_edata往高地址推，mmap是在进程的虚拟地址空间中（一般是堆和栈中间）找一块空闲的。这两种方式分配的都是虚拟内存，没有分配物理内存。在第
一次访问已分配的虚拟地址空间的时候，发生缺页中断，操作系统负责分配物理内存，然后建立虚拟内存和物理内存之间的映射关系。 <br />
在标准C库中，提供了malloc/free函数分配释放内存，这两个函数底层是由brk，mmap，munmap这些系统调用实现的。 <br />
下面以一个例子来说明内存分配的原理：<br />
<img src="http://images.csdn.net/20100325/c-1.jpg" alt="" /></p>
<p>1进程启动的时候，其（虚拟）内存空间的初始布局如图1所示。其中，mmap内存映射文件是在堆和栈的中间（例如libc-2.2.93.so，其它数据文件等），为了简单起见，省略了内存映射文件。_edata指针（glibc里面定义）指向数据段的最高地址。 <br />
2
进程调用A=malloc(30K)以后，内存空间如图2：malloc函数会调用brk系统调用，将_edata指针往高地址推30K，就完成虚拟内存
分配。你可能会问：只要把_edata+30K就完成内存分配了？事实是这样的，_edata+30K只是完成虚拟地址的分配，A这块内存现在还是没有物
理页与之对应的，等到进程第一次读写A这块内存的时候，发生缺页中断，这个时候，内核才分配A这块内存对应的物理页。也就是说，如果用malloc分配了
A这块内容，然后从来不访问它，那么，A对应的物理页是不会被分配的。 <br />
3进程调用B=malloc(40K)以后，内存空间如图3.</p>
<p><img src="http://images.csdn.net/20100325/c-2.jpg" alt="" /></p>
<p>4进程调用C=malloc(200K)以后，内存空间如图4：默认情况下，malloc函数分配内存，如果请求内存大于128K（可由
M_MMAP_THRESHOLD选项调节），那就不是去推_edata指针了，而是利用mmap系统调用，从堆和栈的中间分配一块虚拟内存。这样子做主
要是因为brk分配的内存需要等到高地址内存释放以后才能释放（例如，在B释放之前，A是不可能释放的），而mmap分配的内存可以单独释放。当然，还有
其它的好处，也有坏处，再具体下去，有兴趣的同学可以去看glibc里面malloc的代码了。 <br />
5进程调用D=malloc(100K)以后，内存空间如图5. <br />
6进程调用free(C)以后，C对应的虚拟内存和物理内存一起释放</p>
<p>4进程调用C=malloc(200K)以后，内存空间如图4：默认情况下，malloc函数分配内存，如果请求内存大于128K（可由
M_MMAP_THRESHOLD选项调节），那就不是去推_edata指针了，而是利用mmap系统调用，从堆和栈的中间分配一块虚拟内存。这样子做主
要是因为brk分配的内存需要等到高地址内存释放以后才能释放（例如，在B释放之前，A是不可能释放的），而mmap分配的内存可以单独释放。当然，还有
其它的好处，也有坏处，再具体下去，有兴趣的同学可以去看glibc里面malloc的代码了。 <br />
5进程调用D=malloc(100K)以后，内存空间如图5. <br />
6进程调用free(C)以后，C对应的虚拟内存和物理内存一起释放 <br />
<img src="http://images.csdn.net/20100325/c-3.jpg" alt="" /></p>
<p>7进程调用free(B)以后，如图7所示。B对应的虚拟内存和物理内存都没有释放，因为只有一个_edata指针，如果往回推，那么D这块内存怎
么办呢？当然，B这块内存，是可以重用的，如果这个时候再来一个40K的请求，那么malloc很可能就把B这块内存返回回去了。 <br />
8进程调用free(D)以后，如图8所示。B和D连接起来，变成一块140K的空闲内存。 <br />
9默认情况下：当最高地址空间的空闲内存超过128K（可由M_TRIM_THRESHOLD选项调节）时，执行内存紧缩操作（trim）。在上一个步骤free的时候，发现最高地址空闲内存超过128K，于是内存紧缩，变成图9所示。<br />
<br />
真相大白<br />
说
完内存分配的原理，那么被测模块在内核态cpu消耗高的原因就很清楚了：每次请求来都malloc一块2M的内存，默认情况下，malloc调用mmap
分配内存，请求结束的时候，调用munmap释放内存。假设每个请求需要6个物理页，那么每个请求就会产生6个缺页中断，在2000的压力下，每秒就产生
了10000多次缺页中断，这些缺页中断不需要读取磁盘解决，所以叫做minflt；缺页中断在内核态执行，因此进程的内核态cpu消耗很大。缺页中断分
散在整个请求的处理过程中，所以表现为分配语句耗时（10us）相对于整条请求的处理时间（1000us）比重很小。<br />
<br />
解决办法<br />
将动态内存改为静态分配，或者启动的时候，用malloc为每个线程分配，然后保存在threaddata里面。但是，由于这个模块的特殊性，静态分配，或者启动时候分配都不可行。另外，Linux下默认栈的大小限制是10M，如果在栈上分配几M的内存，有风险。 <br />
禁止malloc调用mmap分配内存，禁止内存紧缩。<br />
在进程启动时候，加入以下两行代码：<br />
mallopt(M_MMAP_MAX, 0); // 禁止malloc调用mmap分配内存<br />
mallopt(M_TRIM_THRESHOLD, -1); // 禁止内存紧缩<br />
效果：加入这两行代码以后，用ps命令观察，压力稳定以后，majlt和minflt都为0。进程的系统态cpu从20降到10。<br />
<br />
小结<br />
可以用命令ps -o majflt minflt -C program来查看进程的majflt, minflt的值，这两个值都是累加值，从进程启动开始累加。在对高性能要求的程序做压力测试的时候，我们可以多关注一下这两个值。 <br />
如果一个进程使用了mmap将很大的数据文件映射到进程的虚拟地址空间，我们需要重点关注majflt的值，因为相比minflt，majflt对于性能的损害是致命的，随机读一次磁盘的耗时数量级在几个毫秒，而minflt只有在大量的时候才会对性能产生影响。</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/333536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-30 13:35 <a href="http://www.blogjava.net/tinysun/archive/2010/09/30/333536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>系统管理中 bash shell 脚本常用方法总结</title><link>http://www.blogjava.net/tinysun/archive/2010/09/30/333468.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 30 Sep 2010 02:54:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/30/333468.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333468.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/30/333468.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333468.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333468.html</trackback:ping><description><![CDATA[<div>
<p>在日常系统管理工作中，需要编写脚本来完成特定的功能，编写shell脚本是一个基本功了！<br />
在编写的过程中，掌握一些常用的技巧和语法就可以完成大部分功能了，也就是2/8原则.</p>
<h1>1. 单引号和双引号的区别</h1>
<p>单引号与双引号的最大不同在于双引号仍然可以引用变量的内容，但单引号内仅是 普通字符 ，不会作变量的引用，直接输出字符窜。请看如下例子：</p>
<pre>  [root@linux ~]# name=HaHa<br />
[root@linux ~]# echo $name<br />
HaHa<br />
[root@linux ~]# myname="$name is wow"<br />
[root@linux ~]# echo $myname<br />
HaHa is wow<br />
[root@linux ~]# myname='$name is wow'<br />
[root@linux ~]# echo $myname<br />
$name is wow</pre>
<p>从上面例子可以看出,使用了单引号的时候，那么$name只是普通字符,直接输出而已！</p>
<h1>2. 逐行读取文件</h1>
<ul>
    <li>使用for循环来读取文件
    <pre>  for line in `cat file.txt`<br />
    do<br />
    echo $line<br />
    done</pre>
    </li>
</ul>
<blockquote>
<p>注意:由于使用for来读入文件里的行时，会自动把空格和换行符作为一样分隔符，如果行里有空格的时候，输出的结果会很乱，所以只适用于行连续不能有空格或者换行符的文件</p>
</blockquote>
<ul>
    <li>使用while循环读取文件
    <pre>  cat file.txt |while read line<br />
    do<br />
    echo $line<br />
    done<br />
    <br />
    或者：<br />
    <br />
    while read line<br />
    do<br />
    echo $line<br />
    done &lt; file.txt</pre>
    </li>
</ul>
<blockquote>
<p>注意:由于使用while来读入文件里的行时，会整行读入，不会关注行的内容(空格..)，所以比for读文件有更好的适用性，推荐使用while循环读取文件</p>
</blockquote>
<h1>3. bash shell 脚本中常用隐含变量</h1>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <td>$0</td>
            <td>当前执行的脚本或者命令名称</td>
        </tr>
        <tr>
            <td>$1-$9</td>
            <td>代表参数的位置. 举例 $1 代表第一个参数.</td>
        </tr>
        <tr>
            <td>$#</td>
            <td>脚本调用的参数的个数</td>
        </tr>
        <tr>
            <td>$@</td>
            <td>所有参数的内容</td>
        </tr>
        <tr>
            <td>$*</td>
            <td>所有参数的内容</td>
        </tr>
        <tr>
            <td>$$</td>
            <td>当前运行脚本的进程号</td>
        </tr>
        <tr>
            <td>$?</td>
            <td>命令执行后返回的状态</td>
        </tr>
        <tr>
            <td>$!</td>
            <td>后台运行的最后一个进程号</td>
        </tr>
    </tbody>
</table>
<blockquote>
<p>注意: $? 用于检查上一个命令执行是否正确(在Linux中，命令退出状态为0表示该命令正确执行，任何非0值表示命令出错)<br />
$$ 变量最常见的用途是用做暂存文件的名字以保证暂存文件不会重复。<br />
$* 和 $@ 如果输出是一样的，但是在使用for循环，在使用 双引号(&#8221;")引用时 &#8220;$*&#8221; 会输出成一个元素 而 &#8220;$@&#8221; 会按照每个参数是一个元素方式输出</p>
</blockquote>
<p>请看测试例子</p>
<pre>  #cat test.sh<br />
#!/bin/sh<br />
echo '"$@" output.....'<br />
for i in "$@"<br />
do<br />
echo $i<br />
done<br />
echo '"$*" output ....'<br />
for i in "$*"<br />
do<br />
echo $i<br />
done</pre>
<p>输出结果</p>
<pre>  #sh test.sh a b c d<br />
"$@" output.....<br />
a<br />
b<br />
c<br />
d<br />
"$*" output ....<br />
a b c d</pre>
<blockquote>
<p>从输出结果可以看出 &#8220;$*&#8221; 输出是一行 而 &#8220;$@&#8221; 输出则是四行</p>
</blockquote>
<h1>4. 变量内容的删除与替换</h1>
<p>我们在一些情况下，需要对变量中的字符窜进行查找删除或者替换，就需要使用下表列出的方法</p>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <th>变量设定方式</th>
            <th>说明</th>
        </tr>
        <tr>
            <td>${变量#关键字}</td>
            <td>若变量内容从头开始的资料符合&#8216;关键字&#8217;，则将符合的最短资料删除</td>
        </tr>
        <tr>
            <td>${变量##关键字}</td>
            <td>若变量内容从头开始的资料符合&#8216;关键字&#8217;，则将符合的最长资料删除</td>
        </tr>
        <tr>
            <td>${变量%关键字}</td>
            <td>若变量内容从尾向前的资料符合&#8216;关键字&#8217;，则将符合的最短资料删除</td>
        </tr>
        <tr>
            <td>${变量%%关键字}</td>
            <td>若变量内容从尾向前的资料符合&#8216;关键字&#8217;，则将符合的最长资料删除</td>
        </tr>
        <tr>
            <td>${变量/旧字串/新字串}</td>
            <td>若变量内容符合&#8216;旧字串&#8217;则&#8216;第一个旧字串会被新字串取代</td>
        </tr>
        <tr>
            <td>${变量//旧字串/新字串}</td>
            <td colspan="2">若变量内容符合&#8216;旧字串&#8217;则&#8216;全部的旧字串会被新字串取代</td>
        </tr>
    </tbody>
</table>
<p>举例如下(删除字符窜中的某个字符):</p>
<pre>  [root@linux ~]# export test_str="/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"<br />
[root@linux ~]# echo ${test_str#/*kerberos/bin:}<br />
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin</pre>
<h1>5. 变量条件测试赋值</h1>
<p>在某些时刻我们需要&#8216;判断&#8217;某个变量是否存在，若变量存在则将此变量值赋值给新的变量，若变量不存在则将其他值赋值给新的变量.</p>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <th>变量设定方式</th>
            <th>str 未定义</th>
            <th>str 为空字串</th>
            <th>str 已赋值为非空字串</th>
        </tr>
        <tr>
            <td>var=${str-expr}</td>
            <td>var=expr</td>
            <td>var=</td>
            <td>var=$str</td>
        </tr>
        <tr>
            <td>var=${str:-expr}</td>
            <td>var=expr</td>
            <td>var=expr</td>
            <td>var=$str</td>
        </tr>
        <tr>
            <td>var=${str+expr}</td>
            <td>var=</td>
            <td>var=expr</td>
            <td>var=expr</td>
        </tr>
        <tr>
            <td>var=${str:+expr}</td>
            <td>var=</td>
            <td>var=</td>
            <td>var=expr</td>
        </tr>
        <tr>
            <td>var=${str?expr}</td>
            <td>expr 输出至 stderr</td>
            <td>var=</td>
            <td>var=$str</td>
        </tr>
        <tr>
            <td>var=${str:?expr}</td>
            <td>expr 输出至 stderr</td>
            <td>expr 输出至 stderr</td>
            <td>var=$str</td>
        </tr>
        <tr>
            <td>var=${str=expr}</td>
            <td>var=expr</td>
            <td>var=</td>
            <td>var=$str</td>
        </tr>
        <tr>
            <td>var=${str:=expr}</td>
            <td>var=expr</td>
            <td>var=expr</td>
            <td colspan="2">var=$str</td>
        </tr>
    </tbody>
</table>
<p>举例如下:</p>
<pre>  [root@linux ~]# test_name=""<br />
[root@linux ~]# test_name=${test_name-root}<br />
[root@linux ~]# echo $test_name<br />
&lt;== 因为 test_name 被设定为空字符窜！所以当然还是保留为空字符窜！<br />
[root@linux ~]# test_name=${test_name:-root}<br />
[root@linux ~]# echo $test_name<br />
root  &lt;== 加上&#8216;:&#8217;后若变量内容为空或者是未设定，都能够以后面的内容替换！</pre>
<blockquote>
<p>基本上这种变量的测试也能够透过 shell script 内的 if&#8230;then&#8230; 来处理,不过通过上述提及的简单的方法来测试变量，是程序看起来更精简一些！</p>
</blockquote>
<h1>6. shell 中分隔符 : 变量IFS 使用</h1>
<p>shell脚本中，如果使用for循环一个字符窜的话，默认使用空格来分割字符窜. 还有前面所提到的
使用for循环逐行读取文件内容时候,文件行中如果有空格的话输出的结果也会变乱. 这个时候 使用 IFS
变量来设置特定的字符窜分割符来，达到输出正确的目的. 默认情况下 IFS 是使用 <strong>&lt;space&gt;&lt;tab&gt;&lt;newline&gt;</strong>， 空格 "t "n 来作为默认的分割符的.</p>
<p>我们将前面使用for逐行读取文件的例子 改进下就可以输出正确了,请看下面</p>
<pre>  #!/bin/bash<br />
IFS_old=$IFS      #将原IFS值保存，以便用完后恢复<br />
IFS=$&#8217;"n&#8217;         #更改IFS值为$&#8217;"n&#8217;<br />
for line in `cat file.txt`<br />
do<br />
echo $line<br />
done</pre>
<p>file.txt 文件内容如下</p>
<pre>  [root@linux]$ cat file.txt<br />
sdfsdfsdfsdf<br />
ssssss ssssss ssssss sssss<br />
sdfsdfsdfsdfsdf</pre>
<p>执行测试程序 输出结果如下(正确输出)</p>
<pre>  [root@linux]$ sh test.sh<br />
sdfsdfsdfsdf<br />
ssssss ssssss ssssss sssss<br />
sdfsdfsdfsdfsdf</pre>
<p>如果未设置IFS变量,使用默认的IFS变量值 ,输出结果如下</p>
<pre>  [root@linux]$ sh test.sh<br />
sdfsdfsdfsdf<br />
ssssss<br />
ssssss<br />
ssssss<br />
sssss<br />
sdfsdfsdfsdfsdf</pre>
<p>从以上测试程序输出结果,可以根据自己的需求来设定 IFS变量,在举一个例子如下:</p>
<pre>  while IFS=: read userName passWord userID groupID geCos homeDir userShell<br />
do<br />
echo "$userName -&gt; $homeDir"<br />
done &lt; /etc/passwd</pre>
<h1>7. shell 数组的使用</h1>
<p>数组赋值方式:</p>
<pre>  (1) array=(var1 var2 var3 ... varN)<br />
(2) array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN)<br />
(3) array[0]=var1<br />
arrya[1]=var2<br />
...<br />
array[n]=varN</pre>
<p>计算数组元素个数或者长度:</p>
<pre>  (1) ${#array[@]}<br />
(2) ${#array[*]}</pre>
<p>了解了数组基础语法，举例说明，请看:</p>
<pre>  #!/bin/bash<br />
NAMESERVERS=("ns1.www.net." "ns2.www.net." "ns3.www.net.")<br />
# 得到数组长度<br />
tLen=${#NAMESERVERS[@]}<br />
<br />
# 循环数组<br />
for (( i=0; i&lt;${tLen}; i++ ));<br />
do<br />
echo ${NAMESERVERS[$i]}<br />
done</pre>
<p>在看一个复杂一点的例子,将文件内容读取到数组中:</p>
<pre>  #!/bin/bash<br />
<br />
# 设置IFS将分割符 设置为 换行符("n)<br />
OLDIFS=$IFS<br />
IFS=$'"n' <br />
<br />
# 读取文件内容到数组<br />
fileArray=($(cat file.txt))<br />
<br />
# restore it<br />
IFS=$OLDIFS<br />
tLen=${#fileArray[@]}<br />
<br />
# 循环显示文件内容<br />
for (( i=0; i&lt;${tLen}; i++ ));<br />
do<br />
echo "${fileArray[$i]}"<br />
done</pre>
<h1>8. 逻辑判断 条件测试</h1>
<ul>
    <li>文件属性的判断</li>
</ul>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <th>操作符</th>
            <th>测试结果</th>
        </tr>
        <tr>
            <td>-e filename</td>
            <td>文件存在返回1， 否则返回0</td>
        </tr>
        <tr>
            <td>-r filename</td>
            <td>文件可读返回1,否则返回0</td>
        </tr>
        <tr>
            <td>-w filename</td>
            <td>文件可写返回1,否则返回0</td>
        </tr>
        <tr>
            <td>-x filename</td>
            <td>文件可执行返回1,否则返回0</td>
        </tr>
        <tr>
            <td>-o filename</td>
            <td>文件属于用户本人返回1, 否则返回0</td>
        </tr>
        <tr>
            <td>-z filename</td>
            <td>文件长度为0返回1, 否则返回0</td>
        </tr>
        <tr>
            <td>-f filename</td>
            <td>文件为普通文件返回1, 否则返回0</td>
        </tr>
        <tr>
            <td>-d filename</td>
            <td colspan="2">文件为目录文件时返回1, 否则返回0</td>
        </tr>
    </tbody>
</table>
<p>举例如下,测试文件是否存在:</p>
<pre>  #!/bin/bash<br />
echo "checks the existence of the messages file."<br />
echo -n "Checking..."<br />
if [ -f /var/log/messages ];then<br />
echo "/var/log/messages exists."<br />
fi<br />
echo<br />
echo "...done."</pre>
<ul>
    <li>字符串比较</li>
</ul>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <th>操作符</th>
            <th>比较结果</th>
        </tr>
        <tr>
            <td>str1 = str2</td>
            <td>当两个字串相等时为真</td>
        </tr>
        <tr>
            <td>str1 != str2</td>
            <td>当两个字串不等时为真</td>
        </tr>
        <tr>
            <td>-n str1</td>
            <td>当字符串的长度大于0时为真</td>
        </tr>
        <tr>
            <td>-z str1</td>
            <td>当字符串的长度为0时为真</td>
        </tr>
        <tr>
            <td>str</td>
            <td colspan="2">当字符串为非空时为真</td>
        </tr>
    </tbody>
</table>
<p>举例如下,比较字符串来测试用户ID :</p>
<pre>  if [ "$(whoami)" != 'root' ]; then<br />
echo "You have no permission to run $0 as non-root user."<br />
exit 1;<br />
fi</pre>
<ul>
    <li>数值比较(整数)</li>
</ul>
<table border="1" cellpadding="4">
    <tbody>
        <tr>
            <th>操作符</th>
            <th>比较结果</th>
        </tr>
        <tr>
            <td>num1 -eq num2</td>
            <td>两数相等为真</td>
        </tr>
        <tr>
            <td>num1 -ne num2</td>
            <td>两数不等为真</td>
        </tr>
        <tr>
            <td>num1 -gt num2</td>
            <td>num1大于num2为真</td>
        </tr>
        <tr>
            <td>num1 -ge num2</td>
            <td>num1大于等于num2为真</td>
        </tr>
        <tr>
            <td>num1 -lt num2</td>
            <td>num1小于num2为真</td>
        </tr>
        <tr>
            <td>num1 -le num2</td>
            <td colspan="2">num1小于等于num2为真</td>
        </tr>
    </tbody>
</table>
<p>举例如下:</p>
<pre>  num=`wc -l work.txt`<br />
if [ $num -gt 150 ];then<br />
echo "you've worked hard enough for today."<br />
echo<br />
fi</pre>
<p>如果要查看详细的测试操作,可以查看man手册 man test</p>
</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/333468.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-30 10:54 <a href="http://www.blogjava.net/tinysun/archive/2010/09/30/333468.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下which、whereis、locate、find 命令的区别</title><link>http://www.blogjava.net/tinysun/archive/2010/09/30/333460.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 30 Sep 2010 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/30/333460.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333460.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/30/333460.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333460.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333460.html</trackback:ping><description><![CDATA[我们经常在linux要查找某个文件，但不知道放在哪里了，可以使用下面的一些命令来搜索。这些是从网上找到的资料，因为有时很长时间不会用到，当要用的时候经常弄混了，所以放到这里方便使用。
<br />
which&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 查看可执行文件的位置
<br />
whereis&nbsp;&nbsp;&nbsp; 查看文件的位置
<br />
locate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 配 合数据库查看文件位置
<br />
find&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实际搜寻硬盘查询文件名称
<br />
<br />
1、which
<br />
语法：
<br />
[root@redhat ~]# which 可执行文件名称
<br />
例如：
<br />
[root@redhat ~]# which passwd
<br />
/usr/bin/passwd
<br />
which是通过 PATH环境变量到该路径内查找可执行文件，所以基本的功能是寻找可执行文件
<br />
<br />
2、whereis
<br />
语法：
<br />
[root@redhat ~]# whereis [-bmsu] 文件或者目录名称
<br />
参数说 明：
<br />
-b ： 只找二进制文件
<br />
-m： 只找在说明文件manual路径下的文件
<br />
-s ： 只找source源文件
<br />
-u ： 没有说明文档的文件
<br />
例如：
<br />
[root@redhat ~]# whereis passwd
<br />
passwd: /usr/bin/passwd /etc/passwd /usr/share/man/man1/passwd.1.gz /usr/share/man/man5/passwd.5.gz
<br />
将和passwd文件相关的文件都查找出来
<br />
<br />
[root@redhat ~]# whereis -b passwd
<br />
passwd: /usr/bin/passwd /etc/passwd
<br />
只将二进制文件 查找出来
<br />
<br />
和find相比，whereis查找的速度非常快，这是因为linux系统会将 系统内的所有文件都记录在一个数据库文件中，当使用whereis和下面即将介绍的locate时，会从数据库中查找数据，而不是像find命令那样，通 过遍历硬盘来查找，效率自然会很高。
<br />
但是该数据库文件并不是实时更新，默认情况下时一星期更新一次，因此，我们在用whereis和locate 查找文件时，有时会找到已经被删除的数据，或者刚刚建立文件，却无法查找到，原因就是因为数据库文件没有被更新。
<br />
<br />
3、 locate
<br />
语法：
<br />
[root@redhat ~]# locate 文件或者目录名称
<br />
例 如：
<br />
[root@redhat ~]# locate passwd
<br />
/home/weblogic/bea/user_projects/domains/zhanggongzhe112/myserver/stage/_appsdir_DB_war/DB.war/jsp/as/user/passwd.jsp
<br />
/home/weblogic/bea/user_projects/domains/zhanggongzhe112/myserver/stage/_appsdir_admin_war/admin.war/jsp/platform/passwd.jsp
<br />
/lib/security/pam_unix_passwd.so
<br />
/lib/security/pam_passwdqc.so
<br />
/usr/include/rpcsvc/yppasswd.x
<br />
/usr/include/rpcsvc/yppasswd.h
<br />
/usr/lib/perl5/5.8.5/i386-linux-thread-multi/rpcsvc/yppasswd.ph
<br />
/usr/lib/kde3/kded_kpasswdserver.la
<br />
/usr/lib/kde3/kded_kpasswdserver.so
<br />
/usr/lib/ruby/1.8/webrick/httpauth/htpasswd.rb
<br />
/usr/bin/vncpasswd
<br />
/usr/bin/userpasswd
<br />
/usr/bin/yppasswd
<br />
&#8230;&#8230;&#8230;&#8230;
<br />
<br />
4、 find
<br />
语法：
<br />
[root@redhat ~]# find 路径 参数
<br />
参 数说明：
<br />
时间查找参数：
<br />
-atime n :将n*24小时内存取过的的文件列出来
<br />
-ctime n :将n*24小时内改变、新增的文件或者目录列出来
<br />
-mtime n :将n*24小时内修改过的文件或者目录列出来
<br />
-newer file ：把比file还要新的文件列出来
<br />
名称查找参数：
<br />
-gid n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ：寻找群组ID为n的文件
<br />
-group name&nbsp; ：寻找群组名称为name的文件
<br />
-uid n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ：寻找拥有者ID为n的文件
<br />
-user name&nbsp;&nbsp; ：寻找用户者名称为name的文件
<br />
-name file&nbsp;&nbsp; ：寻找文件名为file的文件（可以使用通配符）
<br />
例 如：
<br />
[root@redhat ~]# find / -name zgz
<br />
/home/zgz
<br />
/home/zgz/zgz
<br />
/home/weblogic/bea/user_projects/domains/zgz
<br />
/home/oracle/product/10g/cfgtoollogs/dbca/zgz
<br />
/home/oracle/product/10g/cfgtoollogs/emca/zgz
<br />
/home/oracle/oradata/zgz
<br />
<br />
[root@redhat ~]# find / -name '*zgz*'
<br />
/home/zgz
<br />
/home/zgz/zgz1
<br />
/home/zgz/zgzdirzgz
<br />
/home/zgz/zgz
<br />
/home/zgz/zgzdir
<br />
/home/weblogic/bea/user_projects/domains/zgz
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00006
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00002
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00004
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00008
<br />
/home/weblogic/bea/user_projects/domains/zgz/zgz.log00005
<br />
<br />
当我们用whereis和locate无法查找到我们需要的文件时，可以使用find，但是find是在硬盘上遍历查 找，因此非常消耗硬盘的资源，而且效率也非常低，因此建议大家优先使用whereis和locate。
<br />
locate 是在数据库里查找，数据库大至每天更新一次。
<br />
whereis 可以找到可执行命令和man page
<br />
find 就是根据条件查找文件。
<br />
which 可以找到可执行文件和别名(alias)
<img src ="http://www.blogjava.net/tinysun/aggbug/333460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-30 10:17 <a href="http://www.blogjava.net/tinysun/archive/2010/09/30/333460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>bash快捷键</title><link>http://www.blogjava.net/tinysun/archive/2010/09/28/333110.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Tue, 28 Sep 2010 01:57:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/09/28/333110.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/333110.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/09/28/333110.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/333110.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/333110.html</trackback:ping><description><![CDATA[<p>Ctrl-A 相当于HOME键，用于将光标定位到本行最前面</p>
<p><span style="color: #993300;"><strong>Ctrl-E 相当于End键，即将光标移动到本行末尾</strong></span></p>
<p>Ctrl-B 相当于左箭头键，用于将光标向左移动一格</p>
<p>Ctrl-F 相当于右箭头键，用于将光标向右移动一格</p>
<p><span style="color: #993300;"><strong>Ctrl-D 相当于Del键，即删除光标所在处的字符</strong></span></p>
<p><span style="color: #993300;"><strong>Ctrl-K 用于删除从光标处开始到结尾处的所有字符</strong></span></p>
<p><span style="color: #993300;"><strong>Ctrl-L 清屏，相当于clear命令</strong></span></p>
<p><span style="color: #993300;"><strong>Ctrl-R 进入历史命令查找状态，然后你输入几个关键字符，就可以找到你使用过的命令</strong></span></p>
<p><span style="color: #993300;"><strong>Ctrl-U 用于删除从光标开始到行首的所有字符。一般在密码或命令输入错误时常用</strong></span></p>
<p>Ctrl-H 删除光标左侧的一个字符</p>
<p>Ctrl-W 用于删除当前光标左侧的一个单词</p>
<p><span style="color: #993300;"><strong>Ctrl-P 相当于上箭头键，即显示上一个命令</strong></span></p>
<p>Ctrl-N 相当于下箭头键，即显示下一个命令</p>
<p>Ctrl-T 用于颠倒光标所在处字符和前一个字符的位置。<span style="color: #ff0000;">（目前不知道有什么作用，哪位朋友知道？）</span></p>
<p><span style="color: #993300;"><strong>Ctrl-J 相当于回车键</strong></span></p>
<img src ="http://www.blogjava.net/tinysun/aggbug/333110.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-09-28 09:57 <a href="http://www.blogjava.net/tinysun/archive/2010/09/28/333110.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存相关分享</title><link>http://www.blogjava.net/tinysun/archive/2010/08/25/329890.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Wed, 25 Aug 2010 07:09:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/08/25/329890.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/329890.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/08/25/329890.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/329890.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/329890.html</trackback:ping><description><![CDATA[<strong>一 linux内存管理以及内存碎片产生原因</strong>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.cppblog.com/images/cppblog_com/cppexplore/linux.JPG" width="327" border="0" height="217" /><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
最底层使用伙伴算法管理内存页面。系统将所有空闲内存页面分10个组，每个组中的内存块大小依次是1，2，4&#8230;&#8230;512个内存页面，每组中的内存块大小相
同，并且以链表结构保存。大小相同，并且内存地址连续的两个内存块称为伙伴。伙伴算法的中心思想就是将成为伙伴的空闲内存合并成一个更大的内存块。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
os中使用get_free_page获取空闲页面，如果找不到合适大小的空闲页面，则从更大的组中找到空闲内存块，分配出去，并将剩余内存分割，插入到
合适的组中。当归还内存时，启动伙伴算法合并空闲内存。如果不停的申请内存，并且部分归还，但归还的内存不能成为伙伴，长期运行后，所有内存将被分割成不
相邻的小块，当再次申请大块内存时，则可能由于找不到足够大的连续内存块而失败，这种零散的不相邻的小块内存称之为内存碎片。当然这只是理论上的说明，伙
伴算法本身就是为了解决内存碎片问题。</p>
<p><strong>二&nbsp; malloc子系统内存管理（dlmalloc)</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
应用层面的开发并不是直接调用sbrk/mmap之类的函数，而是调用malloc/free等malloc子系统提供的函数，linux上安装的大多为
DougLea的dlmalloc或者其变形ptmalloc。下面以dlmalloc为例说明malloc工作的原理。<br />
<strong>1 dlmalloc下名词解释：</strong><br />
<strong>&nbsp;&nbsp;&nbsp;boundary tag:</strong> 边界标记，每个空闲内存块均有头部表识和尾部标识，尾部表识的作为是合并空闲内存块时更快。这部分空间属于无法被应用层面使用浪费的内存空间。<br />
<strong>&nbsp;&nbsp;&nbsp;smallbins:</strong> 小内存箱。dlmalloc将8,16,24&#8230;&#8230;512大小的内存分箱，相临箱子中的内存相差8字节。每个箱子中的内存大小均相同，并且以双向链表连接。<br />
<strong>&nbsp;&nbsp;&nbsp;treebins:</strong> 树结构箱。大于512字节的内存不再是每8字节1箱，而是一个范围段一箱。比如512~640, 640~896&#8230;..每个箱子的范围段依次是128，256，512&#8230;&#8230;。每箱中的结构不再是双向链表，而是树形结构。<br />
<strong>&nbsp;&nbsp;&nbsp;dv chunk:</strong>&nbsp; 当申请内存而在对应大小的箱中找不到大小合适的内存，则从更大的箱中找一块内存，划分出需要的内存，剩余的内存称之为dv chunk.<br />
<strong>&nbsp;&nbsp;&nbsp;top chunk:</strong> 当dlmalloc中管理的内存都找不到合适的内存时，则调用sbrk从系统申请内存，可以增长内存方向的chunk称为top chunk.<br />
<strong>2 内存分配算法</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从合适的箱子中寻找内存块&#8211;&gt;从相临的箱子中寻找内存块&#8211;&gt;从dv chunk分配内存&#8211;&gt;从其他可行的箱子中分配内存&#8211;&gt;从top chunk中分配内存&#8211;&gt;调用sbrk/mmap申请内存<br />
<strong>3 内存释放算法</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;临近内存合并&#8211;&gt;如属于top chunk，判断top chunk&gt;128k，是则归还系统<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8211;&gt;不属于chunk，则归相应的箱子</p>
dlmalloc还有小内存缓存等其他机制。可以看出经过dlmalloc，频繁调用malloc/free并不会产生内存碎片，只要后续还有相同
的内存大小的内存被申请，仍旧会使用以前的合适内存，除非大量调用malloc之后少量释放free，并且新的malloc又大于以前free的内存大
小，造成dlmalloc不停的从系统申请内存，而free掉的小内存因被使用的内存割断，而使top
chunk&lt;128k，不能归还给系统。即便如此，占用的总内存量也小于的确被使用的内存量的2倍（使用的内存和空闲的内存交叉分割，并且空闲的内
存总是小于使用的内存大小）。因此可以说，在没有内存泄露的情况，常规频繁调用malloc/free并不会产生内存碎片。
<img src ="http://www.blogjava.net/tinysun/aggbug/329890.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-08-25 15:09 <a href="http://www.blogjava.net/tinysun/archive/2010/08/25/329890.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程组及会话的概念理解</title><link>http://www.blogjava.net/tinysun/archive/2010/08/06/328127.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 06 Aug 2010 06:09:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/08/06/328127.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/328127.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/08/06/328127.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/328127.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/328127.html</trackback:ping><description><![CDATA[Linux内核中的进程组及会话<br />
将阐述Linux内核中的如下几个概念<br />
1) 进程组<br />
2) 会话<br />
3) 控制终端<br />
前面的概念来源于前人，我只是站在前人的肩膀上结合内核中的实现加深概念理解。<br />
1.概念：<br />
a)进程组<br />
Shell 上的一条命令行形成一个进程组 <br />
每个进程属于一个进程组 <br />
每个进程组有一个领头进程 <br />
进程组的生命周期到组中最后一个进程终止, 或加入其他进程组为止 <br />
getpgrp: 获得进程组 id, 即领头进程的 pid <br />
setpgid: 加入进程组和建立新的进程组 <br />
前台进程组和后台进程组 <br />
===============================================================================<br />
&nbsp; &nbsp;&nbsp; &nbsp; #include <br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int setpgid (pid_t pid, pid_t pgid);<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;pid_t getpgid (pid_t pid);<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int setpgrp (void);<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;pid_t getpgrp (void);<br />
-------------------------------------------------------------------------------<br />
&nbsp; &nbsp; 进程只能将自身和其子进程设置为进程组 id. <br />
&nbsp; &nbsp; 某个子进程调用 exec 函数之后, 就不能再将该子进程的 id 作为进程组 id.<br />
===============================================================================<br />
b)会话<br />
一次登录形成一个会话 <br />
一个会话可包含多个进程组, 但只能有一个前台进程组. <br />
setsid 可建立一个新的会话 <br />
===============================================================================<br />
&nbsp; &nbsp;&nbsp; &nbsp; #include <br />
&nbsp; &nbsp;&nbsp; &nbsp; pid_t setsid(void);<br />
-------------------------------------------------------------------------------<br />
&nbsp; &nbsp; 如果调用进程不是进程组的领头进程, 该函数才能建立新的会话.<br />
&nbsp; &nbsp; 调用 setsid 之后, 进程成为新会话的领头进程.<br />
&nbsp; &nbsp; 进程成为新进程组的领头进程.<br />
&nbsp; &nbsp; 进程失去控制终端<br />
===============================================================================<br />
c)控制终端<br />
会话的领头进程打开一个终端之后, 该终端就成为该会话的控制终端 (SVR4/Linux) <br />
与控制终端建立连接的会话领头进程称为控制进程 (session leader) <br />
一个会话只能有一个控制终端 <br />
产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程 <br />
终端上的连接断开时 (比如网络断开或 Modem 断开), 挂起信号将发送到控制进程(session leader)
<img src ="http://www.blogjava.net/tinysun/aggbug/328127.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-08-06 14:09 <a href="http://www.blogjava.net/tinysun/archive/2010/08/06/328127.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ulimit命令</title><link>http://www.blogjava.net/tinysun/archive/2010/08/06/328122.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 06 Aug 2010 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/08/06/328122.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/328122.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/08/06/328122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/328122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/328122.html</trackback:ping><description><![CDATA[<div id="blog_text" class="cnt">
<div style="margin: 15px;" width="100%">
<div>1,说明:<br />
ulimit用于shell启动进程所占用的资源.<br />
2,类别:<br />
shell内建命令<br />
3,语法格式:<br />
ulimit [-acdfHlmnpsStvw] [size]<br />
<br />
[john@localhost ~]$ ulimit -a<br />
core file size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr> (blocks, -c) 0<br />
data seg size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr> (kbytes, -d) unlimited<br />
scheduling priority <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr> (-e) 0<br />
file size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr>  <wbr> <wbr> (blocks, -f) unlimited<br />
pending signals <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr>  <wbr> (-i) 4096<br />
max locked memory <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr> <wbr> (kbytes, -l) 32<br />
max memory size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr> (kbytes, -m) unlimited<br />
open files <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> (-n) 1024<br />
pipe size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr> (512 bytes, -p) 8<br />
POSIX message queues <wbr> <wbr> <wbr> <wbr> (bytes, -q) 819200<br />
real-time priority <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr> (-r) 0<br />
stack size <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> (kbytes, -s) 10240<br />
cpu time <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> (seconds, -t) unlimited<br />
max user processes <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr> (-u) 4096<br />
virtual memory <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> (kbytes, -v) unlimited<br />
file locks <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr>  <wbr> <wbr> (-x) unlimited<br />
<br />
4,参数介绍:<br />
-H 设置硬件资源限制.<br />
-S 设置软件资源限制.<br />
-a 显示当前所有的资源限制.<br />
-c size:设置core文件的最大值.单位:blocks<br />
-d size:设置数据段的最大值.单位:kbytes<br />
-f size:设置创建文件的最大值.单位:blocks<br />
-l size:设置在内存中锁定进程的最大值.单位:kbytes<br />
-m size:设置可以使用的常驻内存的最大值.单位:kbytes<br />
-n size:设置内核可以同时打开的文件描述符的最大值.单位:n<br />
-p size:设置管道缓冲区的最大值.单位:kbytes<br />
-s size:设置堆栈的最大值.单位:kbytes<br />
-t size:设置CPU使用时间的最大上限.单位:seconds<br />
-v size:设置虚拟内存的最大值.单位:kbytes 5,简单实例:</div>
<div>5.举例<br />
在Linux下写程序的时候，如果程序比较大，经常会遇到&#8220;段错误&#8221;（segmentation
fault）这样的问题，这主要就是由于Linux系统初始的堆栈大小（stack size）太小的缘故，一般为10M。我一般把stack
size设置成256M，这样就没有段错误了！命令为：<br />
ulimit <wbr> <wbr> -s 262140</div>
<div>如果要系统自动记住这个配置，就编辑/etc/profile文件，在 &#8220;ulimit -S -c 0 &gt; /dev/null 2&gt;&amp;1&#8221;行下，添加&#8220;ulimit <wbr> <wbr> -s 262140&#8221;，保存重启系统就可以了！</div>
<div>1]在RH8的环境文件/etc/profile中,我们可以看到系统是如何配置ulimit的:<br />
#grep ulimit /etc/profile<br />
ulimit -S -c 0 &gt; /dev/null 2&gt;&amp;1<br />
这条语句设置了对软件资源和对core文件大小的设置<br />
2]如果我们想要对由shell创建的文件大小作些限制,如:<br />
#ll h<br />
-rw-r--r-- 1 lee lee 150062 7月 22 02:39 h<br />
#ulimit -f 100 #设置创建文件的最大块(一块=512字节)<br />
#cat h&gt;newh<br />
File size limit exceeded<br />
#ll newh<br />
-rw-r--r-- 1 lee lee 51200 11月 8 11:47 newh<br />
文件h的大小是150062字节,而我们设定的创建文件的大小是512字节x100块=51200字节<br />
当然系统就会根据你的设置生成了51200字节的newh文件.<br />
3]可以像实例1]一样,把你要设置的ulimit放在/etc/profile这个环境文件中.<br />
用途</div>
<div>设置或报告用户资源极限。<br />
语法</div>
<div>ulimit [ -H ] [ -S ] [ -a ] [ -c ] [ -d ] [ -f ] [ -m ] [ -n ] [ -s ] [ -t ] [ Limit ]<br />
描述</div>
<div>ulimit 命令设置或报告用户进程资源极限，如 /etc/security/limits 文件所定义。文件包含以下缺省值极限：</div>
<div>fsize = 2097151<br />
core = 2097151<br />
cpu = -1<br />
data = 262144<br />
rss = 65536<br />
stack = 65536<br />
nofiles = 2000</div>
<div>当新用户添加到系统中时，这些值被作为缺省值使用。当向系统中添加用户时，以上值通过 mkuser 命令设置，或通过 chuser 命令更改。</div>
<div>极限分为软性或硬性。通过 ulimit 命令，用户可将软极限更改到硬极限的最大设置值。要更改资源硬极限，必须拥有 root 用户权限。</div>
<div>很多系统不包括以上一种或数种极限。 特定资源的极限在指定 Limit 参数时设定。Limit 参数的值可以是每个资源中指定单元中的数字，或者为值 unlimited。要将特定的 ulimit 设置为 unlimited，可使用词 unlimited。</div>
<div> <wbr> <wbr> <wbr> 注：在 /etc/security/limits 文件中设置缺省极限就是设置了系统宽度极限， 而不仅仅是创建用户时用户所需的极限。</div>
<div>省略 Limit 参数时，将会打印出当前资源极限。除非用户指定 -H 标志，否则打印出软极限。当用户指定一个以上资源时，极限名称和单元在值之前打印。如果未给予选项，则假定带有了 -f 标志。</div>
<div>由于 ulimit 命令影响当前 shell 环境，所以它将作为 shell 常规内置命令提供。如果在独立的命令执行环境中调用该命令，则不影响调用者环境的文件大小极限。以下示例中正是这种情况：</div>
<div>nohup ulimit -f 10000<br />
env ulimit 10000</div>
<div>一旦通过进程减少了硬极限，若无 root 特权则无法增加，即使返回到原值也不可能。</div>
<div>关于用户和系统资源极限的更多信息，请参见 AIX 5L Version 5.3 Technical Reference: Base
Operating System and Extensions Volume 1 中的 getrlimit、setrlimit 或 vlimit
子例程。<br />
标志<br />
-a <wbr> <wbr> <wbr> <wbr> 列出所有当前资源极限。<br />
-c <wbr> <wbr> <wbr> <wbr> 以 512 字节块为单位，指定核心转储的大小。<br />
-d <wbr> <wbr> <wbr> <wbr> 以 K 字节为单位指定数据区域的大小。<br />
-f <wbr> <wbr> <wbr> <wbr> 使用 Limit 参数时设定文件大小极限（以块计），或者在未指定参数时报告文件大小极限。缺省值为 -f 标志。<br />
-H <wbr> <wbr> <wbr> <wbr> 指定设置某个给定资源的硬极限。如果用户拥有 root 用户权限，可以增大硬极限。任何用户均可减少硬极限。<br />
-m <wbr> <wbr> <wbr> <wbr> 以 K 字节为单位指定物理存储器的大小。<br />
-n <wbr> <wbr> <wbr> <wbr> 指定一个进程可以拥有的文件描述符的数量的极限。<br />
-s <wbr> <wbr> <wbr> <wbr> 以 K 字节为单位指定堆栈的大小。<br />
-S <wbr> <wbr> <wbr> <wbr> 指定为给定的资源设置软极限。软极限可增大到硬极限的值。如果 -H 和 -S 标志均未指定，极限适用于以上二者。<br />
-t <wbr> <wbr> <wbr> <wbr> 指定每个进程所使用的秒数。<br />
退出状态</div>
<div>返回以下退出值：<br />
0 <wbr> <wbr> <wbr> <wbr> 成功完成。<br />
&gt;0 <wbr> <wbr> <wbr> <wbr> 拒绝对更高的极限的请求，或发生错误。<br />
示例</div>
<div>要将文件大小极限设置为 51,200 字节，输入：</div>
<div>ulimit -f 100</div>
</div>
</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/328122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-08-06 13:34 <a href="http://www.blogjava.net/tinysun/archive/2010/08/06/328122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> write的奥秘 转</title><link>http://www.blogjava.net/tinysun/archive/2010/08/06/328099.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 06 Aug 2010 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/08/06/328099.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/328099.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/08/06/328099.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/328099.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/328099.html</trackback:ping><description><![CDATA[<p>来源：<a href="http://www.linuxforum.net/doc/write-coly.html">http://www.linuxforum.net/doc/write-coly.html</a></p>
<p>摘要：介绍了一个简单的字符设备驱动程序，深入剖析了write函数的工作原理</p>
<p>
</p>
<pre><font size="3">     在Linux下我们在使用设备的时候，都会用到write这个函数，通过这个函数我们可以象使<br />
用文件那样向设备传送数据。可是为什么用户使用write函数就可以把数据写到设备里面<br />
去，这个过程到底是怎么实现的呢？ <br />
<br />
这个奥秘就在于设备驱动程序的write实现中，这里我结合一些源代码来解释如何使得一<br />
个简简单单的write函数能够完成向设备里面写数据的复杂过程。<br />
<br />
这里的源代码主要来自两个地方。第一是oreilly出版的《Linux device driver》中的<br />
实例，第二是Linux Kernel 2.2.14核心源代码。我只列出了其中相关部分的内容，如果<br />
读者有兴趣，也可以查阅其它源代码。不过我不是在讲解如何编写设备驱动程序，所以不<br />
会对每一个细节都进行说明，再说有些地方我觉得自己还没有吃透。<br />
<br />
由于《Linux device driver》一书中的例子对于我们还是复杂了一些，我将其中的一个<br />
例程简化了一下。这个驱动程序支持这样一个设备：核心空间中的一个长度为10的数组<br />
kbuf[10]。我们可以通过用户程序open它，read它，write它，close它。这个设备的名<br />
字我称为short_t。 <br />
<br />
现在言归正传。 <br />
对于一个设备，它可以在/dev下面存在一个对应的逻辑设备节点，这个节点以文件的形式<br />
存在，但它不是普通意义上的文件，它是设备文件，更确切的说，它是设备节点。这个节<br />
点是通过mknod命令建立的，其中指定了主设备号和次设备号。主设备号表明了某一类设<br />
备，一般对应着确定的驱动程序；次设备号一般是区分是标明不同属性，例如不同的使用<br />
方法，不同的位置，不同的操作。这个设备号是从/proc/devices文件中获得的，所以一<br />
般是先有驱动程序在内核中，才有设备节点在目录中。这个设备号（特指主设备号）的主<br />
要作用，就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的，当你打开一<br />
个设备文件时，操作系统就已经知道这个设备所对应的驱动程序是哪一个了。这个"知道"<br />
的过程后面就讲。 <br />
<br />
我们再说说驱动程序的基本结构吧。这里我只介绍动态模块型驱动程序（就是我们使用<br />
insmod加载到核心中并使用rmmod卸载的那种），因为我只熟悉这种结构。 <br />
模块化的驱动程序由两个函数是固定的：int init_module(void) ；void<br />
cleanup_module(void)。前者在insmod的时候执行，后者在rmmod的时候执行。 <br />
init_nodule在执行的时候，进行一些驱动程序初始化的工作，其中最主要的工作有三<br />
件：注册设备；申请I/O端口地址范围；申请中断IRQ。这里和我们想知道的事情相关的只<br />
有注册设备。<br />
<br />
下面是一个典型的init_module函数： <br />
<br />
int init_module(void){ <br />
int result = check_region(short_base,1)；/* 察看端口地址*/ <br />
&#8230;&#8230; <br />
request_region(short_base,1,"short"); /* 申请端口地址*/ <br />
&#8230;&#8230; <br />
result = register_chrdev(short_major, "short", &amp;short_fops); /* 注册设备<br />
*/ <br />
&#8230;&#8230; <br />
result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short",<br />
NULL); /* 申请IRQ */ <br />
&#8230;&#8230; <br />
return 0; <br />
}/* init_module*/ <br />
<br />
上面这个函数我只保留了最重要的部分，其中最重要的函数是 <br />
result = register_chrdev(short_major, "short", &amp;short_fops); <br />
这是一个驱动程序的精髓所在！！当你执行indmod命令时，这个函数可以完成三件大事：<br />
第一，申请主设备号(short_major)，或者指定，或者动态分配；第二，在内核中注册设<br />
备的名字("short")；第三，指定fops方法(&amp;short_fops)。其中所指定的fops方法就是<br />
我们对设备进行操作的方法（例如read,write,seek,dir,open,release等），如何实现<br />
这些方法，是编写设备驱动程序大部分工作量所在。 <br />
<br />
现在我们就要接触关键部分了--如何实现fops方法。 <br />
我们都知道，每一个文件都有一个file的结构，在这个结构中有一个file_operations的<br />
结构体，这个结构体指明了能够对该文件进行的操作。<br />
<br />
下面是一个典型的file_operations结构： <br />
struct file_operations { <br />
loff_t (*llseek) (struct file *, loff_t, int); <br />
ssize_t (*read) (struct file *, char *, size_t, loff_t *); <br />
ssize_t (*write) (struct file *, const char *, size_t, loff_t *); <br />
int (*readdir) (struct file *, void *, filldir_t); <br />
unsigned int (*poll) (struct file *, struct poll_table_struct *); <br />
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned<br />
long); <br />
int (*mmap) (struct file *, struct vm_area_struct *); <br />
int (*open) (struct inode *, struct file *); <br />
int (*flush) (struct file *); <br />
int (*release) (struct inode *, struct file *); <br />
int (*fsync) (struct file *, struct dentry *); <br />
int (*fasync) (int, struct file *, int); <br />
int (*check_media_change) (kdev_t dev); <br />
int (*revalidate) (kdev_t dev); <br />
int (*lock) (struct file *, int, struct file_lock *); <br />
}; <br />
<br />
我们可以看到它实际上就是许多文件操作的函数指针，其中就有write，其它的我们就不<br />
去管它了。这个write指针在实际的驱动程序中会以程序员所实现的函数名字出现，它指<br />
向程序员实现的设备write操作函数。下面就是一个实际的例子，这个write函数可以向核<br />
心内存的一个数组里输入一个字符串。 <br />
<br />
int short_write (struct inode *inode, struct file *filp, const char *buf,<br />
int count){ <br />
int retval = count; <br />
extern unsigned char kbuf[10]; <br />
<br />
if(count&gt;10) <br />
count=10; <br />
copy_from_user(kbuf, buf, count); <br />
return retval; <br />
}/* short_write */ <br />
设备short_t对应的fops方法是这样声明的： <br />
struct file_operations short_fops = { <br />
NULL, /* short_lseek */ <br />
short_read, <br />
short_write, <br />
NULL, /* short_readdir */ <br />
NULL, /* short_poll */ <br />
NULL, /* short_ioctl */ <br />
NULL, /* short_mmap */ <br />
short_open, <br />
short_release, <br />
NULL, /* short_fsync */ <br />
NULL, /* short_fasync */ <br />
/* nothing more, fill with NULLs */ <br />
}; <br />
<br />
其中NULL的项目就是不提供这个功能。所以我们可以看出short_t设备只提供了<br />
read,write,open,release功能。其中write功能我们在上面已经实现了，具体的实现函<br />
数起名为short_write。这些函数就是真正对设备进行操作的函数，这就是驱动程序的一<br />
大好处：不管你实现的时候是多么的复杂，但对用户来看，就是那些常用的文件操作函数。<br />
<br />
但是我们可以看到，驱动程序里的write函数有四个参数，函数格式如下： <br />
short_write (struct inode *inode, struct file *filp, const char *buf, int count) <br />
而用户程序中的write函数只有三个参数，函数格式如下： <br />
write(inf fd, char *buf, int count) <br />
那他们两个是怎么联系在一起的呢？这就要靠操作系统核心中的函数sys_write了，下面<br />
是Linux Kernel 2.2.14中sys_write中的源代码： <br />
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) <br />
{ <br />
ssize_t ret; <br />
struct file * file; <br />
struct inode * inode; <br />
ssize_t (*write)(struct file *, const char *, size_t, loff_t *); /* 指向<br />
驱动程序中的wirte函数的指针*/ <br />
<br />
lock_kernel(); <br />
ret = -EBADF; <br />
file = fget(fd); /* 通过文件描述符得到文件指针 */ <br />
if (!file) <br />
goto bad_file; <br />
if (!(file-&gt;f_mode &amp; FMODE_WRITE)) <br />
goto out; <br />
inode = file-&gt;f_dentry-&gt;d_inode; /* 得到inode信息 */ <br />
ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file-&gt;f_pos,<br />
count); <br />
if (ret) <br />
goto out; <br />
ret = -EINVAL; <br />
if (!file-&gt;f_op || !(write = file-&gt;f_op-&gt;write)) /* 将函数开始时声明的<br />
write函数指针指向fops方法中对应的write函数 */ <br />
goto out; <br />
down(&amp;inode-&gt;i_sem); <br />
ret = write(file, buf, count, &amp;file-&gt;f_pos); /* 使用驱动程序中的write函数<br />
将数据输入设备，注意看，这里就是四个参数了 */ <br />
up(&amp;inode-&gt;i_sem); <br />
out: <br />
fput(file); <br />
bad_file: <br />
unlock_kernel(); <br />
return ret; <br />
} <br />
<br />
我写了一个简单的程序来测试这个驱动程序，该程序源代码节选如下（该省的我都省了）： <br />
<br />
main(){ <br />
int fd,count=0; <br />
unsigned char buf[10]; <br />
fd=open("/dev/short_t",O_RDWR); <br />
printf("input string:"); <br />
scanf("%s",buf); <br />
count=strlen(buf); <br />
if(count&gt;10) <br />
count=10; <br />
count=write(fd,buf,count); <br />
close(fd); <br />
return 1; <br />
} <br />
<br />
现在我们就演示一下用户使用write函数将数据写到设备里面这个过程到底是怎么实现的： <br />
1，insmod驱动程序。驱动程序申请设备名和主设备号，这些可以在/proc/devieces中获得。 <br />
2，从/proc/devices中获得主设备号，并使用mknod命令建立设备节点文件。这是通过主<br />
设备号将设备节点文件和设备驱动程序联系在一起。设备节点文件中的file属性中指明了<br />
驱动程序中fops方法实现的函数指针。 <br />
3，用户程序使用open打开设备节点文件，这时操作系统内核知道该驱动程序工作了，就<br />
调用fops方法中的open函数进行相应的工作。open方法一般返回的是文件标示符，实际<br />
上并不是直接对它进行操作的，而是有操作系统的系统调用在背后工作。 <br />
4，当用户使用write函数操作设备文件时，操作系统调用sys_write函数，该函数首先通<br />
过文件标示符得到设备节点文件对应的inode指针和flip指针。inode指针中有设备号信<br />
息，能够告诉操作系统应该使用哪一个设备驱动程序，flip指针中有fops信息，可以告诉<br />
操作系统相应的fops方法函数在那里可以找到。 <br />
5，然后这时sys_write才会调用驱动程序中的write方法来对设备进行写的操作。 <br />
其中1-3都是在用户空间进行的，4-5是在核心空间进行的。用户的write函数和操作系统<br />
的write函数通过系统调用sys_write联系在了一起。 <br />
注意： <br />
对于块设备来说，还存在写的模式的问题，这应该是由GNU C库来解决的，这里不予讨<br />
论，因为我没有看过GNU C库的源代码。 <br />
另外，这是一个测试版的文章，请各位朋友们多提意见和建议，非常感谢！ <br />
http://blog.csdn.net/lphpc/category/170686.aspx<br />
<br />
</font></pre>
<img src ="http://www.blogjava.net/tinysun/aggbug/328099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-08-06 10:00 <a href="http://www.blogjava.net/tinysun/archive/2010/08/06/328099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UNIX环境高级编程（APUE） 总结</title><link>http://www.blogjava.net/tinysun/archive/2010/07/29/327454.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 29 Jul 2010 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/07/29/327454.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/327454.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/07/29/327454.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/327454.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/327454.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Chapter 1. UNIX SystemOverview&nbsp;UNIX 结构：操作系统是控制硬件资源和提供程序运行环境的一种软件。系统调用是访问内核的接口。Shell是特殊的应用程序，提供运行其他程序的接口。&nbsp;登入：用户名，/etc/passwd,shell&nbsp;文件和目录：文件系统，文件名，路径名，工作目录，Home目...&nbsp;&nbsp;<a href='http://www.blogjava.net/tinysun/archive/2010/07/29/327454.html'>阅读全文</a><img src ="http://www.blogjava.net/tinysun/aggbug/327454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-07-29 16:26 <a href="http://www.blogjava.net/tinysun/archive/2010/07/29/327454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学会用core dump调试程序错误</title><link>http://www.blogjava.net/tinysun/archive/2010/07/29/327453.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 29 Jul 2010 08:21:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/07/29/327453.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/327453.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/07/29/327453.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/327453.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/327453.html</trackback:ping><description><![CDATA[，一直不知道用core dump工具来调试程序，花了近一周的时间，才定位问题，老大很生气，后果很严重，呵呵，事后仔细学习了这块的知识，了解一点core dump的知识。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在使用半导体作为内存的材料前，人类是利用线圈当作内存的材料（发明者为王安），线圈就叫作core ，用线圈做的内存就叫作&#8220;core
memory&#8221;。（线圈的单词应该是coil，呵呵）如今，半导体工业澎勃发展，已经没有人用线圈当内存了，不过，在许多情况下，人们还是把内存叫作&#8220;core&#8221;。
所以注意了：这里的core不是核心，而是内存。不过结合实际来看，好像也有点&#8220;内核所占内存&#8221;的意思。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; core dump又是什么东东？<strong>   </strong>我
们在开发（或使用）一个程序时，最怕的就是程序莫明其妙地挂掉。虽然系统没事，但我们下次仍可能遇到相同的问题。于是，这时操作系统就会把程序挂掉时的
内存内容写入一个叫做core的文件里（这个写入的动作就叫dump，dump的英语意思是垃圾、倾倒。从这里来看，这些内存的内容是程序错误运行的结
果，所以算是垃圾，把他弄出来就好比从大的内存池里&#8220;倾倒&#8221;。），以便于我们调试。这个过程，因此叫做core dump。     </p>
<p>1. 在嵌入式系统中，有时core dump直接从串口打印出来，<span style="color: #0000ff;"><span style="color: #000000;">结合objdump查找ra和epa地址，运用栈回溯，可以找到程序出错的地方。</span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">2.
在一般Linux系统中，默认是不会产生core dump文件的，通过ulimit -c来查看core
dump文件的大小，一般开始是0，可以设置core文件大小，ulimit -c 1024(kbytes单位)或者ulimit -c
unlimited。</span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">3. core dump文件输出设置，一般默认是当前目录，可以在/proc/sys/kernel中找<code>到core-user-pid，通过</code></span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">echo </span></span><span style="color: #0000ff;"><span style="color: #000000;">"1" &gt; /proc/sys/kernel/core-user-pid使core文件名加上pid号，还可以用</span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">mkdir -p /root/corefile<br />
</span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">echo "/root/corefile/core-%e-%p-%t" &gt; /proc/sys/kernel/core-pattern</span></span><code style="margin: 0px;" dir="ltr">控制core文件保存位置和文件名格式。</code></p>
<p><code style="margin: 0px;" dir="ltr">以下是参数列表:<br />
&nbsp; &nbsp; %p - insert pid into filename 添加pid<br />
&nbsp; &nbsp; %u - insert current uid into filename 添加当前uid<br />
&nbsp; &nbsp; %g - insert current gid into filename 添加当前gid<br />
&nbsp; &nbsp; %s - insert signal that caused the coredump into the filename 添加导致产生core的信号<br />
&nbsp; &nbsp; %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间<br />
&nbsp; &nbsp; %h - insert hostname where the coredump happened into filename 添加主机名<br />
&nbsp; &nbsp; %e - insert coredumping executable name into filename 添加命令名<br />
</code></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">4. </span></span><code style="margin: 0px;" dir="ltr">用gdb查看core文件:<br />
下面我们可以在发生运行时信号引起的错误时发生core dump了.编译时加上-g<br />
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.<br />
gdb [exec file] [core file]<br />
如:<br />
gdb ./test test.core<br />
在进入gdb后, 用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件行.</code></p>
<p>5. 给个例子</p>
<p>test.c</p>
<p>void a()</p>
<p>{</p>
<p>&nbsp;&nbsp; char *p = NULL;</p>
<p>&nbsp;&nbsp; printf("%d\n", *p);</p>
<p>}</p>
<p>int main()</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; a();</p>
<p>&nbsp;&nbsp;&nbsp; return 0;</p>
<p>}</p>
<p>编译 gcc -g -o test test.c</p>
<p>运行 ./test</p>
<p>报segmentation fault(core dump)</p>
<p>gdb ./test test.core如果生成的是test.core.</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/327453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-07-29 16:21 <a href="http://www.blogjava.net/tinysun/archive/2010/07/29/327453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言的反汇编代码（BP，SP的关系） 转</title><link>http://www.blogjava.net/tinysun/archive/2010/07/29/327413.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Thu, 29 Jul 2010 03:41:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/07/29/327413.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/327413.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/07/29/327413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/327413.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/327413.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1. 最简单的代码：//// test1.cint main(){&nbsp;&nbsp;&nbsp; return 1;}&nbsp;编译、反汇编：gcc test1.cgdb ./a.out(gdb) disassemble main&nbsp;0x08048344 &lt;main+0&gt;:&nbsp;&nbsp;&nbsp;&nbsp;&nb...&nbsp;&nbsp;<a href='http://www.blogjava.net/tinysun/archive/2010/07/29/327413.html'>阅读全文</a><img src ="http://www.blogjava.net/tinysun/aggbug/327413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-07-29 11:41 <a href="http://www.blogjava.net/tinysun/archive/2010/07/29/327413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gcc编译C++程序</title><link>http://www.blogjava.net/tinysun/archive/2010/07/03/325117.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sat, 03 Jul 2010 02:08:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/07/03/325117.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/325117.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/07/03/325117.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/325117.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/325117.html</trackback:ping><description><![CDATA[<p><font style="background-color: #cde9d1">单个源文件生成可执行程序<br />
下面是一个保存在文件 helloworld.cpp 中一个简单的 C++ 程序的代码： <br />
/* helloworld.cpp */<br />
#include &lt;iostream&gt;<br />
int main(int argc,char *argv[])<br />
{<br />
&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "hello, world" &lt;&lt; std::endl;<br />
&nbsp;&nbsp;&nbsp; return(0);<br />
}<br />
程序使用定义在头文件 iostream 中的 cout，向标准输出写入一个简单的字符串。该代码可用以下命令编译为可执行文件： <br />
&nbsp;<br />
$&nbsp; g++ helloworld.cpp<br />
编译器 g++ 通过检查命令行中指定的文件的后缀名可识别其为 C++ 源代码文件。编译器默认的动作：编译源代码文件生成对象文件(object file)，链接对象文件和 libstdc++ 库中的函数得到可执行程序。然后删除对象文件。由于命令行中未指定可执行程序的文件名，编译器采用默认的 a.out。程序可以这样来运行： <br />
$ ./a.out<br />
hello, world<br />
更普遍的做法是通过 -o 选项指定可执行程序的文件名。下面的命令将产生名为 helloworld 的可执行文件： <br />
$ g++ helloworld.cpp -o helloworld<br />
在命令行中输入程序名可使之运行： <br />
$ ./helloworld<br />
hello, world<br />
程序 g++ 是将 gcc 默认语言设为 C++ 的一个特殊的版本，链接时它自动使用 C++ 标准库而不用 C 标准库。通过遵循源码的命名规范并指定对应库的名字，用 gcc 来编译链接 C++ 程序是可行的，如下例所示： <br />
$ gcc helloworld.cpp -lstdc++ -o helloworld<br />
选项 -l (ell) 通过添加前缀 lib 和后缀 .a 将跟随它的名字变换为库的名字 libstdc++.a。而后它在标准库路径中查找该库。gcc 的编译过程和输出文件与 g++ 是完全相同的。 <br />
&nbsp;<br />
在大多数系统中，GCC 安装时会安装一名为 c++ 的程序。如果被安装，它和 g++ 是等同，如下例所示，用法也一致： <br />
$ c++ helloworld.cpp -o helloworld<br />
多个源文件生成可执行程序<br />
如果多于一个的源码文件在 g++ 命令中指定，它们都将被编译并被链接成一个单一的可执行文件。下面是一个名为 speak.h 的头文件；它包含一个仅含有一个函数的类的定义： <br />
/* speak.h */<br />
#include &lt;iostream&gt;<br />
class Speak<br />
{<br />
&nbsp;&nbsp;&nbsp; public:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sayHello(const char *);<br />
};<br />
下面列出的是文件 speak.cpp 的内容：包含 sayHello() 函数的函数体： <br />
/* speak.cpp */<br />
#include "speak.h"<br />
void Speak::sayHello(const char *str)<br />
{<br />
&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "Hello " &lt;&lt; str &lt;&lt; "\n";<br />
}<br />
文件 hellospeak.cpp 内是一个使用 Speak 类的程序： <br />
/* hellospeak.cpp */<br />
#include "speak.h"<br />
int main(int argc,char *argv[])<br />
{<br />
&nbsp;&nbsp;&nbsp; Speak speak;<br />
&nbsp;&nbsp;&nbsp; speak.sayHello("world");<br />
&nbsp;&nbsp;&nbsp; return(0);<br />
}<br />
下面这条命令将上述两个源码文件编译链接成一个单一的可执行程序： <br />
$ g++ hellospeak.cpp speak.cpp -o hellospeak<br />
PS：这里说一下为什么在命令中没有提到&#8220;speak.h&#8220;该文件（原因是：在&#8220;speak.cpp&#8220;中包含有&#8221;#include"speak.h"&#8220;这句代码，它的意思是搜索系统头文件目录之前将先在当前目录中搜索文件&#8220;speak.h&#8220;。而&#8221;speak.h&#8220;正在该目录中，不用再在命令中指定了）。 <br />
源文件生成对象文件<br />
选项 -c 用来告诉编译器编译源代码但不要执行链接，输出结果为对象文件。文件默认名与源码文件名相同，只是将其后缀变为 .o。例如，下面的命令将编译源码文件 hellospeak.cpp 并生成对象文件 hellospeak.o： <br />
$ g++ -c hellospeak.cpp<br />
命令 g++ 也能识别 .o 文件并将其作为输入文件传递给链接器。下列命令将编译源码文件为对象文件并将其链接成单一的可执行程序： <br />
$ g++ -c hellospeak.cpp <br />
$ g++ -c speak.cpp <br />
$ g++ hellospeak.o speak.o -o hellospeak<br />
选项 -o 不仅仅能用来命名可执行文件。它也用来命名编译器输出的其他文件。例如：除了中间的对象文件有不同的名字外，下列命令生将生成和上面完全相同的可执行文件： <br />
$ g++ -c hellospeak.cpp -o hspk1.o <br />
$ g++ -c speak.cpp -o hspk2.o <br />
$ g++ hspk1.o hspk2.o -o hellospeak<br />
编译预处理<br />
选项 -E 使 g++ 将源代码用编译预处理器处理后不再执行其他动作。下面的命令预处理源码文件 helloworld.cpp 并将结果显示在标准输出中： <br />
$ g++ -E helloworld.cpp<br />
本文前面所列出的 helloworld.cpp 的源代码，仅仅有六行，而且该程序除了显示一行文字外什么都不做，但是，预处理后的版本将超过 1200 行。这主要是因为头文件 iostream 被包含进来，而且它又包含了其他的头文件，除此之外，还有若干个处理输入和输出的类的定义。 <br />
预处理过的文件的 GCC 后缀为 .ii，它可以通过 -o 选项来生成，例如： <br />
$ gcc -E helloworld.cpp -o helloworld.ii<br />
生成汇编代码<br />
选项 -S 指示编译器将程序编译成汇编语言，输出汇编语言代码而后结束。下面的命令将由 C++ 源码文件生成汇编语言文件 helloworld.s： <br />
$ g++ -S helloworld.cpp<br />
生成的汇编语言依赖于编译器的目标平台。 <br />
创建静态库 <br />
静态库是编译器生成的一系列对象文件的集合。链接一个程序时用库中的对象文件还是目录中的对象文件都是一样的。库中的成员包括普通函数，类定义，类的对象实例等等。静态库的另一个名字叫归档文件(archive)，管理这种归档文件的工具叫 ar 。 <br />
在下面的例子中，我们先创建两个对象模块，然后用其生成静态库。 <br />
头文件 say.h 包含函数 sayHello() 的原型和类 Say 的定义： <br />
/* say.h */<br />
#include &lt;iostream&gt;<br />
void sayhello(void);<br />
class Say {<br />
&nbsp;&nbsp;&nbsp; private:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *string;<br />
&nbsp;&nbsp;&nbsp; public:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Say(char *str)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string = str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sayThis(const char *str)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; str &lt;&lt; " from a static library\n";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sayString(void);<br />
};<br />
下面是文件 say.cpp 是我们要加入到静态库中的两个对象文件之一的源码。它包含 Say 类中 sayString() 函数的定义体；类 Say 的一个实例 librarysay 的声明也包含在内： <br />
/* say.cpp */<br />
#include "say.h"<br />
void Say::sayString()<br />
{<br />
&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; string &lt;&lt; "\n";<br />
}<br />
Say librarysay("Library instance of Say");源码文件 syshello.cpp 是我们要加入到静态库中的第二个对象文件的源码。它包含函数 sayhello() 的定义： <br />
/* sayhello.cpp */<br />
#include "say.h"<br />
void sayhello()<br />
{<br />
&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "hello from a static library\n";<br />
}<br />
下面的命令序列将源码文件编译成对象文件，命令 ar 将其存进库中： <br />
$ g++ -c sayhello.cpp<br />
$ g++ -c say.cpp<br />
$ ar -r libsay.a sayhello.o say.o<br />
程序 ar 配合参数 -r 创建一个新库 libsay.a 并将命令行中列出的对象文件插入。采用这种方法，如果库不存在的话，参数 -r 将创建一个新的库，而如果库存在的话，将用新的模块替换原来的模块。 <br />
下面是主程序 saymain.cpp，它调用库 libsay.a 中的代码： <br />
/* saymain.cpp */<br />
#include "say.h"<br />
int main(int argc,char *argv[])<br />
{<br />
&nbsp;&nbsp;&nbsp; extern Say librarysay;<br />
&nbsp;&nbsp;&nbsp; Say localsay = Say("Local instance of Say");<br />
&nbsp;&nbsp;&nbsp; sayhello();<br />
&nbsp;&nbsp;&nbsp; librarysay.sayThis("howdy");<br />
&nbsp;&nbsp;&nbsp; librarysay.sayString();<br />
&nbsp;&nbsp;&nbsp; localsay.sayString();<br />
&nbsp;&nbsp;&nbsp; return(0);<br />
}<br />
该程序可以下面的命令来编译和链接： <br />
$ g++ saymain.cpp libsay.a -o saymain<br />
程序运行时，产生以下输出： <br />
hello from a static library<br />
howdy from a static library<br />
Library instance of SayLocal instance of Say</font></p>
<p><font style="background-color: #cde9d1"><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/shanxiao528/archive/2010/05/11/5578743.aspx</font></p>
<img src ="http://www.blogjava.net/tinysun/aggbug/325117.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-07-03 10:08 <a href="http://www.blogjava.net/tinysun/archive/2010/07/03/325117.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>kernel study notes</title><link>http://www.blogjava.net/tinysun/archive/2010/06/28/324680.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Mon, 28 Jun 2010 08:06:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/06/28/324680.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/324680.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/06/28/324680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/324680.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/324680.html</trackback:ping><description><![CDATA[<p>管道<br />
FIFO<br />
信号 承载信息量少， 可靠信号 不可靠信号</p>
<p><br />
共享内存 最快的IPC之一<br />
消息队列<br />
信号量 不同进程间 同一进程不同线程间的同步</p>
<p>Socket 不同机器上的进程间的通信</p>
<p><br />
添加系统调用<br />
添加系统模块</p>
<p>内存模型 分段 分页<br />
逻辑地址-&gt;线性地址-&gt;物理地址</p>
<p>所有的段寄存器 段内偏移一样</p>
<p>页目录 页面 进程私有的<br />
虚拟地址描述符表 虚拟内存管理 分配回收</p>
<p>物理内存管理 </p>
<p><br />
-----<br />
进程<br />
栈<br />
堆<br />
BSS<br />
初始化的数据段<br />
代码段</p>
<p>内核栈<br />
控制块</p>
<p>进程Entry：<br />
进程的虚拟地址空间-&gt;分区-&gt;分页<br />
全局页目录pgd-&gt;pmd-&gt;page</p>
<p><br />
线程同步<br />
互斥锁<br />
pthread_mutex_t<br />
pthread_mutex_initializer<br />
pthread-mutex_int<br />
pthread_mutex_lock<br />
pthread_mutex_unlock</p>
<p>条件变量<br />
pthread_cond_t<br />
pthread_cond_init<br />
ptread_con_wait <br />
调用之前和调用之后都是上锁的，一个条件变量关联一个互斥锁<br />
函数内部实现机制<br />
解锁<br />
睡眠<br />
上锁<br />
pthead_cond_destroy<br />
pthread_cond_broadcast<br />
pthead_cond_signalh</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/324680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-06-28 16:06 <a href="http://www.blogjava.net/tinysun/archive/2010/06/28/324680.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线程控制－－私有数据</title><link>http://www.blogjava.net/tinysun/archive/2010/05/29/322210.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sat, 29 May 2010 07:16:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/05/29/322210.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/322210.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/05/29/322210.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/322210.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/322210.html</trackback:ping><description><![CDATA[&nbsp;在多线程环境下，进程内的所有线程共享进程的数据空间，因此全局变量为所有线程共有。在程序设计中有时需要保存线程自己的全局变量，这种特殊的变量仅在某个线程内部有效。如常见的变量errno，它返回标准的出错代码。errno不应该是一个局部变量，几乎每个函数都应该可以访问它；但它又不能作为一个全局变量，否则在一个线程里输出的很可能是另一个线程的出错信息，这个问题可以通过创建线程的私有数据（Thread-specific Data，或TSD）来解决。在线程内部，线程私有数据可以被各个函数访问，但它对其他线程是屏蔽的。<br />
&nbsp;&nbsp;&nbsp; 线程私有数据采用了一种被称为一键多值的技术，即一个键对应多个数值。访问数据时都是通过键值来访问，好像是对一个变量进行访问，其实是在访问不同的数据。使用线程私有数据时，首先要为每个线程数据创建一个相关联的键。在各个线程内部，都使用这个公用的键来指代线程数据，但是在不同的线程中，这个键代表的数据是不同的。操作线程私有数据的函数主要有4个：pthread_key_create（创建一个键），pthread_setspecific（为一个键设置线程私有数据），pthread_getspecific（从一个键读取线程私有数据），pthread_key_delete（删除一个键）。这几个函数的声明如下：<br />
#include &lt;pthread.h&gt;<br />
int pthread_key_create(pthread_key_t *key,void (*destr_function)(void *));<br />
int pthread_setspecific(pthread_key_t key,const void *pointer));<br />
void *pthread_getspecific(pthread_key_t key);<br />
int pthread_key_delete(pthread_key_t key);<br />
&nbsp;&nbsp;&nbsp; pthread_key_create：从Linux的TSD池中分配一项，将其值赋给key供以后访问使用，它的第一个参数key为指向键值的指针，第二个参数为一个函数指针，如果指针不为空，则在线程退出时将以key所关联的数据为参数调用destr_function()，释放分配的缓冲区。<br />
&nbsp;&nbsp;&nbsp; key一旦被创建，所有线程都可以访问它，但各线程可以根据自己的需要往key中填入不同的值，这就相当于提供了一个同名而不同值的全局变量，一键多值。一键多值靠的是一个关键数据结构数组，即TSD池其结构如下：<br />
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] ={{0,NULL}};<br />
&nbsp;&nbsp;&nbsp; 创建一个TSD就相当于将结构数组中的某一项设置为&#8220;in_use&#8221;，并将其索引返回给＊key，然后设置destructor函数destr_function。<br />
&nbsp;&nbsp;&nbsp; pthread_setspecific：该函数将pointer的值（不是内容）与key相关联。用pthread_setspecific为一个键指定新的线程数据时，线程必须先释放原有的线程数据用以回收空间。<br />
&nbsp;&nbsp;&nbsp; pthread_getspecific：通过该函数得到与key相关联的数据。<br />
&nbsp;&nbsp;&nbsp; pthread_key_delete：该函数用来删除一个键，键所占用的内存将被释放。需要注意的是，键占用的内存被释放，与该键关联的线程数据所占用的内存并不被释放。因此，线程数据的释放必须在释放键之前完成。<br />
&nbsp;&nbsp;&nbsp; 例8－4将实现如何创建和使用线程的私有数据，具体代码如下所示。<br />
&nbsp;&nbsp;&nbsp; 例8－4<br />
#include &lt;stdio.h&gt;<br />
#include &lt;string.h&gt;<br />
#include &lt;pthread.h&gt;<br />
<br />
pthread_key_t key;<br />
<br />
void * thread2(void *arg)<br />
{<br />
int tsd = 5;<br />
printf("thread %d is running\n",pthread_self());<br />
pthread_setspecific(key,(void *)tsd);<br />
printf("thread %d returns %d\n",pthread_self(),pthread_getspecific(key));<br />
}<br />
<br />
void * thread1(void *arg)<br />
{<br />
int tsd = 0;<br />
pthread_t thid2;<br />
<br />
printf("thread %d is running\n",pthread_self());<br />
pthread_setspecific(key,(void *)tsd);<br />
pthread_create(&amp;thid2,NULL,thread2,NULL);<br />
sleep(2);<br />
printf("thread %d return %d\n",pthread_self(),pthread_getspecific(key));<br />
}<br />
<br />
int main(void)<br />
{<br />
pthread_t thid1;<br />
printf("main thread begins running\n");<br />
pthread_key_create(&amp;key,NULL);<br />
pthread_create(&amp;thid1,NULL,thread1,NULL);<br />
sleep(5);<br />
pthread_key_delete(key);<br />
printf("main thread exit\n");<br />
return 0;<br />
}<br />
&nbsp;&nbsp;&nbsp; 编译并执行，结果如下：<br />
$ gcc -o 8-4 8-4.c -g -l pthread<br />
$ ./8-4<br />
main thread begins running<br />
thread -1209746544 is running<br />
thread -1218139248 is running<br />
thread -1218139248 returns 5<br />
thread -1209746544 return 0<br />
main thread exit<br />
&nbsp;&nbsp;&nbsp; 程序说明：程序中，主线程创建了线程thread1，线程thread1创建了线程thread2。两个线程分别将tsd作为线程私有数据。从程序运行结果可以看出，两个线程tsd的修改互不干扰，可以看出thread2先于thread1结束，线程在创建thread2后，睡眠3s等待thread2执行完毕。主线程睡眠5s等待thread1结束。可以看出thread2对tsd的修改并没影响到thread1的tsd的取值。<br />
 <img src ="http://www.blogjava.net/tinysun/aggbug/322210.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-05-29 15:16 <a href="http://www.blogjava.net/tinysun/archive/2010/05/29/322210.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vi使用技巧(二):copy,paste,delete,块编辑，redo/undo</title><link>http://www.blogjava.net/tinysun/archive/2010/05/12/320671.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Wed, 12 May 2010 02:34:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/05/12/320671.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/320671.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/05/12/320671.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/320671.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/320671.html</trackback:ping><description><![CDATA[. copy and paste<br />
&nbsp;&nbsp;&nbsp; yy : copy 光标所在的行<br />
&nbsp; nyy: copy n line<br />
&nbsp;&nbsp;&nbsp; yw: copy 光标所在的单词<br />
&nbsp;&nbsp;nyw: copy 光标所在位置到其后的n 个单词(未必是同一行)<br />
&nbsp;&nbsp;&nbsp; y$:&nbsp; copy 光标所在位置到行尾($是行尾的标示)<br />
&nbsp;&nbsp;ny$:&nbsp; copy 光标所在位置之后的n行(包括当前行，当前行=y$)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p:&nbsp;&nbsp;paste 在光标所在位置之右<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P: --------------------------------左<br />
2. delete, 和copy 类似<br />
&nbsp;&nbsp;&nbsp; dd : delete current line<br />
&nbsp; ndd:&nbsp; delete n line<br />
&nbsp;&nbsp;&nbsp; dw: delete current word<br />
&nbsp;&nbsp;ndw: delete n word<br />
&nbsp;&nbsp;&nbsp; d$ : delete to the end of line.<br />
&nbsp;&nbsp;nd$ : delete n line. (current line = d$)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x: delete one character(无论是ascii 还是unicode)<br />
&nbsp;&nbsp;&nbsp;&nbsp; nx: delete n characters.<br />
3. block edit<br />
&nbsp;&nbsp;&nbsp; 在命令模式下，输入v 进入块编辑状态<br />
&nbsp;&nbsp;&nbsp; a. 移动光标选定操作快<br />
&nbsp;&nbsp;&nbsp; b. c(cut), y(copy)<br />
&nbsp;&nbsp;&nbsp; c. p or P.<br />
4. undo /redo<br />
&nbsp;&nbsp;&nbsp; u: undo <br />
&nbsp;&nbsp;&nbsp; U: 取消最近一行的改动<br />
&nbsp;&nbsp;&nbsp; crtl +r: redo<br />
&nbsp;&nbsp;&nbsp; e!: 放弃所有改动，重新编辑。
<img src ="http://www.blogjava.net/tinysun/aggbug/320671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-05-12 10:34 <a href="http://www.blogjava.net/tinysun/archive/2010/05/12/320671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VMware网络设置（WindowsXP+虚拟Ret Hat Linux 9）</title><link>http://www.blogjava.net/tinysun/archive/2010/05/04/320075.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Tue, 04 May 2010 14:14:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/05/04/320075.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/320075.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/05/04/320075.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/320075.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/320075.html</trackback:ping><description><![CDATA[<p>宿主机：<a onclick="javascript:tagshow(event, 'Windows');" href="javascript:;" target="_self"><u><strong>Windows</strong></u></a> XP Professinoal SP2<br />
<a onclick="javascript:tagshow(event, 'VMware');" href="javascript:;" target="_self"><u><strong>VMware</strong></u></a>:Red Hat <a onclick="javascript:tagshow(event, 'Linux');" href="javascript:;" target="_self"><u><strong>Linux</strong></u></a> 9<br />
网络：ADSL局域网512M</p>
<p>所用软件：<br />
VMware-workstation-6.0.0-45731.exe<br />
Red Hat Linux 9</p>
<p>安装过程：<br />
1.安装VMware-workstation-6.0.0-45731.exe<br />
2.开启VMware.安装Linux.<br />
&nbsp; 安装时可直接用硬盘iso文件.<br />
&nbsp; VMware=&gt;VM=&gt;Settings=&gt;CD-RoM=&gt;Use ISO image(选择iso位置,安装一张,在换另一个iso文件)<br />
3.安装VMware后,会在XP上自动安装2个虚拟网卡:<br />
&nbsp; VMware Virtual Ethernet Adapter for VMnet1<br />
&nbsp; VMware Virtual Ethernet Adapter for VMnet8<br />
&nbsp; XP运行cmd=&gt;ipconfig/all<br />
&nbsp; 会查看到2个虚拟网卡及真实网卡的IP配置.<br />
&nbsp; 因这2个虚拟网卡是装在XP系统上的，所以XP上可以Ping通.<br />
4.此外,虚拟机上的Linux也有1个虚拟网卡,它的配置文件为<br />
&nbsp; /etc/sysconfig/network-scrīpts/ifcfg-eth0<br />
5.我们可以把Linux的网关设置为XP系统虚拟网卡的IP地址,<br />
&nbsp; 这样Linux就可以和XP通信了.<br />
6.VMware=&gt;VM=&gt;Settings=&gt;Ethemet=&gt;Custom:Specific virtual network 选VMnet1(Host-only)<br />
&nbsp; 将VMware Virtual Ethernet Adapter for VMnet1地址<br />
&nbsp; 设置为: 192.168.0.1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 255.255.255.0<br />
&nbsp; 修改ifcfg-eth0文件如下:<br />
&nbsp; DEVICE=eth0<br />
&nbsp; ōNBOOT=yes<br />
&nbsp; BOOTPROTO=none<br />
&nbsp; IPADDR=192.168.0.2<br />
&nbsp; NETMASK=255.255.255.0<br />
&nbsp; GATEWAY=192.168.0.1<br />
&nbsp; TYPE=Ethernet<br />
&nbsp; USERCTL=no<br />
&nbsp; PEERDNS=no<br />
&nbsp; NETWORK=192.168.0.0<br />
&nbsp; BROADCAST=192.168.0.255<br />
&nbsp; 保存后更新下# service network restart</p>
<p>&nbsp; 也可以打开Linux开始=&gt;系统设置=&gt;网络,进行设置.<br />
7.这时XP和Linux可以通信了.<br />
&nbsp; XP运行:ping 192.168.0.2&nbsp; OK<br />
8.此外SecureCRT是个不错的终端访问软件.可以装在XP上，访问Linux.<br />
9.如果想让虚拟机的Linux也上网的话.<br />
&nbsp; 可以在XP=&gt;本地连接属性=&gt;高级=&gt;允许<a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target="_self"><u><strong>其他</strong></u></a>网络用户通过此计算机的Internet连接来连接<br />
&nbsp; (在家庭网络连接中选择VMware Network Adapter VMnet1)<br />
&nbsp; linux&nbsp;主DNS 192.168.0.1<br />
<br />
&nbsp; 这样就可以在虚拟Linux上网了.&nbsp;</p>
<img src ="http://www.blogjava.net/tinysun/aggbug/320075.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-05-04 22:14 <a href="http://www.blogjava.net/tinysun/archive/2010/05/04/320075.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用异步 I/O 大大提高应用程序的性能</title><link>http://www.blogjava.net/tinysun/archive/2010/05/01/319860.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sat, 01 May 2010 11:44:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/05/01/319860.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/319860.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/05/01/319860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/319860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/319860.html</trackback:ping><description><![CDATA[<blockquote>Linux&#174; 中最常用的输入/输出（I/O）模型是同步 I/O。在这个模型中，当请求发出之后，应用程序就会阻塞，直到请求满足为止。这是很好的一种解决方案，因为调用应用程序在等待 I/O 请求完成时不需要使用任何中央处理单元（CPU）。但是在某些情况中，I/O 请求可能需要与其他进程产生交叠。可移植操作系统接口（POSIX）异步 I/O（AIO）应用程序接口（API）就提供了这种功能。在本文中，我们将对这个 API 概要进行介绍，并来了解一下如何使用它。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name="N10056"><span class="atitle">AIO 简介 </span></a></p>
<p>Linux 异步 I/O 是 Linux 内核中提供的一个相当新的增强。它是 2.6 版本内核的一个标准特性，但是我们在 2.4 版本内核的补丁中也可以找到它。AIO 背后的基本思想是允许进程发起很多 I/O 操作，而不用阻塞或等待任何操作完成。稍后或在接收到 I/O 操作完成的通知时，进程就可以检索 I/O 操作的结果。</p>
<p><a name="N10060"><span class="atitle">I/O 模型 </span></a></p>
<p>在深入介绍 AIO API 之前，让我们先来探索一下 Linux 上可以使用的不同 I/O 模型。这并不是一个详尽的介绍，但是我们将试图介绍最常用的一些模型来解释它们与异步 I/O 之间的区别。图 1 给出了同步和异步模型，以及阻塞和非阻塞的模型。</p>
<br />
<a name="fig1"><strong>图 1. 基本 Linux I/O 模型的简单矩阵</strong></a><br />
<img height="225" alt="基本 Linux I/O 模型的简单矩阵" src="http://www.ibm.com/developerworks/cn/linux/l-async/figure1.gif" width="386" /> <br />
<p>每个 I/O 模型都有自己的使用模式，它们对于特定的应用程序都有自己的优点。本节将简要对其一一进行介绍。</p>
<p><a name="N1007C"><span class="smalltitle">同步阻塞 I/O </span></a></p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0" sizset="38" sizcache="2">
    <tbody sizset="39" sizcache="2">
        <tr sizset="39" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td sizset="39" sizcache="2">
            <table cellspacing="0" cellpadding="5" width="100%" border="1" sizset="39" sizcache="2">
                <tbody sizset="39" sizcache="1">
                    <tr>
                        <td bgcolor="#eeeeee"><a name="N10086"><strong>I/O 密集型与 CPU 密集型进程的比较</strong></a><br />
                        <p>I/O 密集型进程所执行的 I/O 操作比执行的处理操作更多。CPU 密集型的进程所执行的处理操作比 I/O 操作更多。Linux 2.6 的调度器实际上更加偏爱 I/O 密集型的进程，因为它们通常会发起一个 I/O 操作，然后进行阻塞，这就意味着其他工作都可以在两者之间有效地交错进行。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>最常用的一个模型是同步阻塞 I/O 模型。在这个模型中，用户空间的应用程序执行一个系统调用，这会导致应用程序阻塞。这意味着应用程序会一直阻塞，直到系统调用完成为止（数据传输完成或发生错误）。调用应用程序处于一种不再消费 CPU 而只是简单等待响应的状态，因此从处理的角度来看，这是非常有效的。</p>
<p>图 2 给出了传统的阻塞 I/O 模型，这也是目前应用程序中最为常用的一种模型。其行为非常容易理解，其用法对于典型的应用程序来说都非常有效。在调用 <code>read</code> 系统调用时，应用程序会阻塞并对内核进行上下文切换。然后会触发读操作，当响应返回时（从我们正在从中读取的设备中返回），数据就被移动到用户空间的缓冲区中。然后应用程序就会解除阻塞（<code>read</code> 调用返回）。</p>
<br />
<a name="fig2"><strong>图 2. 同步阻塞 I/O 模型的典型流程</strong></a><br />
<img height="340" alt="同步阻塞 I/O 模型的典型流程" src="http://www.ibm.com/developerworks/cn/linux/l-async/figure2.gif" width="538" /> <br />
<p>从应用程序的角度来说，<code>read</code> 调用会延续很长时间。实际上，在内核执行读操作和其他工作时，应用程序的确会被阻塞。</p>
<p><a name="N100B3"><span class="smalltitle">同步非阻塞 I/O </span></a></p>
<p>同步阻塞 I/O 的一种效率稍低的变种是同步非阻塞 I/O。在这种模型中，设备是以非阻塞的形式打开的。这意味着 I/O 操作不会立即完成，<code>read</code> 操作可能会返回一个错误代码，说明这个命令不能立即满足（<code>EAGAIN</code> 或 <code>EWOULDBLOCK</code>），如图 3 所示。</p>
<br />
<a name="fig3"><strong>图 3. 同步非阻塞 I/O 模型的典型流程</strong></a><br />
<img height="340" alt="同步非阻塞 I/O 模型的典型流程" src="http://www.ibm.com/developerworks/cn/linux/l-async/figure3.gif" width="474" /> <br />
<p>非阻塞的实现是 I/O 命令可能并不会立即满足，需要应用程序调用许多次来等待操作完成。这可能效率不高，因为在很多情况下，当内核执行这个命令时，应用程序必须要进行忙碌等待，直到数据可用为止，或者试图执行其他工作。正如图 3 所示的一样，这个方法可以引入 I/O 操作的延时，因为数据在内核中变为可用到用户调用 <code>read</code> 返回数据之间存在一定的间隔，这会导致整体数据吞吐量的降低。</p>
<p><a name="N100DF"><span class="smalltitle">异步阻塞 I/O </span></a></p>
<p>另外一个阻塞解决方案是带有阻塞通知的非阻塞 I/O。在这种模型中，配置的是非阻塞 I/O，然后使用阻塞 <code>select</code> 系统调用来确定一个 I/O 描述符何时有操作。使 <code>select</code> 调用非常有趣的是它可以用来为多个描述符提供通知，而不仅仅为一个描述符提供通知。对于每个提示符来说，我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知。</p>
<br />
<a name="fig4"><strong>图 4. 异步阻塞 I/O 模型的典型流程 (select)</strong></a><br />
<img height="340" alt="异步阻塞 I/O 模型的典型流程" src="http://www.ibm.com/developerworks/cn/linux/l-async/figure4.gif" width="541" /> <br />
<p><code>select</code> 调用的主要问题是它的效率不是非常高。尽管这是异步通知使用的一种方便模型，但是对于高性能的 I/O 操作来说不建议使用。</p>
<p><a name="N10106"><span class="smalltitle">异步非阻塞 I/O（AIO） </span></a></p>
<p>最后，异步非阻塞 I/O 模型是一种处理与 I/O 重叠进行的模型。读请求会立即返回，说明 <code>read</code> 请求已经成功发起了。在后台完成读操作时，应用程序然后会执行其他处理操作。当 <code>read</code> 的响应到达时，就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。</p>
<br />
<a name="fig5"><strong>图 5. 异步非阻塞 I/O 模型的典型流程</strong></a><br />
<img height="340" alt="异步非阻塞 I/O 模型的典型流程" src="http://www.ibm.com/developerworks/cn/linux/l-async/figure5.gif" width="481" /> <br />
<p>在一个进程中为了执行多个 I/O 请求而对计算操作和 I/O 处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。当一个或多个 I/O 请求挂起时，CPU 可以执行其他任务；或者更为常见的是，在发起其他 I/O 的同时对已经完成的 I/O 进行操作。</p>
<p>下一节将深入介绍这种模型，探索这种模型使用的 API，然后展示几个命令。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="40" sizcache="2">
    <tbody sizset="40" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="41" sizcache="2">
    <tbody sizset="42" sizcache="2">
        <tr align="right" sizset="42" sizcache="2">
            <td sizset="42" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="42" sizcache="2">
                <tbody sizset="42" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1012D"><span class="atitle">异步 I/O 的动机 </span></a></p>
<p>从前面 I/O 模型的分类中，我们可以看出 AIO 的动机。这种阻塞模型需要在 I/O 操作开始时阻塞应用程序。这意味着不可能同时重叠进行处理和 I/O 操作。同步非阻塞模型允许处理和 I/O 操作重叠进行，但是这需要应用程序根据重现的规则来检查 I/O 操作的状态。这样就剩下异步非阻塞 I/O 了，它允许处理和 I/O 操作重叠进行，包括 I/O 操作完成的通知。</p>
<p>除了需要阻塞之外，<code>select</code> 函数所提供的功能（异步阻塞 I/O）与 AIO 类似。不过，它是对通知事件进行阻塞，而不是对 I/O 调用进行阻塞。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="43" sizcache="2">
    <tbody sizset="43" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="44" sizcache="2">
    <tbody sizset="45" sizcache="2">
        <tr align="right" sizset="45" sizcache="2">
            <td sizset="45" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="45" sizcache="2">
                <tbody sizset="45" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1013D"><span class="atitle">Linux 上的 AIO 简介 </span></a></p>
<p>本节将探索 Linux 的异步 I/O 模型，从而帮助我们理解如何在应用程序中使用这种技术。</p>
<p>在传统的 I/O 模型中，有一个使用惟一句柄标识的 I/O 通道。在 UNIX&#174; 中，这些句柄是文件描述符（这对等同于文件、管道、套接字等等）。在阻塞 I/O 中，我们发起了一次传输操作，当传输操作完成或发生错误时，系统调用就会返回。</p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0" sizset="46" sizcache="2">
    <tbody sizset="47" sizcache="2">
        <tr sizset="47" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td sizset="47" sizcache="2">
            <table cellspacing="0" cellpadding="5" width="100%" border="1" sizset="47" sizcache="2">
                <tbody sizset="47" sizcache="1">
                    <tr>
                        <td bgcolor="#eeeeee"><a name="N1014F"><strong>Linux 上的 AIO</strong></a><br />
                        <p>AIO 在 2.5 版本的内核中首次出现，现在已经是 2.6 版本的产品内核的一个标准特性了。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>在异步非阻塞 I/O 中，我们可以同时发起多个传输操作。这需要每个传输操作都有惟一的上下文，这样我们才能在它们完成时区分到底是哪个传输操作完成了。在 AIO 中，这是一个 <code>aiocb</code>（AIO I/O Control Block）结构。这个结构包含了有关传输的所有信息，包括为数据准备的用户缓冲区。在产生 I/O （称为完成）通知时，<code>aiocb</code> 结构就被用来惟一标识所完成的 I/O 操作。这个 API 的展示显示了如何使用它。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="48" sizcache="2">
    <tbody sizset="48" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="49" sizcache="2">
    <tbody sizset="50" sizcache="2">
        <tr align="right" sizset="50" sizcache="2">
            <td sizset="50" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="50" sizcache="2">
                <tbody sizset="50" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10163"><span class="atitle">AIO API </span></a></p>
<p>AIO 接口的 API 非常简单，但是它为数据传输提供了必需的功能，并给出了两个不同的通知模型。表 1 给出了 AIO 的接口函数，本节稍后会更详细进行介绍。</p>
<br />
<a name="table1"><strong>表 1. AIO 接口 API</strong></a><br />
<table class="data-table-2" cellspacing="0" cellpadding="0" width="100%" summary="AIO interface APIs" border="0" sizset="51" sizcache="2">
    <tbody sizset="51" sizcache="1">
        <tr>
            <th>API 函数</th>
            <th>说明</th>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_read</code></td>
            <td>请求异步读操作</td>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_error</code></td>
            <td>检查异步请求的状态</td>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_return</code></td>
            <td>获得完成的异步请求的返回状态</td>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_write</code></td>
            <td>请求异步写操作</td>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_suspend</code></td>
            <td>挂起调用进程，直到一个或多个异步请求已经完成（或失败）</td>
        </tr>
        <tr>
            <td class="tb-row"><code>aio_cancel</code></td>
            <td>取消异步 I/O 请求</td>
        </tr>
        <tr>
            <td class="tb-row"><code>lio_listio</code></td>
            <td>发起一系列 I/O 操作</td>
        </tr>
    </tbody>
</table>
<br />
<p>每个 API 函数都使用 <code>aiocb</code> 结构开始或检查。这个结构有很多元素，但是清单 1 仅仅给出了需要（或可以）使用的元素。</p>
<br />
<a name="listing1"><strong>清单 1. aiocb 结构中相关的域 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="59" sizcache="2">
    <tbody sizset="59" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            struct aiocb {
            int aio_fildes;               // File Descriptor
            int aio_lio_opcode;           // Valid only for lio_listio (r/w/nop)
            volatile void *aio_buf;       // Data Buffer
            size_t aio_nbytes;            // Number of Bytes in Data Buffer
            struct sigevent aio_sigevent; // Notification Structure
            /* Internal fields */
            ...
            };
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><code>sigevent</code> 结构告诉 AIO 在 I/O 操作完成时应该执行什么操作。我们将在 AIO 的展示中对这个结构进行探索。现在我们将展示各个 AIO 的 API 函数是如何工作的，以及我们应该如何使用它们。</p>
<p><a name="N101F0"><span class="smalltitle">aio_read </span></a></p>
<p><code>aio_read</code> 函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以表示一个文件、套接字甚至管道。<code>aio_read</code> 函数的原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="60" sizcache="2">
    <tbody sizset="60" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">aio_read</span>( struct aiocb *aiocbp );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><code>aio_read</code> 函数在请求进行排队之后会立即返回。如果执行成功，返回值就为 0；如果出现错误，返回值就为 -1，并设置 <code>errno</code> 的值。</p>
<p>要执行读操作，应用程序必须对 <code>aiocb</code> 结构进行初始化。下面这个简短的例子就展示了如何填充 <code>aiocb</code> 请求结构，并使用 <code>aio_read</code> 来执行异步读请求（现在暂时忽略通知）操作。它还展示了 <code>aio_error</code> 的用法，不过我们将稍后再作解释。</p>
<br />
<a name="listing2"><strong>清单 2. 使用 aio_read 进行异步读操作的例子 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="61" sizcache="2">
    <tbody sizset="61" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            #include &lt;aio.h&gt;
            ...
            int fd, ret;
            struct aiocb my_aiocb;
            fd = open( "file.txt", O_RDONLY );
            if (fd &lt; 0) perror("open");
            /* Zero out the aiocb structure (recommended) */
            bzero( (char *)&amp;my_aiocb, sizeof(struct aiocb) );
            /* Allocate a data buffer for the aiocb request */
            my_aiocb.aio_buf = malloc(BUFSIZE+1);
            if (!my_aiocb.aio_buf) perror("malloc");
            /* Initialize the necessary fields in the aiocb */
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_nbytes = BUFSIZE;
            my_aiocb.aio_offset = 0;
            ret = <span class="boldcode">aio_read</span>( &amp;my_aiocb );
            if (ret &lt; 0) perror("aio_read");
            while ( <span class="boldcode">aio_error</span>( &amp;my_aiocb ) == EINPROGRESS ) ;
            if ((ret = aio_return( &amp;my_iocb )) &gt; 0) {
            /* got ret bytes on the read */
            } else {
            /* read failed, consult errno */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 2 中，在打开要从中读取数据的文件之后，我们就清空了 <code>aiocb</code> 结构，然后分配一个数据缓冲区。并将对这个数据缓冲区的引用放到 <code>aio_buf</code> 中。然后，我们将 <code>aio_nbytes</code> 初始化成缓冲区的大小。并将 <code>aio_offset</code> 设置成 0（该文件中的第一个偏移量）。我们将 <code>aio_fildes</code> 设置为从中读取数据的文件描述符。在设置这些域之后，就调用 <code>aio_read</code> 请求进行读操作。我们然后可以调用 <code>aio_error</code> 来确定 <code>aio_read</code> 的状态。只要状态是 <code>EINPROGRESS</code>，就一直忙碌等待，直到状态发生变化为止。现在，请求可能成功，也可能失败。</p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0" sizset="62" sizcache="2">
    <tbody sizset="63" sizcache="2">
        <tr sizset="63" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td sizset="63" sizcache="2">
            <table cellspacing="0" cellpadding="5" width="100%" border="1" sizset="63" sizcache="2">
                <tbody sizset="63" sizcache="1">
                    <tr>
                        <td bgcolor="#eeeeee"><a name="N10260"><strong>使用 AIO 接口来编译程序</strong></a><br />
                        <p>我们可以在 <code>aio.h</code> 头文件中找到函数原型和其他需要的符号。在编译使用这种接口的程序时，我们必须使用 POSIX 实时扩展库（<code>librt</code>）。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>注意使用这个 API 与标准的库函数从文件中读取内容是非常相似的。除了 <code>aio_read</code> 的一些异步特性之外，另外一个区别是读操作偏移量的设置。在传统的 <code>read</code> 调用中，偏移量是在文件描述符上下文中进行维护的。对于每个读操作来说，偏移量都需要进行更新，这样后续的读操作才能对下一块数据进行寻址。对于异步 I/O 操作来说这是不可能的，因为我们可以同时执行很多读请求，因此必须为每个特定的读请求都指定偏移量。</p>
<p><a name="N1027C"><span class="smalltitle">aio_error </span></a></p>
<p><code>aio_error</code> 函数被用来确定请求的状态。其原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="64" sizcache="2">
    <tbody sizset="64" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">aio_error</span>( struct aiocb *aiocbp );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这个函数可以返回以下内容：</p>
<ul>
    <li><code>EINPROGRESS</code>，说明请求尚未完成
    <li><code>ECANCELLED</code>，说明请求被应用程序取消了
    <li><code>-1</code>，说明发生了错误，具体错误原因可以查阅 <code>errno</code> </li>
</ul>
<p><a name="N102AC"><span class="smalltitle">aio_return </span></a></p>
<p>异步 I/O 和标准块 I/O 之间的另外一个区别是我们不能立即访问这个函数的返回状态，因为我们并没有阻塞在 <code>read</code> 调用上。在标准的 <code>read</code> 调用中，返回状态是在该函数返回时提供的。但是在异步 I/O 中，我们要使用 <code>aio_return</code> 函数。这个函数的原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="65" sizcache="2">
    <tbody sizset="65" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">ssize_t <span class="boldcode">aio_return</span>( struct aiocb *aiocbp );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>只有在 <code>aio_error</code> 调用确定请求已经完成（可能成功，也可能发生了错误）之后，才会调用这个函数。<code>aio_return</code> 的返回值就等价于同步情况中 <code>read</code> 或 <code>write</code> 系统调用的返回值（所传输的字节数，如果发生错误，返回值就为 <code>-1</code>）。</p>
<p><a name="N102E1"><span class="smalltitle">aio_write </span></a></p>
<p><code>aio_write</code> 函数用来请求一个异步写操作。其函数原型如下： </p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="66" sizcache="2">
    <tbody sizset="66" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">aio_write</span>( struct aiocb *aiocbp );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><code>aio_write</code> 函数会立即返回，说明请求已经进行排队（成功时返回值为 <code>0</code>，失败时返回值为 <code>-1</code>，并相应地设置 <code>errno</code>）。</p>
<p>这与 <code>read</code> 系统调用类似，但是有一点不一样的行为需要注意。回想一下对于 <code>read</code> 调用来说，要使用的偏移量是非常重要的。然而，对于 <code>write</code> 来说，这个偏移量只有在没有设置 <code>O_APPEND</code> 选项的文件上下文中才会非常重要。如果设置了 <code>O_APPEND</code>，那么这个偏移量就会被忽略，数据都会被附加到文件的末尾。否则，<code>aio_offset</code> 域就确定了数据在要写入的文件中的偏移量。</p>
<p><a name="N10323"><span class="smalltitle">aio_suspend </span></a></p>
<p>我们可以使用 <code>aio_suspend</code> 函数来挂起（或阻塞）调用进程，直到异步请求完成为止，此时会产生一个信号，或者发生其他超时操作。调用者提供了一个 <code>aiocb</code> 引用列表，其中任何一个完成都会导致 <code>aio_suspend</code> 返回。 <code>aio_suspend</code> 的函数原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="67" sizcache="2">
    <tbody sizset="67" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">aio_suspend</span>( const struct aiocb *const cblist[],
            int n, const struct timespec *timeout );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><code>aio_suspend</code> 的使用非常简单。我们要提供一个 <code>aiocb</code> 引用列表。如果任何一个完成了，这个调用就会返回 <code>0</code>。否则就会返回 <code>-1</code>，说明发生了错误。请参看清单 3。</p>
<br />
<a name="listing3"><strong>清单 3. 使用 aio_suspend 函数阻塞异步 I/O </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="68" sizcache="2">
    <tbody sizset="68" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            struct aioct *cblist[MAX_LIST]
            /* Clear the list. */
            bzero( (char *)cblist, sizeof(cblist) );
            /* Load one or more references into the list */
            cblist[0] = &amp;my_aiocb;
            ret = <span class="boldcode">aio_read</span>( &amp;my_aiocb );
            ret = <span class="boldcode">aio_suspend</span>( cblist, MAX_LIST, NULL );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>注意，<code>aio_suspend</code> 的第二个参数是 <code>cblist</code> 中元素的个数，而不是 <code>aiocb</code> 引用的个数。<code>cblist</code> 中任何 <code>NULL</code> 元素都会被 <code>aio_suspend</code> 忽略。</p>
<p>如果为 <code>aio_suspend</code> 提供了超时，而超时情况的确发生了，那么它就会返回 <code>-1</code>，<code>errno</code> 中会包含 <code>EAGAIN</code>。</p>
<p><a name="N10395"><span class="smalltitle">aio_cancel </span></a></p>
<p><code>aio_cancel</code> 函数允许我们取消对某个文件描述符执行的一个或所有 I/O 请求。其原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="69" sizcache="2">
    <tbody sizset="69" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">aio_cancel</span>( int fd, struct aiocb *aiocbp );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>要取消一个请求，我们需要提供文件描述符和 <code>aiocb</code> 引用。如果这个请求被成功取消了，那么这个函数就会返回 <code>AIO_CANCELED</code>。如果请求完成了，这个函数就会返回 <code>AIO_NOTCANCELED</code>。</p>
<p>要取消对某个给定文件描述符的所有请求，我们需要提供这个文件的描述符，以及一个对 <code>aiocbp</code> 的 <code>NULL</code> 引用。如果所有的请求都取消了，这个函数就会返回 <code>AIO_CANCELED</code>；如果至少有一个请求没有被取消，那么这个函数就会返回 <code>AIO_NOT_CANCELED</code>；如果没有一个请求可以被取消，那么这个函数就会返回 <code>AIO_ALLDONE</code>。我们然后可以使用 <code>aio_error</code> 来验证每个 AIO 请求。如果这个请求已经被取消了，那么 <code>aio_error</code> 就会返回 <code>-1</code>，并且 <code>errno</code> 会被设置为 <code>ECANCELED</code>。</p>
<p><a name="N103E4"><span class="smalltitle">lio_listio </span></a></p>
<p>最后，AIO 提供了一种方法使用 <code>lio_listio</code> API 函数同时发起多个传输。这个函数非常重要，因为这意味着我们可以在一个系统调用（一次内核上下文切换）中启动大量的 I/O 操作。从性能的角度来看，这非常重要，因此值得我们花点时间探索一下。<code>lio_listio</code> API 函数的原型如下：</p>
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="70" sizcache="2">
    <tbody sizset="70" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">int <span class="boldcode">lio_listio</span>( int mode, struct aiocb *list[], int nent,
            struct sigevent *sig );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><code>mode</code> 参数可以是 <code>LIO_WAIT</code> 或 <code>LIO_NOWAIT</code>。<code>LIO_WAIT</code> 会阻塞这个调用，直到所有的 I/O 都完成为止。在操作进行排队之后，<code>LIO_NOWAIT</code> 就会返回。<code>list</code> 是一个 <code>aiocb</code> 引用的列表，最大元素的个数是由 <code>nent</code> 定义的。注意 <code>list</code> 的元素可以为 <code>NULL</code>，<code>lio_listio</code> 会将其忽略。<code>sigevent</code> 引用定义了在所有 I/O 操作都完成时产生信号的方法。</p>
<p>对于 <code>lio_listio</code> 的请求与传统的 <code>read</code> 或 <code>write</code> 请求在必须指定的操作方面稍有不同，如清单 4 所示。</p>
<br />
<a name="listing4"><strong>清单 4. 使用 lio_listio 函数发起一系列请求 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="71" sizcache="2">
    <tbody sizset="71" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            struct aiocb aiocb1, aiocb2;
            struct aiocb *list[MAX_LIST];
            ...
            /* Prepare the first aiocb */
            aiocb1.aio_fildes = fd;
            aiocb1.aio_buf = malloc( BUFSIZE+1 );
            aiocb1.aio_nbytes = BUFSIZE;
            aiocb1.aio_offset = next_offset;
            aiocb1.aio_lio_opcode = LIO_READ;
            ...
            bzero( (char *)list, sizeof(list) );
            list[0] = &amp;aiocb1;
            list[1] = &amp;aiocb2;
            ret = <span class="boldcode">lio_listio</span>( LIO_WAIT, list, MAX_LIST, NULL );
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>对于读操作来说，<code>aio_lio_opcode</code> 域的值为 <code>LIO_READ</code>。对于写操作来说，我们要使用 <code>LIO_WRITE</code>，不过 <code>LIO_NOP</code> 对于不执行操作来说也是有效的。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="72" sizcache="2">
    <tbody sizset="72" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="73" sizcache="2">
    <tbody sizset="74" sizcache="2">
        <tr align="right" sizset="74" sizcache="2">
            <td sizset="74" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="74" sizcache="2">
                <tbody sizset="74" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1045F"><span class="atitle">AIO 通知 </span></a></p>
<p>现在我们已经看过了可用的 AIO 函数，本节将深入介绍对异步通知可以使用的方法。我们将通过信号和函数回调来探索异步函数的通知机制。</p>
<p><a name="N10469"><span class="smalltitle">使用信号进行异步通知 </span></a></p>
<p>使用信号进行进程间通信（IPC）是 UNIX 中的一种传统机制，AIO 也可以支持这种机制。在这种范例中，应用程序需要定义信号处理程序，在产生指定的信号时就会调用这个处理程序。应用程序然后配置一个异步请求将在请求完成时产生一个信号。作为信号上下文的一部分，特定的 <code>aiocb</code> 请求被提供用来记录多个可能会出现的请求。清单 5 展示了这种通知方法。</p>
<br />
<a name="listing5"><strong>清单 5. 使用信号作为 AIO 请求的通知 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="75" sizcache="2">
    <tbody sizset="75" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            void setup_io( ... )
            {
            int fd;
            struct sigaction sig_act;
            struct aiocb my_aiocb;
            ...
            /* Set up the signal handler */
            sigemptyset(&amp;sig_act.sa_mask);
            sig_act.sa_flags = SA_SIGINFO;
            sig_act.sa_sigaction = aio_completion_handler;
            /* Set up the AIO request */
            bzero( (char *)&amp;my_aiocb, sizeof(struct aiocb) );
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_buf = malloc(BUF_SIZE+1);
            my_aiocb.aio_nbytes = BUF_SIZE;
            my_aiocb.aio_offset = next_offset;
            /* Link the AIO request with the Signal Handler */
            my_aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
            my_aiocb.aio_sigevent.sigev_signo = SIGIO;
            my_aiocb.aio_sigevent.sigev_value.sival_ptr = &amp;my_aiocb;
            /* Map the Signal to the Signal Handler */
            ret = sigaction( SIGIO, &amp;sig_act, NULL );
            ...
            ret = <span class="boldcode">aio_read</span>( &amp;my_aiocb );
            }
            void aio_completion_handler( int signo, siginfo_t *info, void *context )
            {
            struct aiocb *req;
            /* Ensure it's our signal */
            if (info-&gt;si_signo == SIGIO) {
            req = (struct aiocb *)info-&gt;si_value.sival_ptr;
            /* Did the request complete? */
            if (<span class="boldcode">aio_error</span>( req ) == 0) {
            /* Request completed successfully, get the return status */
            ret = <span class="boldcode">aio_return</span>( req );
            }
            }
            return;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 5 中，我们在 <code>aio_completion_handler</code> 函数中设置信号处理程序来捕获 <code>SIGIO</code> 信号。然后初始化 <code>aio_sigevent</code> 结构产生 <code>SIGIO</code> 信号来进行通知（这是通过 <code>sigev_notify</code> 中的 <code>SIGEV_SIGNAL</code> 定义来指定的）。当读操作完成时，信号处理程序就从该信号的 <code>si_value</code> 结构中提取出 <code>aiocb</code>，并检查错误状态和返回状态来确定 I/O 操作是否完成。</p>
<p>对于性能来说，这个处理程序也是通过请求下一次异步传输而继续进行 I/O 操作的理想地方。采用这种方式，在一次数据传输完成时，我们就可以立即开始下一次数据传输操作。</p>
<p><a name="N104B0"><span class="smalltitle">使用回调函数进行异步通知 </span></a></p>
<p>另外一种通知方式是系统回调函数。这种机制不会为通知而产生一个信号，而是会调用用户空间的一个函数来实现通知功能。我们在 <code>sigevent</code> 结构中设置了对 <code>aiocb</code> 的引用，从而可以惟一标识正在完成的特定请求。请参看清单 6。</p>
<br />
<a name="listing6"><strong>清单 6. 对 AIO 请求使用线程回调通知 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="60%" border="0" sizset="76" sizcache="2">
    <tbody sizset="76" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            void setup_io( ... )
            {
            int fd;
            struct aiocb my_aiocb;
            ...
            /* Set up the AIO request */
            bzero( (char *)&amp;my_aiocb, sizeof(struct aiocb) );
            my_aiocb.aio_fildes = fd;
            my_aiocb.aio_buf = malloc(BUF_SIZE+1);
            my_aiocb.aio_nbytes = BUF_SIZE;
            my_aiocb.aio_offset = next_offset;
            /* Link the AIO request with a thread callback */
            my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
            my_aiocb.aio_sigevent.notify_function = aio_completion_handler;
            my_aiocb.aio_sigevent.notify_attributes = NULL;
            my_aiocb.aio_sigevent.sigev_value.sival_ptr = &amp;my_aiocb;
            ...
            ret = <span class="boldcode">aio_read</span>( &amp;my_aiocb );
            }
            void aio_completion_handler( sigval_t sigval )
            {
            struct aiocb *req;
            req = (struct aiocb *)sigval.sival_ptr;
            /* Did the request complete? */
            if (<span class="boldcode">aio_error</span>( req ) == 0) {
            /* Request completed successfully, get the return status */
            ret = <span class="boldcode">aio_return</span>( req );
            }
            return;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 6 中，在创建自己的 <code>aiocb</code> 请求之后，我们使用 <code>SIGEV_THREAD</code> 请求了一个线程回调函数来作为通知方法。然后我们将指定特定的通知处理程序，并将要传输的上下文加载到处理程序中（在这种情况中，是个对 <code>aiocb</code> 请求自己的引用）。在这个处理程序中，我们简单地引用到达的 <code>sigval</code> 指针并使用 AIO 函数来验证请求已经完成。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="77" sizcache="2">
    <tbody sizset="77" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="78" sizcache="2">
    <tbody sizset="79" sizcache="2">
        <tr align="right" sizset="79" sizcache="2">
            <td sizset="79" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="79" sizcache="2">
                <tbody sizset="79" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N104E8"><span class="atitle">对 AIO 进行系统优化 </span></a></p>
<p>proc 文件系统包含了两个虚拟文件，它们可以用来对异步 I/O 的性能进行优化：</p>
<ul>
    <li>/proc/sys/fs/aio-nr 文件提供了系统范围异步 I/O 请求现在的数目。
    <li>/proc/sys/fs/aio-max-nr 文件是所允许的并发请求的最大个数。最大个数通常是 64KB，这对于大部分应用程序来说都已经足够了。 </li>
</ul>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="80" sizcache="2">
    <tbody sizset="80" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="81" sizcache="2">
    <tbody sizset="82" sizcache="2">
        <tr align="right" sizset="82" sizcache="2">
            <td sizset="82" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="82" sizcache="2">
                <tbody sizset="82" sizcache="1">
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/linux/l-async/#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N104FA"><span class="atitle">结束语 </span></a></p>
<p>使用异步 I/O 可以帮助我们构建 I/O 速度更快、效率更高的应用程序。如果我们的应用程序可以对处理和 I/O 操作重叠进行，那么 AIO 就可以帮助我们构建可以更高效地使用可用 CPU 资源的应用程序。尽管这种 I/O 模型与在大部分 Linux 应用程序中使用的传统阻塞模式都不同，但是异步通知模型在概念上来说却非常简单，可以简化我们的设计。 </p>
<br />
<img src ="http://www.blogjava.net/tinysun/aggbug/319860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-05-01 19:44 <a href="http://www.blogjava.net/tinysun/archive/2010/05/01/319860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LINUX下的用户和组管理</title><link>http://www.blogjava.net/tinysun/archive/2010/05/01/319858.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sat, 01 May 2010 11:20:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/05/01/319858.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/319858.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/05/01/319858.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/319858.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/319858.html</trackback:ping><description><![CDATA[LINUX下的用户分为三类：超级用户，普通用户，伪用户。
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1 超级用户：用户名为root，具有一切管理权限，他的UID为0，可以创建多个管理员的。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2 普通用户：在默认情况下，普通用户的UID是介于500-60000之间的。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3 伪用户：这些用户的存在是为了方便系统管理，满足相应的系统进程对文件属主的要求。他不能够登录，它的ID值介于1--499之间。</div>
<div>&nbsp;</div>
<div>LINUX的组：分为私有组和标准组</div>
<div>私有组：新创建一个用户时，如果没有指定所属组，则系统会为用户创建一个同名的私有组。</div>
<div>标准组：事先创建好的，新创建一个用户时就可以指定他所属的组，它可以容纳多个用户。</div>
<div>&nbsp;</div>
<div>创建用户命令：useradd 用户名&nbsp;&nbsp; 然后再加密码：passwd 用户名</div>
<div>&nbsp;</div>
<div>LINUX系统是多用户，多任务操作系统，但是当系统管理员需要备份时，就不允许其它用户登录进来。下面介绍一个方法来解决这个问题。</div>
<div>&nbsp;&nbsp;&nbsp; 可以在/etc目录下创建一个名为nologin的文件，当其他用户登录时，系统只要发现此文件存在，就会禁止其他用户登录。若要再次恢复用户登录，则只要把nologin文件删除即可，或重新启动后，系统即会直接删除这个文件，恢复用户登录。</div>
<img src ="http://www.blogjava.net/tinysun/aggbug/319858.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-05-01 19:20 <a href="http://www.blogjava.net/tinysun/archive/2010/05/01/319858.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux 网络相关命令</title><link>http://www.blogjava.net/tinysun/archive/2010/04/16/318563.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Fri, 16 Apr 2010 12:56:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/04/16/318563.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/318563.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/04/16/318563.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/318563.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/318563.html</trackback:ping><description><![CDATA[===============<br />
<br />
第5章：<a href="http://www.bitscn.com/network/"><u>网络</u></a>应用 <br />
　　<br />
　　5.1 常用<a href="http://www.bitscn.com/network/"><u>网络</u></a>命令 <br />
　　在Red Hat Linux 7.1系统提供了与<a href="http://www.bitscn.com/network/"><u>网络</u></a>相关的工具，掌握好这些工具是十分必要的： <br />
　　第一类：设置工具 <br />
　　1.netconf： <br />
　　netconf是Red Hat Linux提供的Linuxconf的一部分，主要用于设置与<a href="http://www.bitscn.com/network/"><u>网络</u></a>相关的参数。它可以在consle下运行（文本菜单），也可以在X-Window中运行（图形界面）。在前面，我们介绍过了netconf的一些应用，它的使用比较简单，只要认识上面的英文就可以了，所以在此就不再多说。BTW，如果你设置好了X-Window的话，用用图形界面的netconf，会更漂亮的哟。 <br />
　　2.ifconfig <br />
　　ifconfig是Linux系统中最常用的一个用来显示和设置<a href="http://www.bitscn.com/network/"><u>网络</u></a>设备的工具。其中&#8220;if&#8221;是&#8220;interface&#8221;的缩写。它可以用来设备网卡的状态，或是显示当前的设置。 <br />
　　下面我们简单地说明常用的命令组合： <br />
　　1） 将第一块网卡的IP地址设置为192.168.0.1：
<div class="Rdd604">bbs.bitsCN.com中国网管论坛</div>
<br />
　　ifconfig eth0 192.168.0.1 （格式：ifconfig <a href="http://www.bitscn.com/network/"><u>网络</u></a>设备名 IP地址） <br />
　　2） 暂时关闭或启用网卡： <br />
　　关闭第一块网卡：ifconfig eth0 down <br />
　　启用第一块网卡：ifconfig eth0 up <br />
　　3） 将第一块网卡的子网掩码设置为255.255.255.0： <br />
　　ifconfig eth0 netmask 255.255.255.0（格式：ifconfig <a href="http://www.bitscn.com/network/"><u>网络</u></a>设备名 netmask 子网掩码） <br />
　　我们也可以同时设置IP地址和子网掩码： <br />
　　ifconfig eth0 192.168.0.1 netmask 255.255.255.0 <br />
　　4） 将第一块网卡的广播地址设置为192.168.0.255： <br />
　　ifconfig eth0 -broadcast 192.168.0.255 <br />
　　5） 将第一块网卡设置为不接收多播数据包： <br />
　　ifconifg eth0 allmulti <br />
　　如果要让其接收，则使用命令：ifconfig eth0 -allmulti <br />
　　6） 查看第一块网卡的状态： <br />
　　ifconfig eth0 <br />
　　如果要查看所有的网卡状态，则直接使用不带参数的ifconfig命令即可。 <br />
　　ifconfig输出的状态信息是十分有用的，下面，我们就简单说明一下： <br />
　　有几个状态比较重要： <br />
　　&#216; UP/DOWN：网卡是否启动了，如果是DOWN的话，那肯定无法用的； <font color="#fafafd">[bitsCN.Com]</font> <br />
　　&#216; RX packets中的errors包的数量如果过大说明网卡在接收时有问题； <br />
　　&#216; TX packets中的errors包的数量如果过大说明网卡在发送时有问题； <br />
　　3.route <br />
　　route命令是用来查看和设置Linux系统的路由信息，以实现与其它<a href="http://www.bitscn.com/network/"><u>网络</u></a>的通讯。要实现两个不同的子网之间的<a href="http://www.bitscn.com/network/"><u>网络</u></a>通讯，需要一台连接两个<a href="http://www.bitscn.com/network/"><u>网络</u></a>路由器或者同时位于两个<a href="http://www.bitscn.com/network/"><u>网络</u></a>的网关来实现。 <br />
　　在Linux系统中，我们通常设置路由是为了解决以下问题：该Linux机器在一个局域网中，局域网中有一个网关，能够让你的机器访问Internet，那么我们就需要将这台机器的IP地址设置为Linux机器的默认路由。 <br />
　　1） 增加一个默认路由： <br />
　　route add 0.0.0.0 gw 网关地址 <br />
　　2） 删除一个默认路由： <br />
　　route del 0.0.0.0 gw 网关地址 <br />
　　3） 显示出当前路由表 <br />
　　route <br />
<br />
<br />
　　第二类：诊断工具 <br />
　　1.ping <br />
　　ping是一个最常用的检测是否能够与远端机器建立<a href="http://www.bitscn.com/network/"><u>网络</u></a>通讯连接。它是通过Internet控制报文协议ICMP来实现的。而现在有些主机对ICMP进行过滤，在这种特殊的情况下，有可能使得一些主机Ping不通，但能够建立<a href="http://www.bitscn.com/network/"><u>网络</u></a>连接。这是一种特例，在此事先说明。 <font color="#fafafd">bitsCN#com中国网管联盟</font> <br />
　　同样的，在此不罗列ping命令的所有可选参数，而是通过实例来说明一些常用的组合，需要更详细地了解的，可以通过www.linuxaid.com.cn网站在线培训的命令查询工具获得。 <br />
　　1） 检测与某机器的连接是否正常： <br />
　　ping 192.168.0.1 <br />
　　ping www.linuxaid.com.cn <br />
　　也就是说，我们可以用IP地址或域名来指定机器。 <br />
　　2） 指定ping回应次数为4： <br />
　　在Linux下，如果你不指定回应次数，ping命令将一直不断地向远方机器发送ICMP信息。我们可以通过-c参数来限定：ping -c 4 192.168.0.1 <br />
　　3） 通过特定的网卡进行ping： <br />
　　有时，我们需要检测某块网卡（系统中有多块）能否ping通远方机器。我们需要在执行ping命令时指出： <br />
　　ping -I eth0 192.168.0.1 <br />
　　2.traceroute <br />
　　如果你ping不通远方的机器，想知道是在什么地方出的问题；或者你想知道你的信息到远方机器都经过了哪些路由器，可以使用traceroute命令。顾名思义：trace是跟踪，route是路由，也就是跟踪路由。 <br />
　　使用这个命令很简单： <br />
　　traceroute 远程主机IP地址或域名 <br />
　　这个命令的输出类似： <br />
　　1 路由器（网关）的IP地址 访问所需时间1 访问所需时间2 访问所需时间3 <span class="Rdd604">www@bitscn@com</span> <br />
　　2 路由器（网关）的IP地址 访问所需时间1 访问所需时间2 访问所需时间3 <br />
　　&#8230;&#8230;&#8230; <br />
　　1） 最前面的数字代表&#8220;经过第几站&#8221;； <br />
　　2） 路由器（网关）的IP地址就是&#8220;该站&#8221;的IP地址； <br />
　　3） 访问所需时间1、2、3是指访问到这个路由器（网关）需要的时间。 <br />
　　<br />
　　3.netstat <br />
　　在Linux系统中，提供了一个功能十分强大的查看<a href="http://www.bitscn.com/network/"><u>网络</u></a>状态的工具：netstat。它可以让您得知整个Linux系统的<a href="http://www.bitscn.com/network/"><u>网络</u></a>情况。 <br />
　　1）统计出各<a href="http://www.bitscn.com/network/"><u>网络</u></a>设备传送、接收数据包的情况： <br />
　　使用命令：netstat -i <br />
　　这个命令将输出一张表，其中包括： <br />
　　Iface：<a href="http://www.bitscn.com/network/"><u>网络</u></a>接口名 MTU：最大传输单元 <br />
　　RX-OK：共成功接收多少个包 RX-ERR：接收的包中共有多少个错误包 <br />
　　RX-DRP：接收时共丢失多少个包 RX-OVR：共接收了多少个碰撞包 <br />
　　TX-OK：共成功发送多少个包 TX-ERR：发送的包中共有多少个错误包 <br />
　　TX-DRP：发磅时共丢失多少个包 TX-OVR：共接收了多少个碰撞包
<p class="Rdd604">bitsCN.nET*中国网管博客</p>
<br />
　　2）显示<a href="http://www.bitscn.com/network/"><u>网络</u></a>的统计信息 <br />
　　使用命令：netstat -s <br />
　　使用这个命令，将会以摘要的形式统计出IP、ICMP、TCP、UDP、TCPEXT形式的通信信息。 <br />
　　3）显示出TCP传输协议的<a href="http://www.bitscn.com/network/"><u>网络</u></a>连接情况： <br />
　　使用命令：netstat -t <br />
　　这个命令的输出也是一张表，其中包括： <br />
　　Local Address：本地地址，格式是IP地址：端口号 <br />
　　Foreign Address：远程地址，格式也是IP地址：端口号 <br />
　　State：连接状态，包括LISTEN、ESTABLISHED、TIME_WAIT等。 <br />
　　4）只显示出使用UDP的<a href="http://www.bitscn.com/network/"><u>网络</u></a>连接情况： <br />
　　使用命令：netstat -t <br />
　　输出格式也是一样的。 <br />
　　5）显示路由表： <br />
　　使用命令：netstat -r <br />
　　这个命令的输出与route命令的输出相同。 <br />
　　<br />
　　5.2 <a href="http://www.bitscn.com/network/"><u>网络</u></a>配置文件 <br />
　　在Red Hat Linux 7.1中有一些用于存放<a href="http://www.bitscn.com/network/"><u>网络</u></a>配置的文件： <br />
　　1./etc/hosts <br />
　　在该文件中存放的是一组IP地址与主机名的列表，如果在该列表中指出某台主机的IP地址，那么访问该主机时将无需进行DNS解析。 <span class="Rdd604">bbs.bitsCN.com中国网管论坛</span> <br />
　　2./etc/host.conf <br />
　　该文件用来指定域名解析方法的顺序，如： <br />
　　order hosts,bind <br />
　　它说明，首先通过/etc/hosts文件解析，如果在该文件中没有相应的主机名与IP地址的对应关系，再通过域名服务器bind进行解析。 <br />
　　3./etc/resolv.conf <br />
　　在该文件中存放域名服务器的IP地址。 <br />
　　4./etc/protocols <br />
　　Red Hat Linux 7.1系统使用该文件辨别本主机使用的，并通过它完成协议和协议号之间的映射，用户不应修改该文件。 <br />
　　5./etc/services <br />
　　该用户用于定义现有的<a href="http://www.bitscn.com/network/"><u>网络</u></a>服务，用户无需修改它，它通常由安装<a href="http://www.bitscn.com/network/"><u>网络</u></a>服务的程序来维护。该文件包括<a href="http://www.bitscn.com/network/"><u>网络</u></a>服务名、<a href="http://www.bitscn.com/network/"><u>网络</u></a>端口号和使用的协议类型，其中<a href="http://www.bitscn.com/network/"><u>网络</u></a>端口号和使用的协议类型之间有一个斜杠分开，在设置行的最后还可以添加一些服务的别名。 <br />
　　5./etc/xinetd.d目录 <br />
　　在Linux系统中有一个超级服务程序inetd，大部分的<a href="http://www.bitscn.com/network/"><u>网络</u></a>服务都是由它启动的，如chargen、echo、finger、talk、telnet、wu-ftpd等&#8230;，在7.0之间的版本它的设置是在/etc/inetd.conf中配置的，在Red Hat 7.0后，它就改成了一个xinetd.d目录。 <span class="Rdd604">bbs.bitsCN.com</span> <br />
　　在xinetd.d目录中，每一个服务都有一个相应的配置文件，我们以telnet为例，说明一下各个配置行的含义： <br />
　　service telnet <br />
　　{ <br />
　　socket_type=stream <br />
　　wait=no <br />
　　user=root <br />
　　server=/usr/sbin/in.telnetd <br />
　　log_on_failure+=USERID <br />
　　disable=yes <br />
　　} <br />
　　第一行，说明该配置用来设置telnet服务。 <br />
　　第二行，说明Socket连接类型是stream，也就是TCP <br />
　　第三行，是指不等待到启动完成 <br />
　　第四行，是指以root用户启动服务进程 <br />
　　第五行，是指服务进程是/usr/sbin/in.telnetd <br />
　　第六行，是用于做一些出错日志 <br />
　　第七行，是指禁止远方telnet，如果需要开放则将该配置改为：disable=no <br />
　　修改了xinetd的配置，需要重启xinetd才能够生效，有两种方法可以实现： <br />
　　1） 执行如下命令： <br />
　　/etc/rc.d/init.d/xinetd restart <br />
　　2） 执行如下命令： <br />
　　killall -HUP xinetd <br />
　　<br />
　　5.3 <a href="http://www.bitscn.com/network/"><u>网络</u></a>服务访问限制 <br />
　　在Red Hat Linux 7.1中加强了<a href="http://www.bitscn.com/network/"><u>网络</u></a>安全的防范，如果你安装时安全等级不是在最低一级的话，那么本机之外的所有访问都可能被拒绝。这是因为在Red Hat 7.1中做了一些默认的ipchains设置，这是Linux内置的防火墙机制，它可以使用一些规则来允许或禁止某种访问。
<p class="Rdd604">bbs.bitsCN.com</p>
<br />
　　它的规则存放在/etc/sysconfig/ipchains文件中，如果你想让它暂时不生效，那你可以运行/etc/rc.d/init.d/ipchains stop，那么所有的规则都被取消，所有的<a href="http://www.bitscn.com/network/"><u>网络</u></a>访问都将被允许。 <br />
　　你可以运行/etc/rc.d/init.d/ipchains status来获知现在对<a href="http://www.bitscn.com/network/"><u>网络</u></a>访问的限制。关于这方面的知识，本文限于篇幅无法详细介绍，有兴趣的读者可参考《Linux防火墙》一书。 <br />
　　<br />
　　5.4 WEB服务器 <br />
　　在Linux系统中最适合于做服务器的当数Apache，Red H<br />
<img src ="http://www.blogjava.net/tinysun/aggbug/318563.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-04-16 20:56 <a href="http://www.blogjava.net/tinysun/archive/2010/04/16/318563.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>频繁分配释放内存导致的性能问题的分析[转]</title><link>http://www.blogjava.net/tinysun/archive/2010/03/28/316737.html</link><dc:creator>何克勤</dc:creator><author>何克勤</author><pubDate>Sun, 28 Mar 2010 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/tinysun/archive/2010/03/28/316737.html</guid><wfw:comment>http://www.blogjava.net/tinysun/comments/316737.html</wfw:comment><comments>http://www.blogjava.net/tinysun/archive/2010/03/28/316737.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tinysun/comments/commentRss/316737.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tinysun/services/trackbacks/316737.html</trackback:ping><description><![CDATA[<strong>现象</strong><br />
1 压力测试过程中，发现被测对象性能不够理想，具体表现为： <br />
进程的系统态CPU消耗20，用户态CPU消耗10，系统idle大约70 <br />
2 用ps -o majflt,minflt -C program命令查看，发现majflt每秒增量为0，而minflt每秒增量大于10000。<br />
<br />
<strong>初步分析</strong><br />
majflt代表major fault，中文名叫大错误，minflt代表minor fault，中文名叫小错误。<br />
这两个数值表示一个进程自启动以来所发生的缺页中断的次数。<br />
当一个进程发生缺页中断的时候，进程会陷入内核态，执行以下操作： <br />
检查要访问的虚拟地址是否合法 <br />
查找/分配一个物理页 <br />
填充物理页内容（读取磁盘，或者直接置0，或者啥也不干） <br />
建立映射关系（虚拟地址到物理地址） <br />
重新执行发生缺页中断的那条指令 <br />
如果第3步，需要读取磁盘，那么这次缺页中断就是majflt，否则就是minflt。 <br />
此进程minflt如此之高，一秒10000多次，不得不怀疑它跟进程内核态cpu消耗大有很大关系。<br />
<br />
<strong>分析代码</strong><br />
查看代码，发现是这么写的：一个请求来，用malloc分配2M内存，请求结束后free这块内存。看日志，发现分配内存语句耗时10us，平均一条请求处理耗时1000us 。 原因已找到！ <br />
虽然分配内存语句的耗时在一条处理请求中耗时比重不大，但是这条语句严重影响了性能。要解释清楚原因，需要先了解一下内存分配的原理。 <br />
<br />
<strong>内存分配的原理</strong><br />
从操作系统角度来看，进程分配内存有两种方式，分别由两个系统调用完成：brk和mmap（不考虑共享内存）。brk是将数据段(.data)的最高地址指针_edata往高地址推，mmap是在进程的虚拟地址空间中（一般是堆和栈中间）找一块空闲的。这两种方式分配的都是虚拟内存，没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候，发生缺页中断，操作系统负责分配物理内存，然后建立虚拟内存和物理内存之间的映射关系。 <br />
在标准C库中，提供了malloc/free函数分配释放内存，这两个函数底层是由brk，mmap，munmap这些系统调用实现的。 <br />
下面以一个例子来说明内存分配的原理：<br />
<img alt="" src="http://images.csdn.net/20100325/c-1.jpg" /><br />
1进程启动的时候，其（虚拟）内存空间的初始布局如图1所示。其中，mmap内存映射文件是在堆和栈的中间（例如libc-2.2.93.so，其它数据文件等），为了简单起见，省略了内存映射文件。_edata指针（glibc里面定义）指向数据段的最高地址。 <br />
2进程调用A=malloc(30K)以后，内存空间如图2：malloc函数会调用brk系统调用，将_edata指针往高地址推30K，就完成虚拟内存分配。你可能会问：只要把_edata+30K就完成内存分配了？事实是这样的，_edata+30K只是完成虚拟地址的分配，A这块内存现在还是没有物理页与之对应的，等到进程第一次读写A这块内存的时候，发生缺页中断，这个时候，内核才分配A这块内存对应的物理页。也就是说，如果用malloc分配了A这块内容，然后从来不访问它，那么，A对应的物理页是不会被分配的。 <br />
3进程调用B=malloc(40K)以后，内存空间如图3. <br />
<br />
<img alt="" src="http://images.csdn.net/20100325/c-2.jpg" /><br />
4进程调用C=malloc(200K)以后，内存空间如图4：默认情况下，malloc函数分配内存，如果请求内存大于128K（可由M_MMAP_THRESHOLD选项调节），那就不是去推_edata指针了，而是利用mmap系统调用，从堆和栈的中间分配一块虚拟内存。这样子做主要是因为brk分配的内存需要等到高地址内存释放以后才能释放（例如，在B释放之前，A是不可能释放的），而mmap分配的内存可以单独释放。当然，还有其它的好处，也有坏处，再具体下去，有兴趣的同学可以去看glibc里面malloc的代码了。 <br />
5进程调用D=malloc(100K)以后，内存空间如图5. <br />
6进程调用free(C)以后，C对应的虚拟内存和物理内存一起释放 <br />
<img alt="" src="http://images.csdn.net/20100325/c-3.jpg" /><br />
<br />
7进程调用free(B)以后，如图7所示。B对应的虚拟内存和物理内存都没有释放，因为只有一个_edata指针，如果往回推，那么D这块内存怎么办呢？当然，B这块内存，是可以重用的，如果这个时候再来一个40K的请求，那么malloc很可能就把B这块内存返回回去了。 <br />
8进程调用free(D)以后，如图8所示。B和D连接起来，变成一块140K的空闲内存。 <br />
9默认情况下：当最高地址空间的空闲内存超过128K（可由M_TRIM_THRESHOLD选项调节）时，执行内存紧缩操作（trim）。在上一个步骤free的时候，发现最高地址空闲内存超过128K，于是内存紧缩，变成图9所示。<br />
<br />
真相大白<br />
说完内存分配的原理，那么被测模块在内核态cpu消耗高的原因就很清楚了：每次请求来都malloc一块2M的内存，默认情况下，malloc调用mmap分配内存，请求结束的时候，调用munmap释放内存。假设每个请求需要6个物理页，那么每个请求就会产生6个缺页中断，在2000的压力下，每秒就产生了10000多次缺页中断，这些缺页中断不需要读取磁盘解决，所以叫做minflt；缺页中断在内核态执行，因此进程的内核态cpu消耗很大。缺页中断分散在整个请求的处理过程中，所以表现为分配语句耗时（10us）相对于整条请求的处理时间（1000us）比重很小。<br />
<br />
解决办法<br />
将动态内存改为静态分配，或者启动的时候，用malloc为每个线程分配，然后保存在threaddata里面。但是，由于这个模块的特殊性，静态分配，或者启动时候分配都不可行。另外，Linux下默认栈的大小限制是10M，如果在栈上分配几M的内存，有风险。 <br />
禁止malloc调用mmap分配内存，禁止内存紧缩。<br />
在进程启动时候，加入以下两行代码：<br />
mallopt(M_MMAP_MAX, 0); // 禁止malloc调用mmap分配内存<br />
mallopt(M_TRIM_THRESHOLD, -1); // 禁止内存紧缩<br />
效果：加入这两行代码以后，用ps命令观察，压力稳定以后，majlt和minflt都为0。进程的系统态cpu从20降到10。<br />
<br />
小结<br />
可以用命令ps -o majflt minflt -C program来查看进程的majflt, minflt的值，这两个值都是累加值，从进程启动开始累加。在对高性能要求的程序做压力测试的时候，我们可以多关注一下这两个值。 <br />
如果一个进程使用了mmap将很大的数据文件映射到进程的虚拟地址空间，我们需要重点关注majflt的值，因为相比minflt，majflt对于性能的损害是致命的，随机读一次磁盘的耗时数量级在几个毫秒，而minflt只有在大量的时候才会对性能产生影响。
<img src ="http://www.blogjava.net/tinysun/aggbug/316737.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tinysun/" target="_blank">何克勤</a> 2010-03-28 10:48 <a href="http://www.blogjava.net/tinysun/archive/2010/03/28/316737.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>