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

<channel>
	<title>绿色记忆 &#187; Linux知识</title>
	<atom:link href="https://blog.gmem.cc/tag/linux%e7%9f%a5%e8%af%86/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Fri, 17 Apr 2026 09:20:32 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Linux的三种Init机制</title>
		<link>https://blog.gmem.cc/linux-init-mechanisms</link>
		<comments>https://blog.gmem.cc/linux-init-mechanisms#comments</comments>
		<pubDate>Mon, 26 Oct 2015 13:00:37 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=13637</guid>
		<description><![CDATA[<p>一台安装了Linux操作系统的计算机的启动，从BIOS加电开始，随后进入Bootloader，由Bootloader加载Linux内核并初始化。 内核本身不能做任何有意义的事情，内核初始化的最后一步，就是创建PID为1、名为init的守护进程，该进程是操作系统中所有用户进程的祖先——所有用户进程都由它创建。 所谓Init机制，就是确定了init进程行为的一套规范及其实现（此实现通常就反应在/sbin/init上）。Linux发展到今天，依次出现了sysvinit、upstart 、systemd三种广泛应用的Init机制。 Sysvinit sysvinit就是System V风格的初始化机制，它来源于System V风格的UNIX。绝大部分Linux发行版均兼容sysvinit。 sysvinit引入了运行级别（runlevel ）的概念，不同模式下需要进行的系统初始化工作不一样。各发行版对运行级别的定义各有不同，但是通常：0表示关机、1表示单用户模式、6表示重启。 sysvinit正常启动（单用户或者GUI模式）系统时，大致执行的工作流如下： 读取/etc/inittab获得需要进入的运行级别 依次执行： 执行rc.sysinit脚本，完成一些重要的系统初始化工作，例如：设置系统时钟、设置定义在/etc/sysctl.conf中的内核参数、启用swap、挂载文件系统等 执行rc*.d中的脚本，其中*为运行级别。被执行的脚本均以S开头，S后面的数字表示其被执行的顺序 执行rc.local脚本 如果需要，启动X显示管理器 sysvinit正常关闭（关机或者重启）系统时，则以数字顺序，执行rc*.d中的K开头的脚本。 sysvinit在启动、关闭时均是串行的执行各脚本的，这一方面让问题排查简单，一方面则导致运行速度缓慢。 很多Linux发行版为sysvinit配套了管理工具，例如service、chkconfig等。 参考Linux运行级别和启动顺序了解sysvinit的更多知识。 Upstart <a class="read-more" href="https://blog.gmem.cc/linux-init-mechanisms">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-init-mechanisms">Linux的三种Init机制</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><p>一台安装了Linux操作系统的计算机的启动，从BIOS加电开始，随后进入Bootloader，由Bootloader加载Linux内核并初始化。</p>
<p>内核本身不能做任何有意义的事情，内核初始化的最后一步，就是创建PID为1、名为init的守护进程，该进程是操作系统中所有用户进程的祖先——所有用户进程都由它创建。</p>
<p>所谓Init机制，就是确定了init进程行为的一套规范及其实现（此实现通常就反应在/sbin/init上）。Linux发展到今天，依次出现了sysvinit、upstart 、systemd三种广泛应用的Init机制。</p>
<div class="blog_h1" style="text-align: center;"><span class="graybg">Sysvinit</span></div>
<p>sysvinit就是System V风格的初始化机制，它来源于System V风格的UNIX。绝大部分Linux发行版均兼容sysvinit。</p>
<p>sysvinit引入了运行级别（runlevel ）的概念，不同模式下需要进行的系统初始化工作不一样。各发行版对运行级别的定义各有不同，但是通常：0表示关机、1表示单用户模式、6表示重启。</p>
<p>sysvinit正常启动（单用户或者GUI模式）系统时，大致执行的工作流如下：</p>
<ol>
<li>读取/etc/inittab获得需要进入的运行级别</li>
<li>依次执行：
<ol>
<li>执行rc.sysinit脚本，完成一些重要的系统初始化工作，例如：设置系统时钟、设置定义在/etc/sysctl.conf中的内核参数、启用swap、挂载文件系统等</li>
<li>执行rc*.d中的脚本，其中*为运行级别。被执行的脚本均以S开头，S后面的数字表示其被执行的顺序</li>
<li>执行rc.local脚本</li>
<li>如果需要，启动X显示管理器</li>
</ol>
</li>
</ol>
<p>sysvinit正常关闭（关机或者重启）系统时，则以数字顺序，执行rc*.d中的K开头的脚本。</p>
<p>sysvinit在启动、关闭时均是串行的执行各脚本的，这一方面让问题排查简单，一方面则导致运行速度缓慢。</p>
<p>很多Linux发行版为sysvinit配套了管理工具，例如<a href="/linux-command-faq#service">service、chkconfig</a>等。</p>
<p>参考<a href="https://blog.gmem.cc/linux-run-level">Linux运行级别和启动顺序</a>了解sysvinit的更多知识。</p>
<div class="blog_h1" style="text-align: center;"><span class="graybg">Upstart </span></div>
<div class="blog_h2"><span class="graybg">基础知识</span></div>
<p>从2.6版本的内核开始，即插即用被良好的支持，Linux被越来越多的用在个人计算机领域，这让sysvinit面临两个问题：</p>
<ol>
<li>串行化、阻塞的初始化过程导致启动消耗的时间长，而个人计算机却经常性的重启</li>
<li>一次性启动所有系统服务浪费很多资源，而即插即用要求按需启动、卸载服务</li>
</ol>
<p>早期的Ubuntu版本（6.10+）即启用了upstart这一init机制，以解决上述问题。</p>
<p>upstart是一个事件驱动的init机制，例如，当U盘插入到接口时，内核会通知upstart，后者会进行挂载操作。upstart的优势包括：</p>
<ol>
<li>能够很好的实现即插即用——当新硬件被发现时动态启动服务，硬件被拔出时停止服务</li>
<li>提高系统启动速度，一方面减少启动时的工作量，一方面事件驱动改变了串行话的执行方式</li>
</ol>
<div class="blog_h3"><span class="graybg">Upstart的启动过程</span></div>
<ol>
<li>执行内部的初始化</li>
<li>触发一个名为<pre class="crayon-plain-tag">startup</pre> 的特殊事件，该事件会触发系统剩余部分的初始化</li>
<li>init进程触发定义在/etc/init/mountall.conf的任务mountall，这是因为mountall声明了<pre class="crayon-plain-tag">start on startup</pre> </li>
<li>mountall进一步触发若干事件：local-filesystem、virtual-filesystem、all-swaps</li>
<li>local-filesystem导致udev的执行，后者导致upstart-udev-bridge的执行</li>
<li>local-filesystem发布filesystem事件、upstart-udev-bridge发布net-device-up IFACE=lo，这两个事件导致rc-sysinit的执行</li>
<li>rc-sysinit导致telinit的执行，后者发布runlevel事件</li>
<li>runlevel事件导致rc的执行，rc负责读取/etc/rc*.d中的脚本并执行</li>
</ol>
<div class="blog_h3"><span class="graybg">对sysvinit的兼容</span></div>
<p>很多现有的服务已经基于System V风格的Init脚本来编写，Upstart必须兼任它。尽管Upstart完全是基于任务+事件的，但是它模拟了对运行级别的支持。</p>
<p>修改/etc/init/rc-sysinti.conf中的DEFAULT_RUNLEVEL可以改变默认的“运行级别”。在启动过程中，rc任务会根据设置的运行级别，访问系统中的sysvinit脚本。</p>
<div class="blog_h2"><span class="graybg">重要概念</span></div>
<div class="blog_h3"><span class="graybg">任务（Job）</span></div>
<p>Job就是一个明确定义的工作单元，它可以启动一个系统服务，或者运行一个配置命令。每个Job都会等待一个或者多个事件，一旦事件发生，upstart就会触发Job完成工作。</p>
<p>Job可以分为三种类型：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">Job类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>task job</td>
<td>表示一个在有限时间内会完成的任务，例如删除一个文件</td>
</tr>
<tr>
<td>service job</td>
<td>
<p>表示后台服务进程，例如HTTP服务。这种任务通常不会停止，它启动后即由init进程管理，后者会在它崩溃后重新启动之</p>
<p>某些事件可以触发service job的终止，但是upstart也提供了手工管理的工具</p>
</td>
</tr>
<tr>
<td>abstract job</td>
<td>和upstart内部运作机制有关。尽管没有脚本段（Sections）或者exec节（<span style="color: #000000;">stanzas</span>），这种任务仍然可以启动，但是它没有对应的子进程（PID），除非被管理员停止，这种任务会一直运行</td>
</tr>
</tbody>
</table>
<p>根据Job的作用范围，可以将其分为系统级服务、会话级服务。后者仅为某个用户服务。</p>
<div class="blog_h3"><span class="graybg">任务状态</span></div>
<p>在Job的生命周期中，它可以处于以下状态之一：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">状态</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Waiting</td>
<td>初始状态</td>
</tr>
<tr>
<td>Starting</td>
<td>即将开始任务。此状态触发<strong>starting</strong>事件</td>
</tr>
<tr>
<td>Pre-start</td>
<td>任务启动前应该完成的准备工作</td>
</tr>
<tr>
<td>Spawned</td>
<td>准备执行 script 或者 exec 段</td>
</tr>
<tr>
<td>Post-start</td>
<td>任务启动完毕后执行的后置工作。此状态触发<strong>started</strong>事件</td>
</tr>
<tr>
<td>Running</td>
<td>post-start之后的一个临时状态</td>
</tr>
<tr>
<td>Pre-stop</td>
<td>任务停止前应该完成的准备工作</td>
</tr>
<tr>
<td>Stopping</td>
<td>pre-stop之后的一个临时状态。此状态触发<strong>stopping</strong>事件</td>
</tr>
<tr>
<td>Killed</td>
<td>任务即将被停止</td>
</tr>
<tr>
<td>Post-stop</td>
<td>任务停止后的清理工作。此状态触发<strong>stopped</strong>事件</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">事件（Event）</span></div>
<p>事件是一个通知，由Upstart发送给所有订阅者（任务或者其它事件），管理员随时可以手工的触发事件：</p>
<pre class="crayon-plain-tag">initctl emit myevent</pre>
<p>一个Job的启动/停止行为，可能是其它Job启动/停止行为的结果，这是依靠事件来驱动的。Upstart提供了一系列特殊事件（starting、started、stopping、stopped），用来广播Job的状态转换。尽管某些Job状态的名称与事件名称相同，但是它们不是一回事。</p>
<p>根据产生、消费方式的不同，事件分为：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">事件分类</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Signals</td>
<td>
<p>非阻塞的异步事件，发出信号后调用者立即返回，调用者不关心谁关注此信号，手工触发信号的方式如下：</p>
<pre class="crayon-plain-tag">initctl emit --no-wait mysignal</pre>
</td>
</tr>
<tr>
<td>Methods</td>
<td>阻塞的同步事件，这类事件的行为类似于编程语言中的方法/函数调用，调用者必须等待事件的订阅者处理完毕。手工触发时，initctl执行后的$?反应订阅者的处理结果</td>
</tr>
<tr>
<td>Hooks</td>
<td>
<p>介于信号和方法之间：
<ol>
<li>它像信号一样，通知系统发生了变化</li>
<li>它像方法一样，是阻塞的同步事件</li>
</ol>
<p>钩子的典型例子是starting、stopping这两个Job事件</p>
</td>
</tr>
</tbody>
</table>
<p>系统中预定义的事件取决于发行版，可以执行<pre class="crayon-plain-tag">man 7 upstart-events</pre> 查看之。 </p>
<div class="blog_h2"><span class="graybg">定义自己的Job</span></div>
<p>Job需要进行的工作由一个配置文件描述，该文件位于/etc/init目录中，以.conf结尾。</p>
<div class="blog_h3"><span class="graybg">小节详解</span></div>
<p>配置文件由多个小节（stanza） 构成，小节定义Job某个方面的特性：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 28px; text-align: center;">类</td>
<td style="width: 15%; text-align: center;">小节</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;" rowspan="2"><strong>文</strong><br /><strong>档</strong></td>
<td>author</td>
<td>定义任务的作者</td>
</tr>
<tr>
<td>description</td>
<td>定义任务的描述</td>
</tr>
<tr>
<td style="text-align: center;" rowspan="4"><strong>进</strong><br /><strong>程</strong><br /><strong>控<br />制<br /> </strong></td>
<td>expect</td>
<td>
<p>Upstart基于进程PID来跟踪Job，如果不明确指定，Upstart使用它执行exec/script小节时产生的第一个进程的PID</p>
<p>但是，大部分作为系统服务的Job都会守护进程化（daemonize），这些Job会再一次的fork出子进程，以确保守护程序与Job的初始进程没有关联性</p>
<p>Upstart本身无法知晓任务是否fork两次，这就需要使用expect来声明：</p>
<ol>
<li><pre class="crayon-plain-tag">expect fork</pre> 表示任务的进程只会fork一次次</li>
<li><pre class="crayon-plain-tag">expect daemonize</pre> 表示任务的进程会fork两次</li>
</ol>
<p>此外，<pre class="crayon-plain-tag">expect stop</pre> 期望Job的主进程触发一个SIGSTOP信号来指示它已经准备好，Upstart会等待此信号，然后发送SIGCONT信号给Job主进程提示它继续，并执行任务的post-start脚本</p>
</td>
</tr>
<tr>
<td>kill signal</td>
<td>
<p>设置Upstart发送何种信号以停止Job，默认SIGTERM，示例：</p>
<pre class="crayon-plain-tag">kill signal INT
kill signal SIGINT</pre>
</td>
</tr>
<tr>
<td>kill timeout</td>
<td>
<p>设置Upstart强制杀死Job进程前等待的秒数，默认5
</td>
</tr>
<tr>
<td>reload signal</td>
<td>
<p>设置任务需要被reload时，Upstart发送的信号，默认SIGHUP</p>
</td>
</tr>
<tr>
<td style="text-align: center;" rowspan="6"><strong>进</strong><br /><strong>程</strong><br /><strong>定</strong><br /><strong>义</strong></td>
<td>exec</td>
<td>
<p>指定Job的主逻辑。定义一个需要执行的单行的命令，如果命令行中包含任何Shell元字符，会直接传递给Shell，以确保Shell重定向、变量展开等功能的正常。语法格式：</p>
<pre class="crayon-plain-tag">exec COMMAND [ ARG ]...
# 示例
# exec /usr/bin/my-daemon --option foo -v</pre>
</td>
</tr>
<tr>
<td>pre-start</td>
<td>
<p>定义Job进入Pre-start状态时需要执行的逻辑，例如清空临时/缓存目录，最好不要包含耗时的逻辑。语法格式：
<pre class="crayon-plain-tag">pre-start exec|script
# 脚本可以具有多行内容
pre-start script
  # 在这里编写Shell脚本
  ...
  # 下面的这行脚本用于取消Job的启动
  stop ; exit 0
end script</pre>
</td>
</tr>
<tr>
<td>post-start</td>
<td>
<p>定义Job主线程产生之后（spawned）需要执行的逻辑，此时Job的started事件尚未发布。语法格式类似pre-start
<p>可以使用该小节来实现这样的功能：让Job进入started状态前，需要一个延迟或者达到某个条件。例如MySQL，在接受网络流量之前，它需要先执行Recovery操作</p>
</td>
</tr>
<tr>
<td>pre-stop</td>
<td>
<p>定义Job主线程被杀死（SIGTERM）之前、stopping事件被发布之前需要执行的逻辑。语法格式类似pre-start</p>
<p>停止Job时Upstart发送SIGTERM信号，如果超过Kill timeout（默认5秒）Job尚未退出，Upstart会发送SIGKILL，这可能导致数据丢失（例如磁盘操作）。可以把敏感操作转移到pre-stop中以避免此情况</p>
<p>你也可以使用该小节来取消服务的停止</p>
</td>
</tr>
<tr>
<td>post-stop</td>
<td>在Job进程被杀死后，执行清理工作</td>
</tr>
<tr>
<td>script</td>
<td>指定Job的主逻辑。定义一个多行的脚本，以<pre class="crayon-plain-tag">end script</pre> 标注脚本块的结束</td>
</tr>
<tr>
<td style="text-align: center;" rowspan="3"><strong>事</strong><br /><strong>件</strong><br /><strong>定<br />义</strong> </td>
<td>manual</td>
<td>提示Upstart，忽略start on和stop on小节，总是手工的启动、停止</td>
</tr>
<tr>
<td>start on</td>
<td>
<p>该小节定义导致Job启动的一系列事件，语法格式：</p>
<pre class="crayon-plain-tag"># EVENT为事件的名称，可以指定多个事件，并用and或者or连接，支持用( ) 来限定逻辑操作的优先级
# KEY和VALUE可以用来限定环境变量，这些变量可以由事件定义，并传递给Job的小节
# VALUE支持fnmatch风格的通配符
# KEY和VALUE可以使用 != 表示不相等
start on EVENT [[KEY=]VALUE]... [and|or...]</pre>
<p>该小节的内容必须编写在单行中</p>
<p>如果要 在系统的基础设施准备完毕后启动Job，参考下面的例子：</p>
<pre class="crayon-plain-tag"># 在本地文件系统、非环回网卡准备好后，启动该Job
# 如果你的Job绑定了非0.0.0.0地址，则它需要在目标网卡启动后，才能启动
start on (local-filesystems and net-device-up IFACE!=lo)

# 在指定的运行级别启动Job
start on runlevel [2345]

# 在抽象任务network-services启动完毕之后，启动Job
start on started network-services</pre>
<p>如果要在一个服务启动之后，启动Job，使用：</p>
<pre class="crayon-plain-tag">start on started other-service</pre>
<p>反之，如果要在一个服务启动之前，启动Job，使用：</p>
<pre class="crayon-plain-tag">start on starting other-service </pre>
</td>
</tr>
<tr>
<td>stop on</td>
<td>
<p>该小节定义一系列导致Job停止的事件（如果Job正在运行），语法格式：
<pre class="crayon-plain-tag">stop on EVENT [[KEY=]VALUE]... [and|or...]</pre>
<p>用法类似于start on，示例：</p>
<p><pre class="crayon-plain-tag">stop on runlevel [016]
stop on stopping network-services
stop on stopped other-service</pre>
</td>
</tr>
<tr>
<td style="text-align: center;" rowspan="2"><strong>任</strong><br /><strong>务</strong><br /><strong>环境</strong> </td>
<td>env </td>
<td>设置一个所有小节都能看到的环境变量，语法格式：<pre class="crayon-plain-tag">env KEY[=VALUE]</pre> </td>
</tr>
<tr>
<td>export</td>
<td>导出Job中通过env小节设置的所有环境变量到该Job触发的全部事件中去</td>
</tr>
<tr>
<td style="text-align: center;" rowspan="5"><strong>其</strong><br /><strong>它</strong></td>
<td>normal exit</td>
<td>
<p>指定那些退出码（exit status）表示正常退出，默认0。示例：</p>
<pre class="crayon-plain-tag"># 0和13视为正常退出
normal exit 0 13
# 如果因为SIGUSR1 SIGWINCH信号而退出，也视为正常
normal exit 0 13 SIGUSR1 SIGWINCH</pre>
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td>respawn</td>
<td>
<p>在不指定此小节的情况下，静默退出的Job进入 stop/waiting 状态</p>
<p>使用此小节的情况下，一旦Job从它的主逻辑exec/script退出，Upstart都会重新启动它，一并执行定义的pre-start,、post-start、 post-stop小节（pre-stop 不被执行）</p>
<p>使用此特性的例子包括各种网络服务，例如MySQL，只要不是管理员有意的停止它，都应该一直运行</p>
</td>
</tr>
<tr>
<td>respawn limit</td>
<td>
<p>限制重新启动的次数，语法格式：</p>
<pre class="crayon-plain-tag"># 最多重新启动limit次，每次启动间隔INTERVAL秒
respawn limit COUNT INTERVAL | unlimited
# 示例：
respawn limit 10 5
respawn limit unlimited</pre>
</td>
</tr>
<tr>
<td>instance</td>
<td>声明Job的实例名，有时你需要运行一个Job的多个实例</td>
</tr>
<tr>
<td>task</td>
<td>
<p>从概念上说，task是一种短暂运行完毕的Job
<p>使用此关键字后，导致此Job启动的事件将被阻塞，直到当前Job转换到stopped状态</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">编写Job的注意点</span></div>
<p>当开发自己的Job时，需要注意一下内容：</p>
<ol>
<li>fork的次数需要声明：很多Linux后台服务基于Double fork实现守护进程，这必须在Job配置文件中声明</li>
<li>fork后即可用：你必须保证在Double fork后，服务立刻进入可用状态，因为Upstart基于fork计数判断服务是否就绪</li>
<li>遵循SIGHUP要求：Upstart发送SIGHUP信号给Job后，期望Job的行为满足以下要求：
<ol>
<li>完成必要的重现初始化工作，例如重新读取配置文件</li>
<li>Job必须使用现有的PID，即Job不能通过fork来实现reload</li>
</ol>
</li>
<li>遵循SIGTEM要求：Job收到此信号后，应当立即停止，并释放所有资源</li>
</ol>
<div class="blog_h3"><span class="graybg">自定义Job的例子</span></div>
<p>这里我们讲解一下MySQL的Job定义：</p>
<pre class="crayon-plain-tag"># 任务的描述信息
description     "MySQL Server"
author          "Mario Limonciello &lt;superm1@ubuntu.com&gt;"

# 在2345运行级别启动服务
start on runlevel [2345]
# 在rc服务启动之前，如果目标运行级别是016则停止服务
stop on starting rc RUNLEVEL=[016]

# 当任务以外退出时重启，最多重启2次，间隔5秒
respawn
respawn limit 2 5
# 设置整个文件内可见的变量
env HOME=/etc/mysql
# 设置进程的umask
umask 007

# 延长强杀超时，因为MySQL刷出缓冲需要时间
kill timeout 300

pre-start script
    # 在启动前的准备工作，普通Shell脚本，略
    # 访问变量的语法与Bash相同
    $HOME
end script

# 主逻辑脚本
exec /usr/sbin/mysqld

post-start script
   # 启动后的清理工作
end script</pre>
<div class="blog_h2"><span class="graybg">相关命令</span></div>
<p>和Upstart相关的命令包括：initctl，关于此命令的细节请参考<a href="/linux-command-faq#initctl">Linux命令知识集锦</a>。</p>
<div class="blog_h1"><span class="graybg">Systemd</span></div>
<div class="blog_h2"><span class="graybg">基础知识</span></div>
<p>Systemd是主流Init系统中最新的一个，是Upstart的竞争者。值得注意的是，作为Upstart起源的Ubuntu从15.04版本开始，也改用了Systemd。</p>
<p>Systemd具有以下特点：</p>
<ol>
<li>兼容SysVinit或者 LSB initscripts</li>
<li>提供比Upstart更加激进的并行启动能力，通过socket / D-Bus activation等技术启动服务，速度更快</li>
<li>提供按需启动的能力</li>
<li>利用cgroup跟踪进程的生命周期，比Upstart基于PID的方式更加高效。在fork时，子进程会遍历父进程的cgroup，Systemd通过遍历cgroup可以把服务相关的所有进程都正确的停止掉</li>
<li>内建挂载服务，以便自动挂载U盘、光盘，不再需要安装autofs服务</li>
<li>事务性的依赖管理，Systemd在最大程度上保证并发启动的同时，确保那些有明确依赖顺序的服务（例如NFS依赖网络），按照顺序启动</li>
<li>内建日志服务journald，可以代替syslog，克服了syslog的一系列缺点</li>
</ol>
<div class="blog_h2"><span class="graybg">重要概念</span></div>
<div class="blog_h3"><span class="graybg">单元（Unit）</span></div>
<p>Systemd把系统启动过程中的每一项工作，例如：</p>
<ol>
<li>启动后台服务，例如SSHD</li>
<li>执行系统配置，例如挂载文件系统</li>
</ol>
<p>等都抽象为一个配置单元，它们的角色类似于Upstart的Job。配置单元被划分为不同的类型：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">配置单元类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>service</td>
<td>封装一个后台服务，例如MySQL，最常用</td>
</tr>
<tr>
<td>socket</td>
<td>封装系统中的一个套接字，每个socket单元都有一个相应的service单元，当第一个连接到达时，与socket关联的service就会启动</td>
</tr>
<tr>
<td>device</td>
<td>封装一个存在于Linux设备树中的设备，每个使用udev规则标记的设备都会在Systemd中作为一个device单元。例如/dev/sda2会自动被转换为dev-sda2.device单元</td>
</tr>
<tr>
<td>mount</td>
<td>
<p>封装文件系统中的一个挂载点，Systemd会对这个挂载点进行监控管理，例如在系统启动时执行挂载，在某些情况下卸载<br />/etc/fstab中的条目均被转换为mount单元并在系统启动时处理，例如/home会被转换为home.mount单元</p>
</td>
</tr>
<tr>
<td>automount</td>
<td>封装文件系统中需要自动挂载的挂载点，每个automount单元都对应一个mount单元，当自动挂载点被访问时，它被自动的挂载</td>
</tr>
<tr>
<td>swap</td>
<td>管理交换分区</td>
</tr>
<tr>
<td>target</td>
<td>对其它配置单元进行逻辑分组，这样可以很方便的实现运行级别，例如multi-user.target相当于sysvinit中的运行级别5，分组中的所有配置单元都被执行</td>
</tr>
</tbody>
</table>
<p>每个配置单元都有一个对应的配置文件，例如MySQL服务对应mysql.service。配置文件的语法比起System V脚本要简单的多。</p>
<div class="blog_h3"><span class="graybg">依赖关系</span></div>
<p>Systemd尽量的保证了并发启动，但是某些任务之间存在天然的依赖关系，不能通过套接字激活（socket activation）、D-Bus激活、autofs这三种方法来解除依赖。为了满足先后启动的需要，Systemd允许在配置文件中声明单元的依赖关系。</p>
<p>要声明一个强依赖，可以使用<pre class="crayon-plain-tag">A require B</pre> 的语法，声明弱依赖则使用<pre class="crayon-plain-tag">A want B</pre> 。如果服务之间存在依赖循环，则先去掉弱依赖，如果循环仍然存在，则Systemd会报错。</p>
<div class="blog_h3"><span class="graybg">Target和运行级别</span></div>
<p>前面我们提到过target这类特殊的配置单元，用于分组其它配置单元。利用Target把sysvinit某个运行级别中需要启动的所有服务归纳到同一个组中，可以很方便的模拟运行级别。Systemd预定义了运行级别和Target之间的映射：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">运行级别</td>
<td style="text-align: center;">运行级别含义</td>
<td style="text-align: center;">Systemd targets</td>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>关机</td>
<td>runlevel0.target, poweroff.target</td>
</tr>
<tr>
<td>1,s,single</td>
<td>单用户模式</td>
<td>runlevel1.target, rescue.target</td>
</tr>
<tr>
<td>2,4</td>
<td>用户定义的运行级别</td>
<td>runlevel2.target, runlevel4.target, multi-user.target</td>
</tr>
<tr>
<td>3</td>
<td>多用户模式，非图形化</td>
<td>runlevel3.target, multi-user.target</td>
</tr>
<tr>
<td>5</td>
<td>多用户模式，图形化</td>
<td>runlevel5.target, graphical.target</td>
</tr>
<tr>
<td>6</td>
<td>重现启动</td>
<td>runlevel6.target, reboot.target</td>
</tr>
<tr>
<td>emergency</td>
<td>紧急Shell</td>
<td>emergency.target</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">依赖解除原理</span></div>
<p>Systemd通过并行加速启动过程，要提高并行度，必须解除服务之前的依赖关系。Systemd通过三种手段来解除依赖，这三种手段都基于延迟（Lazy）启动的思想。</p>
<div class="blog_h3"><span class="graybg">解除Socket依赖</span></div>
<p>绝大部分服务之间的依赖是套接字依赖——被依赖者（A）的套接字（S）必须准备好监听，依赖者（B）才能启动。传统的Init机制都是启动A，然后再启动B。</p>
<p>Systemd的想法是，直接启动S，当B第一次向S发起连接时，延迟的启动A。能让服务A的套接字先于A本身启动，需要依靠内核的一个特性：<span style="background-color: #c0c0c0;">子进程可以继承父进程的文件描述符（文件句柄，file descriptor） </span>，而套接字是一种文件句柄。因此先于A启动S的流程可以如下：</p>
<ol>
<li>由init进程创建套接字S，清空其close_on_exec位，以便允许它被子进程继承</li>
<li>当B向S发起连接时，由init进程fork/exec出A的主进程</li>
<li>A的主进程自动获得套接字S</li>
</ol>
<p>其实这种内核特性以前就被使用过，例如inetd。</p>
<div class="blog_h3"><span class="graybg">解除D-Bus依赖</span></div>
<p>所谓D-Bus即桌面总线（Desktop-bus），是一种低延迟、低开销、高可用的进程间通信机制。它被越来越多的用于应用程序之间的通信，以及应用程序与内核的通信。例如NetworkManager就使用D-Bus和其它应用程序交互。</p>
<p>D-Bus支持所谓Bus activation特性：如果A依赖于B的D-Bus服务，而B尚未运行，则D-Bus可以在A第一次请求B的时候，启动B。在B启动完毕之前A会一直等待。依赖于D-Bus的这一特性，也可以实现并行启动。</p>
<div class="blog_h3"><span class="graybg">解除文件系统依赖</span></div>
<p>文件系统相关的活动是相当耗时的，因为与之相关的配置单元而导致串行化执行，会大大的拖累系统启动速度。</p>
<p><span style="background-color: #c0c0c0;">Autofs是一个实现按需挂载</span>的软件，它利用了内核的automounter模块：当open()系统调用作用在/mnt/dsk/file1时，/mnt/dsk可能尚未挂载，此时内核将让open()调用挂起，并通知Autofs执行挂载，完毕后把控制权返回给open()调用。</p>
<p>Systemd在内部集成了Autofs的实现。对于系统中的挂载点它会在启动时创建一个临时的Mock，这样，依赖于此挂载点的服务就可以立即启动。当文件系统相关的配置单元都启动成功后，Systemd再把那些Mock替换为真正的挂载点。如果在文件系统尚未准备好时，Mock就被访问，Systemd则让访问者陷入等待。</p>
<div class="blog_h2"><span class="graybg">用户级单元</span></div>
<p>Systemd允许用户管理自己控制下的服务 —— 启动、停止、禁用自己的配置单元，每个用户都具有自己Systemd实例。</p>
<p>用户级单元可以放在/usr/lib/systemd/user、 ~/.config/systemd/user等位置。</p>
<p>使用--user参数，systemctl管理当前用户的systemd单元：</p>
<pre class="crayon-plain-tag">systemctl --user enable myapp.service </pre>
<div class="blog_h2"><span class="graybg">自定义配置单元</span></div>
<p>配置单元同样由文件文件来定义，命名格式为<pre class="crayon-plain-tag">unitName.unitType</pre> （后缀为此配置单元的类型），<span style="background-color: #c0c0c0;">存放路径</span>取决于具体发行版，例如<span style="background-color: #c0c0c0;">/etc/systemd/system</span>，Ubuntu则存放在<span style="background-color: #c0c0c0;">/lib/systemd/system</span>目录中。</p>
<div class="blog_h3"><span class="graybg">编写配置单元</span></div>
<p>配置单元由一系列的段组成，格式类似于Windows下的INI文件。</p>
<p><em><strong>Unit段：基础元数据和依赖关系</strong></em></p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 18%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Description</td>
<td>对单元的描述</td>
</tr>
<tr>
<td>Documentation</td>
<td>文档URL </td>
</tr>
<tr>
<td>Requires </td>
<td>当前单元所强依赖的其它单元，多个单元用空白分隔 ，如果依赖没法满足，该单元无法启动</td>
</tr>
<tr>
<td>Wants</td>
<td>当前单元所弱依赖于的其它单元 </td>
</tr>
<tr>
<td>BindsTo</td>
<td>类似于Requires，如果所依赖的单元退出，则当前单元也退出</td>
</tr>
<tr>
<td>Before</td>
<td>限定当前单元必须在指定的单元之前启动</td>
</tr>
<tr>
<td>After</td>
<td>限定当前单元必须在指定的单元之后启动</td>
</tr>
<tr>
<td>Conflicts</td>
<td>指定不能和当前单元同时运行的单元</td>
</tr>
<tr>
<td>Condition***</td>
<td>
<p>限定当前单元运行必须满足的条件（如果不满足则不运行），例如：</p>
<pre class="crayon-plain-tag"># 只有/etc/ssh/sshd_not_to_be_run文件不存在时，才运行此单元
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run </pre>
</td>
</tr>
<tr>
<td>Assert***</td>
<td>限定当前单元运行必须满足的条件（如果不满足则报启动失败）</td>
</tr>
</tbody>
</table>
<p><strong><em><span style="color: #111111;">Service段：专用于服务类单元的配置</span></em></strong>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 18%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Type</td>
<td>定义启动时的进程行为，有效值：<br />simple  默认值，执行ExecStart指定的命令，启动服务主进程<br />forking  以fork调用创建子进程，并退出父进程<br />oneshot  一次性服务，Systemd会等待该服务退出，然后继续init过程<br />dbus  当前服务通过D-Bus启动<br />notify  当服务启动完毕，通知Systemd继续<br />idle  当其它单元都启动完毕后，才执行此单元</td>
</tr>
<tr>
<td>ExecStart</td>
<td>
<p>启动服务的命令行</p>
<p>默认情况下，这些生命周期钩子执行失败会终止后续流程。要改变此逻辑，使用<pre class="crayon-plain-tag">=-</pre>操作符：</p>
<pre class="crayon-plain-tag">ExecStartPre=-/bin/chmod 700       /run/thing</pre>
<p>这样，即使 ExecStartPre 执行失败，后续的ExecStart仍然会执行</p>
</td>
</tr>
<tr>
<td>ExecStartPre</td>
<td>
<p>启动服务的前置命令行</p>
<p>这些<span style="background-color: #c0c0c0;">生命周期钩子可以指定多次，前面的先执行</span></p>
</td>
</tr>
<tr>
<td>ExecStartPost</td>
<td>启动服务的后置命令行</td>
</tr>
<tr>
<td>ExecReload</td>
<td>重新加载服务时执行的命令行</td>
</tr>
<tr>
<td>ExecStop</td>
<td>停止服务时执行的命令行</td>
</tr>
<tr>
<td>ExecStopPost</td>
<td>停止服务的后置命令行</td>
</tr>
<tr>
<td>RestartSec</td>
<td>重启的间隔秒数</td>
</tr>
<tr>
<td>Restart</td>
<td>
<p>重启服务的时机，有效值：</p>
<p style="padding-left: 30px;">no 不重启<br />always  总是重启<br />on-success  干净退出后重启<br />on-failure  非干净退出后重启，包括Core Dump+非<br />on-abnormal 异常后重启<br />on-abort  中断后重启<br />on-watchdog</p>
<p>干净退出：退出码为0，或者因为SIGHUP, SIGINT, SIGTERM or SIGPIPE退出。或者定义在 SuccessExitStatus中的其它退出码、信号</p>
</td>
</tr>
<tr>
<td>TimeoutSec</td>
<td>启动超时的秒数，超过这个时间还未启动完毕，终止服务</td>
</tr>
<tr>
<td>Environment</td>
<td>用于指定环境变量</td>
</tr>
</tbody>
</table>
<p><strong><em>Install段：定义如何启动单元，是否开机启动</em></strong></p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 18%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>WantedBy</td>
<td>
<p>指定一个或者多个目标（Target）对当前单元具有弱依赖，当systemctl enable当前单元时，会把当前单元的符号连接放到/etc/systemd/system下的Target名称.wants目录中</p>
<p>该名称项可用于模拟sysvinit的初始化方式</p>
</td>
</tr>
<tr>
<td>RequiredBy</td>
<td>指定一个或者多个目标（Target）对当前单元具有强依赖</td>
</tr>
<tr>
<td>Alias</td>
<td>调用systemctl时可以使用的当前单元的别名</td>
</tr>
<tr>
<td>Also</td>
<td>指定当前单元被systemctl enable时，会同时激活的其它单元</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">编写配置单元的注意点</span></div>
<ol>
<li>后台服务不再需要使用Double fork来产生守护进程</li>
<li>不要调用 setsid()，应该由Systemd处理</li>
<li>不需要维护PID文件</li>
<li>Systemd提供了日志功能，服务进程只需要：
<ol>
<li>输出到stderr即可，取决于配置</li>
<li>打开systemd-cat管道并输出，例如 <pre class="crayon-plain-tag">echo "message" | systemd-cat</pre> </li>
<li>可以在服务脚本中调用log_daemon_msg、log_failure_msg、log_progress_msg、log_end_msg等LSB函数</li>
</ol>
</li>
<li>正确处理SIGTERM信号，立即停止当前服务</li>
<li>正确处理SIGHUP信号，重新启动服务</li>
<li>需要套接字的服务，不要自己创建，让Systemd传入</li>
<li>调用<pre class="crayon-plain-tag">sd_notify()</pre>函数告知Systemd服务本身状态的改变。当服务初始化完毕后进入就绪状态时，即可调用之 </li>
</ol>
<div class="blog_h3"><span class="graybg">模板化</span></div>
<p>Systemd支持模板化的Unit文件，用于从单个配置文件启动多个单元。调用模板化Unit时，需要使用特殊的@符号：<pre class="crayon-plain-tag">&lt;service_name&gt;@&lt;argument&gt;.service</pre>。</p>
<p>其中argument是传递给模板的字符串，在模板文件中可以这样访问：</p>
<ol>
<li><pre class="crayon-plain-tag">%i</pre>：进行转义的参数</li>
<li><pre class="crayon-plain-tag">%I</pre>：原样传递的参数</li>
</ol>
<p>Ceph Mon就是这样的模板Unit：</p>
<pre class="crayon-plain-tag">[Unit]
Description=Ceph cluster monitor daemon

After=network-online.target local-fs.target time-sync.target
Wants=network-online.target local-fs.target time-sync.target

PartOf=ceph-mon.target

[Service]
LimitNOFILE=1048576
LimitNPROC=1048576
EnvironmentFile=-/etc/default/ceph
Environment=CLUSTER=ceph
; 访问参数
ExecStart=/usr/bin/ceph-mon -f --cluster ${CLUSTER} --id %i --setuser ceph --setgroup ceph
ExecReload=/bin/kill -HUP $MAINPID
PrivateDevices=yes
ProtectHome=true
ProtectSystem=full
PrivateTmp=true
TasksMax=infinity
Restart=on-failure
StartLimitInterval=30min
StartLimitBurst=5
RestartSec=10

[Install]
WantedBy=ceph-mon.target</pre>
<p>在目录下有指向上述模板的符号链接，参数就是通过符号链接的名字传递的：</p>
<pre class="crayon-plain-tag">cd /etc/systemd/system#
ls ceph-mon.target.wants/ -la
# ceph-mon@xenon.service -&gt; /lib/systemd/system/ceph-mon@.service
# ceph-mon@Xenon.service -&gt; /lib/systemd/system/ceph-mon@.service</pre>
<div class="blog_h3"><span class="graybg">自定义配置单元的例子</span></div>
<p>这里我们讲解一下MySQL的Unit定义：</p>
<pre class="crayon-plain-tag"># MySQL systemd service file

[Unit]
# 配置单元描述
Description=MySQL Community Server
# 声明依赖
After=network.target

[Install]
# 该服务在多用户模式下启动
WantedBy=multi-user.target

[Service]
User=mysql
Group=mysql
PermissionsStartOnly=true
# 启动服务之前需要运行的命令
ExecStartPre=/usr/share/mysql/mysql-systemd-start pre
# 启动服务主程序
ExecStart=/usr/sbin/mysqld
# 启动服务之后需要运行的命令
ExecStartPost=/usr/share/mysql/mysql-systemd-start post
TimeoutSec=600
Restart=on-failure
RuntimeDirectory=mysqld
RuntimeDirectoryMode=755</pre>
<p>我们再看看multi-user.target这个配置单元组的定义：</p>
<pre class="crayon-plain-tag">[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
# 该组被启动之前basic组必须被启动，同样的，basic停止前该组必须先停止
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

[Install]
# 为本单元组定义别名，调用systemctl命令时可以使用别名
Alias=default.target</pre>
<div class="blog_h2"><span class="graybg">相关命令</span></div>
<p>和Systemd相关的命令包括：systemctl、journal、systemd-analyze、hostnamectl、localectl、timedatectl、loginctl等，关于此命令的细节请参考<a href="/linux-command-faq#systemctl">Linux命令知识集锦</a>。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-init-mechanisms">Linux的三种Init机制</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-init-mechanisms/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux目录层次和配置文件</title>
		<link>https://blog.gmem.cc/linux-files</link>
		<comments>https://blog.gmem.cc/linux-files#comments</comments>
		<pubDate>Fri, 13 Jul 2012 07:43:07 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=1268</guid>
		<description><![CDATA[<p>/ 唯一的根目录，没有Windows的盘符概念。根目录下的任何一个子目录都可以作为一个挂载点（mount point）来挂载某个文件系统（例如分区）。 /proc 即procfs，包含系统中正在运行的进程的映射文件，以及对应其它功能的若干子目录。 /proc/uptime 自从上次系统自举以来的秒数，以及其中有多少秒处于空闲。这主要供uptime程序使用。比较这两个数字能够告诉你长期来看CPU周期浪费的比例。 /proc/version 操作系统的相关信息 /proc/cmdline 这个文件给出了内核启动的命令行。它和用于进程的cmdline项非常相似。 /proc/modules 可加载内核模块的信息。lsmod程序用这些信息显示有关模块的名称，大小，使用数目方面的信息。 /proc/cpuinfo 包含CPU的详细信息 /proc/meminfo 内存状态的信息。它显示出系统中空闲内存，已用物理内存和交换内存的总量。它还显示出内核使用的共享内存和缓冲区总量。这些信息的格式和free命令显示的结果类似。 /proc/kcore 系统的物理内存以core文件格式保存的文件。例如，GDB能用它考察内核的数据结构。它不是纯文本，而是/proc目录下为数不多的几个二进制格式的项之一。 /proc/cgroups 本系统上已知的Cgroups的信息 /proc/devices <a class="read-more" href="https://blog.gmem.cc/linux-files">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-files">Linux目录层次和配置文件</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">/</span></div>
<p>唯一的根目录，没有Windows的盘符概念。根目录下的任何一个子目录都可以作为一个<strong>挂载点（mount point）</strong>来挂载某个文件系统（例如分区）。</p>
<div class="blog_h1"><span class="graybg">/proc</span></div>
<p>即procfs，包含系统中正在运行的进程的映射文件，以及对应其它功能的若干子目录。</p>
<div class="blog_h2"><span class="graybg">/proc/uptime</span></div>
<p>自从上次系统自举以来的秒数，以及其中有多少秒处于空闲。这主要供uptime程序使用。比较这两个数字能够告诉你长期来看CPU周期浪费的比例。</p>
<div class="blog_h2"><span class="graybg">/proc/version</span></div>
<p>操作系统的相关信息</p>
<div class="blog_h2"><span class="graybg">/proc/cmdline</span></div>
<p>这个文件给出了内核启动的命令行。它和用于进程的cmdline项非常相似。</p>
<div class="blog_h2"><span class="graybg">/proc/modules</span></div>
<p>可加载内核模块的信息。lsmod程序用这些信息显示有关模块的名称，大小，使用数目方面的信息。</p>
<div class="blog_h2"><span class="graybg">/proc/cpuinfo</span></div>
<p>包含CPU的详细信息</p>
<div class="blog_h2"><span class="graybg">/proc/meminfo</span></div>
<p>内存状态的信息。它显示出系统中空闲内存，已用物理内存和交换内存的总量。它还显示出内核使用的共享内存和缓冲区总量。这些信息的格式和free命令显示的结果类似。</p>
<div class="blog_h2"><span class="graybg">/proc/kcore</span></div>
<p>系统的物理内存以core文件格式保存的文件。例如，GDB能用它考察内核的数据结构。它不是纯文本，而是/proc目录下为数不多的几个二进制格式的项之一。</p>
<div class="blog_h2"><span class="graybg">/proc/cgroups</span></div>
<p>本系统上已知的Cgroups的信息</p>
<div class="blog_h2"><span class="graybg">/proc/devices</span></div>
<p>列出字符和块设备的主设备号，以及分配到这些设备号的设备名称。</p>
<div class="blog_h2"><span class="graybg">/proc/mounts</span></div>
<p>指向/proc/self/mounts</p>
<p>已经挂载的文件系统的信息，这个文件以/etc/mtab文件的格式给出当前系统所安装的文件系统信息。这个文件也能反映出任何手工安装从而在/etc/mtab文件中没有包含的文件系统。</p>
<div class="blog_h2"><span class="graybg">/proc/partitions</span></div>
<p>块设备分区使用情况</p>
<div class="blog_h2"><span class="graybg">/proc/filesystems</span></div>
<p>列出可供使用的文件系统类型，一种类型一行。虽然它们通常是编入内核的文件系统类型，但该文件还可以包含可加载的内核模块加入的其它文件系统类型。</p>
<div class="blog_h2"><span class="graybg">/proc/net </span></div>
<p>指向/proc/self/net，此目录下的文件描述或修改了联网代码的行为。可以通过使用arp,netstat,route和ipfwadm命令设置或查询这些特殊文件中的许多文件。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">/proc/net/arp</td>
<td>转储每个网络接口的arp表中dev包的统计</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/dev</td>
<td>来自网络设备的统计</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/dev_mcast </td>
<td>列出二层（数据链路层）多播组</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/igmp</td>
<td>加入的IGMP多播组</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/netlink</td>
<td>netlink套接字的信息</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/netstat</td>
<td>网络流量的多种统计。第一行是信息头，带有每个变量的名称。接下来的一行保存相应变量的值</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/raw</td>
<td>原始套接字的套接字表</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/route</td>
<td>静态路由表</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/rpc</td>
<td>包含RPC信息的目录</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/rt_cache</td>
<td>路由缓存</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/snmp</td>
<td>snmp agent的ip/icmp/tcp/udp协议统计，各行交替给出字段名和值</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/sockstat</td>
<td>列出使用的tcp/udp/raw/pac/syc_cookies的数量</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/tcp</td>
<td>TCP连接的套接字<br />
<pre class="crayon-plain-tag">cat /proc/2177/net/tcp | grep 102681918
#  sl  local_address rem_address   st tx_queue rx_queue tr tm-&gt;when retrnsmt   
   0: 00000000:18EB 00000000:0000 0A 00000000:00000000 00:00000000 00000000     
#  uid  timeout inode                                                     
   0        0 102681919 1 ffff8d5ff6b45540 100 0 0 10 0</pre>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/udp</td>
<td>UDP连接的套接字表</td>
</tr>
<tr>
<td class="blog_h3">/proc/net/unix</td>
<td>UNIX域套接字的套接字表</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">/proc/sys</span></div>
<p>包括所有的内核运行时参数信息，某些文件支持写操作。</p>
<p>这些文件和内核参数相互对应，所有内核参数参考：<a href="/linux-faq#kernel-params">Linux知识集锦</a>。</p>
<div class="blog_h2"><span class="graybg">/proc/sys/fs</span></div>
<p>文件系统相关参数：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">参数</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">file-max</td>
<td>系统范围（全局总和）的文件句柄（描述符）数量限制</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">/proc/sys/net</span></div>
<p>这个目录下的某些文件被网络命名空间隔离。</p>
<p>网络相关参数：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">参数</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>netfilter/nf_log_all_netns</td>
<td>
<p>默认情况下，来自容器（非全局网络命名空间）的iptable logging被忽略，你无法在 dmesg -w中看到</p>
<p>设置该参数为1改变此行为</p>
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td>ipv4/conf/*/route_localnet</td>
<td>允许转发目的或源地址为127.0.0.0/8的数据包，也就是来自或去往lo设备的数据包。这样，你就可以使用NAT强制将目的地址是127.0.0.1的封包的目的地址修改为其它值</td>
</tr>
<tr>
<td>ipv4/conf/*/accept_local</td>
<td>设置为1，则接受源地址是本地IP的封包。必须配合rp_filter为非0才能工作</td>
</tr>
<tr>
<td>ipv4/conf/*/rp_filter</td>
<td>设置为0，不进行源地址校验</td>
</tr>
<tr>
<td>ipv4/conf/*/arp_ignore</td>
<td><span style="font-family: Ubuntu, sans-serif;"><span style="font-family: Ubuntu, sans-serif;">IPVS的DR模式下，每个真实服务器节点都要在环回网卡上绑定VIP。如果客户端对于VIP的ARP请求广播到了各个真实服务器节点，当：<br /></span></span>
<ol>
<li><span style="font-family: Ubuntu, sans-serif;">arp_ignore=0，<span style="background-color: #c0c0c0;">则各个真实服务器节点都会响应该arp请求</span>，此时客户端就无法正确获取LVS（LB）节点上正确的VIP所在网卡的MAC地址。客户端可能将以太网帧绕过LB直接发给RS</span></li>
<li>arp_ignore=1，<span style="background-color: #c0c0c0;">只响应目的IP地址为<strong>接收网卡</strong>上的本地地址的arp请求</span></li>
</ol>
<p>&nbsp;</p>
<p>IPVS的DR模式下要求arp_ignore参数配置为1</p>
</td>
</tr>
<tr>
<td>ipv4/conf/*/arp_announce</td>
<td>每台服务器或者交换机中都有一张arp表，该表用于存储对端通信节点IP地址和MAC地址的对应关系。当
<ol>
<li><span style="color: #333333; font-family: Ubuntu, sans-serif; font-size: 13px; line-height: 22px;">收到一个未知IP地址的arp请求，就会在本机的arp表中新增对端的IP和MAC记录</span></li>
<li><span style="color: #333333; font-family: Ubuntu, sans-serif; font-size: 13px; line-height: 22px;">当收到一个已知IP地址（arp表中已有记录的地址）的arp请求，则会根据arp请求中的源MAC刷新自己的arp表</span></li>
</ol>
<p><span style="background-color: #c0c0c0;">如果arp_announce参数配置为0，则网卡在发送arp请求时，可能<strong>选择的源IP地址并不是该网卡自身的IP地址</strong></span>，这时候收到该arp请求的其他节点或者交换机上的arp表中记录的该网卡IP和MAC的对应关系就不正确，可能会引发一些未知的网络问题，存在安全隐患。</p>
<p>IPVS的DR模式下要求arp_announce参数配置为2</p>
</td>
</tr>
<tr>
<td>ipv4/conf/*/send_redirects</td>
<td>是否启用ICMP重定向</td>
</tr>
<tr>
<td>ipv4/ip_local_port_range</td>
<td>发起连接时，本地端口范围</td>
</tr>
<tr>
<td>ipv4/tcp_keepalive_time</td>
<td>TCP保活，记时器触发延迟</td>
</tr>
<tr>
<td>ipv4/tcp_keepalive_intvl</td>
<td>TCP保活，保活封包发送间隔</td>
</tr>
<tr>
<td>ipv4/tcp_keepalive_probes</td>
<td>TCP保活，保活封包发送次数</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">/proc/loadavg</span></div>
<p>给出以几个不同的时间间隔计算的系统平均负载，这就如同uptime命令显示的结果那样。前三个数字是平均负载。这是通过计算过去1分钟，5分钟，15分钟里运行队列中的平均任务数得到的。随后是正在运行的任务数和总任务数。最后是上次使用的进程号。</p>
<div class="blog_h2"><span class="graybg">/proc/stat</span></div>
<p>这个文件包含的信息有CPU利用率，磁盘，内存页，内存换页，中断信息。</p>
<div class="blog_h2"><span class="graybg">/proc/vmstat</span></div>
<p>vmstat 命令信息来源之一。</p>
<div class="blog_h2"><span class="graybg">/proc/$PID</span></div>
<p>指向进程映射目录，其下的子文件包含了进程的详细信息。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">/proc/$PID/cmdline</td>
<td>
<p>包含进程的完整命令行信息。如果这个进程是zombie进程，则这个文件没有任何内容</p>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/root</td>
<td>进程的root目录符号链接</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/cwd</td>
<td>当前工作目录的符号链接</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/comm</td>
<td>进程的名字</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/environ</td>
<td>进程的环境变量</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/exe</td>
<td>进程的二进制文件的符号链接</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/fd</td>
<td>
<p>进程的打开文件</p>
<pre class="crayon-plain-tag">ls -la /proc/2177/fd
total 0
dr-x------ 2 root root  0 Feb 23 21:23 .
dr-xr-xr-x 9 root root  0 Feb 23 21:23 ..
lrwx------ 1 root root 64 Feb 23 21:23 0 -&gt; /dev/null
l-wx------ 1 root root 64 Feb 23 21:23 1 -&gt; pipe:[102672120]
# 当创建好epoll句柄后，它就是会占用一个fd值，所以在使用完epoll后，必须调用close()关闭
lrwx------ 1 root root 64 Feb 23 21:23 13 -&gt; anon_inode:[eventpoll]
# 最后的数字是套接字的inode号
lrwx------ 1 root root 64 Feb 23 21:23 83 -&gt; socket:[102681918]</pre>
<p>从这里可以得到套接字的inode号，并进而查询套接字的详细信息：</p>
<p><pre class="crayon-plain-tag">cat /proc/2177/net/tcp | grep 102681918</pre>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/net</td>
<td>参考/proc/net</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/io</td>
<td>进程的IO信息 </td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/limits</td>
<td>进程的资源配额</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/maps</td>
<td>进程的内存映射信息</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/stack</td>
<td>显示当前进程的内核调用栈信息，只有内核编译时打开了<pre class="crayon-plain-tag">CONFIG_STACKTRACE</pre>编译选项，才会生成这个文件</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/mountinfo</td>
<td rowspan="3">进程所属mount命名空间的挂载信息</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/mounts</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/mountstats</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/cgroup</td>
<td>
<p>显示某个进程所属的进程组。输出 / 表示进程没有划分到特定的Cgroups</p>
<p><pre class="crayon-plain-tag">$PID</pre>是进程的ID，关键字<pre class="crayon-plain-tag">self</pre>表示当前进程。</p>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/ns</td>
<td>
<p>每个进程都有这样的一个目录，里面存放若干符号连接，提示进程所属的各种命名空间： </p>
<pre class="crayon-plain-tag">ls -la /proc/self/ns
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 ipc -&gt; ipc:[4026531839]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 mnt -&gt; mnt:[4026531840]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 net -&gt; net:[4026531968]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 pid -&gt; pid:[4026531836]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 user -&gt; user:[4026531837]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 uts -&gt; uts:[4026531838]</pre>
<p>如果两个进程指向同一符号连接（方括号内的inode相同），则它们属于同一命名空间</p>
<p>只要/proc/$PID/ns下的文件被打开（例如进行bind挂载），则相应的命名空间就会存在，即使命名空间内没有任何进程</p>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/net</td>
<td>进程使用的网络内核参数</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/stat </td>
<td>
<p>包含了所有CPU活跃的信息，该文件中的所有值都是从系统启动开始累计到当前时刻</p>
<pre class="crayon-plain-tag">19211 (docker-proxy) S 18582 18582 18582 0 -1 1077936384 400 0 24 0 385 
870 0 0 20 0 5 0 1476270373 111624192 397 18446744073709551615 94908437864448 
94908439349548 140733819451920 140733819451256 94908438772659 0 2079996453 0 
2143420159 0 0 0 17 0 0 0 16 0 0 94908441446824 94908442361816 94908444303360 
140733819457191 140733819457307 140733819457307 140733819457506 0</pre>
<ol>
<li>pid=19211 进程(包括轻量级进程，即线程)号</li>
<li>comm=docker-proxy 应用程序或命令的名字</li>
<li>task_state=R 任务的状态，R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disk sleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop,Z:zombie, X:dead</li>
<li>ppid=6723 父进程ID</li>
<li>pgid=6873 线程组号</li>
<li>sid=6723 c该任务所在的会话组ID</li>
<li>tty_nr=34819(pts/3) 该任务的tty终端的设备号，INT（34817/256）=主设备号，（34817-主设备号）=次设备号</li>
<li>tty_pgrp=6873 终端的进程组号，当前运行在该任务所在终端的前台任务(包括shell 应用程序)的PID。</li>
<li>task-&gt;flags=8388608 进程标志位，查看该任务的特性</li>
<li>min_flt=77 该任务不需要从硬盘拷数据而发生的缺页（次缺页）的次数</li>
<li>cmin_flt=0 累计的该任务的所有的waited-for进程曾经发生的次缺页的次数目</li>
<li>maj_flt=0 该任务需要从硬盘拷数据而发生的缺页（主缺页）的次数</li>
<li>cmaj_flt=0 累计的该任务的所有的waited-for进程曾经发生的主缺页的次数目</li>
<li>utime=1587 该任务在用户态运行的时间，单位为jiffies</li>
<li>stime=1 该任务在核心态运行的时间，单位为jiffies</li>
<li>cutime=0 累计的该任务的所有的waited-for进程曾经在用户态运行的时间，单位为jiffies</li>
<li>cstime=0 累计的该任务的所有的waited-for进程曾经在核心态运行的时间，单位为jiffies</li>
<li>priority=25 任务的动态优先级</li>
<li>nice=0 任务的静态优先级</li>
<li>num_threads=3 该任务所在的线程组里线程的个数</li>
<li>it_real_value=0 由于计时间隔导致的下一个 SIGALRM 发送进程的时延，以 jiffy 为单位.</li>
<li>start_time=5882654 该任务启动的时间，单位为jiffies</li>
<li>vsize=1409024（page） 该任务的虚拟地址空间大小</li>
<li>rss=56(page) 该任务当前驻留物理地址空间的大小</li>
<li>rlim=4294967295（bytes） 该任务能驻留物理地址空间的最大值</li>
<li>start_code=134512640 该任务在虚拟地址空间的代码段的起始地址</li>
<li>end_code=134513720 该任务在虚拟地址空间的代码段的结束地址</li>
<li>start_stack=3215579040 该任务在虚拟地址空间的栈的结束地址</li>
<li>kstkesp=0 esp(32 位堆栈指针) 的当前值, 与在进程的内核堆栈页得到的一致.</li>
<li>kstkeip=2097798 指向将要执行的指令的指针, EIP(32 位指令指针)的当前值.</li>
<li>pendingsig=0 待处理信号的位图，记录发送给进程的普通信号</li>
<li>block_sig=0 阻塞信号的位图</li>
<li>sigign=0 忽略的信号的位图</li>
<li>sigcatch=082985 被俘获的信号的位图</li>
<li>wchan=0 如果该进程是睡眠状态，该值给出调度的调用点</li>
<li>nswap 被swapped的页数，当前没用</li>
<li>cnswap 所有子进程被swapped的页数的和，当前没用</li>
<li>exit_signal=17 该进程结束时，向父进程所发送的信号</li>
<li>task_cpu(task)=0 运行在哪个CPU上</li>
<li>task_rt_priority=0 实时进程的相对优先级别</li>
<li>task_policy=0 进程的调度策略，0=非实时进程，1=FIFO实时进程；2=RR实时进程 </li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/statm</td>
<td>显示进程所占用内存大小的统计信息，包含七个值，度量单位是页：<br />
<pre class="crayon-plain-tag"># cat /proc/2948/statm  
72362 12945 4876 569 0 24665 0</pre></p>
<p>七个字段分别表示：</p>
<ol>
<li>进程占用的总的内存</li>
<li>进程当前时刻占用的物理内存</li>
<li>同其它进程共享的内存</li>
<li>进程的代码段</li>
<li>共享库（从2.6版本起，这个值为0）</li>
<li>进程的堆栈</li>
<li>dirty pages（从2.6版本起，这个值为0）</li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/status</td>
<td>进程状态信息<br />
<pre class="crayon-plain-tag">Name:	dockerd	# 进程名
State:	S (sleeping)	# 进程状态
## R (running)", "S (sleeping)", "D (disk sleep)", "T (stopped)", "T(tracing stop)", "Z (zombie)", or "X (dead)"
Tgid:	18582	# 线程组的ID,一个线程一定属于一个线程组(进程组)
Ngid:	0
Pid:	18582	# 进程的ID,更准确的说应该是线程的ID
PPid:	1	# 当前进程的父进程ID
TracerPid:	0	# 跟踪当前进程的进程ID,如果是0,表示没有跟踪，例如用strace跟踪
Uid:	0	0	0	0
Gid:	0	0	0	0
# 第一列数字(RUID):实际用户ID,指的是进程执行者是谁.
# 第二列数字(EUID):有效用户ID,指进程执行时对文件的访问权限.
# 第三列数字(SUID):保存设置用户ID,作为effective user ID的副本,在执行exec调用时后能重新恢复原来的effectiv user ID.
# 第四列数字(FSUID):目前进程的文件系统的用户识别码.一般情况下,文件系统的用户识别码(fsuid)与有效的用户识别码(euid)是相同的.
# RUID和EUID是由启动进程的用户决定的
# EUID，如果程序设定了setuid,那么在程序运行时是用程序的owner权限来运行程序,而不是启动的用户权限
终端1)
FDSize:	128	# FDSize是当前分配的文件描述符,这个值不是当前进程使用文件描述符的上限.
# 而如果超过32个文件描述符,将以32进行递增,如果是64位系统,将以64进行递增.
# FDSize这个值不会减少,如果我们程序打开了300个文件,并不会因为关闭文件,而减少FDSize这个值.
Groups:	# 这里的groups表示启动这个进程的用户所在的组.
NStgid:	18582
NSpid:	18582
NSpgid:	18582
NSsid:	18582
VmPeak:	  581816 kB	# 当前进程运行过程中占用内存的峰值
VmSize:	  581752 kB	# 进程现在正在占用的内存
VmLck:	       0 kB	# 进程已经锁住的物理内存的大小.锁住的物理内存不能交换到硬盘
VmPin:	       0 kB
VmHWM:	   89832 kB	# 得到分配到物理内存的峰值
VmRSS:	   40128 kB	# 现在使用的物理内存
VmData:	  476676 kB	# 进程数据段的大小
VmStk:	     132 kB	# 进程堆栈段的大小.
VmExe:	   47116 kB	# 进程代码的大小
VmLib:	    6128 kB	# 进程所使用LIB库的大小
VmPTE:	     396 kB	# 占用的页表的大小.
VmPMD:	      24 kB
VmSwap:	       0 kB	# 进程占用Swap的大小.
HugetlbPages:	       0 kB
Threads:	12	# 表示当前进程组有12个线程.
SigQ:	0/7338	# 当前待处理信号的个数
SigPnd:	0000000000000000	# 屏蔽位,存储了该线程的待处理信号,等同于线程的PENDING信号
ShdPnd:	0000000000000000	# 屏蔽位,存储了该线程组的待处理信号.等同于进程组的PENDING信号
SigBlk:	0000000000000000	# 存放被阻塞的信号,等同于BLOCKED信号
SigIgn:	0000000000000000	# 存放被忽略的信号,等同于IGNORED信号
SigCgt:	ffffffffffc1feff	# 存放捕获的信号,等同于CAUGHT信号
CapInh:	0000000000000000	# 表示能够被当前进程执行的程序继承的能力
CapPrm:	0000003fffffffff	# 表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力，这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集
CapEff:	0000003fffffffff	# 当一个进程要进行某个特权操作时,操作系统会检查cap_effective的对应位是否有效,而不再是检查进程的有效UID是否为0
CapBnd:	0000003fffffffff	# 是系统的边界能力,我们无法改变它
CapAmb:	0000000000000000
Seccomp:	0
Cpus_allowed:	1	# 指出该进程可以使用CPU的亲和性掩码,因为我们指定为两块CPU,所以这里就是3,如果该进程指定为4个CPU(如果有话),这里就是F(1111)
Cpus_allowed_list:	0	# 0-1指出该进程可以使用CPU的列表,这里是0-1
Mems_allowed:	00000000,00000001 # 通cpu
Mems_allowed_list:	0	# 通cpu，使用了结点0的内存资源
voluntary_ctxt_switches:	24243383	# 表示进程主动切换的次数.
nonvoluntary_ctxt_switches:	6102321	# 表示进程被动切换的次数</pre>
</td>
</tr>
<tr>
<td class="blog_h3">/proc/$PID/syscall</td>
<td>显示当前进程正在执行的系统调用</td>
</tr>
<tr>
<td>/proc/$PID/task</td>
<td>进程包含的的线程信息</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">/sys</span></div>
<p>即sysfs文件系统，包括<span style="background-color: #c0c0c0;">系统所有的硬件信息以及内核模块等信息</span>，映射了<span style="background-color: #c0c0c0;">相关的kobject</span>，该文件系统代替了以前需要ioctl（作用于一个设备节点）和procfs文件系统完成的功能。</p>
<p>该目录在2.6引入，解决/proc目录内容杂乱的问题。</p>
<div class="blog_h2"><span class="graybg">/sys/fs/cgroup</span></div>
<p>代表了Cgroup的伪文件系统</p>
<div class="blog_h1"><span class="graybg">/etc</span></div>
<p>存放系统或者应用的配置文件，etc即etcetera</p>
<div class="blog_h2"><span class="graybg">受信任CA证书</span></div>
<p>CentOS系统的受信任证书存放位置：<pre class="crayon-plain-tag">/etc/pki/ca-trust/source/anchors/</pre>，将你的证书存放到该目录后，需要执行<pre class="crayon-plain-tag">update-ca-trust</pre>更新受信任证书列表。</p>
<p>Ubuntu系统受信任证书存放在<pre class="crayon-plain-tag">/usr/local/share/ca-certificates/</pre>，将你的证书存放到该目录后，需要执行<pre class="crayon-plain-tag">update-ca-certificates</pre>更新受信任证书列表。</p>
<div class="blog_h2"><span class="graybg">包管理器配置</span></div>
<div class="blog_h3"><span class="graybg">APT相关</span></div>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 35%; text-align: center;">文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/apt/sources.list</td>
<td>存放软件源的列表</td>
</tr>
<tr>
<td>/etc/apt/sources.list.d/</td>
<td>存放软件源的列表文件片段</td>
</tr>
<tr>
<td>/etc/apt/apt.conf</td>
<td>APT配置文件</td>
</tr>
<tr>
<td>/etc/apt/apt.conf.d/</td>
<td>APT配置文件片段</td>
</tr>
<tr>
<td>/var/cache/apt/archives/</td>
<td>下载文件的本地存储区域</td>
</tr>
<tr>
<td>/var/cache/apt/archives/partial/</td>
<td>正在传输中的文件的本地存储区域</td>
</tr>
<tr>
<td>/var/lib/aptitude/pkgstates</td>
<td>存储软件包的状态、标记</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">YUM相关</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 35%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/yum.repos.d/</td>
<td>YUM仓库的配置</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">网络配置</span></div>
<table class="full-width fixed-word-wrap" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/hostname</td>
<td>Debain主机名称配置，RedHat在/etc/sysconfig/network中配置</td>
</tr>
<tr>
<td>/etc/hosts</td>
<td>静态IP-域名映射配置</td>
</tr>
<tr>
<td>/etc/network/interfaces</td>
<td>
<p>Debian及其衍生系统的网络配置文件。<br />在<span style="background-color: #c0c0c0;">桌面版的Ubuntu上，一般由网络管理器（Network Manager）组件负责管理网络</span>，不需要手工修改此文件。</p>
<pre class="crayon-plain-tag">auto lo
iface lo inet loopback

#第一张以太网卡的配置
auto eth0
iface eth0 inet dhcp

#第二张以太网卡的配置
auto eth1
iface eth1 inet static
  address 192.168.0.90
  netmask 255.255.255.0
  gateway 192.168.0.1
  #更改硬件地址
  hwaddress ether 10:1F:74:18:6A:47
  #设置DNS服务器
  dns-nameservers 178.79.131.110 223.5.5.5
  mtu 1380
#使用post-up命令可以更改路由
  post-up route add default gw 192.168.0.1
  post-up route del default dev $IFACE</pre>
<p>修改完毕后执行<pre class="crayon-plain-tag">sudo service networking restart</pre> 可以立即生效。</p>
<p>要为单个网卡配置多重IP地址，可以参考：</p>
<pre class="crayon-plain-tag">auto eth0
allow-hotplug eth0
iface eth0 inet static
    address 192.168.1.42
    netmask 255.255.255.0
    gateway 192.168.1.1

auto eth0:0
allow-hotplug eth0:0
iface eth0:0 inet static
    address 192.168.1.43
    netmask 255.255.255.0</pre>
</td>
</tr>
<tr>
<td>/etc/NetworkManager/NetworkManager.conf</td>
<td>
<p>Debian及其衍生系统上的网络管理组件，主要用于简化网络配置
<p>如果要禁止网络管理器管理某个特定网络接口，配置：</p>
<pre class="crayon-plain-tag">[keyfile]
unmanaged-devices=mac:d0:bf:9c:5d:31:d3</pre>
<p>并重启网络管理器：<pre class="crayon-plain-tag">sudo service network-manager restart</pre> ，然后你可以在NM的系统托盘区看到提示：device mot managed</p>
</td>
</tr>
<tr>
<td>/etc/init/failsafe.conf</td>
<td>可以用来修改网络配置失败时，等待的时间</td>
</tr>
<tr>
<td>/etc/sysconfig/network</td>
<td>RedHat、CentOS的主机名和基本网络配置，格式如下：<br />
<pre class="crayon-plain-tag"># 是否启用网络
NETWORKING=yes
NETWORKING_IPV6=yes
# 设置主机名
HOSTNAME=xcentos6
# 设置网关
GATEWAY=192.168.1.1</pre>
</td>
</tr>
<tr>
<td>/etc/sysconfig/network-scripts/ifcfg-eth0</td>
<td>RedHat、CentOS网络接口eth0的配置，格式如下：<br />
<pre class="crayon-plain-tag">DEVICE="eth0"
BOOTPROTO=none   # 可选值dhcp、static、none，分别表示启用DHCP、固定IP、手工配置
NM_CONTROLLED="yes"
ONBOOT=yes       # 网络接口是否随系统启动
TYPE="Ethernet"
UUID="557f0607-a29e-412e-8786-28269f0cd12f"
IPADDR=192.168.0.90
PREFIX=24
GATEWAY=192.0.0.168
DEFROUTE=yes     # 是否把此接口设置为默认路由
IPV4_FAILURE_FATAL=yes
IPV6INIT=no      # 是否启用IPv6
NAME="System eth0"
HWADDR=00:0C:29:39:6E:57</pre></p>
<p>完毕后执行 <pre class="crayon-plain-tag">/etc/init.d/network restart</pre> 即可生效</p>
</td>
</tr>
<tr>
<td>/etc/resolv.conf</td>
<td>包含DNS服务的配置：<br />
<pre class="crayon-plain-tag"># 本地域，针对此域内的DNS名称，可以仅仅使用短名（前缀）进行查询
# 如果指定为.则使用根域
# 如果为空，则自动根据gethostname调用的结果获得本地域，取第一个.后的内容为本地域
domain gmem.cc

# DNS服务器的IP地址，最多可以指定MAXNS个DNS（当前3）
# 如果指定多个，会从上到下一个个尝试
# 如果不指定nameserver，则默认使用使用本机上的DNS服务器
nameserver 8.8.8.8


# 搜索后缀列表，对于针对少于ndots（默认1）个.号的名字的DNS查询，会自动尝试添加这些后缀进行查询
search devops.svc.k8s.gmem.cc svc.k8s.gmem.cc k8s.gmem.cc

# 选项
options ndots:n     # 使用search作为后缀的阈值
        timeout:n   # 超时，单位秒，默认5，最大30
        attempts:n  # 失败之前最多发送DNS查询的次数，默认2，最大5
        rotate      # 轮询的使用nameserver中的服务器，而非总是从上往下一个个尝试</pre></p>
<p>注意，在Ubuntu 14.04下，由于网络管理器组件的存在，你不应修改此文件，因为重启后修改会丢失。你可以：</p>
<ol>
<li>通过系统托盘区网络图标，点击Edit Connections，修改指定网络Profile使用的DNS</li>
<li>修改/etc/network/interfaces，为指定的网络接口指定DNS</li>
<li>修改/etc/resolvconf/resolv.conf.d/下的文件：
<ol>
<li>head，编写位于自动生成DNS前面的nameserver</li>
<li>base，编写位于自动生成DNS后面的nameserver</li>
</ol>
</li>
</ol>
<p>默认的DNS查找超时是5秒，两次尝试失败后不再尝试，则会导致DNS查找很慢，可以修改：</p>
<pre class="crayon-plain-tag">options timeout:1 attempts:1</pre>
</td>
</tr>
<tr>
<td>/etc/sysctl.conf</td>
<td>该文件用于修改内核运行时参数，可以指定很多网络相关的条目</td>
</tr>
<tr>
<td><span style="color: #444444;">/var/lib/bluetooth/**/linkkeys</span></td>
<td>存放蓝牙配对使用的安全码</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">资源配额</span></div>
<p>Linux对用户使用资源的具有配额功能，例如如果打开文件描述符的数量超过限制，可能导致“java.io.IOException: Too many open files”错误发生
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">文件 </td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/security/limits.conf</td>
<td>文件中的每一行表示对某个用户/组的某种资源限制声明：<br />
<pre class="crayon-plain-tag">#每行的内容格式如下：
#            
#domain：用户名、组名、通配符*
#type：可以是soft、hard，表示软硬限制
#item：可以是：
#  core  限制核心文件大小KB
#  data  最大数据大小KB
#  fsize 最大文件大小KB
#  memlock 最大的锁定在内存中地址空间KB
#  nofile 最大打开文件的数量
#  rss 限制驻留工作集（进程所使用的非交换区的物理内存）的大小KB
#  stack 最大栈大小KB
#  cpu 最大CPU时间MIN
#  nproc 最大进程数量
#  as 地址空间限制KB
#  maxlogins 最大用户登陆次数
#  maxsyslogins 最大的系统登陆次数
#  priority 用户进程的运行优先级
#  locks 用户最多可以持有的文件锁数量
#  sigpending 最大未决信号数量
#  msgqueue POSIX消息队列的最大长度bytes
#  nice nice优先级范围[-20, 19]
#  rtprio 最大实时优先级
#  chroot 仅Debian，是否可以修改根目录的指向
* soft nofile 65536 
* hard nofile 65536</pre></p>
<p>该配置文件与ulimit命令的目的相似，但是后者仅作用于当前Shell及其子进程</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">引导程序配置</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;">文件 </td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/boot/grub/menu.lst</td>
<td>GRUB启动引导文件</td>
</tr>
<tr>
<td>/etc/lilo.conf</td>
<td>LILO启动引导文件</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">系统启动脚本</span></div>
<p>很多配置文件中，具有<pre class="crayon-plain-tag">rc</pre> 字样，通常表示“run commands”。 </p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;">文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/rc0.d<br />/etc/rc1.d<br />/etc/rc2.d<br />/etc/rc3.d<br />/etc/rc4.d<br />/etc/rc5.d<br />/etc/rc6.d</td>
<td>包含一系列的符号链接，符号链接一律指向/etc/init.d/下的文件，在对应的运行级别下会被系统自动执行</td>
</tr>
<tr>
<td>/etc/init.d/</td>
<td>包含若干System V脚本，用于作为系统服务</td>
</tr>
<tr>
<td>/etc/init/</td>
<td>包含一些系统启动时需要执行的任务（upstart job）</td>
</tr>
<tr>
<td>/etc/rc.local</td>
<td>在所有系统服务启动完毕后，该脚本的内容会被执行</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">文件系统挂载</span></div>
<table class="full-width fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 180px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/fstab</td>
<td>包含静态文件系统信息，参考：<a href="/linux-file-and-privileges#static-filesystem-info">静态文件系统信息</a></td>
</tr>
<tr>
<td>/proc/filesystems</td>
<td>支持的文件系统类型列表</td>
</tr>
<tr>
<td>/proc/mounts<br />/proc/self/mounts</td>
<td>
<p>当前mount命名空间下的挂载点列表</p>
<p>格式：</p>
<pre class="crayon-plain-tag">/dev/sda1 /mnt/40A2AAB1A2AAAABC fuseblk ro,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other,blksize=4096 0 0
tmpfs /run/netns tmpfs rw,nosuid,noexec,relatime,size=3286248k,mode=755 0   0
 (1)  (2)        (3)   (4)                                             (5) (6)

# 1 被挂载的设备
# 2 挂载点
# 3 文件系统类型
# 4 挂载选项
# 5 dummay value，用于匹配 /etc/mtab中的格式</pre>
</td>
</tr>
<tr>
<td>/proc/$PID/mountinfo</td>
<td>
<p>指定进程所在的挂载命名空间的挂载点列表
<p>格式：</p>
<pre class="crayon-plain-tag">36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)

# 1  mount ID，mount的唯一标识
# 2  parent ID，父mount的标识，如果当前时命名空间中的根mount，则指向自己
# 3  major:minor， 设备主次号
# 4  root，文件系统中构成此挂载的根的目录的路径
# 5  mount，相对于进程根目录的，挂载点的位置
# 6  mount potins，挂载选项
# 7  0-N个可选字段，格式tag[:value]
# 8  可选字段终止符
# 9  filesystem type，文件系统类型
# 10 mount source，挂载的源，可能时设备文件路径，例如/dev/sdd1
# 11 superblock信息</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Shell配置</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/environment</td>
<td>
<p>全局的环境变量，修改后重新登录以生效。常用的全局环境变量包括：
<ol>
<li>http_proxy：HTTP代理地址，例如http://192.168.0.89:8087</li>
<li>no_proxy：不使用代理的地址集合，例如localhost,127.0.0.1,192.168.0.89</li>
</ol>
<p>注意该文件中的环境变量不支持变量扩展，例如<pre class="crayon-plain-tag">PWD2=$PWD</pre> 会被按字面的解释为$PWD</p>
</td>
</tr>
<tr>
<td>/etc/profile</td>
<td>可以包含任意脚本。全局的设置，所有用户登录时有效</td>
</tr>
<tr>
<td>~/.profile</td>
<td>可以包含任意脚本。当前用户的设置，登陆时有效</td>
</tr>
<tr>
<td>/etc/bashrc</td>
<td>可以包含任意脚本。对所有用户新创建的shell有效</td>
</tr>
<tr>
<td>~/.bashrc</td>
<td>可以包含任意脚本。对当前用户新创建的shell有效</td>
</tr>
<tr>
<td>~/.bash_logout</td>
<td>每次退出shell时，该文件被读取并执行</td>
</tr>
<tr>
<td>~/.bash_history</td>
<td>命令历史记录</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">定时任务</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/var/spool/cron/crontabs/*<br />/etc/cron.allow<br />/etc/cron.deny</td>
<td>crontab相关的配置文件，用于配置基于cron表达式的定时任务</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">数字证书</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/ca-certificates.conf</td>
<td>列出安装在/etc/ssl/certs下，你想使用或者忽略的证书的列表，要忽略某个证书，可以在其前面添加<pre class="crayon-plain-tag">!</pre> 然后执行：<br />
<pre class="crayon-plain-tag">sudo update-ca-certificates  </pre>
</td>
</tr>
<tr>
<td>/etc/ssl/certs</td>
<td>存放当前信任的数字证书的符号链接</td>
</tr>
<tr>
<td>/usr/share/ca-certificates<br />/usr/local/share/ca-certificates</td>
<td>存放所有数字证书文件，不一定被信任</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">字体配置</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/fonts/fonts.conf</td>
<td>字体相关的配置文件，包含例如字体安装目录等信息</td>
</tr>
<tr>
<td>/usr/share/fonts</td>
<td rowspan="4">
<p>字体安装目录</p>
<p>Ubuntu下通过GUI安装字体文件时，安装到~/.local/share/fonts</p>
</td>
</tr>
<tr>
<td>/usr/local/share/fonts</td>
</tr>
<tr>
<td>~/.local/share/fonts</td>
</tr>
<tr>
<td>~/.fonts</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">用户和组配置</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/default/useradd</td>
<td>新添加用户的默认属性值</td>
</tr>
<tr>
<td>/etc/passwd</td>
<td>用户账户信息，内容格式如下：<br />
<pre class="crayon-plain-tag">#每一行包含一个用户的信息，字段使用冒号分隔
root:x:0:0:root:/root:/bin/bash
#字段说明如下：
# root 用户名
# x 密码占位符，密码存放在/etc/shadow文件中
# 0 用户ID（uid）
# 0 主要组ID（gid）
# root 用户全名
# /root 用户家目录
# /bin/bash 用户的Shell，设置为/sbin/nologin禁止登陆Shell</pre>
</td>
</tr>
<tr>
<td>/etc/group</td>
<td>用户组的配置信息</td>
</tr>
<tr>
<td>/etc/shadow</td>
<td>加密的用户密码信息，以及密码修改时间、过期时间等</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">应用程序配置</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/ssh/sshd_config</td>
<td>SSH服务器配置文件</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">开发相关配置</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/ld.so.conf </td>
<td>额外共享库的存放位置。修改后需要调用ldconfig命令使之生效</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">/dev</span></div>
<div class="blog_h2"><span class="graybg">/dev/hd*</span></div>
<p>IDE硬盘的设备文件，第一块硬盘为hda，第二块hdb，类推。</p>
<div class="blog_h2"><span class="graybg">/dev/sd*</span></div>
<p>SATA硬盘的设备文件。</p>
<div class="blog_h2"><span class="graybg">/dev/disk/by-*</span></div>
<p>各种指向设备文件的符号链接。</p>
<div class="blog_h3"><span class="graybg">/dev/disk/by-id/</span></div>
<p>根据设备的唯一标识。</p>
<div class="blog_h3"><span class="graybg">/dev/disk/by-label/</span></div>
<p>根据设备的标签（磁盘的名字）。</p>
<div class="blog_h3"><span class="graybg">/dev/disk/by-path/</span></div>
<p>根据设备的PCI路径。</p>
<div class="blog_h3"><span class="graybg">/dev/disk/by-uuid/</span></div>
<p>根据磁盘的UUID。</p>
<div class="blog_h2"><span class="graybg">/dev/console</span></div>
<p>代表系统控制台，过去它常常是一个专用的打印终端；在现代Linux上它通常是一个虚拟控制台；在X Window中它则是一个虚拟的控制台窗口。</p>
<p>系统主控制台，内核代码 <pre class="crayon-plain-tag">printk()</pre>总是向此控制台写入消息。第一个非内核进程，也就是init，也将/dev/console作为标准输入、输出、错误。</p>
<p>/dev/console是一个可以通过内核参数进行重定向的虚拟设备集。你可以在grub.conf中配置 console=ttyS0，这样/dev/console就重定向到了/dev/ttyS0。你还可以在grub.conf中多次配置console参数，这样内核主控制台的输出被传播到多个设备。</p>
<div class="blog_h2"><span class="graybg">/dev/mapper/* </span></div>
<p>设备映射，用于把物理块设备映射为高层的虚拟块设备。逻辑磁盘卷存放在这里</p>
<div class="blog_h2"><span class="graybg">/dev/tty</span></div>
<p>这是一个别名，对应打开/dev/tty的那个进程所关联的（物理，虚拟，伪）控制台。打开/dev/tty进行读写，不需要root权限。</p>
<p>需要注意，cron之类批处理服务启动的进程，没有关联的控制台，因而也就没有可用的/dev/tty。这类进程的 ps -ef输出的TTY列显示为 <pre class="crayon-plain-tag">?</pre>。</p>
<div class="blog_h2"><span class="graybg">/dev/tty*</span></div>
<p>原本含义是连接到服务器的串口终端设备。现在/dev/tty0总是表示当前控制台，而/dev/tty1…对应Ctrl + Alt + F1…切换到的那些虚拟控制台。</p>
<div class="blog_h2"><span class="graybg">/dev/ttyS*</span></div>
<p>代表连接到服务器的串口终端设备。有段时间串口设备被称为终端设备，因为那时串口设备的主要用途就是用作终端。</p>
<div class="blog_h2"><span class="graybg">/dev/null</span></div>
<p>空设备，所有写入内容被丢弃；所有读尝试立即返回EOF</p>
<div class="blog_h1"><span class="graybg">/run</span></div>
<div class="blog_h3"><span class="graybg">/run/netns</span></div>
<p>创建了网络命名空间后，在此目录下出现一个对应的文件。</p>
<div class="blog_h1"><span class="graybg">/var</span></div>
<p>经常发生改变的系统文件所在目录。</p>
<div class="blog_h2"><span class="graybg">/var/run</span></div>
<p>目前通常是指向/run的符号链接</p>
<div class="blog_h2"><span class="graybg">/var/tmp</span></div>
<p>临时文件目录，不会自动清空。</p>
<div class="blog_h2"><span class="graybg">/var/log</span></div>
<p>各种日志文件：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class=" blog_h3">/var/log/dmesg </td>
<td>包含系统启动时的详细日志，例如硬件检测</td>
</tr>
<tr>
<td class=" blog_h3">/var/log/auth.log</td>
<td>系统身份认证日志，企图进行的登陆记录其中</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">/boot</span></div>
<p>内核和加载内核所需的文件。</p>
<div class="blog_h1"><span class="graybg">/lost+found</span></div>
<p>文件系统崩溃后，恢复文件存放在这里。</p>
<div class="blog_h1"><span class="graybg">/kernel</span></div>
<p>内核组件。</p>
<div class="blog_h1"><span class="graybg">/bin</span></div>
<p>系统核心应用程序所在目录，启动系统时需要的程序在此目录。</p>
<div class="blog_h1"><span class="graybg">/sbin</span></div>
<p>可能是System binary的缩写，包含一些重要的系统应用程序，系统管理的PATH中包含此目录。</p>
<div class="blog_h1"><span class="graybg">/lib</span></div>
<p>基础的共享库、内核模块，该目录中的共享库镜像对于启动系统、执行/bin、/sbin下的命令是必须的。</p>
<p>对于/bin /sbin目录下程序不必须的共享库，不会放在此目录下。</p>
<p>目录中的库也能是32位的，也可能是 64位的。</p>
<p>/lib32  /lib64存放库的32或64位变体，/lib目录中可能是指向这些目录的符号链接。</p>
<div class="blog_h1"><span class="graybg">/home</span></div>
<p>用户家目录的父目录，每个用户对应一个子目录。可以使用<pre class="crayon-plain-tag">~</pre>来访问当前用户的家目录。</p>
<div class="blog_h1"><span class="graybg">/root</span></div>
<p>超级用户root的家目录。</p>
<div class="blog_h1"><span class="graybg">/usr</span></div>
<p>存放应用程序和库，usr的含义可以解释为Unix System Resources。</p>
<div class="blog_h2"><span class="graybg">/usr/bin</span></div>
<p>应用程序，存放用户使用的标准程序</p>
<div class="blog_h2"><span class="graybg">/usr/sbin</span></div>
<p>系统应用程序，类似于/sbin。</p>
<div class="blog_h2"><span class="graybg">/usr/include</span></div>
<p>开发人员使用的头文件。子目录c++为GNU C++的头文件；子目录X11为X Windows头文件。</p>
<div class="blog_h2"><span class="graybg">/usr/lib</span></div>
<p>编程、包需要的库文件，例如动态链接库、python的模块。</p>
<div class="blog_h2"><span class="graybg">/usr/lib64</span></div>
<p>64位库文件。</p>
<div class="blog_h2"><span class="graybg">/usr/fonts</span></div>
<p>字体文件。</p>
<div class="blog_h2"><span class="graybg">/usr/share</span></div>
<p>包含很多程序、库、文档，其中/usr/share/doc是文档中心。</p>
<div class="blog_h2"><span class="graybg">/usr/src</span></div>
<p>存放一些源代码，一些发行版把内核源码存放到/usr/src/linux。</p>
<div class="blog_h1"><span class="graybg">/usr/local</span></div>
<p>用户在本地添加的应用、库等文件，例如/usr/local/lib/python2.7/。具有和/usr类似的结构。</p>
<div class="blog_h1"><span class="graybg">/mnt</span></div>
<p><span style="background-color: #c0c0c0;">以前通常把分区挂载在该目录下</span>，可以作为可移动介质上文件系统的临时挂载点。</p>
<div class="blog_h1"><span class="graybg">/media</span></div>
<p>通常把<span style="background-color: #c0c0c0;">可移动介质上的文件系统挂载到</span>这里。</p>
<div class="blog_h2"><span class="graybg">/cdrom</span></div>
<p>到/media/cdrom的链接。</p>
<div class="blog_h1"><span class="graybg">/tmp</span></div>
<p>临时文件目录，每次系统引导即清空。</p>
<div class="blog_h1"><span class="graybg">/opt</span></div>
<p>可选操作系统组件、附加软件安装位置，使用的不多。</p>
<div class="blog_h1"><span class="graybg">/stand </span></div>
<p>独立的工具、磁盘格式化工具、诊断工具等。</p>
<div class="blog_h1"><span class="graybg">/srv</span></div>
<p>服务器系统所在目录。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-files">Linux目录层次和配置文件</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-files/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux运行级别和启动顺序</title>
		<link>https://blog.gmem.cc/linux-run-level</link>
		<comments>https://blog.gmem.cc/linux-run-level#comments</comments>
		<pubDate>Thu, 05 Apr 2012 06:33:21 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>
		<category><![CDATA[SystemV]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=1251</guid>
		<description><![CDATA[<p>本文主要介绍Sysvinit初始化系统，牵涉到一部分upstart的知识。参考Linux的三种Init机制详细的了解Linux系统的初始化机制。 运行级别 标准的Linux运行级别定义 一共有7个运行级别： 运行级别 说明 0  停机，机器关闭 1  单用户模式 2  没有用到/可以用户自定义 3  完全多用户模式，没有图形界面 4  没有用到/可以用户自定义 5  完全多用户模式，有图形界面 6  重新启动 Debian及其衍生的系统（例如Ubuntu）运行级别定义 一共有7个运行级别，但是2-5没有区别 <a class="read-more" href="https://blog.gmem.cc/linux-run-level">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-run-level">Linux运行级别和启动顺序</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><p>本文主要介绍Sysvinit初始化系统，牵涉到一部分upstart的知识。参考<a href="https://blog.gmem.cc/linux-init-mechanisms">Linux的三种Init机制</a>详细的了解Linux系统的初始化机制。</p>
<div class="blog_h2"><span class="graybg">运行级别</span></div>
<div class="blog_h3"><span class="graybg">标准的Linux运行级别定义</span></div>
<div>一共有7个运行级别：</div>
<div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">运行级别</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td> 停机，机器关闭</td>
</tr>
<tr>
<td>1</td>
<td> 单用户模式</td>
</tr>
<tr>
<td>2</td>
<td> 没有用到/可以用户自定义</td>
</tr>
<tr>
<td>3</td>
<td> 完全多用户模式，没有图形界面</td>
</tr>
<tr>
<td>4</td>
<td> 没有用到/可以用户自定义</td>
</tr>
<tr>
<td>5</td>
<td> 完全多用户模式，有图形界面</td>
</tr>
<tr>
<td>6</td>
<td> 重新启动</td>
</tr>
</tbody>
</table>
</div>
<div class="blog_h3"><span class="graybg">Debian及其衍生的系统（例如Ubuntu）运行级别定义</span></div>
<div>一共有7个运行级别，但是2-5没有区别</div>
<div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">运行级别</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td> 停机，机器关闭</td>
</tr>
<tr>
<td>1</td>
<td> 单用户模式</td>
</tr>
<tr>
<td>2</td>
<td> 完全多用户模式，有图形界面</td>
</tr>
<tr>
<td>3</td>
<td> 完全多用户模式，有图形界面</td>
</tr>
<tr>
<td>4</td>
<td> 完全多用户模式，有图形界面</td>
</tr>
<tr>
<td>5</td>
<td> 完全多用户模式，有图形界面</td>
</tr>
<tr>
<td>6</td>
<td> 重新启动</td>
</tr>
</tbody>
</table>
</div>
<div class="blog_h3"><span class="graybg">检查和修改系统的默认运行级别</span></div>
<pre class="crayon-plain-tag">#查看运行级别命令
runlevel

#修改运行级别
sudo vi /etc/inittab
#添加以下内容，则默认运行级别修改3
id:3:initdefault:
#重启后，再使用命令runlevel查看，会发现运行级别变成了3

#下面是个非主流重启操作
sudo init 6</pre>
<div class="blog_h3"><span class="graybg">不同运行级别到底有什么区别</span></div>
<p>不同运行级别，会执行不同的脚本，这些脚本是在：/etc/rc0.d 到 /etc/rc5.d这些目录中定义的，目录名中的数字和运行级别对应。<br /> 这些脚本都是指向/etc/init.d/下文件的符号连接。如果你留意的话，发现这些脚本均是以如下方式命名：</p>
<table border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">前缀</td>
<td style="text-align: center;">两位数字</td>
<td style="text-align: center;">脚本名称</td>
</tr>
</thead>
<tbody>
<tr>
<td>可以是K或者S</td>
<td>表示脚本被执行的顺序</td>
<td>/etc/init.d中对应脚本的名称</td>
</tr>
</tbody>
</table>
<p>例如：S91apache2、K09apache2等。</p>
<p>前缀<span style="color: #000000;"><strong><span style="background-color: #99cc00;">S</span></strong></span>或者<span style="color: #000000;"><strong><span style="background-color: #ff0000;">K</span></strong></span>表示<strong><span style="background-color: #99cc00; color: #000000;">启动</span></strong>或者<strong><span style="background-color: #ff0000; color: #000000;">停止</span></strong>某个服务。分别会调用init.d下脚本的do_start()、do_stop()函数。</p>
<p>如果要在运行级别N下禁用服务myservice，则只需要删除rcN.d中删除符号连接，例如：</p>
<pre class="crayon-plain-tag">#进入运行级别2的目录
cd /etc/rc2.d
#修改前缀S为K
rm S11myservice</pre>
<div class="blog_h2"><span class="graybg">自定义服务</span></div>
<p>Linux下的<pre class="crayon-plain-tag">service</pre> 命令可以用于启动或者停止一个服务，该命令会<span style="background-color: #c0c0c0;">忽略大部分环境变量</span>，并且设置工作目录为<pre class="crayon-plain-tag">/</pre> 。</p>
<p>该命令的本质执行以下二者之一：</p>
<ol>
<li>执行位于/etc/init.d/下的System V init script</li>
<li>执行位于/etc/init/下的启动任务（upstart job）</li>
</ol>
<div class="blog_h3"><span class="graybg">System V init脚本模板</span></div>
<p>因此，我们只需要编写符合规范的脚本，并按照上一节描述的命名规则放置到对应位置，即可自己定义“服务”，下面是Ubuntu 14的System V init脚本的模板：</p>
<pre class="crayon-plain-tag">#! /bin/sh
### BEGIN INIT INFO
# Provides:          Sample daemon
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: System V init script  example
# Description:       System V init script  example
### END INIT INFO

#注意，上面的INIT INFO会被chkconfig命令读取，自动完成/etc/rc*.d中符号链接的更新
# Author: Alex Wang


# 如当前服务在mountnfs.sh后运行，PATH只应当包含/usr/*下的内容
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="System V init script  example"
NAME=smpld
# 守护程序的路径
DAEMON=/usr/local/bin/$NAME
DAEMON_ARGS="--msg HelloWorld"
#写入进程ID的文件
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# 如果守护程序不可执行，则退出
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] &amp;&amp; . /etc/default/$NAME

# 加载rcS变量
. /lib/init/vars.sh

# 执行此文件以定义LSB log_*函数
. /lib/lsb/init-functions

#
# 用于启动（S）守护程序/服务的函数
#
do_start()
{
	# 返回值说明：
	#   0 守护程序启动成功
	#   1 守护程序（在此函数执行之前）已经启动
	#   2 守护程序无法启动
    # 一般使用start-stop-daemon来进行守护程序的启动与关闭
    # 测试守护程序是否已经被启动
	#   start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test &gt; /dev/null || return 1
    # 尝试启动守护程序
	#   start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_ARGS || return 2
	# 添加额外的代码，例如等待进程准备好提供服务，或者仅仅是等待一段时间。等待以便依赖于此守护程序的其它Daemon能够启动
    $DAEMON $DAEMON_ARGS
}

#
# 用于停止（K）守护程序/服务的函数
#
do_stop()
{
	# 返回值说明：
	#   0 守护程序停止成功
	#   1 守护程序（在此函数执行之前）已经停止
	#   2 守护程序无法停止
	#   其它值：有错误发生
    # 一般使用start-stop-daemon来进行守护程序的启动与关闭
	#   start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
	#   RETVAL="$?"
	#   [ "$RETVAL" = 2 ] &amp;&amp; return 2
    #
	# 等待守护程序fork出的子进程停止：
	#   start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
    #	[ "$?" = 2 ] &amp;&amp; return 2
	# 某些守护程序不会删除其PID文件，需要手工删除
	#  rm -f $PIDFILE
	#  return "$RETVAL"
}

#
# 用户发送SIGHUP信号给守护程序的函数
#
do_reload() {
	# start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
	return 0
}

#根据第一个参数是start、stop、reload……，执行不同的函数
case "$1" in
  start)
	[ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Starting $DESC" "$NAME"
	do_start
	case "$?" in
		0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
	esac
	;;
  stop)
	[ "$VERBOSE" != no ] &amp;&amp; log_daemon_msg "Stopping $DESC" "$NAME"
	do_stop
	case "$?" in
		0|1) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 0 ;;
		2) [ "$VERBOSE" != no ] &amp;&amp; log_end_msg 1 ;;
	esac
	;;
  status)
	status_of_proc "$DAEMON" "$NAME" &amp;&amp; exit 0 || exit $?
	;;
  reload|force-reload)

	log_daemon_msg "Reloading $DESC" "$NAME"
	do_reload
	log_end_msg $?
	;;
  restart|force-reload)
	log_daemon_msg "Restarting $DESC" "$NAME"
	do_stop
	case "$?" in
	  0|1)
		do_start
		case "$?" in
			0) log_end_msg 0 ;;
			1) log_end_msg 1 ;; # 旧的进程仍然在运行
			*) log_end_msg 1 ;; # 启动失败
		esac
		;;
	  *)
		# Failed to stop
		log_end_msg 1
		;;
	esac
	;;
  *)
	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" &gt;&amp;2
	exit 3
	;;
esac
#空命令，返回0
:</pre>
<div class="blog_h3"><span class="graybg">System V init脚本样例</span></div>
<p>下面是实际中的一个具体例子：</p>
<pre class="crayon-plain-tag">#! /bin/sh
### BEGIN INIT INFO
# Provides:          tomcat7d
# Required-Start:    $syslog
# Required-Stop:     $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Gmem tomcat7d daemon
# Description:       Gmem tomcat7d daemon
### END INIT INFO

# Author: Alex Wang

export PDT_DIR=/usr/local/Tomcat7
. $PDT_DIR/setenv.sh
LOG_FILE=$LOG_DIR/tomcat7d.log
touch $LOG_FILE
 
do_log()
{
    echo `date "+%Y-%m-%d %H:%M:%S"` $1 &gt;&gt; $LOG_FILE
    echo $1
}

do_start()
{    
    ps -fC "mysqld_safe" | grep "$PDT_DIR/db/my.cnf" &gt; /dev/null
    if [[ $? -eq 0 ]]; then
        do_log "$PDT_NAME database service is already up."
    else
        do_log "Starting $PDT_NAME database service..."
        $PDT_DIR/startup-db.sh
    fi
       
    ps -fC "java"  | grep "$PDT_DIR/jre/bin/java" &gt; /dev/null
    if [[ $? -eq 0 ]]; then
        do_log "$PDT_NAME application service is already up."
    else
        do_log "Starting $PDT_NAME application service..."
        $PDT_DIR/startup-app.sh
    fi
    
    return 0
}

do_stop()
{
    ps -fC "mysqld_safe" | grep "$PDT_DIR/db/my.cnf" &gt; /dev/null
    if [[ $? -eq 0 ]]; then
        do_log "Stopping $PDT_NAME database service..."
        $PDT_DIR/db/bin/mysqladmin -uroot -hlocalhost --protocol=tcp shutdown
        do_log "$PDT_NAME database service stopped."
    else
        do_log "$PDT_NAME database service is already down."
    fi
       
    ps -fC "java"  | grep "$PDT_DIR/jre/bin/java" &gt; /dev/null
    if [[ $? -eq 0 ]]; then
        do_log "Stopping $PDT_NAME application service..."
        $PDT_DIR/bin/shutdown.sh
        do_log "$PDT_NAME application service stopped."
    else
        do_log "$PDT_NAME application service is already down."
    fi
    
    return 0
}

case "$1" in
  start)
  do_start
  ;;
  stop)
  do_stop
  ;;
  restart)
  do_stop
  do_start
  ;;
esac

:
#该脚本放置于/etc/init.d下
#执行命令： chkconfig --add tomcat7d 即可注册服务</pre>
<div class="blog_h3"><span class="graybg">注册服务</span></div>
<p>命令<pre class="crayon-plain-tag">chkconfig</pre> 用于配置系统启动时服务的行为，新近的Ubuntu版本已经不支持该命令，使用<pre class="crayon-plain-tag">update-rc.d</pre> 命令代替之：</p>
<pre class="crayon-plain-tag">sudo update-rc.d myservice defaults</pre>
<div class="blog_h2"><span class="graybg">启动脚本rc.local</span></div>
<p>在Ubuntu下，你可能会注意到rc2.d - rc5.d里面有一个数字最大的<span style="color: #000000;"><span style="background-color: #ff0000;">S99</span><span style="background-color: #ff0000;"><strong>rc.local</strong></span></span>文件，查看其链接目标/etc/init.d/rc.local ，可以看到类似如下脚本：</p>
<pre class="crayon-plain-tag">#! /bin/sh
### BEGIN INIT INFO
# Provides:          rc.local
# Required-Start:    $all
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:
# Short-Description: Run /etc/rc.local if it exist
### END INIT INFO


PATH=/sbin:/usr/sbin:/bin:/usr/bin

. /lib/init/vars.sh
. /lib/lsb/init-functions

do_start() {
	if [ -x /etc/rc.local ]; then
	        [ "$VERBOSE" != no ] &amp;&amp; log_begin_msg "Running local boot scripts (/etc/rc.local)"
		/etc/rc.local
		ES=$?
		[ "$VERBOSE" != no ] &amp;&amp; log_end_msg $ES
		return $ES
	fi
}

case "$1" in
    start)
	do_start
        ;;
    restart|reload|force-reload)
        echo "Error: argument '$1' not supported" &gt;&amp;2
        exit 3
        ;;
    stop)
        ;;
    *)
        echo "Usage: $0 start|stop" &gt;&amp;2
        exit 3
        ;;
esac</pre>
<p>在执行S操作的时候，/etc/init.d/rc.local 会调用脚本<span style="color: #000000;"><strong><span style="background-color: #99cc00;">/etc/rc.local</span></strong></span>，我们常常用<span style="color: #000000; background-color: #99cc00;"><strong>这个脚本</strong></span>来执行一些操作：这些操作在<span style="color: #000000;"><strong><span style="background-color: #99cc00;">所有系统服务启动完毕后</span></strong></span>执行。</p>
<div class="blog_h2"><span class="graybg">Linux系统启动时执行脚本的整体步骤</span></div>
<p>脚本执行由<pre class="crayon-plain-tag">init</pre> 进程发起，以Ubuntu为例，步骤如下：</p>
<div>
<ol>
<li><pre class="crayon-plain-tag">/etc/init/</pre> 目录下的所有*.conf文件被执行，这些文件可能具有类似如下的内容：<br />
<pre class="crayon-plain-tag">description     "deferred execution scheduler"
#启动的时机，可能是运行级别（runlevel）、文件系统（filesystem）等
start on runlevel [2345]
stop on runlevel [!2345]

expect fork
respawn

exec atd</pre>
</li>
<li><pre class="crayon-plain-tag">/etc/rc*.d/</pre> 目录下的脚本，按照顺序执行</li>
<li><pre class="crayon-plain-tag">/etc/rc.local</pre> 脚本被执行</li>
</ol>
</div>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-run-level">Linux运行级别和启动顺序</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-run-level/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux进程间通信</title>
		<link>https://blog.gmem.cc/linux-ipc</link>
		<comments>https://blog.gmem.cc/linux-ipc#comments</comments>
		<pubDate>Thu, 03 Sep 2009 03:54:26 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>
		<category><![CDATA[Linux编程]]></category>
		<category><![CDATA[系统编程]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=6964</guid>
		<description><![CDATA[<p>管道 当从一个进程连接数据流到另外一个进程时，使用术语“管道”。通常是把一个进程的输出通过管道连接到另外一个进程的输入。Shell命令通过管道字符可以实现命令的连接： [crayon-69e29b271f149669603467/] popen函数 Linux提供了类似的API，允许通过编程的方式，利用管道在两个程序之间传递数据。在两个程序之间进行数据传递的最简单方式是使用popen/pclose函数： [crayon-69e29b271f14c874606624/] 下面是一个简单的示例，执行uname命令并获取其输出： [crayon-69e29b271f14f700557497/] pipe调用 在底层，Linux提供了pipe函数，通过该函数可以在两个进程之间传递数据，不需要启动Shell，该函数提供了对读写数据的更多控制： [crayon-69e29b271f151746903362/] 初看，pipe函数没有什么价值，但由于fork调用创建新进程时，默认原先打开的文件描述符仍然保持打开状态，因此，管道可以被父子进程共享，从而用来在其间进行数据传递，下面是pipe在父子进程之间使用的例子： [crayon-69e29b271f154921080941/] 上面的例子中，父子进程运行的是相同的程序，如果两个进程是完全不同的程序呢？通过exec()调用后，只需要将文件描述符传递给新进程就可以继续使用管道了，因为文件描述符本质上只是一个数字而已： [crayon-69e29b271f156549156923/] 关于管道的使用，应当注意： 对于已经关闭写端的管道，对其指向read()调用不会阻塞，而会立即返回0。这与读取无效文件描述符不同，后者会返回-1 如果通过fork()调用使用管道，就会存在两个不同的文件描述符可以用来向管道写数据，一个在父进程中，一个在子进程中。只有在父子文件中均把针对管道的写描述符关闭，管道才认为是关闭的，对其进行read才会立即返回 将管道用作标准输入输出 通过管道连接两个进程，具有更加简洁的方法： [crayon-69e29b271f159958915522/] 命名管道 <a class="read-more" href="https://blog.gmem.cc/linux-ipc">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-ipc">Linux进程间通信</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">管道</span></div>
<p>当<span style="background-color: #c0c0c0;">从一个进程连接数据流到另外一个进程</span>时，使用术语“管道”。通常是把一个进程的<span style="background-color: #c0c0c0;">输出通过管道连接到</span>另外一个进程的<span style="background-color: #c0c0c0;">输入</span>。Shell命令通过管道字符可以实现命令的连接：</p>
<pre class="crayon-plain-tag">cmd1 | cmd2

#cmd1的标准输入来自终端键盘
#cmd1的标准输出传递给cmd2，作为它的标准输入
#cmd2的标准输出连接到终端屏幕</pre>
<div class="blog_h3"><span class="graybg">popen函数</span></div>
<p>Linux提供了类似的API，允许通过编程的方式，利用管道在两个程序之间传递数据。在两个程序之间进行数据传递的最简单方式是使用popen/pclose函数：</p>
<pre class="crayon-plain-tag">#include &lt;stdio.h&gt;
/**
 * 允许将另外一个程序作为新进程启动，并可以传递数据或者接收数据
 * @param command 需要运行的程序和参数
 * @param open_mode 打开模式，必须是r或者w
 *     如果是r，被调用程序的输出可以被当前程序使用，通过返回的文件指针进行fread读取
 *     如果是w，当前程序可以通过fwrite向被调用程序发送数据，后者可以在stdin上读取这些数据
 */
FILE *popen( const char *command, const char *open_mode );
/**
 * 关闭文件指针，该函数只有在新进程结束后才会返回，否则会一直阻塞
 */
int pclose( FILE *stream_to_close );</pre>
<p>下面是一个简单的示例，执行uname命令并获取其输出：</p>
<pre class="crayon-plain-tag">#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
int main()
{
    char buffer[BUFSIZ + 1];
    int chars_read;
    memset( buffer, 0, sizeof ( buffer ) );
    FILE *read_fp = popen( "uname -a", "r" ); //创建新进程并读取其标准输出
    if ( read_fp != NULL )
    {
        //像读取文件一样，将新进程的标准输出读取到缓冲区
        chars_read = fread( buffer, sizeof(char), BUFSIZ, read_fp );
        if ( chars_read &gt; 0 )
        {
            printf( "Output: %s\n", buffer );
        }
        pclose( read_fp );
        exit( EXIT_SUCCESS );
    }
    exit( EXIT_FAILURE );
}</pre>
<div class="blog_h3"><span class="graybg">pipe调用</span></div>
<p>在底层，Linux提供了pipe函数，通过该函数可以在两个进程之间传递数据，不需要启动Shell，该函数提供了对读写数据的更多控制：</p>
<pre class="crayon-plain-tag">#include &lt;unistd.h&gt;
/**
 * 创建一个管道，该函数对入参数组填上两个新的文件描述符，然后返回0
 * 返回的两个文件描述符通过一种特殊的方式连接：依据FIFO原则，写入
 * file_descriptor[1]的数据，都可以从file_descriptor[0]中读取回来
 *
 * @param file_descriptor 长度为2的文件描述符数组
 * @return 如果成功返回0否则返回-1并设置errno：
 *     EMFILE：进程使用的文件描述符过多
 *     ENFILE：系统的文件表已满
 *     EFAULT：文件描述符无效
 */
int pipe( int file_descriptor[2] );</pre>
<p>初看，pipe函数没有什么价值，但由于fork调用创建新进程时，默认<span style="background-color: #c0c0c0;">原先打开的文件描述符仍然保持打开状态</span>，因此，管道可以被父子进程共享，从而用来在其间进行数据传递，下面是pipe在父子进程之间使用的例子：</p>
<pre class="crayon-plain-tag">#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "MSG";
    char buffer[BUFSIZ + 1];
    pid_t fork_result;
    memset( buffer, '\0', sizeof ( buffer ) );
    if ( pipe( file_pipes ) == 0 ) //创建一个共享的管道
    {
        fork_result = fork(); //创建子进程
        if ( fork_result == -1 ) exit( EXIT_FAILURE );
        if ( fork_result == 0 )
        {
            //这里是子进程，从文件描述符0中读取数据
            data_processed = read( file_pipes[0], buffer, BUFSIZ );
            printf( "Read %d bytes: %s\n", data_processed, buffer );
            exit( EXIT_SUCCESS );
        }
        else
        {
            //这里是父进程，向文件描述符1中写入数据
            data_processed = write( file_pipes[1], some_data, strlen( some_data ) );
            printf( "Wrote %d bytes\n", data_processed );
        }
    }
    exit( EXIT_SUCCESS );
}</pre>
<p>上面的例子中，父子进程运行的是相同的程序，如果两个进程是完全不同的程序呢？通过exec()调用后，只需要将文件描述符传递给新进程就可以继续使用管道了，因为文件描述符本质上只是一个数字而已：</p>
<pre class="crayon-plain-tag">//将管道文件描述符保存到字符串中
sprintf(buffer, "%d", file_pipes[0]);
//传递给子进程
execl("command", buffer, (char *)0);
//读取参数为文件描述符
sscanf(argv[0], "%d", &amp;file_descriptor);
//从文件描述符中读取数据
data_processed = read( file_descriptor, buffer, BUFSIZ );</pre>
<p>关于管道的使用，应当注意：</p>
<ol>
<li>对于已经关闭写端的管道，对其指向read()调用不会阻塞，而会立即返回0。这与读取无效文件描述符不同，后者会返回-1</li>
<li>如果通过fork()调用使用管道，就会存在<span style="background-color: #c0c0c0;">两个不同的文件描述符可以用来向管道写数据</span>，一个在父进程中，一个在子进程中。只有在父子文件中均把针对管道的写描述符关闭，管道才认为是关闭的，对其进行read才会立即返回</li>
</ol>
<div class="blog_h3"><span class="graybg">将管道用作标准输入输出</span></div>
<p>通过管道连接两个进程，具有更加简洁的方法：</p>
<pre class="crayon-plain-tag">if ( fork_result == ( pid_t ) 0 ) // 子进程
{
    close( 0 ); //关闭标准输入
    //复制管道读，根据dup的特点，它将复制为最小数值的文件描述符，即作为标准输入
    dup( file_pipes[0] );
    //关闭管道中两个文件描述符
    close( file_pipes[0] );
    close( file_pipes[1] );
    execlp( "od", "od", "-c", ( char * ) 0 );
}</pre>
<div class="blog_h3"><span class="graybg">命名管道</span></div>
<p>要在两个不相关（不具备共同祖先）的进程之间传递数据，可以使用命名管道（Named pipe），<span style="background-color: #c0c0c0;">命名管道</span>又被称为<span style="background-color: #c0c0c0;">FIFO文件</span>。命名管道是一种特殊类型的文件，在文件系统中以文件名的形式存在，其行为却与前一节所述的管道类似。可以通过Shell命令<pre class="crayon-plain-tag">mknode</pre> 或者<pre class="crayon-plain-tag">mkfifo</pre> 来创建命名管道：</p>
<pre class="crayon-plain-tag">mkfifo /tmp/my_fifo
#尝试读取这个空白的命名管道
cat &lt; /tmp/my_fifo  #阻塞

#在另外一个终端尝试写入这个空白的命名管道
echo "Hello World" &gt; /tmp/my_fifo 
#第一个终端读取到内容并输出在屏幕上</pre>
<p>在程序中，可以使用以下两个调用：</p>
<pre class="crayon-plain-tag">#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
int mkfifo( const char *filename, mode_t mode );
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0);

//创建命名管道的例子：
int main()
{
    int res = mkfifo( "/tmp/my_fifo", 0777 ); //尝试创建777权限的文件，当然需要受到用户掩码umask的制约
    if ( res == 0 ) exit( EXIT_SUCCESS );
}</pre>
<p>通过open命令访问FIFO文件时，需要注意一个限制：不能以<pre class="crayon-plain-tag">O_RDWR</pre> 模式打开，因为FIFO只是为了<span style="background-color: #c0c0c0;">单向的传递数据</span>。如果以读写方式打开，进程将从管道中读取到自己写入的数据。如果确实需要双向的数据传输，可以使用一对命名管道。此外选项<pre class="crayon-plain-tag">O_NONBLOCK</pre> 也会影响对管道的读写请求的处理方式：</p>
<pre class="crayon-plain-tag">//调用一直阻塞，除非另外一个进程以写方式打开同一命名管道
open( const char *path, O_RDONLY );
//即使没有其它进程以写模式打开同一命名管道，调用也会成功并立即返回
open(const char *path, O_RDONLY | O_NONBLOCK);
//调用一直阻塞，除非另外一个进程以读方式打开同一命名管道
open( const char *path, O_WRONLY );
//调用会立即返回，但是如果没有另外一个进程以读模式打开命名管道，调用将返回-1，并且FIFO也不会被打开
open(const char *path, O_WRONLY | O_NONBLOCK);</pre>
<div class="blog_h2"><span class="graybg">信号量</span></div>
<p>与线程之间通信的信号量类似，Linux还提供了更通用的，可以在不同进程之间进行通信的信号量机制，这些信号量接口都是针对成组的通用信号量进行操作，而不是针对一个二进制信号量。</p>
<pre class="crayon-plain-tag">#include &lt;sys/sem.h&gt;
/**
 * 创建一个新的信号量，或者获取一个已经存在的信号量
 * @param key  一个整数，不相关的进程可以通过同一key来访问同一信号量
 *             特殊值IPC_PRIVATE表示创建一个只有当前进程才能看见的信号量
 * @param num_sems 需要的信号量的数目，一般为1
 * @param sem_flags 位或标记。低9位类似于文件权限；IPC_CREAT表示创建一个新的信号量；
 *                  IPC_EXCL | IPC_CREAT 表示确保获得一个新的、唯一的信号量，如果
 *                  信号量已经存在，会返回错误
 * @return 成功返回正整数，表示信号量的唯一标识（sem_id）；失败返回-1
 */
int semget( key_t key, int num_sems, int sem_flags );
/**
 * 用于改变信号量的值，这是一个原子操作
 * @param sem_id 信号量的唯一标识
 * @param sem_ops 指向一个结构的指针
 * @param num_sem_ops
 *
 */
struct sembuf
{
    short sem_num; //信号量的数量，除非需要使用一组信号量，否则取值0
    short sem_op;  //信号量在一次操作中需要改变的值，可以使用非1值来改变信号量
                   //通常只会用到两个值：-1表示P操作，表示等待信号量可用；+1表示V操作，表示发送信号量可用的信息
    short sem_flg; //通常设置为SEM_UNDO，它使得操作系统跟踪该信号量的修改情况
                   //如果进程没有释放持有的信号量就终止，操作系统会代为释放
};
int semop( int sem_id, struct sembuf *sem_ops, size_t num_sem_ops );
/**
 * 直接控制信号量信息
 * @param sem_id 信号量的唯一标识
 * @param sem_num 信号量的数量，除非需要使用一组信号量，否则取值0
 * @param command 需要指向的操作
 * @param semun 提供命令参数的联合体
 */
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
int semctl( int sem_id, int sem_num, int command, union semun semun );</pre>
<div class="blog_h2"><span class="graybg">共享内存</span></div>
<p>共享内存允许不相关的进程<span style="background-color: #c0c0c0;">访问同一块逻辑内存</span>。这是一种在进程之间传递数据的非常有效的方式，大部分实现都把共享内存安排为同一段物理内存。共享内存是进程地址空间中的一个特殊的范围。共享内存没有通过同步机制，因此需要使用其它同步机制来对共享内存的访问进行同步。</p>
<p>与共享内存相关的函数有：</p>
<pre class="crayon-plain-tag">#include &lt;cygwin/types.h&gt;
#include &lt;stddef.h&gt;

//#include &lt;sys/shm.h&gt;

/**
 * 创建共享内存
 * @param key 共享内存段的命名，特殊键IPC_PRIVATE表示创建进程私有的共享内存
 * @param size 共享内存的容量
 * @param shmflg 位或，包含9个代表访问权限的位，IPC_CREAT用于创建一个新的共享内存
 *               给此函数传递已经存在的key并不是错误，此时的IPC_CREAT会被忽略
 *
 * @return 如果成功，返回一个正整数，作为共享内存的标识符；否则返回-1
 */
int shmget( key_t key, size_t size, int shmflg );
/**
 * 第一次创建共享内存段时，它不能被任何进程访问。要启用对共享内存的访问，必须
 * 将其连接到一个进程的地址空间中，这通过shmat函数完成
 * @param shm_id 共享内存标识符
 * @param shm_addr 连接到当前进程的地址位置，通常设置为空指针，表示让系统选择，否则硬件依赖性太高
 * @param shmflg 标记位：SHM_RND与shm_addr联用，来控制连接地址；SHM_RDONLY 是共享内存对当前进程只读
 *
 * @return 如果成功，返回指向共享内存第一个字节的指针；否则返回-1
 */
void *shmat( int shm_id, const void *shm_addr, int shmflg );
/**
 * 将共享内存段从当前进程分离，该函数不会删除共享内存，只是使当前进程不再能访问它
 * @param shm_addr shmat的返回值
 */
int shmdt( const void *shm_addr );
/**
 * 控制共享内存
 * @param shm_id 共享内存标识符
 * @param cmd 采取的动作：
 *            IPC_STAT 将shmid_ds中的数据设置为共享内存的当前关联值
 *            IPC_SET 如果有足够权限，则把shmid_ds中的值设置到共享内存
 *            IPC_RMID 删除共享内存段
 * @param buf 指针，指向共享内存模式和访问权限的结构
 */
struct shmid_ds
{
    uid_t uid;
    uid_t gid;
    mode_t mode;
};
int shmctl( int shm_id, int cmd, struct shmid_ds *buf );</pre>
<div class="blog_h2"><span class="graybg">消息队列</span></div>
<p>消息队列类似于命名管道，但是不具有打开、关闭管道以及阻塞通信方面的复杂性。消息队列提供了一种从一个进程向另外一个进程发送数据块的机制，每个数据块被认为含有一个类型。下面是消息队列相关API：</p>
<pre class="crayon-plain-tag">MSGMAX
//单个消息的最大字节数
MSGMNB
//队列最大深度

#include &lt;sys/msg.h&gt;
/**
 * 创建和访问一个消息队列
 * @param key 队列的名字，IPC_PRIVATE用于创建私有队列
 * @param msgflg 标记位，包含9个权限位，IPC_CREAT必须与这些位或才能创建新的队列
 * @return 如果成功返回消息队列的标识符，否则返回-1
 */
int msgget( key_t key, int msgflg );
/**
 * 把消息放入到队列中
 * @param msqid 消息队列标识符
 * @param msg_ptr 待发送消息的指针，目标应当是一个结构，且第一个成员变量是long型，用于表示消息类型
 * @param msg_sz msg_ptr指向的消息的长度，不包括long型的消息类型的长度
 * @param msgflg 控制当队列满或者队列消息到达系统范围限制时的行为，位或
 *               IPC_NOWAIT：立即返回-1，不发送消息；如果该标记被清除，则发送进程挂起直到队列有空闲
 */
int msgsnd( int msqid, const void *msg_ptr, size_t msg_sz, int msgflg );
/**
 * 从队列里接收一个消息
 * @param msqid 消息队列标识符
 * @param msg_ptr 准备接收消息的指针
 * @param msg_sz msg_ptr指向消息的长度，不包括long型消息类型的长度
 * @param msgtype 消息类型，用于实现简单的优先级机制：如果小于0获取消息类型小于等于其绝对值的第一个消息
 *                如果为0获取第一个消息；如果大于0获取对应类型的第一个消息
 * @param msgflg 控制当队列为空时的行为，位或。IPC_NOWAIT类似msgsnd
 */
int msgrcv( int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg );
/**
 * 控制消息队列
 * @param msqid 消息队列标识符
 * @param cmd 命令。IPC_STAT，将消息队列关联值设置到buf；IPC_SET，将buf中的值设置到消息队列
 *            IPC_RMID，删除消息队列
 * @param buf 存放命令参数
 */
struct msqid_ds
{
    uid_t uid;
    uid_t gid;
    mode_t mode;
};
int msgctl( int msqid, int cmd, struct msqid_ds *buf );</pre>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-ipc">Linux进程间通信</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-ipc/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux信号、进程和会话</title>
		<link>https://blog.gmem.cc/linux-signals-processes-and-sessions</link>
		<comments>https://blog.gmem.cc/linux-signals-processes-and-sessions#comments</comments>
		<pubDate>Mon, 10 Aug 2009 06:47:11 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[C]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>
		<category><![CDATA[Linux编程]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[系统编程]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=5772</guid>
		<description><![CDATA[<p>进程 进程和信号是Linux操作环境的基础部分，控制着Linux和其它类UNIX系统的几乎所有活动。 UNIX标准对进程的定义：其中运行着一个或者多个线程的地址空间，以及这些线程需要的系统资源。Linux系统的进程是非常轻量级的。 进程基础知识 每个Linux进程包含以下部分： PID：进程的唯一表示，是范围从2~32768的正整数，数字回绕一圈后，重新从2开始计数（数字1被系统第一个进程init占用） 程序代码：以自动方式加载到内存，正常情况下Linux进程不能对用来存放程序代码的内存区域进行写操作，因而可以被多个进程安全的共享 函数库：系统函数库也可以被共享，不管多少进程调用printf，内存中只需要它的一份副本即可，这种做法类似于微软的DLL机制但更为复杂 进程数据：存放进程的全局变量 栈空间：进程有属于自己的栈空间，用于保存函数中的局部变量、控制函数的调用和返回 环境空间：包含专为该进程建立的环境变量 程序计数器：记录进程执行到的位置，即在执行线程中的位置。进程可以包含多个执行线程 Linux系统使用一个被称为进程表的结构来存放当前加载到内存的所有进程的信息。这些信息包括：进程ID、进程状态、进程命令字符串以及其它一些ps命令输出的信息。操作系统通过PID对进程进行管理，早起的UNIX系统只能同时运行256个进程。 进程状态 注意：这些状态也适用于线程。进程中的线程通常处于不同的状态。 通过ps的STAT列，可以查看进程的状态，其代码如下表：  STAT代码 说明  S 睡眠。通常是在等待某个事件的发生，如信号、输入、Time slot R <a class="read-more" href="https://blog.gmem.cc/linux-signals-processes-and-sessions">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-signals-processes-and-sessions">Linux信号、进程和会话</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">进程</span></div>
<p>进程和信号是Linux操作环境的基础部分，控制着Linux和其它类UNIX系统的几乎所有活动。</p>
<p>UNIX标准对进程的定义：其中运行着一个或者多个线程的<span style="background-color: #c0c0c0;">地址空间</span>，以及这些线程需要的<span style="background-color: #c0c0c0;">系统资源</span>。Linux系统的进程是<span style="background-color: #c0c0c0;">非常轻量级</span>的。</p>
<div class="blog_h2"><span class="graybg">进程基础知识</span></div>
<p>每个Linux进程包含以下部分：</p>
<ol>
<li>PID：进程的唯一表示，是范围从2~32768的正整数，数字回绕一圈后，重新从2开始计数（数字1被系统第一个进程init占用）</li>
<li>程序代码：以自动方式加载到内存，正常情况下Linux进程不能对用来存放程序代码的内存区域进行写操作，因而可以被多个进程安全的共享</li>
<li>函数库：系统函数库也可以被共享，不管多少进程调用printf，内存中只需要它的一份副本即可，这种做法类似于微软的DLL机制但更为复杂</li>
<li>进程数据：存放进程的全局变量</li>
<li>栈空间：进程有属于自己的栈空间，用于保存函数中的局部变量、控制函数的调用和返回</li>
<li>环境空间：包含专为该进程建立的环境变量</li>
<li>程序计数器：记录进程执行到的位置，即在执行线程中的位置。进程可以包含多个执行线程</li>
</ol>
<p>Linux系统使用一个被称为<span style="background-color: #c0c0c0;">进程表</span>的结构来存放当前加载到内存的<span style="background-color: #c0c0c0;">所有进程</span>的信息。这些信息包括：进程ID、进程状态、进程命令字符串以及其它一些ps命令输出的信息。操作系统通过PID对进程进行管理，早起的UNIX系统只能同时运行256个进程。</p>
<div class="blog_h3"><span class="graybg">进程状态</span></div>
<p>注意：<span style="background-color: #c0c0c0;">这些状态也适用于线程</span>。进程中的线程通常处于不同的状态。</p>
<p>通过ps的STAT列，可以查看进程的状态，其代码如下表：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> STAT代码</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>S</td>
<td>睡眠。通常是在等待某个事件的发生，如信号、输入、Time slot</td>
</tr>
<tr>
<td>R</td>
<td>运行。严格来说应是“可运行”，即在运行队列中，处于正在执行或即将运行状态</td>
</tr>
<tr>
<td>D</td>
<td>
<p>不可中断的睡眠（Uninterruptible sleep）。通常是在等待输入或输出（网络、磁盘、其它外设的IO）完成</p>
<p>处于此状态的进程，无法处理信号（<span style="background-color: #c0c0c0;">无法被信号唤醒，只能被它所等待的东西唤醒，或超时，如果在睡眠前设置了超时的话</span>），即使是kill -9 也无法处理</p>
<p>不允许中断的原因是，保护系统数据一致，防止数据读取错误</p>
</td>
</tr>
<tr>
<td>T</td>
<td>
<p>停止。通常被Shell作业控制所停止，或者进程正处于调试器的控制下</p>
<p>在Terminal中键入Ctrl + Z会导致当前<span style="background-color: #c0c0c0;">前台进程停止，暂停运行</span>。<span style="background-color: #c0c0c0;">此时输入bg，则让该进程继续在后台运行</span></p>
</td>
</tr>
<tr>
<td>Z</td>
<td>
<p>死（Defunct）进程或者僵尸（Zombie）进程</p>
<p>子进程死亡后，处于僵尸状态，其父进程负责收集其退出码等信息并完全释放它</p>
</td>
</tr>
<tr>
<td>N</td>
<td>低优先级任务（nice）</td>
</tr>
<tr>
<td>W</td>
<td>分页。不适用于2.6+内核</td>
</tr>
<tr>
<td>s</td>
<td>进程是Session leader</td>
</tr>
<tr>
<td>+</td>
<td>进程属于前台进程组</td>
</tr>
<tr>
<td>l</td>
<td>进程是多线程的</td>
</tr>
<tr>
<td>&lt;</td>
<td>高优先级任务</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">系统进程</span></div>
<p>一般而言，每个Linux进程都是由另外一个被称为<span style="background-color: #c0c0c0;">父进程</span>的进程所启动的，前者相对的被称为<span style="background-color: #c0c0c0;">子进程</span>。在Linux系统启动时，它运行一个PID为1的init进程，可以把该进程看做操作系统的进程管理器，它是所有进程的祖先进程。</p>
<p>启动进程并等待其结束，是Linux中最基本的进程管理任务。应用程序可以通过fork、exec、wait等系统调用完成这些任务。</p>
<div class="blog_h3"><span class="graybg">进程调度</span></div>
<p>每个进程被分配以非常短暂的时间片，在时间片范围内进程代码被CPU执行，由于CPU非常快、时间片又非常短，你会感觉到多个程序同时运行的假象。Linux内核使用进程调度器来决定下一个时间片应该分配给哪个进程，其判断的依据是进程的优先级，优先级高的进程获得的时间片更多。在Linux中进程的运行时间<span style="background-color: #c0c0c0;">不可能超过分配给它的时间片</span>，Linux使用的是<span style="background-color: #c0c0c0;">抢占式处理</span>，因此进程的挂起、继续运行不需要彼此之间的协作。</p>
<div class="blog_h3"><span class="graybg">进程死亡</span></div>
<p>进程调用<pre class="crayon-plain-tag">exit()</pre> 系统调用可以让自身退出，进程占用的内存将被释放，并利用信号通知其父进程。</p>
<p>父进程可能先于子进程死亡，这种情况下，init进程可以成为子进程的养父。</p>
<p>子进程在死亡后，将处于僵尸状态。这种状态的进程不能被调度，并占据少量的系统资源，以保证其父进程可以访问其退出码等信息。父进程负责完全的释放子进程。</p>
<div class="blog_h3"><span class="graybg">进程和线程</span></div>
<p>进程包含：虚拟地址空间、打开的系统对象的描述符、安全上下文、进程标识符、环境变量、最小-最大工作集大小，以及最少一个线程 —— 主线程。</p>
<p>线程是进程内部的一个可调度的实体（执行路径），进程内的<span style="background-color: #c0c0c0;">所有线程共享地址空间、打开的描述符</span>。每个线程维护自己的<span style="color: #242729;">exception handlers、调度优先级、线程本地存储、线程标识符、线程上下文结构。线程可以具有自己的安全上下文。</span></p>
<p><span style="color: #242729;">线程上下文中包括的数据项：寄存器数据、内核栈、线程环境块、位于进程地址空间的用户栈。</span></p>
<div class="blog_h2"><span class="graybg">Linux进程相关API</span></div>
<div class="blog_h3"><span class="graybg">启动进程</span></div>
<p>可以在程序内部启动另外一个程序，从而创建新的进程，可以通过库函数system()完成：</p>
<pre class="crayon-plain-tag">#include &lt;stdlib.h&gt;
/**
 * 运行指定的命令并等待其完成
 * @param cmd 需要执行的命令
 * @return 如果无法启动Shell返回127，其它错误返回-1，否则返回所执行命令的退出码
 */
int system( const char *cmd );</pre>
<p>上述函数的重大缺点是必须等待子进程的完成，并且依赖于Shell，因此使用的不多。</p>
<div class="blog_h3"><span class="graybg">替换进程映像</span></div>
<p>exec系列函数用于<span style="background-color: #c0c0c0;">把当前进程替换为一个新的进程（新进程执行结束后不会返回原进程）</span>。 新的程序启动后，原有的程序就不再运行了：</p>
<pre class="crayon-plain-tag">#include &lt;unistd.h&gt;
char **environ; // 该全局变量可用来设置传递到新程序的环境变量

// ***p函数通过搜索PATH环境变量来查找新的可执行文件的路径，如果目标程序不在PATH中，则path参数应当使用绝对路径
// ***e函数支持通过envp数组指定环境变量

// 下面三个函数支持变长参数列表，此列表以一个空指针结束
int execl(const char *path, const char *arg0, ..., (char *)0);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const envp[]);

// 下面三个函数使用数组来表示参数列表
int execv( const char *path, char * const argv[] );
int execvp( const char *file, char * const argv[] );
int execve( const char *path, char * const argv[], char * const envp[] );

//举例：
char * const ps_argv[] = { "ps", "ax", 0 };
char * const ps_envp[] = { "PATH=/bin:/usr/bin", "TERM=console", 0 };
execl("/bin/ps", "ps", "ax", 0);
execlp("ps", "ps", "ax", 0);
execle("/bin/ps", "ps", "ax", 0, ps_envp);
execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);</pre>
<p>一般情况下，<span style="background-color: #c0c0c0;">exec函数是不会返回的，除非发生错误</span>，此时返回-1并设置errno。</p>
<p>exec启动的新进程保留了原进程的许多特性，特别是，原进程打开的文件描述符仍然有效，除非这些描述符的close on exec flag标记位被设置。任何在原进程中打开的目录流都会在新进程中被关闭。</p>
<div class="blog_h3"><span class="graybg">复制进程映像</span></div>
<p>系统调用fork允许创建以当前进程为模板，<span style="background-color: #c0c0c0;">复制</span>出一个新的进程。fork调用会在进程表中创建一个新的表项，其中很多属性都和原进程相同，包括所执行的代码。但<span style="background-color: #c0c0c0;">新进程具有自己的数据空间、环境、文件描述符</span>。fork函数的原型如下：</p>
<pre class="crayon-plain-tag">#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
pid_t fork( void );</pre>
<p>fork()函数<span style="background-color: #c0c0c0;">很巧妙，其具有“两次返回”的效果</span>，实质上对应了父子进程的不同执行路径。对于父进程，fork()返回子进程的ID；而对于子进程，fork()总是返回0。通过这一特点，可以判断当前执行的代码是父进程还是子进程：</p>
<pre class="crayon-plain-tag">pid_t new_pid;
new_pid = fork();
switch ( new_pid )
{
    case -1 :   /* 错误 */
        break;
    case 0 :    /* 子进程 */
        break;
    default :   /* 父进程 */
        break;
}</pre>
<p>结合fork、exec函数，创建新进程的条件就完备了。</p>
<div class="blog_h3"><span class="graybg">等待进程结束</span></div>
<p>当fork启动一个子进程后，子进程就有了自己的生命周期，并将独立运行，有时候，需要知道子进程何时结束，可以在父进程中用wait系统调用：</p>
<pre class="crayon-plain-tag">#include &lt;sys/types.h&gt;
#include &lt;sys/wait.h&gt;
/**
 * 暂停父进程的执行，直到其子进程结束
 * @param stat_loc 存放状态信息，用于了解子进程的退出状态（即子进程main函数的返回值或者exit函数的退出码）
 *     WIFEXITED(stat_val)     如果子进程正常结束，则取值非0
 *     WEXITSTATUS(stat_val)   如果WIFEXITED非0，取值子进程的退出码
 *     WIFSIGNALED(stat_val)   如果子进程死于未捕获的信号，则取值非0
 *     WTERMSIG(stat_val)      如果WIFSIGNALED非0，取值目标信号的代码
 *     WIFSTOPPED(stat_val)    如果子进程意外终止，取值非0
 *     WSTOPSIG(stat_val)      如果WIFSTOPPED非0，返回信号代码
 *
 * @return 子进程的PID
 */
pid_t wait( int *stat_loc );

/**
 * 等到某个特定的子进程结束
 * @param pid 子进程的PID，如果指定为-1将返回任一子进程的信息
 * @param stat_loc 如果不是空指针，则用来存放状态信息
 * @param option 用于定制waitpid的行为
 */
pid_t waitpid( pid_t pid, int *stat_loc, int options );

/**
 * 检查某个子进程是否结束，立即返回
 * 
 * @return 如果目标子进程没有结束或者意外终止，返回0；否则返回子进程PID
 *         如果函数调用失败，返回-1并设置errno
 */
waitpid(child_pid, (int *) 0, WNOHANG);</pre>
<div class="blog_h2"><span class="graybg">僵尸进程</span></div>
<p>使用fork创建的子进程终止时，其与父进程的关联还会保持，直到<span style="background-color: #c0c0c0;">父进程也正常终止或者父进程调用wait</span>才会结束。在此之前，虽然子进程已经无事可做，但是其在进程表中的项不会被删除，其退出码需要被保存，以备后续父进程的wait调用，这种进程被称为僵尸进程（Zombie，也称为死defunct进程）。</p>
<p>如果父进程<span style="background-color: #c0c0c0;">异常终止</span>，子进程自动把PID=1的进程（init）作为自己的父进程，这类僵尸进程会一直保存在进程表中直到init发现并释放它。</p>
<div class="blog_h1"><span class="graybg"><a id="session"></a>会话</span></div>
<p>在Linux中，会话（Session）通常是指Shell会话，即会话的概念和Shell是分不开的。所谓Shell是用户访问Linux系统的接口，是用户与内核之间的桥梁。</p>
<p>在大部分的Linux发行版中，BASH是默认的Shell实现。每当你：</p>
<ol>
<li>本地打开Terminal窗口</li>
<li>通过SSH远程登录</li>
</ol>
<p>时，一个关联的新会话会被自动创建。不管本地还是远程登录，<span style="background-color: #c0c0c0;">用户都会得到一个与终端（Terminal）相关联的（Shell）进程，该进程作为Session Leader</span>，会话的ID就是该进程的PID。</p>
<p>你也可以编程式的创建新的会话，调用<pre class="crayon-plain-tag">pid_t setsid(void) </pre> 函数可以让当前进程（不管它是否为Shell）作为Session Leader，创建新的会话。如果当前进程已经是Session Leader则会出错。新的会话中只有一个进程，并且它没有关联终端，因此你需要对其输入、输出进行重定向。</p>
<div class="blog_h2"><span class="graybg">会话的终止</span></div>
<p>当终端被挂断（Hangup），即：</p>
<ol>
<li>对于本地Terminal，窗口被关闭</li>
<li>对于SSH终端，网络连接被断开</li>
</ol>
<p>时，Session Leader会接收到SIGHUP信号而退出。在Session Leader退出前，它会<span style="background-color: #c0c0c0;">向所有子进程也发送SIGHUP信号</span>，通常会导致会话中所有进程都结束掉。要想让某个子进程超越Session的生命周期而存活，你可以：</p>
<ol>
<li>让Session Leader主动退出，一般来说就是在Shell里调用exit/logout等命令。默认情况（Shell选项huponexit=off）下，主动退出不会发送SIGHUP给子进程</li>
<li>守护进程化：即所谓Double fork技巧，让Shell的子进程fork出孙子进程，在孙子进程中执行程序逻辑，而子进程立即退出（孙子进程变成孤儿进程）。由于Shell<span style="background-color: #c0c0c0;">只会将SIGHUP发送给直接子进程</span>，孙子进程就不会受到影响了</li>
<li>使用<pre class="crayon-plain-tag">setsid()</pre> 系统调用，让某个子进程变成新的Session的Leader，自立门户</li>
</ol>
<div class="blog_h2"><span class="graybg">进程组</span></div>
<p>顾名思义，进程组（Process group）就是包含了1~N个进程的分组。进程组的主要作用是利于信号的分发——当信号发送给进程组时，其内部的所有进程都会接收到信号。</p>
<p>一个会话中可以包含1~N个进程组，进程组不允许跨会话迁移，进程没有资格创建属于其它会话的进程组，进程也不能加入属于其它会话的进程组。</p>
<div class="blog_h2"><span class="graybg">终端</span></div>
<p>一个Session有且只有一个<a href="/io-faq#terminal">终端</a>，该终端称为控制终端（controlling terminal），改变Session关联的终端这一操作，只能由Session Leader完成。</p>
<p>终端的生命周期可能：</p>
<ol>
<li>与Session相同，这类终端是随着会话而创建的伪终端</li>
<li>与系统相同，这类终端是随着系统启动的，<span style="background-color: #c0c0c0;">init进程在会话结束后重启getty来监听该终端</span></li>
</ol>
<div class="blog_h1"><span class="graybg">信号</span></div>
<div class="blog_h2"><span class="graybg">信号的工作机制</span></div>
<p>软中断信号（简称信号，signal）用来通知进程发生了异步事件（通常是某种错误）。信号是进程之间（包括用户进程之间、用户进程与内核进程之间）进行通信的一种简单方式，信号不会给目标进程发送任何数据。</p>
<p>使用信号并挂起程序（例如pause调用）是Linux程序设计的一个重要部分，这意味着程序不需要总是在执行，在一个无限循环中检查某个事件是否发生，相反，它可以等待事件的发生。这种机制对于只有一个CPU的多任务环境非常重要，进程共享一个处理器，繁忙的循环会对系统性能造成极大的影响。</p>
<div class="blog_h3"><span class="graybg">信号产生时机</span></div>
<p>尽管用户可以通过命令手工发出信号，但是内核是主要的信号发出者。以下场景下会目标进程会收到信号：</p>
<ol>
<li>检测到一个可能出现的硬件故障，如电源故障</li>
<li>程序出现异常行为，比如尝试访问进程外部内存空间、尝试写入只读内存区域</li>
<li>用户从终端向目标程序发出某些指令（ctrl + z、ctrl + c等），终端进程会接收到对应信号</li>
<li>进程的一个子进程终止时</li>
</ol>
<div class="blog_h3"><span class="graybg">信号如何发送给目标进程</span></div>
<p>在进程的进程表项（内核通过进程表对进程进行管理，每个进程在进程表中占有一项）中，有一个<span style="background-color: #c0c0c0;">信号域</span>，信号域的<span style="background-color: #c0c0c0;">每一个slot对应一种信号</span>，对于同一种信号，进程无法知道在处理前来过多少个。</p>
<p>内核通过设置进程的信号域对应slot，来给进程发送信号。</p>
<p>当内核将信号发送给正在睡眠的进程时：</p>
<ol>
<li>如果进程处于可中断的睡眠状态，则唤醒之</li>
<li>如果进程处于不可中断的睡眠状态：则仅仅设置slot</li>
</ol>
<div class="blog_h3"><span class="graybg">进程何时检查是否收到信号</span></div>
<p>进程在以下场景下检查自己是否收到信号：</p>
<ol>
<li>进程即将从内核态返回到用户态时：进程在内核态运行时，信号不起作用，直到其将要返回用户态时才进行处理，信号处理函数在进程上下文中进行</li>
<li>进程即将进入或离开一个适当的低调度优先级睡眠状态时</li>
</ol>
<div class="blog_h3"><span class="graybg">进程如何处理收到的信号</span></div>
<p>进程接收到信号后，可以：</p>
<ol>
<li>忽略该信号</li>
<li>捕获该信号：当前进程继续执行前，调用一个用户定义的函数。这种处理机制类似于中断处理程序</li>
<li>让内核执行与该信号相关的默认动作，大部分信号的默认动作导致进程终止</li>
</ol>
<div class="blog_h2"><span class="graybg">谁接收信号</span></div>
<p>根据POSIX标准，<span style="background-color: #c0c0c0;">信号为进程而产生，但是仅仅其中一个线程可以接收信号并处理</span>。至于哪个线程负责处理信号，取决于实现。</p>
<p>对于Linux：</p>
<ol>
<li>信号可以针对进程的整体而产生，例如kill命令产生的信号</li>
<li>信号可以针对特定线程，例如SIGSEGV、SIGFPE信号，或者pthread_kill命令产生的信号</li>
<li>对于针对进程整体的信号，任何没有被信号阻塞的线程都可以处理之。如果有多个这样的线程（has the signal unblocked），内核会随机的选择一个</li>
</ol>
<p>对于pthreads：每个线程具有独立的信号栈设置（signal stack settings），但是新线程总是从父线程拷贝此设置</p>
<div class="blog_h2"><span class="graybg">常见信号列表（Ubuntu 14）</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 80px; text-align: center;"> 信号</td>
<td style="width: 30px; text-align: center;"> No.</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td> SIGHUP</td>
<td>1</td>
<td>
<p>如果进程通过终端运行，而终端忽然关闭后，进程将收到该信号。HUP是hang up的简写</p>
<p>在终端被关闭时，交互式的Shell会重新发送SIGHUP信号给所有任务（Jobs），不管是运行中的还是挂起（Stopped）的。挂起的任务还会收到SIGCONT信号，确保它们会处理SIGHUP。要阻止向某个任务发送SIGHUP，可以对其调用disown命令</p>
<p>对于BASH等Shell，调用exit、logout时，是否向所有任务发送SIGHUP取决于Shell选项：<pre class="crayon-plain-tag">shopt | grep huponexit</pre> ，该选项默认值是off，即不发送</p>
</td>
</tr>
<tr>
<td> SIGINT</td>
<td>2 </td>
<td>进程被中断（<span style="background-color: #c0c0c0;">int</span>errupted），当通过终端按ctrl + c导致进程接收到该信号</td>
</tr>
<tr>
<td> SIGQUIT</td>
<td>3 </td>
<td>与SIGINT类似，只是该信号是由ctrl + \，该信号会在终结进程时生成core dump（核心转储，即进程的内存映像，可以后续分析）</td>
</tr>
<tr>
<td> SIGILL</td>
<td>4 </td>
<td>非法（<span style="background-color: #c0c0c0;">Ill</span>egal）指令。程序执行了CPU无法理解的机器码时将收到该信号</td>
</tr>
<tr>
<td> SIGTRAP</td>
<td>5 </td>
<td>主要用于调试和程序跟踪</td>
</tr>
<tr>
<td> SIGABRT</td>
<td>6 </td>
<td>程序调用abort()函数时触发，导致程序紧急停止 </td>
</tr>
<tr>
<td> SIGBUS</td>
<td>7 </td>
<td>尝试以错误的方式访问内存时触发</td>
</tr>
<tr>
<td> SIGFPE</td>
<td>8 </td>
<td>程序中出现浮点数异常（<span style="background-color: #c0c0c0;">f</span>loating <span style="background-color: #c0c0c0;">p</span>oint <span style="background-color: #c0c0c0;">e</span>xception）时触发</td>
</tr>
<tr>
<td> SIGKIL</td>
<td>9 </td>
<td>立即终止进程，该信号不能被忽略。可以由ctrl + c引发</td>
</tr>
<tr>
<td> SIGUSR1</td>
<td>10 </td>
<td>供编程人员使用</td>
</tr>
<tr>
<td> SIGSEGV</td>
<td>11 </td>
<td>段错误，无效内存段访问，尝试越界访问内存（不是分配给当前进程的内存）时触发</td>
</tr>
<tr>
<td> SIGUSR2</td>
<td>12 </td>
<td>供编程人员使用</td>
</tr>
<tr>
<td> SIGPIPE</td>
<td>13 </td>
<td>当进程通过管道机制，将信息输出给目标进程的输入时，目标进程挂掉，当前进程收到此信号</td>
</tr>
<tr>
<td> SIGALRM</td>
<td>14 </td>
<td>进程调用alarm()函数，定时器到期后，系统通过该信号提示进程</td>
</tr>
<tr>
<td> SIGTERM</td>
<td>15 </td>
<td>这是一个一般的、用于“礼貌的”终结进程的信号。与SIGKIL不同，该信号可能被阻塞、处理或者忽略</td>
</tr>
<tr>
<td> SIGCHLD</td>
<td>17 </td>
<td>进程先前通过fork() 创建了子进程，这些子进程中的一个或者多个挂掉时，父进程收到此信号 </td>
</tr>
<tr>
<td> SIGCONT</td>
<td>18 </td>
<td>可以使由SIGSTOP导致休眠的进程恢复 </td>
</tr>
<tr>
<td> SIGSTOP</td>
<td>19 </td>
<td>如果系统发送该信号给进程，进程的状态将被保存，并且不再获得CPU周期</td>
</tr>
<tr>
<td> SIGTSTP</td>
<td>20 </td>
<td>Terminal SToP。本质上与SIGSTOP一样，该信号由终端操作ctrl + z导致</td>
</tr>
<tr>
<td> SIGTTIN</td>
<td>21 </td>
<td>当后台运行的进程尝试从stdin读取数据时，系统发送该信号给它。目标进程的典型响应是进入暂停，一直到进入前台时，SIGCONT信号到达 </td>
</tr>
<tr>
<td> SIGTTOU</td>
<td>22 </td>
<td>类似于SIGTTIN，当后台进程尝试写数据到stdout时触发</td>
</tr>
<tr>
<td> SIGURG</td>
<td>23 </td>
<td>带外数据（out-of-band，OOB）到达时，使用网络连接的进程接收到该信号。带外数据不使用与普通数据相同的通道。对于TCP协议，由于没有所谓带外通道，是通过URG位实现的。带外数据通常是一些紧急的重要数据</td>
</tr>
<tr>
<td> SIGXCPU</td>
<td>24 </td>
<td>系统发送该信号到使用CPU到达限制的进程 </td>
</tr>
<tr>
<td> SIGXFSZ</td>
<td>25 </td>
<td>系统发送该信号到尝试创建超过尺寸限制的文件的进程</td>
</tr>
<tr>
<td> SIGVTALRM</td>
<td>26 </td>
<td>与SIGALRM类似，但不通过真实时间计时，而是通过目标进程使用的的CPU时间计时</td>
</tr>
<tr>
<td> SIGPROF</td>
<td>27 </td>
<td>与SIGVTALRM类似，但是计时除了目标进程使用的CPU时间，而包括为了目标进程服务的系统代码执行时间</td>
</tr>
<tr>
<td> SIGIO</td>
<td>29 </td>
<td>亦即SIGPOLL。当有输入等待进程处理，或者输出通道可以供进程写入时，系统给进程发出该信号</td>
</tr>
<tr>
<td> SIGPWR</td>
<td>30 </td>
<td>当切换到紧急备用电源时，进程接收到该信号</td>
</tr>
<tr>
<td> SIGSYS</td>
<td>31 </td>
<td>未使用</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Linux信号相关API</span></div>
<div class="blog_h3"><span class="graybg">处理信号</span></div>
<p>通过signal可以设置信号的处理函数：</p>
<pre class="crayon-plain-tag">#include &lt;signal.h&gt;
/**
 * 设置信号的处理函数
 * @param sig 信号
 * @param func 处理回调函数
 * @return 返回先前的信号处理函数的指针，如果未定义信号处理函数返回SIG_ERR并设置errno为正数
 *         如果给出一个无效的信号，或者尝试处理不可捕获、不可忽略的信号（例如SIGKILL），则将errno设置为EINVAL
 */
typedef void (*signal_handler_t )( int );
signal_handler_t signal( int sig, signal_handler_t );

//两个特殊的信号处理函数：
// SIG_IGN 忽略信号
// SIG_DFL 恢复此信号的默认处理行为

#include &lt;unistd.h&gt;
/**
 * 导致当前进程暂停执行，直到接收到一个信号
 * 当暂停被一个信号中断时，返回-1并且设置errno为EINTR
 */
int pause( void );</pre>
<p>信号处理函数中调用某些函数是不安全的，例如printf，最好是在信号处理函数中设置一个标记，然后在主程序中检查标记再调用某些函数。下面是信号处理函数的例子：</p>
<pre class="crayon-plain-tag">#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
void ouch( int sig )
{
    printf( "OUCH! - I got signal %d\n", sig );
    //该信号处理函数恢复默认行为:停止程序
    signal( SIGINT, SIG_DFL );
}

int main()
{
    //进程启动后，设置信号的处理函数
    signal( SIGINT, ouch );
    while ( 1 )
    {
        //此循环会不停执行，除非接收到信号
        printf( "Hello World!\n" );
        sleep( 1 );
    }
}</pre>
<p>X/Open和UNIX规范推荐了更加健壮的信号编程接口：</p>
<pre class="crayon-plain-tag">#include &lt;signal.h&gt;
/**
 * 指定接收到sig信号后采取的动作
 * @param sig 处理的信号
 * @param act 需要指向的动作
 * @param oact 如果不为空，此函数调用前sig信号的处理动作被转储到该指针
 * @return 如果成功，返回0，失败返回-1，如果给出的信号无效或者对不允许忽略或者
 *         捕获的信号进行忽略或者捕获，则设置errno=EINVAL
 */
int sigaction( int sig, const struct sigaction *act, struct sigaction *oact );
typedef  void (*sa_handler_t)( int );
struct sigaction
{
    sa_handler_t sa_handler; //信号处理函数，包括SIG_DFL、SIG_IGN
    //一个信号集，在sa_handler被调用之前，此信号集中的信号不会传递给进程，
    //可以防止信号处理函数尚未执行完毕就接收到新信号并重入信号处理函数的情况
    //信号处理函数在指向过程中，可能被新的信号中断而再次调用，这不仅仅是递归调用
    //的问题，更牵涉到可重入（安全的进入和再次指向）的问题
    sigset_t sa_mask;
    //标记位：
    //SA_RESETHAND 表示处理函数调用后（入口第一句后），即重置默认处理函数（SIG_DFL）
    //SA_NOCLDSTOP 子进程停止后不产生SIGCHLD信号
    //SA_RESTART   重启可中断函数而不是给出EINTR错误。许多系统调用是可中断的，也就是
    //             说接收到信号后系统调用会返回一个错误并设置errno=EINTR
    //             以表示该调用因为信号而返回。设置该标记后，调用将重启而不是被信号中断
    //SA_NODEFER   捕获到信号时不将其加入到信号屏蔽掩码中。通常的做法：为防止同一信号不
    //             断到达，新接收到的信号会被加入到掩码中，直到处理函数指向完毕
    int sa_flags;
};

//向信号集中增加一个信号
int sigaddset( sigset_t *set, int signo );
//创建空白信号集
int sigemptyset( sigset_t *set );
//创建包含所有已定义信号的信号集
int sigfillset( sigset_t *set );
//从信号集中删除指定的信号
int sigdelset( sigset_t *set, int signo );
//判断信号是否为信号集的成员，如果是返回1否则返回0，如果给定的信号无效返回-1并设置EINVAL
int sigismember( sigset_t *set, int signo );
/**
 * 根据how指定的方式修改进程的信号屏蔽掩码
 * @param how
 *        SIG_BLOCK    将set加入到进程的掩码
 *        SIG_SETMASK  将进程的掩码设置为set
 *        SIG_UNBLOCK  从进程的掩码中删除set
 * @param set 新的信号屏蔽掩码，如果设置为空，仅仅是把当前信号屏蔽掩码保存到oset
 * @param oset 原先的信号屏蔽掩码
 * @return 如果成功返回0；如果how无效返回-1并设置errno=EINVAL
 *
 */
int sigprocmask( int how, const sigset_t *set, sigset_t *oset );

/**
 * 如果一个信号被进程阻塞，就不会传递给进程而停留在待处理状态。
 * 该函数可以查看阻塞的信号中那些处于待处理状态
 */
int sigpending( sigset_t *set );

/**
 * 挂起进程自己，等待信号集中某个信号到达
 * 如果接收到的信号终止了程序，该调用不会返回；否则返回-1并设置errno=EINTR
 */
int sigsuspend( const sigset_t *sigmask );</pre>
<p>下面是对前一个信号处理例子的改写：</p>
<pre class="crayon-plain-tag">#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
void ouch( int sig )
{
    printf( "OUCH! - I got signal %d\n", sig );
}
int main()
{
    struct sigaction act;
    act.sa_handler = ouch; //设置信号处理函数
    sigemptyset( &amp;act.sa_mask ); //创建空的信号屏蔽掩码
    act.sa_flags = SA_RESETHAND;
    sigaction( SIGINT, &amp;act, 0 );
    while ( 1 )
    {
        printf( "Hello World!\n" );
        sleep( 1 );
    }
}</pre>
<p>信号处理函数中会遇到可重入问题，下表列出可以被信号处理函数安全调用的函数，他们本身是可重入、或者本身不会再生成信号：<img class="size-full wp-image-6937 aligncenter" src="https://blog.gmem.cc/wp-content/uploads/2009/08/safe-func-in-sig-hdl.png" alt="safe-func-in-sig-hdl" width="98%" /></p>
<div class="blog_h3"><span class="graybg">发送信号</span></div>
<p>进程可以调用kill函数来向<span style="background-color: #c0c0c0;">包括其自身在内的进程</span>发送一个信号。如果进程没有发送目标信号的权限，对kill的调用就会失败，失败的常见原因是目标进程是由另外一个用户所拥有。下面的函数可以用来发送信号：</p>
<pre class="crayon-plain-tag">#include &lt;sys/types.h&gt;
#include &lt;signal.h&gt;
/**
 * 将信号发送给指定进程，如果成功返回0，失败返回-1并设置errno，errno可以为以下值：
 * EINVAL  给定的信号无效
 * EPERM   发送进程的权限不够，一般只能发送给同一用户的进程，超级用户可以发送信号给所有进程
 * ESRCH   目标进程不存在
 *
 */
int kill( pid_t pid, int sig );

#include &lt;unistd.h&gt;
/**
 * 在延迟seconds秒以后，发送一个SIGALRM信号
 * 每个进程只能有一个闹钟，因此后续的调用将导致重新计时并返回上一次闹钟设置的剩余秒数
 * 如果seconds设置为0则取消闹钟请求
 */
unsigned int alarm( unsigned int seconds );</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-signals-processes-and-sessions">Linux信号、进程和会话</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-signals-processes-and-sessions/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux命令知识集锦</title>
		<link>https://blog.gmem.cc/linux-command-faq</link>
		<comments>https://blog.gmem.cc/linux-command-faq#comments</comments>
		<pubDate>Tue, 15 Apr 2008 04:17:30 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Command]]></category>
		<category><![CDATA[Linux知识]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=2439</guid>
		<description><![CDATA[<p>Shell基本知识 Shell快捷键  快捷键 用途 ctrl + z 暂停当前程序，回到shell，注意当前任务不会被终止，只是被挂起（在jobs命令的输出中显示为Stopped）。例如，在执行vim编辑的时候，可以按Ctrl+Z挂起（stdout将显示jobnumber），回到Shell执行命令，然后输入fg继续vim编辑 注意：挂起的任务不再参与进程调度，要让一个任务在后台持续运行，可以使用 &#38; 后缀调用一个任务的命令行，或者在挂起后对其作业号运行bg命令 ctrl + c 停止当前程序，回到shell ctrl + d 发送EOF ctrl + r <a class="read-more" href="https://blog.gmem.cc/linux-command-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-command-faq">Linux命令知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">Shell基本知识</span></div>
<div class="blog_h2"><span class="graybg">Shell快捷键</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 15%; text-align: center;"> 快捷键</td>
<td style="text-align: center;">用途</td>
</tr>
</thead>
<tbody>
<tr>
<td>ctrl + z</td>
<td>
<p>暂停当前程序，回到shell，注意当前任务不会被终止，只是被挂起（在jobs命令的输出中显示为Stopped）。<br />例如，在执行vim编辑的时候，可以按Ctrl+Z挂起（stdout将显示jobnumber），回到Shell执行命令，然后输入fg继续vim编辑</p>
<p>注意：挂起的任务不再参与进程调度，要让一个任务在后台持续运行，可以使用 &amp; 后缀调用一个任务的命令行，或者在挂起后对其作业号运行bg命令</p>
</td>
</tr>
<tr>
<td>ctrl + c</td>
<td>停止当前程序，回到shell</td>
</tr>
<tr>
<td>ctrl + d</td>
<td>发送EOF</td>
</tr>
<tr>
<td>ctrl + r</td>
<td>快速搜索命令历史，比上下箭头高效</td>
</tr>
<tr>
<td>ctrl + s</td>
<td>使快速刷屏的终端界面静止下来</td>
</tr>
<tr>
<td>ctrl + q</td>
<td>恢复ctrl + s的造成的效果</td>
</tr>
<tr>
<td>ctrl + a</td>
<td>光标移动到行首</td>
</tr>
<tr>
<td>ctrl + e</td>
<td>光标移动到行尾</td>
</tr>
<tr>
<td>ctrl + u</td>
<td>从光标位置删除到行首</td>
</tr>
<tr>
<td>ctrl + k</td>
<td>从光标位置删除到行尾</td>
</tr>
<tr>
<td>backspace</td>
<td>向前删除一个字符</td>
</tr>
<tr>
<td>ctrl + d</td>
<td>向后删除一个字符</td>
</tr>
<tr>
<td>alt + backspace</td>
<td>向前删除一个单词</td>
</tr>
<tr>
<td>alt + d</td>
<td>向后删除一个单词</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">后台运行命令</span></div>
<pre class="crayon-plain-tag">#下面的命令将在后台运行
./command.sh &amp;
#下面的命令将不挂掉的在后台运行，标准输出被重定向到nohup.out，即使当前终端退出任然继续运行

#nohup命令可以忽略所有挂断（SIGHUP）信号
nohup ./command.sh &amp;

#忽略标准输出、错误。 0 1 2 分别表示标准输入、输出、错误，2&gt;&amp;1表示把标准错误重定向到标准输出，而前者已经重定向到空设备
nohup ./command &gt; /dev/null 2&gt;&amp;1 &amp;</pre>
<div class="blog_h1"><span class="graybg">Tmux</span></div>
<div class="blog_h2"><span class="graybg">子命令</span></div>
<pre class="crayon-plain-tag"># 列出当前Tmux服务中有哪些会话
tmux ls
# dev-120: 1 windows (created Fri Feb 23 19:57:35 2018) [180x49] (attached)

# 连接到会话
tmux a -t dev-120

# 命令
# 在当前位置插入新窗口
new-window -a</pre>
<div class="blog_h2"><span class="graybg">快捷键</span></div>
<p>所有快捷键必须在按下前缀键后才能使用，前缀键默认Ctrl + B</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">快捷键</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>"</td>
<td>垂直方向拆分面板</td>
</tr>
<tr>
<td>%</td>
<td>水平方向拆分面板</td>
</tr>
<tr>
<td>o</td>
<td>切换到当前窗口的下一个面板</td>
</tr>
<tr>
<td>PgUp</td>
<td>启用Page Up/Down滚动，按Q退出滚动模式</td>
</tr>
<tr>
<td>$</td>
<td>重命名当前会话</td>
</tr>
<tr>
<td>,</td>
<td>重命名当前窗口</td>
</tr>
<tr>
<td>.</td>
<td>修改窗口编号</td>
</tr>
<tr>
<td>s</td>
<td>列出所有会话，通过j，k，回车切换</td>
</tr>
<tr>
<td>c</td>
<td>创建新窗口</td>
</tr>
<tr>
<td>&lt;N&gt;</td>
<td>切换到第N-1个窗口</td>
</tr>
<tr>
<td>p</td>
<td>切换到上一个窗口</td>
</tr>
<tr>
<td>n</td>
<td>切换到下一个窗口</td>
</tr>
<tr>
<td>!</td>
<td>创建一个新窗口，并把当前面板置于其中</td>
</tr>
<tr>
<td>Space</td>
<td>切换面板布局</td>
</tr>
<tr>
<td>d</td>
<td>从当前会话断开，回到父Shell</td>
</tr>
<tr>
<td>D</td>
<td>选择一个会话以断开</td>
</tr>
<tr>
<td>Shift + Mouse1</td>
<td>选中文本，可以进行复制</td>
</tr>
<tr>
<td>:</td>
<td>
<p>输入命令，示例：set-option -g history-limit 100000</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">文件与目录命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">命令</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">ls</td>
<td>
<p>列出目录中的文件</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />ls [OPTION]... [FILE]...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a, --all 包含.号开始的文件<br />-A, --almost-all 包含除了.和..以外的任何文件<br />--author 与-l联用 打印文件的作者<br />-b, --escape 使用C-style转义来显示不可见字符<br />--block-size=SIZE 使用block size来显示尺寸<br />-B, --ignore-backups 忽略~结尾的备份文件<br />-c -lt:根据最后修改时间排序 -l: show 显示修改时间并按名称排序<br />-C 按列显示<br />--color[=always|never|auto] 输出语法高亮配置 <br />-d, --directory 显示目标，而不是其内容，不解引用符号链接<br />-f 禁止排序<br />-F, --classify 附加指示器(*/=&gt;@|) 到所有条目<br />--file-type 类似上面，只是不附加*<br />--format=WORD 格式：across -x, commas -m, horizontal -x, long -l, single-column -1, verbose -l, vertical -C<br />--full-time 类似-l --time-style=full-iso<br />-g 类似-l, 但是不显示所有者<br />--group-directories-first 在文件前显示目录，与--sort一起使用<br />-G, --no-group 在long listing时不显示组名<br />-h, --human-readable 与-l联用，使用易于人类阅读的方式显示尺寸<br />--si 与上面类似，但是计算倍数时使用1000而非1024<br />-H, --dereference-command-line Follow符号链接<br />--dereference-command-line-symlink-to-dir Follow指向目录的符号链接链接<br />--hide=PATTERN 隐藏所有匹配PATTERN的条目（被-a或-A覆盖）<br />-i, --inode 显示文件的索引号<br />-I, --ignore=PATTERN 隐藏所有匹配PATTERN的条目<br />-k 类似--block-size=1K<br />-l 使用长格式<br />-L, --dereference 显示符号链接的信息时，显示目标文件，而不是符号链接本身的信息<br />-n, --numeric-uid-gid 类似-l，但是列出用户和组标识符<br />-N, --literal 打印原始文件名<br />-o 类似-l，但是不列出组信息<br />-p, --indicator-style=slash 附加斜线到目录<br />-q, --hide-control-chars 打印?来代替控制字符<br />--show-control-chars 显示控制字符<br />-Q, --quote-name 在双引号中包含条目<br />-r, --reverse 排序时反转顺序（由降序改为升序）<br />-R, --recursive 递归的列出目录<br />-s, --size 打印文件实际分配尺寸，以块为单位<br />-S 根据文件尺寸排序<br />--sort=WORD 根据指定方式排序。无：-U, 扩展名：-X, 尺寸：-S,时间：-t,版本：-v<br />-t 根据修改时间排序<br />-T, --tabsize=COLS TAB的尺寸，默认8<br />-U 不进行排序<br />-w, --width=COLS 假设屏幕宽度<br />-x 按行列出条目<br />-X 根据扩展名排序 </p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 设置时间格式
ls -l --time-style '+%Y-%m-%d %H:%M:%S'
# 可以修改文件vim ~/.bashrc，添加以下内容：
# alias ll='ls -alF --time-style "+%Y-%m-%d %H:%M:%S"'
# 对所有以后登陆的Terminal均生效

# 根据修改时间降序排列
ll -t
# 根据修改时间升序排列
ll -tr
# 根据文件尺寸升序排列
ll -Sr

# 需要注意文件尺寸和实际分配空间可能不一致
# 下面生成的文件，尺寸是10G，但是实际分配空间仅4K
dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M
ls -lsh /tmp
# 4.0K -rw-r--r-- 1 root root 11G Jun 3 15:04 data.img</pre>
</td>
</tr>
<tr>
<td class="blog_h2">vdir</td>
<td>
<p>显示详细的目录列表， 与 ls -l 的效果类似
</td>
</tr>
<tr>
<td class="blog_h2">cp</td>
<td>
<p>拷贝文件到目标文件，或者拷贝若干文件到目录<br /><span style="background-color: #c0c0c0;">格式：</span><br />cp [OPTION]... [-T] SOURCE DEST<br />cp [OPTION]... SOURCE... DIRECTORY<br />cp [OPTION]... -t DIRECTORY SOURCE...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a, --archive 等价于 -dR --preserve=all<br /> --attributes-only 不拷贝文件内容，只拷贝其属性<br />-b, --backup 对每个DEST文件进行备份<br />-d 等价于 --no-dereference --preserve=links<br />-f, --force 如果DEST文件不能打开，移除其并重试，使用-n时无效<br />-i, --interactive 在覆盖DEST文件前进行提示<br />-H 仅跟随（解引用）命令行指定的SOURCE本身的符号连接，递归（-R）的子文件不跟随<br />-l, --link 硬链接而不是拷贝文件<br />-L, --dereference 总是跟随（解引用）SOURCE 的符号连接<br />-n, --no-clobber 不覆盖已经存在的DEST文件，覆盖-i<br />-P, --no-dereference 绝不跟随（解引用）SOURCE 的符号连接<br />-p 等价于 --preserve=mode,ownership,timestamps<br />--preserve[=ATTR_LIST] 维持指定的文件属性<br />--no-preserve=[ATTR_LIST] 清除指定的属性<br />-R, -r, --recursive 递归的拷贝目录<br />--remove-destination 尝试打开目标文件前，删除它，与-f对比<br />-s, --symbolic-link 创建符号连接，而不是拷贝<br />-S, --suffix=SUFFIX 指定备份文件的后缀，默认~<br />-t, --target-directory=DIRECTORY 拷贝所有源文件到目标目录<br />-T, --no-target-directory 将DEST作为普通文件看待，可用于覆盖目标目录<br />-u, --update 如果目标文件不存在，或者源文件时间更新，才执行拷贝</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
mkdir src &amp;&amp; mkdir src/sub &amp;&amp; mkdir dest
echo FILE1_SRC &gt; src/file1 &amp;&amp; echo SUBFILE1_SRC &gt; src/sub/subfile1
touch dest/file1

cp src/file1 dest/               #dest/file1被覆盖，但是inode维持不变
cp src/file1 dest/file1_src      #直接指定DEST为文件

#复制src目录及其所有内容到dest目录，如果src本身是一个指向目录的符号链接
#则进行解引用，如果src的子目录是符号链接，则仅仅在dest下创建相同的符号链接
cp -R -H src/ dest/
#与上一个类似，但是所有src子目录的符号链接也被解引用
cp -R -H src/ dest/</pre>
</td>
</tr>
<tr>
<td class="blog_h2">mv</td>
<td>
<p>文件移动命令，移动文件的位置，移动多个文件到目录中，或者将目录重命名
<p>该命令的行为：</p>
<ol>
<li>如果目标位置是当前文件系统，则执行rename系统调用，将inode关联到新的目录项，文件本身（inode，文件系统级别概念）没有任何变化</li>
<li>如果目标位置是其它文件系统，则实际上是拷贝数据并在目标文件系统创建inode。然后，旧文件被unlink（解除和目录项的关联），如果没有打开的文件描述符则删除inode</li>
</ol>
<p>进程打开文件后，系统会返回文件描述符给它。不管何种情况下，mv都不会导致这些已经打开的文件描述符失效。也就是mv不影响已经打开文件的进程使用文件</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />mv [OPTION]... [-T] SOURCE DEST<br />mv [OPTION]... SOURCE... DIRECTORY<br />mv [OPTION]... -t DIRECTORY SOURCE...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />--backup[=CONTROL] 对每一个DEST文件进行备份<br />-b 与上面类似，但是不支持指定参数<br />-f, --force 覆盖DEST前不进行提示<br />-i, --interactive 覆盖DEST前进行提示<br />-n, --no-clobber 不覆盖已经存在的文件<br />-S, --suffix=SUFFIX 备份文件的后缀<br />-t, --target-directory=DIRECTORY 移动所有文件到目标目录<br />-T, --no-target-directory 将DEST作为文件看待<br />-u, --update 只有当目标不存在，或者源比较新的时候，才移动</p>
<pre class="crayon-plain-tag">cd ~/Test
mkdir src &amp;&amp; mkdir src/sub &amp;&amp; mkdir dest
echo FILE1_SRC &gt; src/file1 &amp;&amp; echo SUBFILE1_SRC &gt; src/sub/subfile1

mv src/file1 dest      #把单个文件移动到目标目录
mv src/file1 destfile  #把单个文件移动到目标文件，如果目标文件已存在，则覆盖

mv src   dest          #把src目录移动作为dest的子目录
mv src/* dest          #把src目录下所有内容移动到dest目录下</pre>
</td>
</tr>
<tr>
<td class="blog_h2">rm</td>
<td>
<p>删除文件或者目录。默认情况下，该命令不会执行目录删除
<p><span style="background-color: #c0c0c0;">格式：</span><br />rm [OPTION]... FILE...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-f, --force 忽视不存在的文件或者参数，不进行提示<br />-i 删除每个文件前提示<br />-I 删除超过3个文件时，提示一次<br />--one-file-system  递归删除时，遇到不再同一文件系统上目录时跳过<br />-r, -R, --recursive 递归的移除目录及其内容<br />-d, --dir 移除空目录</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
touch file1 &amp;&amp; touch file2 &amp;&amp; touch file3
mkdir subdir &amp;&amp; touch subdir/file4

rm file1 file2     #删除两个文件
rm -r subdir       #删除子目录及其内容
rm -r *            #清空当前目录
rm -r /*           #尝试删除Linux目录树下所有文件
rm -- -foo         #移除 -开头的文件</pre>
</td>
</tr>
<tr>
<td class="blog_h2">rmdir</td>
<td>
<p>删除空目录
<p><span style="background-color: #c0c0c0;">格式：</span><br />rmdir [OPTION]... DIRECTORY...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />--ignore-fail-on-non-empty 不提示非空目录的失败<br />-p, --parents 删除祖先目录<br />-v, --verbose 打印冗余信息</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
mkdir -p ed1/ed2/ed3

rmdir -p ed1            #失败，目录非空
rmdir -p ed1/ed2/ed3    #OK

mkdir ed1 &amp;&amp; mkdir ed2 &amp;&amp; mkdir ed3
rmdir ed1 ed2 ed3</pre>
</td>
</tr>
<tr>
<td class="blog_h2">mkdir</td>
<td>
<p>创建目录或者目录树
<p><span style="background-color: #c0c0c0;">格式：</span><br /> mkdir [OPTION]... DIRECTORY...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-m, --mode=MODE 设置文件的模式，类似于chmod中的模式<br />-p, --parents 创建必要的父目录<br />-v, --verbose 打印冗余信息</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
mkdir -m 777 tmp         #创建一个权限大开的目录
#在Ubuntu14上，新创建的目录权限为775
mkdir -p dir/sub         #自动创建缺少的父目录</pre>
</td>
</tr>
<tr>
<td class="blog_h2">chmod</td>
<td>修改文件权限，参见：<a href="/linux-file-and-privileges" target="_blank">Linux文件和权限</a></td>
</tr>
<tr>
<td class="blog_h2">namei</td>
<td>
<p>显示指定路径上，所有节点的权限：
<pre class="crayon-plain-tag">namei -om ~/Scripts/apache-init.sh 
# f: /home/alex/Scripts/apache-init.sh
#  drwxr-xr-x root root /
#  drwxr-xr-x root root home
#  drwxr-xr-x alex alex alex
#  drwxrwxr-x alex alex Scripts
#  -rwxrwxr-x alex alex apache-init.sh </pre>
</td>
</tr>
<tr>
<td class="blog_h2">umask</td>
<td>设定创建文件时权限的缺省权限</td>
</tr>
<tr>
<td class="blog_h2">chown</td>
<td>修改一个或多个文件的所有权</td>
</tr>
<tr>
<td class="blog_h2">chgrp</td>
<td>修改一个或多个文件的所属组</td>
</tr>
<tr>
<td class="blog_h2">chattr</td>
<td>
<p>修改Linux文件系统上的文件属性
<p><span style="background-color: #c0c0c0;">格式：</span><br />chattr [ -RVf ] [ -v version ] [ mode ] files...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-R 递归的修改目录及其内容的属性<br />-f 忽略大部分错误信息<br />-v 设置文件的版本号</p>
<p><span style="background-color: #c0c0c0;">模式：</span></p>
<p>mode：+-=效果类似于chmod，后面可以附加以下标记位：</p>
<p>a：文件只能被附加内容<br />c：文件被压缩<br />d：no dump<br />e：扩展格式<br />i：文件不可变，即使ROOT也不能改变其内容<br />j：数据日志<br />s：安全删除（删除后以0填充）<br />t：no ail-merging<br />u：禁止删除<br />A：禁止atime更新<br />D：同步目录更新<br />S：同步更新<br />T：目录层次的顶端<br />下面的属性为只读，不能被chattr修改<br />h：巨大文件<br />E：压缩错误<br />I：被索引的目录<br />X：压缩原始访问<br />Z：被压缩的脏文件</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
touch  file.tar.gz

#添加一个压缩属性
chattr +c file.tar.gz
#lsattr显示：
#--------c----e-- ./file.tar.gz</pre>
</td>
</tr>
<tr>
<td class="blog_h2">lsattr</td>
<td>
<p>列出Linux文件系统上文件的属性
<p><span style="background-color: #c0c0c0;">格式：</span><br />lsattr [ -RVadv ] [ files... ]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-R 递归的列出目录及其内容的属性<br />-a 列出目录中所有文件<br />-d 列出目录，而不是其中的文件<br />-v 显示文件版本</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~
ls Test        #列出Test下文件的属性
#输出--------c----e-- Test/file.tar.gz
ls -d Test     #列出目录Test本身的属性
#输出-------------e-- Test</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ln</td>
<td>
<p>创建文件链接，TARGET为已经存在的被链接的文件
<p><span style="background-color: #c0c0c0;">格式：</span><br />ln [OPTION]... [-T] TARGET LINK_NAME <br />ln [OPTION]... TARGET <br />ln [OPTION]... TARGET... DIRECTORY<br />ln [OPTION]... -t DIRECTORY TARGET...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />--backup[=CONTROL] 创建目标文件的备份<br />-b 与上面类似<br />-d, -F, --directory 允许超级用户硬链接目录<br />-f, --force 移除已经存在的DEST文件<br />-i, --interactive 提示用户是否删除目标<br />-L, --logical 为符号链接引用创建硬链接<br />-n, --no-dereference 按照普通文件的方式对待是符号链接的目标<br />-P, --physical 创建硬链接<br />s, --symbolic 创建符号链接<br />-S, --suffix=SUFFIX 备份文件后缀<br />-T, --no-target-directory 将链接名称作为普通文件看待<br /><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">cd ~/Test
mkdir target &amp;&amp; mkdir target/tgtsub  &amp;&amp; touch target/tgtfile

ln -s target target.ln                #创建target目录的符号链接
ln -s target.ln target.ln.ln          #创建符号链接的符号链接

ln -P target/tgtfile  tgtfile.pln     #创建文件的硬链接
rm target/tgtfile                     #文件tgtfile.pln仍然有效
ln -P target target.pln               #出错，不得对目录进行硬链接</pre>
</td>
</tr>
<tr>
<td class="blog_h2">readlink</td>
<td>
<p>显示符号连接所指向的文件
</td>
</tr>
<tr>
<td class="blog_h2">find</td>
<td>
<p>在目录层次中搜索匹配的文件</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]</p>
<p><span style="background-color: #c0c0c0;">基本选项：</span><br />-H -L -P 三个选项用于处理符号链接。<br />-P 从不跟随（解引用）符号链接，这是默认行为。当检查符号链接的属性或者打印其信息时，针对符号链接本身而不是其链接目标<br />-L 跟随（解引用）符号链接<br />-H 不跟随（解引用）符号链接，除了命令行参数指定的符号链接文件<br />-D debugoptions 打印诊断信息，调试选项使用逗号分隔<br />-Olevel 启用查询优化，启用后测试表达式的顺序将会被调整，以提高执行速度<br />　　0 与级别1相同<br />　　1 默认优化级别，表达式被重新排序，使与文件名称相关的表达式（-name、-regex）优先执行<br />　　2 任何-type、-xtype测试在-name、-regex后执行，但是被提前到需要从inode获取信息的表达式之前<br />　　3 完全的基于成本的优化器，低成本的测试被优先执行</p>
<p><span style="background-color: #c0c0c0;">表达式由选项、测试、动作三部分组成</span></p>
<p><span style="background-color: #c0c0c0;">表达式选项：</span><br />所有选项的返回值都是true，除了-daystart、-follow、-regextype，这些选项影响所有测试，选项最好放在表达式的最前面<br />-daystart 以自然日的开始点，而不是24小时前来度量时间（-amin, -atime, -cmin, -ctime, -mmin, -mtime）<br />-depth -d 在处理目录本身之前，先处理其内容<br />-ignore_readdir_race 正常情况下，如果find命令stat文件失败时（比如在find期间目标文件被删除）将发出一个错误信息，指定该选项择不会提示<br />-maxdepth levels 向下搜索子目录的最大深度<br />-mindepth levels 对于小于该选项的文件不执行任何测试或者动作，指定为1，表示处理除了命令行参数以外的所有文件<br />-mount 不去向下搜索位于其它文件系统上的文件<br />-noignore_readdir_race <br />-noleaf 禁止在非UNUX文件系统，MS-DOS系统，CD-ROM文件系统中进行最优化查找<br />-regextype type 改变-regex、-iregex 测试所使用的正则式类型，默认emacs，其它可以是posix-awk、posix-basic、posix-egrep、posix-extended<br />-warn, -nowarn 打开或者关闭警告信息</p>
<p><span style="background-color: #c0c0c0;">表达式测试：</span><br />数字参数可以设置为以下形式：+n 多于n；-n 少于n, n 等于n<br />-amin n 当前文件在n分钟前被访问过<br />-anewer file 当前文件的访问时间比file的修改时间更晚<br />-atime n 文件在n*24小时前被访问过，-atime +1表示多于1个24小时之前被最后访问过，即2天前被最后访问过<br />-cmin n 文件的状态在n分钟前改变过<br />-cnewer file 当前文件状态的改变时间比file的修改时间更晚<br />-ctime n 文件的状态在n*24小时前改变过<br />-empty 文件是空白的，或者是空目录<br />-executable 文件时刻执行的、或者命令是可搜索的<br />-false 总返回false<br />-fstype type 文件处于type文件系统上<br />-gid n 文件的组ID（数字表示）是n<br />-group gname 文件的组名称是gname<br />-ilname pattern 与-lname类似，但是大小写不敏感<br />-iname pattern 与-name类似，但是大小写不敏感<br />-inum n 文件的inode为n<br />-iregex pattern 与-regex类似，但是大小写不敏感<br />-iwholename pattern 与wholename类似，但是大小写不敏感<br />-links n 文件有n个链接<br />-lname pattern 文件是一个符号链接，并且其内容匹配pattern<br />-mmin n 当前文件在n分钟前被修改过<br />-mtime n 文件在n*24小时前被修改过<br />-name pattern 文件的基本名称（去除目录部分）匹配pattern<br />-newer file 当前文件的修改时间比file的修改时间更晚<br />-newerXY reference 比较当前文件与reference的时间戳，reference是一个文件名或者绝对时间，XY为其它字母的占位符，这些占位符用于表示reference的什么字段被用于比较：<br />　　a 访问时间<br />　　B 创建时间<br />　　c inode状态改变时间<br />　　m 修改时间<br />　　t reference被解释为绝对时间<br />-nogroup 没有组与文件的数字GID对应<br />-nouser 没有用户与文件的数字UID对应<br />-path pattern 文件名称匹配pattern<br />-perm mode 文件权限比特位精确匹配<br />-perm -mode 所有权限比特位被设置给当前文件<br />-perm /mode 任一权限比特位被设置给当前文件<br />-readable 匹配可读的文件<br />-regex pattern 文件名匹配正则式<br />-samefile name 当前文件与name指定的文件具有相同的inode<br />-size n[cwbkMG] 文件的尺寸为n。b表示512字节块；c表示字节；w表示双字节；k表示KB；M表示MB；G表示GB<br />-true 总是真<br />-type c 文件类型匹配。 b块；c字符；d目录；p命名管道；f普通文件；l符号链接；s套接字文件<br />-uid n 文件的数字UID是n<br />-used n 文件在其状态改变后n天被访问过<br />-user uname 文件的所有者用户名为uname<br />-wholename pattern 同-path<br />-writable 文件是可写入的<br />-xtype c 与-type类似，除了对于符号链接，如果-H -P被指定，与目标文件进行比较；如果-L被指定，则c为l是返回true</p>
<p><span style="background-color: #c0c0c0;">表达式动作：</span><br />-delete <br />删除匹配的文件，如果删除成功，返回true，否者打印错误信息，如果删除失败，find的退出状态为非0<br />-exec command ; <br />执行一个命令，如果命令的退出状态为0，返回true。在此动作之后的所有命令行参数被认为是command的参数，直到遇到分号（;），{}被替换为当前文件的名称<br />-exec command {} + <br />与上面类似，但是匹配的文件名将作为command的尾部参数，command执行的次数要远小于匹配文件数<br />-execdir command ; -execdir command {} + <br />与上面类似，但是命令将在找到文件的子目录中执行<br />-fls file <br />返回true。类似于ls，但是使用-fprint格式输出到目标文件<br />-fprint file <br />返回true。打印完整的文件名到文件，如果file不存在，会自动创建（即使测试不匹配），否则会被截断，文件/dev/stdout、/dev/stderr被作为标准输出、标准错误特别处理<br />-fprint0 file <br />返回true。类似 -print0，但是使用-fprint格式输出到目标文件<br />-fprintf file format <br />返回true。类似-fprint和-printf的组合<br />-ls <br />返回true; 以ls -dils形式打印到标准输出，块的单位为1KB，如果环境变量POSIXLY_CORRECT被设置，块单位为512B<br />-ok command ; <br />类似于-exec，但是会提示用户是否执行，如果用户同意，则执行之，否则返回false，如果命令运行，其标准输入重定向到/dev/null<br />-okdir command ; <br />类似-execdir和-ok组合<br />-print <br />返回true。打印完整的文件名到标准输出并换行<br />-print0 <br />返回true。打印完整的文件名到标准输出并附加null字符，便于处理文件名中包含换行符或者其他空白字符的情况<br />-printf format 返回true。在标准输出上打印format，支持\转义和%指令，列宽度和精度可以使用C语言printf函数风格指定，详见man手册<br />-prune <br />返回true; 如果文件是目录，不向下处理其内容<br />-quit <br />立即退出</p>
<p><span style="background-color: #c0c0c0;">表达式组合操作符：</span><br />　　( expr ) 强制优先级，由于括号在shell中是特殊字符，可能需要进行转义处理<br />　　! expr 取反<br />　　expr1 expr2 并且<br />　　expr1 -a expr2 并且<br />　　expr1 -o expr2 或者<br />　　expr1 , expr2 两个表达式都被计算，但是返回第二个表达式的值</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">find ~/ -name '*.txt' #查找txt文件
#在/tmp目录下寻找名为core的文件，并删除之
find /tmp -name core -type f -print | xargs /bin/rm -f
#在/tmp目录下寻找名为core的文件，并删除之，能正确处理文件名中的特殊字符
find /tmp -name core -type f -print0 | xargs -0 /bin/rm -f
#对当前目录下所有普通文件执行file命令，以获取其类型，注意;被转义，{}被括号保护
find . -type f -exec file '{}' \;
#遍历整个文件系统一次，输出setuid文件到suid.txt，输出大文件到big.txt
find / \
\( -perm -4000 -fprintf /root/suid.txt %#m %u %p\n \) , \
\( -size +100M -fprintf /root/big.txt %-10s %p\n \)
#在当前用户家目录中查找最近24小时内被修改过的文件
find $HOME -mtime 0
#搜索可执行却不可读的文件
find /sbin /usr/sbin -executable \! -readable -print
#搜索当前目录下权限模式为664的文件并打印
find . -perm 664 -print 
#搜索当前目录下权限模式包含了664所指定的权限位的文件
find . -perm -664
#搜索可以被某个人写入的文件

#删除core开头的文件，{}表示用匹配的文件路径名替换
find ~/ -name 'core*' -exec rm '{}' \; 

# 显示当前目录下包含的文件总数
find . -type f | wc -l

# 显示当前目录的所有直接子目录或文件
find . -maxdepth 1</pre>
</td>
</tr>
<tr>
<td class="blog_h2">touch</td>
<td>更新文件被存取或修改的时间，如果不存在文件则创建</td>
</tr>
<tr>
<td class="blog_h2">basename</td>
<td>从文件名中去掉路径信息，只打印出文件名</td>
</tr>
<tr>
<td class="blog_h2">dirname</td>
<td>从带路径的文件名中去掉文件名，只打印出路径信息</td>
</tr>
<tr>
<td class="blog_h2">cksum</td>
<td>sum, cksum, md5sum, sha1sum等均可用来生成校验和，验证文件的完整性</td>
</tr>
<tr>
<td class="blog_h2">shred</td>
<td>用随机字符填充文件，使得文件无法恢复</td>
</tr>
<tr>
<td class="blog_h2">mktemp</td>
<td>使用一个唯一的文件名来创建一个临时文件</td>
</tr>
<tr>
<td class="blog_h2">file</td>
<td>
<p>确定文件类型的工具
<p><span style="background-color: #c0c0c0;">格式：</span><br />file[OPTIONS...] [FILE...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-b,--brief 不显示文件名字<br />-f,--files 读取待测试的文件<br />-F,--seperator 设置分隔符，默认:<br />-i,--mime 显示文件的MIME类型<br />-L,--dereference 显示符号链接所指向文件信息<br />-h,--no-dereference</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">#显示当前目录下所有文件的类型
file *
#仅显示类型，不显示名称
file --brief 1.txt
#显示文件的MIME类型
file --mime 1.txt</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">时间与日期相关命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">date</td>
<td>
<p>打印日期和时间到 stdout
<p><span style="background-color: #c0c0c0;">格式：</span><br />date [OPTION]... [<span style="background-color: #ff0000;">+</span>FORMAT]<br />date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-d, --date=STRING 显示字符串所代表的日期，而非当前日期<br />-r, --reference=FILE 显示文件的最后修改日期时间<br />-R, --rfc-2822 使用RFC 2822格式输出时间，例如：Mon, 07 Aug 2006 12:34:56 -0600<br />--rfc-3339=TIMESPEC 使用RFC 3339格式输出日期时间<br />-s, --set=STRING 根据字符串所代表的时间来设置<br />-u, --utc, --universal 打印标准时间</p>
<p><span style="background-color: #c0c0c0;">日期时间格式：</span><br />%% 转义%<br />%a 本地化的周几简称，例如Sun<br />%A 本地化的周几完整名称，例如Sunday<br />%b 本地化的月份简称，例如Jan<br />%B 本地化月份完整名称，例如January<br />%c 本地化日期时间，例如Thu Mar 3 23:05:25 2005<br />%C 世纪<br />%d 月中的第几天，例如01<br />%D 日期，即%m/%d/%y<br />%e 月中的第几天，空格补白，即%_d<br />%F 日期，即%Y-%m-%d<br />%h 即%b<br />%H 小时(00..23)<br />%I 小时(01..12)<br />%j 年中的第几天(001..366)<br />%k 小时( 0..23)<br />%l 小时( 1..12)<br />%m 月份(01..12)<br />%M 分钟(00..59)<br />%n 新行<br />%N 纳秒(000000000..999999999)<br />%p 本地化的AM/PM，如果没有留空<br />%P 类似%p,小写<br />%r 本地化的12小时制时间(11:11:04 PM)<br />%R 24小时时间 %H:%M<br />%s 从1970-01-01 00:00:00 UTC开始的秒数<br />%S 秒(00..60)<br />%t 制表<br />%T 时间，即%H:%M:%S<br />%u 周中的第几天，周一为1(1..7)<br />%U 年中的第几周，周日为第一天(00..53)<br />%V ISO年中的第几周，周一为第一天 (01..53)<br />%W 年中的第几周，周一为第一天(00..53)<br />%x 本地化日期展示形式(12/31/99)<br />%X 本地化时间展示形式(23:13:48)<br />%y 年度最后2位(00..99)<br />%Y 年度<br />下面的符号可以紧随%后<br />- 不要补白字段<br />_ 使用空格补白<br />0 使用0补白<br />^ 如果可能，使用大写<br /># 如果可能，使反转大小写</p>
<div><span style="background-color: #c0c0c0;">举例：</span></div>
<pre class="crayon-plain-tag">#注意格式的+号
date "+%Y%m%d%H%M%S"
#输出20150510165155

date '+%s'
#输出1970-01-01到当前时间点消逝的秒数

date -R
#输出Fri, 15 May 2015 09:30:05 -0400
#当地时间9点30分，比世界时晚4小时（西四区）
#北京比世界时早8小时（东八区）</pre>
</td>
</tr>
<tr>
<td class="blog_h2">zdump</td>
<td>
<p>查看特定时区的当前时间
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#显示中国标准时间
zdump PRC</pre>
</td>
</tr>
<tr>
<td class="blog_h2">time</td>
<td>
<p>输出统计出来的命令执行的时间
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">time ls  &gt; /dev/null
#输出内容：
#real    0m0.002s  真实世界的时间
#user    0m0.001s  用户态时间
#sys     0m0.002s  内核态时间</pre>
</td>
</tr>
<tr>
<td class="blog_h2">cal</td>
<td>从 stdout 中输出一个格式比较整齐的日历</td>
</tr>
<tr>
<td class="blog_h2">sleep</td>
<td>指定需要暂停的秒数并休眠当前进程</td>
</tr>
<tr>
<td class="blog_h2">usleep</td>
<td>Microsleep 睡眠微秒</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">未分类的基本命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">xargs</td>
<td>
<p>从标准输入解析并执行命令行
<p>xargs从标准输入读取数据，使用空白符或者换行符分隔成多个条目（标准输入的空白行被忽略），并执行命令<br />如果不指定命令，则默认使用/bin/echo。<strong>读取到的条目作为参数附加在命令初始参数（initial-arguments）后面</strong><br />由于UNIX文件名可以包含空白符甚至换行符，因此默认选项往往导致错误，最好使用-0选项<br />如果命令调用的退出状态是25，则xargs立即中止读入任何标准输入并在stderr显示错误信息</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br /><strong>xargs</strong> <br />　　[-0prtx] [-E eof-str] [-e[eof-str]] [--eof[=eof-str]] [--null] <br />　　[-d delimiter] [--delimiter delimiter] [-I replace-str] [-i[replace-str]] <br />　　[--replace[=replace-str]] [-l[max-lines]] [-L　max-lines] <br />　　[--max-lines[=max-lines]] [-n max-args] [--max-args=max-args] <br />　　[-s max-chars] [--max-chars=max-chars] [-P max-procs] [--max-procs=max-procs] <br />　　[--interactive] [--verbose] [--exit]　[--no-run-if-empty] [--arg-file=file] <br />　　[--show-limits] [--version] [--help]<strong> [command [initial-arguments]]</strong></p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a --arg-file 从文件而不是stdin读取条目<br />-0 --null 使用null字符而不是空白符分隔条目，并且所有字符（包括引号和反斜杠）作为字面值看待 <br />-d --delimiter 指定条目的分隔符为指定的字符，不支持多字节字符<br />-E eof-str 设置EOF字符串，如果该字符串作为输入的一行出现，则剩余的输入被忽略<br />-I replace-str 替换initial-arguments中的replace-str为从stdin读取到的名称<br />-L max-lines 每一个命令行最多使用的非空白行数<br />-n --max-args 每一个命令行最多使用的参数个数<br />-r --no-run-if-empty 如果输入没有非空白行，则不执行命令 <br />-s --max-chars 每个命令行最多使用的字符数<br />-t --verbose 在执行命令前打印之<br />-x --exit 如果字符数超过设定（-s）则退出s<br />-P --max-procs 同时最多运行的进程数，默认1，如果设置为0，则尽可能运行更多地进程，须与-n联用，否则只会有开启一个进程</p>
<p><span style="background-color: #c0c0c0;">退出状态：</span><br />　　0 执行成功<br />　　123 如果任何命令返回1-125之间的状态码<br />　　124 如果命令返回255状态码<br />　　125 如果命令被信号杀死<br />　　126 如果命令无法执行<br />　　127 如果命令不存在<br />　　1 发生其它错误</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#查找目录/tmp中的core文件，并且删除之，能够正确处理带有空白符的文件名
find /tmp -name core -type f -print0 | xargs -0 /bin/rm -f
#输出系统中所有的用户名 
cut -d: -f1 &lt; /etc/passwd | sort | xargs echo

# 此命令可以用来去除（trim）首尾空白
echo "   hello  " | xargs</pre>
</td>
</tr>
<tr>
<td class="blog_h2">
<p>env
</td>
<td>
<p>在修改的环境下（即一系列修改后的环境变量）运行一个程序</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-i 从空白环境开始<br />-u 取消环境变量的值</p>
</td>
</tr>
<tr>
<td class="blog_h2">printenv</td>
<td>显示出所有的环境变量</td>
</tr>
<tr>
<td class="blog_h2">which</td>
<td>给出命令的完整路径</td>
</tr>
<tr>
<td>locate</td>
<td>
<p>给出指定文件的完整位置，示例：</p>
<pre class="crayon-plain-tag">locate nginx.conf
# /usr/conf/nginx.conf</pre>
</td>
</tr>
<tr>
<td class="blog_h2">whereis</td>
<td>给出命令的完整路径、man页的完整路径</td>
</tr>
<tr>
<td class="blog_h2">chroot</td>
<td>改变根目录来运行某个命令</td>
</tr>
<tr>
<td class="blog_h2">pivot_root</td>
<td>
<p>改变根文件系统，将当前进程的根文件系统移动到其它目录，并将另外一个目录作为新的根文件系统
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">pivot_root new_root put_old</pre>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">cd new_root
pivot_root . put_old
exec chroot . command</pre>
<p> 需要注意，chroot必须在新、旧根目录都可用。pivot_root可能不会自动为当前Shell chroot</p>
</td>
</tr>
<tr>
<td>man</td>
<td>
<p>查看系统手册，可以查看陈故乡、工具、编程函数等各类对象的手册。</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">man [section] page
# 示例
man  2 clone</pre>
<p>手册是分为多个段（Section）的，各个段中可以具有名字相同的page。当你在网络上看到namespaces(7)这样的Man Page链接时，就知道目标是在段7的namespaces页面。</p>
<p>段的定义如下：</p>
<ol>
<li>可执行程序或Shell命令</li>
<li>系统调用（内核提供的函数）</li>
<li>库调用（编程库中提供的函数）</li>
<li>特殊文件，主要在/dev/...这些页面</li>
<li>文件格式和约定，例如/etc/passwd</li>
<li>游戏</li>
<li>杂项</li>
<li>系统管理命令，通常仅供root使用</li>
<li>内核例程（非标准）</li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">作业控制命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">at</td>
<td>作业控制命令， 用来在指定时间执行给定的命令集合</td>
</tr>
<tr>
<td class="blog_h2">crontab</td>
<td>
<p>维护某个用户的CRON守护程序列表。<br />crontab可以用来添加、删除、列出CRON守护程序，每个用户有自己的crontab定义，一律位于目录/var/spool/cron/crontabs中，通常不需要直接编辑这些文件<br />如果配置文件/etc/cron.allow存在，那么只有其中列出的用户才能使用crontab<br />如果配置文件/etc/cron.deny存在，那么其中列出的用户不能使用crontab</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />crontab [ -u user ] file<br />crontab [ -u user ] [ -i ] { -e | -l | -r }</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-u 指定crontab所属的用户，每个用户都有独立的crontab，里面的条目都以该用户为身份执行<br />-l 列出crontab的内容<br />-r 移除当前crontab<br />-e 使用默认编辑器编辑crontab</p>
<p><span style="background-color: #c0c0c0;">桌面应用支持：</span></p>
<p>守护程序脚本中需要添加：<pre class="crayon-plain-tag">export DISPLAY=:0.0</pre></p>
<p><span style="background-color: #c0c0c0;">模拟执行：</span></p>
<p>如果你的脚本在命令行中运行正常，但是在crontab中不能运行，可能是两种环境下的环境变量有差异。参考下面的方法立即模拟crontab的执行：</p>
<p>首先导出crontab的环境变量：</p>
<pre class="crayon-plain-tag">* * * * *  /usr/bin/env &gt; /root/cron-env</pre>
<p>然后模拟执行：</p>
<pre class="crayon-plain-tag">/usr/bin/env -i $(cat /root/cron-env) /my/crontab/item.sh</pre>
</td>
</tr>
<tr>
<td class="blog_h2">cron</td>
<td>
<p>计划任务调度器
</td>
</tr>
<tr>
<td class="blog_h2">jobs</td>
<td>查看所有后台运行的作业，可以看到jobnumber</td>
</tr>
<tr>
<td class="blog_h2">batch</td>
<td>在系统平均载量降到0.8 以下时执行一次性的任务</td>
</tr>
<tr>
<td class="blog_h2">nice</td>
<td>使用修改后的优先级来运行一个后台作业。 优先级从19(最低)到-20(最高)。 只有root 用户可以设置负的优先级</td>
</tr>
<tr>
<td class="blog_h2">nohup</td>
<td>保持一个命令的运行，即使用户退出Terminal也不会中断其执行（不会收到SIGHUP信号）</td>
</tr>
<tr>
<td class="blog_h2">pidof</td>
<td>取得一个正在运行的作业的PID</td>
</tr>
<tr>
<td class="blog_h2">cron</td>
<td>任务调度程序</td>
</tr>
<tr>
<td class="blog_h2">watch</td>
<td>
<p>以指定的时间间隔来重复运行一个命令，可以查看程序输出的变化</p>
<p><span style="background-color: #c0c0c0;">格式：</span><pre class="crayon-plain-tag">watch [options] command</pre></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-d 高亮显示两次运行的结果变化<br />-n 运行间隔，单位秒<br />-t 不显示头信息，包括运行间隔，运行的命令等</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 持续监控软中断
watch -d -n 1 'cat /proc/softirqs'</pre>
</td>
</tr>
<tr>
<td class="blog_h2">fg</td>
<td>将后台中的命令调至前台继续运行。如果后台中有多个命令，可以用 fg %jobnumber将选中的作业调到前台</td>
</tr>
<tr>
<td class="blog_h2">bg</td>
<td>将作业调度到后台执行，如果有多个后台作业处于暂停（ctrl + z将导致暂停）状态，可以用bg %jobnumber将选中的作业调度执行</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">文本处理命令</span></div>
<div class="blog_h2"><span class="graybg">简单命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">sort</td>
<td>
<p>将文件的每一行作为一个单位，按ASCII顺序进行比较，并按升序输出
<p><span style="background-color: #c0c0c0;">格式：</span><br />sort [OPTION]... [FILE]...<br />sort [OPTION]... --files0-from=F</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-u 在输出中去除重复行<br />-r 按降序排列<br />-o 将结果输出到目标文件，输出到原文件时用于替代重定向<br />-n 按照数值来排序<br />-t 按列排序，列的分隔符<br />-k 按列排序，第几列，例如： -k 3r -k 2 按第3列降序排列，按第2行作次要升序排列<br />-f 忽略大小写排序<br />-c 检查文件是否排好序，如果不是，输出第一行乱序的信息，退出码1<br />-C 检查文件是否排好序，如果不是，退出码1<br />-M 按月份进行排序（例如：JAN小于FEB）<br />-b 忽略前导空白符</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#使用第二列排序，分隔符为冒号
sort -n -k 2 -t : readme.txt</pre>
</td>
</tr>
<tr>
<td class="blog_h3">cat,tac</td>
<td>
<p>输出文件的完整内容<br />-n 在目标文件中的所有行前边插入行号<br />-b 与 -n 选项类似，但不对空行进行编号<br />-v 可以使用 ^ 标记法来显示出不可打印字符<br />-s 把多个空行压缩成一个空行
<p>tac从文件的结尾列出文件</p>
</td>
</tr>
<tr>
<td class="blog_h3">tee</td>
<td>
<p>读取标准输入，并将内容输出到文件和标准输出</p>
<p><span style="background-color: #c0c0c0;">格式：</span><pre class="crayon-plain-tag">tee [OPTION]... [FILE]...</pre></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-a, --append 附加内容到输出文件尾部，而不是覆盖它的内容<br />-i, --ignore-interrupts 忽视中断信号</p>
</td>
</tr>
<tr>
<td class="blog_h3">more, less</td>
<td>
<p>分页显示文本文件或stdout</p>
<p>more命令逐页的显示文本的内容，它在启动时读取整个文件的全部内容，按Space键可以向后翻页</p>
<p>less更加强大，它和more一样支持份也显示，但是支持PageUp、PageDown进行前后翻页，并且支持搜索。在提示符（:）后输入：</p>
<ol>
<li>/searchText，向下搜索指定的文本</li>
<li>?searchText，向上搜索指定的文本</li>
<li>n，向后继续查找搜索结果</li>
<li>N，向前继续查找搜索结果</li>
<li>d，向后翻半页</li>
<li>u，向前翻半页</li>
<li>Enter，向后滚动一行</li>
<li>y，向前滚动一行</li>
<li>Q，退出less命令</li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h3">rev</td>
<td>把每一行中的内容反转，并且输出到 stdout 上</td>
</tr>
<tr>
<td class="blog_h3">uniq</td>
<td>
<p>过滤器，将会删除一个已排序文件中的重复行.<span style="background-color: #c0c0c0;">这个命令经常出现在 sort 命令的管道后</span></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-c 在输出行前面加上每行在输入文件中出现的次数</p>
<p><span style="background-color: #c0c0c0;">举例</span></p>
<pre class="crayon-plain-tag">#输出3文件的内容，排序、去重、输出到final.list
cat list-1 list-2 list-3 | sort | uniq &gt; final.list  </pre>
</td>
</tr>
<tr>
<td class="blog_h3">expand, unexpand</td>
<td>expand 将会把每个tab 转化为一个空格；unexpand功能相反</td>
</tr>
<tr>
<td class="blog_h3">dos2unix</td>
<td>转换换行符的格式</td>
</tr>
<tr>
<td class="blog_h3">cut</td>
<td>
<p>从文件中提取特定字段（列）<br /><span style="background-color: #c0c0c0;">格式：</span><br />cut OPTION... [FILE]...
<p><span style="background-color: #c0c0c0;">选项：</span><br />-d 字段定界符<br />-f 选定字段列表</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#按空格分隔字段，提取第1、3、11、12个字段
uname -a | cut -d" " -f1,3,11,12</pre>
</td>
</tr>
<tr>
<td class="blog_h3">paste</td>
<td>将多个文件，以每个文件一列的形式合并到一个文件</td>
</tr>
<tr>
<td class="blog_h3">join</td>
<td>
<p>能够操作2个文件，它可以将那些具有特定标记域的行合并起来，并且将结果输出到stdout
</td>
</tr>
<tr>
<td class="blog_h3">head</td>
<td>将一个文件的头（ 默认为10 行）打印到 stdout 上 </td>
</tr>
<tr>
<td class="blog_h3">tail</td>
<td>
<p>将一个文件的尾（ 默认为10 行）打印到 stdout 上</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-f 继续显示添加到文件中的行<br />-s N 和-f配合使用时，每次获取更多数据后休眠N秒<br />-c N 显示文件的最后N字节<br />-n N 显示文件的最后N行</p>
</td>
</tr>
<tr>
<td class="blog_h3">grep</td>
<td>
<p>使用正则表达式的多用途文本搜索工具</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />grep [OPTIONS] PATTERN [FILE...]<br />grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br /><span style="background-color: #c0c0c0;">-E, --extended-regexp</span> 将PATTERN解释为扩展正则式（extended regular expression），等价于egrep<br />-<span style="background-color: #c0c0c0;">F, --fixed-strings</span> 将PATTERN解释为固定字符串的列表，以换行分隔，等价于fgrep <br />-G, --basic-regexp 将PATTERN解释为基本正则式（basic regular expression）<br />-P, --perl-regexp 将PATTERN解释为Perl正则式（basic regular expression），可以使用前向捕获等高级功能，例如<pre class="crayon-plain-tag">grep -P -o '[0-9]+(?=,)'</pre>要求匹配类似123,这样的子串，但是不消耗掉逗号</p>
<p>-e PATTERN, --regexp=PATTERN 指定匹配的PATTERN<br />-f FILE, --file=FILE 从文件读取PATTERN，空文件不匹配任何目标<br />-i,-y, --ignore-case 忽略PATTERN和输入文件中的大小写<br />-v, --invert-match 反转匹配，即选择不匹配的行<br />-w, --word-regexp 仅选择匹配项组成完整单词的行<br />-x, --line-regexp 仅选择匹配完整行的</p>
<p>-c, --count 取消正常输出，仅显示输入文件中匹配的行总数<br />-L, --files-without-match 取消正常输出，打印出第一个不匹配的文件名<br />-l, --files-with-matches 取消正常输出，打印出第一个匹配的文件名<br />-m NUM, --max-count=NUM 在到达NUM行后停止读入输入文件<br />-o, --only-matching 仅仅打印行的匹配部分<br />-q, --quiet, --silent 不打印，仅仅输出退出码<br />-s, --no-messages 取消对不存在或者不可见文件的错误信息</p>
<p>-b, --byte-offset 在每行前打印基于文件的行首的偏移量，如果指定-o则打印匹配部分的偏移量<br />-H, --with-filename 为每个匹配在行首打印文件名称，如果有多个输入文件，这是默认选项<br />-h, --no-filename 取消打印行首的文件名称，如果只有一个输入文件，这是默认选项<br />-n, --line-number 为每一行输出打印其相对于所在文件的行号</p>
<p>-a, --text 将二进制文件作为文本处理<br />-D ACTION, --devices=ACTION 如果输入文件时设备、FIFO或者Socket，使用ACTION来处理之，默认的ACTION是read<br />-d ACTION, --directories=ACTION 输入输入文件时目录，使用ACTION来处理之，默认的ACTION是read，即作为普通文件读入；recurse读入其中的所有子文件<br />--exclude=GLOB 排除匹配通配符的文件<br />--exclude-from=FILE 排除匹配从FILE中读取的通配符的文件<br />--exclude-dir=DIR 在递归搜索中，排除匹配DIR的目录<br />-I 等价于--binary-files=without-match <br />--include=GLOB 仅搜索匹配通配符的文件<br />-r, --recursive 递归搜索子文件与文件夹<br />-R, --dereference-recursive 递归搜索子文件与文件夹，并且Follow所有符号链接</p>
<p>--line-buffered 使用输出缓冲<br />--mmap 如果可能，使用系统调用mmap来读取输入，而不是read<br />-U, --binary 以二进制方式处理文件<br />-z, --null-data 把输入作为一系列行，每行以ASCII NUL（\0）分隔，而不是换行符</p>
<p><span style="background-color: #c0c0c0;">上下文行控制：</span></p>
<p>-A NUM,  --after-context=NUM 打印匹配行后面的NUM行<br /> -B NUM, --before-context=NUM  打印匹配行前面的NUM行<br />-C NUM,  打印匹配行前面、后面的NUM行</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">grep root /etc/passwd        # 搜索文件，并把包含roog的行打印出来
cat /etc/passwd | grep root  # 同上

# 在当前目录下递归搜索包含oo且oo前面没有g的文件，打印文件名而不显示行内容
grep -l -r '[^g]oo' * 
# 搜索全部日志
grep -r 'hello' /var/log

# 寻找名为java或者mysql的进程
ps -A | egrep '(java|mysql)'

# 显示文件名、行号
cat */*.php | grep "wp-content" --color=auto -iRnH

# 不打印匹配的部分

# 使用向后肯定预查
grep -oP "(?&lt;=image:\s).*" /tmp/cronjobs/*
# 对于 images: docker.gmem.cc/busybox-amd64:1.31 
# 前面的images: 引起匹配，但是不包含在匹配结果中
# 匹配结果是：docker.gmem.cc/busybox-amd64:1.31 

# 使用向前肯定预查
grep -m1 -oP '(?&lt;=template:\s).*(?=not\s+defined)'
# 对于 statefulset.yaml:49: function "MongoVersion" not defined
# 用于向后肯定预查、向前肯定预查，template、not defined引起匹配，但是结果是
# statefulset.yaml:49: function "MongoVersion"</pre>
</td>
</tr>
<tr>
<td class="blog_h3">egrep</td>
<td>
<p>基于正则式的grep
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 搜索，并且仅仅输出匹配的部分
echo 1.1.1 | egrep -oh '[0-9]+.[0-9]+.[0-9]+' </pre>
</td>
</tr>
<tr>
<td class="blog_h3">wc</td>
<td>
<p>可以统计文件或 I/O 流中的单词数量<br /><span style="background-color: #c0c0c0;">格式：</span><br />wc [OPTION]... [FILE]...<br />wc [OPTION]... --files0-from=F
<p><span style="background-color: #c0c0c0;">选项：</span><br />-w 统计单词数量<br />-l 统计行数量<br />-c 统计字节数量<br />-m 统计字符数量<br />-L 给出文件中最长行的长度</p>
</td>
</tr>
<tr>
<td class="blog_h3">tr</td>
<td>
<p>读取标准输入，转换或者删除字符，并打印到标准输出</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />tr [OPTION]... SET1 [SET2]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-c 使用SET1的补集<br />-d 删除SET1指定的字符，不进行转换<br />-s 删除重复出现的SET1指定的字符</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#把花括号替换为圆括号
tr '{}' '()'
#删除全部空格
tr -d ' '
#把小写转换为大写
tr 'a-z' 'A-Z'
#使用问号替换所有显示字符，*表示让SET2长度自动扩充到与SET1一致
tr -c '[:print:][:cntrl:]' '[?*]'
#使用#代替空白符，连续出现的空白符被合并为1个
tr -s '[:space:]' '[#*]'</pre>
</td>
</tr>
<tr>
<td class="blog_h3">fold</td>
<td>将输入按照指定宽度进行折行</td>
</tr>
<tr>
<td class="blog_h3">fmt</td>
<td>简单的文件格式化器，通常用在管道中，将一个比较长的文本行输出进行折行</td>
</tr>
<tr>
<td class="blog_h3">col</td>
<td>来滤除标准输入的反向换行符号</td>
</tr>
<tr>
<td class="blog_h3">column</td>
<td>
<p>将“列类型”的文本转化为“易于打印”的表格式进行输出
<p>可以用来按列对齐，例如：</p>
<pre class="crayon-plain-tag">sudo iptables -L -nv -t filter | column -t -s " "</pre>
</td>
</tr>
<tr>
<td class="blog_h3">colrm</td>
<td>列删除过滤器。 这个工具将会从文件中删除指定的列并且写到文件中</td>
</tr>
<tr>
<td class="blog_h3">nl</td>
<td>计算行号过滤器，在输出中添加前缀的行号</td>
</tr>
<tr>
<td class="blog_h3">pr</td>
<td>格式化打印过滤器. 这个命令会将文件（或stdout）分页</td>
</tr>
<tr>
<td class="blog_h3">gettext</td>
<td>将程序的输出翻译或者本地化为不同国家语言</td>
</tr>
<tr>
<td class="blog_h3">iconv</td>
<td>将文件转化为不同编码格式（字符集）</td>
</tr>
<tr>
<td class="blog_h3">strings</td>
<td>在二进制或数据文件中找出可打印字符</td>
</tr>
<tr>
<td class="blog_h3">diff</td>
<td>
<p>以一行接一行的形式来比较目标文件
<p><span style="background-color: #c0c0c0;">格式：</span><br />diff [OPTION]... FILES</p>
<p><span style="background-color: #c0c0c0;">参数：</span><br />--side-by-side 以左右方式显示，并把不同的行标记出来</p>
<p><span style="background-color: #c0c0c0;">退出码：</span><br />0 如果文件完全一样</p>
</td>
</tr>
<tr>
<td>jq</td>
<td>
<p>命令行JSON处理器，能够选取、迭代、reduce、修改JSON文档</p>
<p><span style="background-color: #c0c0c0;">格式：</span>jq [options...] filter [files...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>--seq 使用MIME类型application/json-seq来分隔jq输入输出的JSON文档，这意味着JSON记录前后分别有ASCII字符RS、LF。无法解析的输入JSON被忽略，同时忽略所有后续输入，直到RS出现<br />--stream 流式的解析输入，输出路径数组和叶子值<br />--slurp/-s 读取整个输入，然后一次运行Filter，而非逐个读取JSON对象，逐个运行Filter<br />--raw-input/-R 不要将输入读取为JSON，而是每行均以字符串形式传递给Filter<br />-c 紧凑输出，不格式化<br />--tab 使用Tab而非空格进行格式化<br />--indent 缩进对应空格数量<br />-C 彩色化输出<br />-S 按键排序字段<br />-r 原始输出，如果结果是字符串，直接输出内容，不要引号<br />-f  从文件而非命令行读取过滤器<br />-L 指定模块搜索目录<br />--arg name value 定义变量$name为value<br />--argjson name JSON-text 以JSON文档的形式定义变量</p>
<p><span style="background-color: #c0c0c0;">过滤器：</span></p>
<p>每个jq程序都称为过滤器，它将输入转换为输出，大量内置过滤器可以实现字段抽取、类型转换等标准化工作</p>
<p>过滤器可以用管道的形式连接起来，你还可以把过滤器的输出收集到数组中。某些过滤器会产生多个输出，通过管道连接在其后的过滤器会对每个输出执行处理</p>
<p>需要注意，任何过滤器都具有输入输出，哪怕是18这样的字面值也可以是过滤器——不管输入是什么，都输出18</p>
<p><span style="background-color: #c0c0c0;">基本过滤器：</span></p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.</pre> 直接输出输入，不处理</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.foo.bar</pre>属性点号导航，路径有null值则返回null，如果JSON键有特殊字符，需要双引号<pre class="crayon-plain-tag">."foo$"</pre>，注意.foo.bar 和<pre class="crayon-plain-tag">.foo|.bar</pre>这种管道形式是等价的</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.foo?</pre>，类似于.foo，但是当.不是对象或数组时不会报错，例如<pre class="crayon-plain-tag">echo '{"name":"alex"}' | jq '[.age?]'</pre>返回<pre class="crayon-plain-tag">[null]</pre></p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.[&lt;string&gt;]</pre>这种形式放为属性也可以</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.[2]</pre>访问数组元素</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.[10:15]</pre> 切片，获得子数组</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">.[]</pre> 迭代数组的所有元素，每个元素产生一个输出</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">filter1,filter2</pre>分别执行两个过滤器，然后先输出filter1的全部输出，后输出filter2的全部输出</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">filter1|filter2</pre> filter1的输出作为filter2的输入</p>
<p><span style="background-color: #c0c0c0;">类型和值：</span></p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">[]</pre>用于构造数组，[.user, .projects[]]表示读取user属性、projects的所有元素，合并为一个数组</p>
<p style="padding-left: 30px;"><pre class="crayon-plain-tag">{}</pre> 用于构造对象，{user: .user, title: .title}可以简写为<pre class="crayon-plain-tag">{user, title}</pre>，用括号包围key，表示解析它的值，例如<pre class="crayon-plain-tag">{(.user): .titles}</pre>可以产生{"alex": [1,2]}</p>
<p><span style="background-color: #c0c0c0;">内置操作符和函数：</span></p>
<p style="padding-left: 30px;">+ 数字加、数组连接、字符串连接<br />- 数字减、数组对象差集<br />* / %<br />length 获得长度<br />keys, keys_unsorted 获得键的数组<br />has(key) 返回是否存在键，true/false<br />del(path_expression) 删除指定的键值</p>
<p><span style="background-color: #c0c0c0;">格式化和转义：</span></p>
<p style="padding-left: 30px;">@text 调用tostring<br />@json 串行化输入为JSON<br />@html 执行HTML转义<br />@base64 执行Base64编码</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">awk</span></div>
<p>AWK编程语言的GNU实现。</p>
<div class="blog_h3"><span class="graybg">命令格式</span></div>
<pre class="crayon-plain-tag"># GNU的Awk实现
gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
gawk [ POSIX or GNU style options ] [ -- ] program-text file ...

# pgawk是剖析版本的Awk，速度比较慢
pgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
pgawk [ POSIX or GNU style options ] [ -- ] program-text file ...

# dgawk是Awk的调试器
dgawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...</pre>
<div class="blog_h3"><span class="graybg">命令示例</span></div>
<pre class="crayon-plain-tag"># 读取文件的第1、4列
awk '{print $1, $4}' filename

# 列出最近5个登陆记录，逐行处理。以空格作为字段分隔符，打印第一个字段
# $0表示所有字段，$1表示第一个字段，类推
last -n 5 | awk '{print $1}'

# 行过滤，仅仅显示第一列为tcp6且第六列为SYN_SENT的行
netstat -tn | awk '$1=="tcp6" &amp;&amp; $6=="SYN_SENT"'
# 同上，但是包含表头（第一行）
netstat -tn | awk '$1=="tcp6" &amp;&amp; $6=="SYN_SENT" || NR==1'


# 使用 : 作为分隔符，取第1、2个字段，之间用制表符分隔
cat /etc/passwd | awk -F ':' '{print $1 "\t" $2 }'

# 使用正则表达式作为分隔符
echo 'Hello darkness my old friend' | awk -F'\\s+' '{print "["$5"]"}'
# [friend]

# 使用正则式进行行过滤
# 如果第六列匹配正则式FIN，或者是第一行，则打印4、5、6列，表头总是打印，输出字段使用制表符分隔
awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
# 正则式取反
netstat | awk '!/WAIT/'

# BEGIN 在处理输入之前执行
# END   在处理完所有输入行之后执行
cat /etc/passwd | head -5 | awk -F':' 'BEGIN {print "用户名"} {print $1} END {print "结束"}'
# 用户名
# root
# daemon
# bin
# sys
# sync
# 结束

# 格式化输出，C语言风格
netstat -nlt4 | awk '{printf "%-8s %-8s %-8s %-18s %-22s %-15s\n",$1,$2,$3,$4,$5,$6}'

# 拆分输出：仅仅输出指定的列
netstat -nt | awk 'NR!=1{print &gt; $6}'


# 统计数量
# 在映射a中存放每个网络状态的计数，默认计数为0，每次自动增加计数
# 处理完所有行后，遍历映射并打印
netstat -t | awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}'
# LAST_ACK, 4
# CLOSE_WAIT, 1
# ESTABLISHED, 22
# Foreign, 1
# CLOSING, 1
# TIME_WAIT, 1

# 寻找log中长度大于80的行
awk 'length&gt;80' log</pre>
<div class="blog_h3"><span class="graybg">内置变量</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">变量</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>$0</td>
<td>当前记录，即整个行的内容</td>
</tr>
<tr>
<td>$1~$n</td>
<td>当前记录的第n个字段，字段间由FS分隔</td>
</tr>
<tr>
<td>FS</td>
<td>输入字段分隔符 默认是空格或Tab</td>
</tr>
<tr>
<td>NF</td>
<td>当前记录中的字段个数，就是有多少列</td>
</tr>
<tr>
<td>NR</td>
<td>
<p>已经读出的记录数，就是行号，从1开始，如果有多个文件话，这个值也是不断累加中</p>
</td>
</tr>
<tr>
<td>FNR</td>
<td>已经读出的记录数，就是行号，从1开始，如果有多个文件话，每个文件分别计数</td>
</tr>
<tr>
<td>RS</td>
<td>输入的记录分隔符， 默认为换行符</td>
</tr>
<tr>
<td>OFS</td>
<td>输出字段分隔符， 默认也是空格</td>
</tr>
<tr>
<td>ORS</td>
<td>输出的记录分隔符，默认为换行符</td>
</tr>
<tr>
<td>FILENAME</td>
<td>当前输入文件的名字</td>
</tr>
<tr>
<td>ENVIRON</td>
<td>环境变量的Map</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">内置函数</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">函数</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2"><em>数学函数</em></td>
</tr>
<tr>
<td>atan2( y, x )</td>
<td>返回 y/x 的反正切</td>
</tr>
<tr>
<td>cos( x )</td>
<td>返回 x 的余弦；x 是弧度</td>
</tr>
<tr>
<td>sin( x )</td>
<td>返回 x 的正弦；x 是弧度</td>
</tr>
<tr>
<td>exp( x )</td>
<td>返回 x 幂函数</td>
</tr>
<tr>
<td>log( x )</td>
<td>返回 x 的自然对数</td>
</tr>
<tr>
<td>sqrt( x )</td>
<td>返回 x 平方根</td>
</tr>
<tr>
<td>int( x )</td>
<td>返回 x 的截断至整数的值</td>
</tr>
<tr>
<td>rand( )</td>
<td>返回随机数字 n，其中 0 &lt;= n &lt; 1</td>
</tr>
<tr>
<td>srand( [Expr] )</td>
<td>将 rand 函数的种子值设置为 Expr 参数的值，或如果省略 Expr 参数则使用时间。返回先前的种子值</td>
</tr>
<tr>
<td colspan="2"><em>字符串函数</em></td>
</tr>
<tr>
<td>length [(String)]</td>
<td>计算字符串中的字符数量</td>
</tr>
<tr>
<td>blength [(String)]</td>
<td>计算字符串的字节数</td>
</tr>
<tr>
<td>sub( Ere, Repl, [ In ] )</td>
<td>
<p>用Repl替换In中第一个匹配正则式Ere的子串。如果不指定In则使用$0</p>
<p>注意：In直接被修改，返回值是替换的子串数量</p>
<p>示例：</p>
<pre class="crayon-plain-tag">df | awk 'NR&gt;1{sub("%","",$5);print $1 " usage " $5/100 }'</pre>
</td>
</tr>
<tr>
<td> gsub( Ere, Repl, [ In ] )</td>
<td>用Repl替换In中全部的匹配正则式Ere的子串。如果不指定In则使用$0</td>
</tr>
<tr>
<td>index( Str, Sub )</td>
<td>返回Sub第一次出现在Str中的位置，索引以1为基准，如果不存在Sub子串返回0</td>
</tr>
<tr>
<td>substr( Str, M, [ N ] )</td>
<td>提取Str从索引M开始，N结束的子串，不指定N则到结尾</td>
</tr>
<tr>
<td>match( Str, Ere )</td>
<td>返回Str匹配Ere的子串的出现位置，如果没有匹配返回0</td>
</tr>
<tr>
<td>split( Str, A, [Ere] )</td>
<td>分割字符串Str，将子串存放到数组A中。分隔符默认等于-F参数指定的字符，或者Ere正则式</td>
</tr>
<tr>
<td>tolower( Str )<br />toupper( Str )</td>
<td>大小写转换</td>
</tr>
<tr>
<td>sprintf(Format, Expr, . . . )</td>
<td>格式化打印：<br />%d   十进制有符号整数<br />%u  十进制无符号整数<br />%f   浮点数<br />%s   字符串<br />%c   单个字符<br />%p   指针的值<br />%e   指数形式的浮点数<br />%x %X  无符号以十六进制表示的整数<br />%o   无符号以八进制表示的整数<br />%g   自动选择合适的表示法</td>
</tr>
<tr>
<td colspan="2"><em>时间函数</em></td>
</tr>
<tr>
<td>mktime( YYYY MM DD HH MM SS[ DST])  </td>
<td>解析字符串为时间 </td>
</tr>
<tr>
<td>strftime([format [, timestamp]])</td>
<td>
<p>格式化时间：
<p>%a 星期几的缩写(Sun)<br />%A 星期几的完整写法(Sunday)<br />%b 月名的缩写(Oct)<br />%B 月名的完整写法(October)<br />%c 本地日期和时间<br />%d 十进制日期<br />%D 日期 08/20/99<br />%e 日期，如果只有一位会补上一个空格<br />%H 用十进制表示24小时格式的小时<br />%I 用十进制表示12小时格式的小时<br />%j 从1月1日起一年中的第几天<br />%m 十进制表示的月份<br />%M 十进制表示的分钟<br />%p 12小时表示法(AM/PM)<br />%S 十进制表示的秒<br />%U 十进制表示的一年中的第几个星期(星期天作为一个星期的开始)<br />%w 十进制表示的星期几(星期天是0)<br />%W 十进制表示的一年中的第几个星期(星期一作为一个星期的开始)<br />%x 重新设置本地日期(08/20/99)<br />%X 重新设置本地时间(12：00：00)<br />%y 两位数字表示的年(99)<br />%Y 当前月份<br />%Z 时区(PDT)<br />%% 百分号(%)</p>
</td>
</tr>
<tr>
<td>systime()</td>
<td>返回UNIX纪元到现在的秒数</td>
</tr>
<tr>
<td colspan="2"><em>其它函数</em></td>
</tr>
<tr>
<td>close(expr)</td>
<td>
<p>关闭由print、printf、getline打开的文件或管道，关闭成功返回0</p>
<p>如果打算写一个文件，并稍后在同一个程序中读取文件，则 close 语句是必需的</p>
</td>
</tr>
<tr>
<td>system(cmd)</td>
<td>
<p>执行cmd参数给出的系统命令，返回退出状态。示例：</p>
<pre class="crayon-plain-tag">jcmd -l | awk '{system("ps  --no-headers -p "$1 " -o command " ) }'</pre>
</td>
</tr>
<tr>
<td>getline</td>
<td>
<p>逐行读取外部文件的内容。示例：
<pre class="crayon-plain-tag">{ while( "cat /etc/passwd" | getline ){ print $0; }; close("/etc/passwd"); }
# 或者   getline &lt; "/etc/passwd"</pre>
</td>
</tr>
<tr>
<td>exit</td>
<td>提前结束处理，不再处理后续的记录。如果awk位于管道中间，后续命令获得处理机会</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">语言入门</span></div>
<p>Awk程序通常是读取一个个记录（默认每行是一个记录），并寻找到其中匹配pattern的行，执行指定的动作：
<pre class="crayon-plain-tag">pattern { action }

# 示例
$3 == 0 { print $1 }
# 可以不指定pattern，这样就匹配所有记录
{ print }  # 不带参数的print等价于print $0

# 起始和结束块，分别在处理任何记录之前、处理所有记录之后执行
BEGIN { action }
END   { action }</pre>
<p>Pattern分为：<span style="background-color: #c0c0c0;">BEGIN、END、 表达式、/正则表达式/ 、Pattern的逻辑组合，范围模式几类</span>。</p>
<p>范围模式：<pre class="crayon-plain-tag">pattern1，pattern2 { 语句 }</pre>，匹配从匹配pattern1的行、到匹配pattern2的行之间的所有行，包括匹配pattern1、pattern2的那两个首尾行。</p>
<p>你可以对记录中的字段，进行各种运算：</p>
<pre class="crayon-plain-tag">{ print $1, $2 * $3 }
$2 * $3 &gt; 50 { printf("$%.2f for %s\n", $2 * $3, $1) }</pre>
<p>pattern可以通过逻辑操作符进行组合，包括：<pre class="crayon-plain-tag">&amp;&amp;</pre>、<pre class="crayon-plain-tag">||</pre>和<pre class="crayon-plain-tag">!</pre>，支持使用括号：</p>
<pre class="crayon-plain-tag">!($2 &lt; 4 &amp;&amp; $3 &lt; 20)</pre>
<p>字符串拼接语法 —— <span style="background-color: #c0c0c0;">直接连续写多个字符串（或变量）即可，不需要操作符，空格被忽略</span>：</p>
<pre class="crayon-plain-tag">netstat -nlt4 | awk 'NR &gt; 2 {print $4}' | awk -F':' '{print $1}' | sort -u | \
    #         赋值语法
    #           字符串拼接语法
    awk '{ips = ips $1 "|"} END{print ips}'
# 0.0.0.0|10.0.0.1|127.0.0.1|172.17.0.1|172.18.0.1|172.21.0.1|192.168.142.140|</pre>
<div class="blog_h3"><span class="graybg">控制结构 </span></div>
<p>if-else分支结构： </p>
<pre class="crayon-plain-tag">echo -e "0\n-1\n1" | awk '{ if($1&gt;0) print "TRUE"; else if($1&lt;0) print "N/A"; else print "FALSE" }'
# FALSE
# N/A
# TRUE</pre>
<p>while循环结构： </p>
<pre class="crayon-plain-tag">while (i &lt;= $3) { i = i + 1 }</pre>
<p>for循环结构：</p>
<pre class="crayon-plain-tag">{ 
for (i = 1; i &lt;= $3; i = i + 1)
    printf("\t%.2f\n", $1 * (1 + $2) ^ i)
}</pre>
<div class="blog_h3"><span class="graybg">数组支持</span></div>
<pre class="crayon-plain-tag">{ line[NR] = $0 }  # 记录每个输入行，将其存为数组元素

# 逆序打印
END { 
  i = NR
  while (i &gt; 0) {
    print line[i]
    i = i - 1
  }
}</pre>
<div class="blog_h2"><span class="graybg">sed</span></div>
<p>sed是流式文本编辑器，该命令用于在输入流（文件或者管道输入）上执行基本的文本转换，该命令只会对输入进行一次逐行的顺序的处理，因而效率较高。</p>
<div>
<div class="blog_h3"><span class="graybg">命令格式</span></div>
</div>
<pre class="crayon-plain-tag">sed [OPTION]... {script-only-if-no-other-script} [input-file]...</pre>
<div>
<div class="blog_h3"><span class="graybg">命令选项</span></div>
</div>
<pre class="crayon-plain-tag">-n, --quiet, --silent
       #不自动打印当前要处理的行
-e script, --expression=script
       #指定需要执行的命令（脚本），可以指定多个-e
-f script-file, --file=script-file
       #指定文件中的脚本
--follow-symlinks
       #跟随符号链接到目标文件
-i[SUFFIX], --in-place[=SUFFIX]
       #指定备份文件，修改直接应用到原始文件
-l N, --line-length=N
       #对于l类型的命令，指定期望的换行的行长度阈值
--posix
       #禁用所有GNU扩展
-r, --regexp-extended
       #在此脚本中使用正则式
-s, --separate
       #把输入文件看作若干单个文件，而不是一个连续的流
-u, --unbuffered
       #使用尽量少的缓冲区，尽量读入较少的文件内容，并把输出刷空
-z, --null-data
       #使用NUL字符来划分行</pre>
<div>
<div class="blog_h3"><span class="graybg">脚本语法</span></div>
</div>
<pre class="crayon-plain-tag">[address][!] cmd [cmd-args]

# address 指定后面的命令的作用范围，有以下几种方式：
# 不指定：作用于输入的所用行
# n,m：从第n到第m行，$表示最后一行
# n,+m：从第n行开始，到n+m行为止
# n~m：从第n行开始，每隔m行执行一次命令
# /pattern/：基于正则式匹配，所有匹配的行均执行命令
# /pattern1/,/pattern2/：基于正则式匹配，从pattern1匹配的行开始，到pattern2匹配的行为止

 #命令，包括添加、插入、替换、删除、打印等
#                 p                           打印
#                 =                           打印行号
# [addr1[,addr2]] i text                      在指定的行的前面插入文本
# [addr1[,addr2]] a text                      在指定的行的后面添加文本
# [addr1[,addr2]] d                           删除指定的行
# [addr1[,addr2]] s/pattern/replace/[opts]    把指定的行内的pattern替换为replace，
#                                               默认只会替换第一个匹配pattern的文本
#                                               选项：
#                                                 n 替换第n个匹配
#                                                 g 替换所有匹配
#                                                 p 如果替换成功打印该行
#                                                 w 存储改变的行到文件
#                                                 i 匹配时忽略大小写
#                 s;pattern;replace;          如果replace中有/，可以用;代替/</pre>
<div class="blog_h3"><span class="graybg">应用举例</span></div>
<pre class="crayon-plain-tag"># 替换文本中每行的所有WangZhen为Alex
sed "s/WangZhen/Alex/g" greetings.txt &gt; result.txt
# 同上，直接写回到原文件

sed -i "s/WangZhen/Alex/g" greetings.txt
# 只替换每行的第2个匹配
sed -i "s/WangZhen/Alex/2" greetings.txt
# 只替换每行的第3个以后的匹配
sed -i "s/WangZhen/Alex/3g" greetings.txt
# 只替换3-10行的内容
sed -i "3,10s/WangZhen/Alex/g" greetings.txt

# 在每一行的开始处添加一个#
sed 's/^/#/g' greetings.txt 
# 在每行的结尾添加一个#
sed 's/$/#/g' greetings.txt 


# 同时执行多个脚本（两种等价方式）
sed '1,3s/WangZhen/Alex/g; 3,$s/Is/Are/g' greetings.txt
sed -e '1,3s/WangZhen/Alex/g' -e '3,$s/Is/Are/g'  greetings.txt

# 正则式匹配引用，使用\1、\2依次引用第1、2个括号内的匹配内容
# 正则式([^,]*).*says(.*)
#替换为：\1:\2
sed 's/\([^,]*\).*says\(.*\)/\1:\2/g' greetings.txt

# 在输入的每一行首部，加上  docker.gmem.cc/
sed "s;^;docker.gmem.cc/;"
# 常用正则式关键字
# ^ 开头
# $ 结尾
# \&lt; 词首，例如 \&lt;abc 表示abc开头的词
# \&gt; 词尾
# . 任意单词
# * 重复
# [] 字符集</pre>
<div class="blog_h1"><span class="graybg">归档与压缩命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令 </td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">tar</td>
<td>
<p>打包和解压文件。该命令支持使用短横线（-）来代替标准输入/输出，以代替输入/输出tar文件名</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />tar [options] [pathname ...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-c 创建一个新的归档文件<br />-x 从归档文件中解压文件<br />-z 用gzip压缩归档文件（压缩还是解压, 依赖于是否组合了-c或-x）<br />--delete 从存在的归档文件中删除文件<br />-r 将文件添加到现存的归档文件的尾部<br />-A 将tar文件添加到现存的归档文件的尾部<br />-t 列出现存的归档文件中包含的内容<br />-u 更新归档文件<br />-d 使用指定的文件系统比较归档文件<br />-j 用bzip2压缩归档文件<br />-d, --diff, --compare 查找文件系统和归档文件的不同<br />-a, --auto-compress 根据后缀名自动确认压缩算法<br />-C, --directory DIR 修改输出目录为DIR<br />--exclude=PATTERN 根据指定的PATTERN排除文件<br />-f, --file ARCHIVE 使用归档文件（后面要直接连接归档文件名）<br />-h, --dereference 压缩时使用符号连接指向的真实文件<br />-H, --format FORMAT 创建指定格式的文件<br />-k, --keep-old-files 解压时，不覆盖已经存在的旧文件<br />-p ：使用原文件的原来属性<br />-P ：使用绝对路径来压缩<br />-N ：比日期(yyyy/mm/dd)更新的文件才会被打包进去</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 将/etc下的文件全部打包
tar -cvf /tmp/etc.tar /etc
# 同上，使用gzip压缩
tar -zcvf /tmp/etc.tar.gz /etc 
# 同上，使用bzip2压缩
tar -jcvf /tmp/etc.tar.bz2 /etc 
# 查阅归档文件内有哪些文件
tar -ztvf /tmp/etc.tar.gz
# 将/tmp/etc.tar.gz解压缩在/usr/local/src下
tar -zxvf /tmp/etc.tar.gz -C /usr/local/src
# 递归搜索当前目录下的日志文件，解符号引用，打包
tar czfh logs.tgz $(find . -name *.log)</pre>
</td>
</tr>
<tr>
<td class="blog_h2"> 7za</td>
<td>
<p>高压缩比压缩工具。支持7z, ZIP, CAB, ARJ, GZIP, BZIP2, TAR, CPIO, RPM, DEB等格式
<p><span style="background-color: #c0c0c0;">格式：</span><br />7za [adeltux] [-] [SWITCH] [ARCHIVE_NAME] [ARGUMENTS]...</p>
<p>子命令：<br />a 添加<br />d 删除<br />e 解压<br />l 列出明细<br />t 测试<br />u 更新<br />x 解压，保留路径</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-bd 禁用百分比指示器<br />-l 跟踪符号链接 <br />-m{Parameters} 参数<br />-mhe=on|off 启用或者禁用归档文件头加密，仅用于7z<br />-o{Directory} 输出目录<br />-p{Password} 设置密码<br />-si 从标准输入读取数据<br />-so 写出数据到标准输出<br />-sfx[{name}] 创建自解压文档<br />-t{Type} 归档文件的类型 7z, zip, gzip, bzip2, tar，默认7z<br />-u[-][p#][q#][r#][x#][y#][z#][!newArchiveName] 更新选项<br />-w[path] 设置工作目录<br />-y 对所有提问默认选择是</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#把dir目录中所有文件加入到archive.7z中
#-m0=lzma 使用lzma方式
#-mx=9 使用最高压缩级别
#-mfb=64 设置LZMA快速比特
#-md=32m 字典大小设置为32MB
#-ms=on 启用固化归档，所有文件被作为一个大数据块看待
7za a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on archive.7z dir

#把dir目录中所有文件加入到自解压文件archive.exe中
7za a -sfx archive.exe dir
#压缩单个文件
7za a -t7z /tmp/gmem.sql.7z /tmp/gmem.sql

#压缩并加密
7za a -mhe=on -ppswd archive.7z dir

#解压到当前文件夹，保持目录结构
7za x archive.zip
#解压到指定/tmp目录
7za x /tmp/gmem.sql.7z -o/tmp


#Linux/Unix备份，注意不能直接使用7za，因为文件所有权信息无法保存
#备份并压缩为7z格式
tar cvf  - dir | 7za a -si archive.tar.7z
#解压缩并展开备份文件
7za x -so archive.tar.7z | tar xf -</pre>
</td>
</tr>
<tr>
<td class="blog_h2">shar</td>
<td>标准的 GNU/UNIX 压缩工具</td>
</tr>
<tr>
<td class="blog_h2">bzip2</td>
<td>用来压缩的一个可选的工具, 通常比 gzip 命令压缩率更高</td>
</tr>
<tr>
<td class="blog_h2">zip, unzip</td>
<td>跨平台的文件归档和压缩工具</td>
</tr>
<tr>
<td>gzip,gunzip</td>
<td>
<p>跨平台的文件归档和压缩工具
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 从标准输入读取数据并压缩
docker save busybox:1.30.1 | gzip -c &gt; busybox.tar.gz

# 解压缩到标准输出
gunzip -c busybox.tar.gz</pre>
</td>
</tr>
<tr>
<td>cpio</td>
<td>
<p>从归档文件拷贝出、拷贝到归档文件
<p>拷出模式：</p>
<p style="padding-left: 30px;">将文件拷贝到归档文件中，从stdin读取一系列文件名（每个一行），然后将归档文件写出到标准输出</p>
<p>拷入模式：</p>
<p style="padding-left: 30px;">从归档文件拷贝文件，或者列出归档文件的内容</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-d 如果必要，创建需要的目录<br />-i  以拷入模式运行<br />-m 保持文件的修改时间不变</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">数学与计算命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;"> 命令</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">factor</td>
<td>分解质因数，空格分隔结果</td>
</tr>
<tr>
<td class="blog_h2">bc</td>
<td>支持浮点运算</td>
</tr>
<tr>
<td class="blog_h2">expr</td>
<td>
<p>通用求值表达式：通过给定的操作（参数必须以空格分开）连接参数进行算术操作、比较操作、字符串操作或者是逻辑操作</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">expr 5 + 3
a=`expr 5 + 3` #等价于 $((5+3))</pre>
</td>
</tr>
<tr>
<td class="blog_h2">seq</td>
<td>
<p>打印一系列连续的数字
<p>格式：</p>
<pre class="crayon-plain-tag"># 从1开始，LAST结束
seq [OPTION]... LAST
# 从FIRST开始，LAST结束
seq [OPTION]... FIRST LAST
# 从FIRST开始，LAST结束，步长INCREMENT
seq [OPTION]... FIRST INCREMENT LAST </pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">通信与网络命令</span></div>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">host</td>
<td>
<p>DNS查找工具，用于进行域名和IP地址的双向转换
<p><span style="background-color: #c0c0c0;">格式：</span><br />host [-aCdlriTwv] [-c class] [-N ndots] [-t type] [-W time]<br />　　 [-R number] [-m flag] hostname [server]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a 等价于 -v -t ANY<br />-R 指定重试次数（发送UDP包的个数）<br />-c 查询类，一般不用指定，默认IN表示互联网地址查询<br />-t 指定查询类型，支持A,NS,CNAME,PTR,MX,ANY等，<br />-v -d 启用冗长输出<br />-w 一直等待不超时<br />-W 指定最多等待多久，单位秒<br />-4 强制基于IPv4发送查询<br />-6 强制基于IPv6发送查询</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#查询域名gmem.cc的所有类型的记录
host -v -t ANY gmem.cc 8.8.4.4
#输出内容：
#Trying "gmem.cc"
#Using domain server:
#Name: 8.8.4.4
#Address: 8.8.4.4#53
#Aliases:
#
#;; -&gt;&gt;HEADER&lt;&gt;HEADER&lt;</pre>
</td>
</tr>
<tr>
<td class="blog_h2">nslookup</td>
<td>
<p>和DNS服务器交互。如果不指定任何参数，进入交互模式
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<p>nslookup [-option] [name | -] [server]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>[no]debug 调试模式<br />port=value  DNS服务器端口<br />querytype=value ，type=value 查询类型，默认A<br />[no]recurse，提示DNS服务器，是否递归的请求上游DNS<br />[no]fail，如果一个DNS服务器应答SERVFAIL，是否尝试下一个<br />[no]vc，发送请求时是否总是使用虚拟环路（virtual circuit）</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 通过TCP 5353端口进行查询
nslookup -vc kubernetes.default.svc.k8s.gmem.cc localhost -port=5353

# 发起AAAA查询
nslookup -q=AAAA tredis-keydb-0.tredis-keydb 10.96.0.10</pre>
</td>
</tr>
<tr>
<td class="blog_h2">arp</td>
<td>
<p>操控系统的ARP缓存
<p><span style="background-color: #c0c0c0;">格式：</span><br />arp [-vn] [-H type] [-i if] [-a] [hostname]<br />arp [-v] [-i if] -d hostname [pub]<br />arp [-v] [-H type] [-i if] -s hostname hw_addr [temp]<br />arp [-v] [-H type] [-i if] -s hostname hw_addr [netmask nm] pub<br />arp [-v] [-H type] [-i if] -Ds hostname ifname [netmask nm] pub<br />arp [-vnD] [-H type] [-i if] -f [filename]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-v, --verbose 显示冗余信息<br />-d 删除 ARP表中对应IP的条目条目：arp -d address<br />-s 新增一条ARP表条目：arp  -s address hw_addr<br />-n, --numeric 显示数字地址，而不是尝试解析主机名、端口<br />-H type, --hw-type type 在读取ARP缓存时，该参数指明那种条目需要被检查，默认值是以太网ether<br />-a 使用备选的BSD风格的输出格式 （不固定列宽）<br />-D, --use-device 使用网络接口的名称，而不是硬件地址<br />-i If, --device If 选择一个网络接口</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#eth1将应答针对192.168.3.90的ARP请求，并将该IP地址关联到eth0的MAC地址
arp -i eth1 -Ds 192.168.3.90 eth0 pub

#删除eth1上关于192.168.3.90的ARP条目
arp -i eth1 -d 192.168.3.90

#查询eth1上的ARP条目
arp -i eth1
#Address        HWtype  HWaddress           Flags Mask            Iface
#192.168.0.89   ether   0c:82:68:52:76:3e   C                     eth1
#192.168.0.243  ether   00:d0:23:82:ba:12   C                     eth1
#192.168.0.61   ether   e0:db:55:ad:29:3c   C                     eth1



# 根据Mac地址查询IP地址
ip neighbor | grep "00:e0:4c:68:7e:45" | cut -d" " -f1</pre>
</td>
</tr>
<tr>
<td class="blog_h2">arp-scan</td>
<td>
<p>ARP扫描器<br />该命令向本地网络上的主机发送ARP数据报，并打印任何响应信息<br />可以使用--I指定使用的网络接口，如果不指定，该命令会使用所有的配置好的网卡（loopback除外）<br />默认情况下，数据报被发送到以太网广播地址ff:ff:ff:ff:ff:ff
<p><span style="background-color: #c0c0c0;">选项：</span><br />-f 从文件而不是命令行读取目标主机名称或者地址<br />-l 从网络接口的配置（IP和子网掩码）中生成目标主机的地址，包含广播地址<br />-r 每个目标主机的总计重试次数，默认2<br />-t 初始的每个主机的超时时间，默认100<br />-i 最小的数据报发送间隔<br />-v 显示冗余进度信息<br />-R 随机打乱目标主机列表的顺序<br />-N 仅使用IP地址，不使用主机名，使用该选项后，所有目标主机必须以IP地址的方式指定<br />-I 使用的本机网络接口的名称，该接口必须支持ARP协议<br />-q 仅显示最小化的输出<br />-g 不显示重复的数据报，默认的会显示并添加(DUP: n)后缀</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#通过eth1网卡执行扫描，并显示其中IP地址为192.168.0.232的条目
#该命令可以用于IP查重
sudo arp-scan -I eth1 -l | grep 192.168.0.232</pre>
</td>
</tr>
<tr>
<td class="blog_h2">traceroute</td>
<td>
<p>跟踪到目的主机的路由信息
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">traceroute [-46dFITUnreAV] [-f first_ttl] [-g gate,...]
           [-i device] [-m max_ttl] [-p port] [-s src_addr]
           [-q nqueries] [-N squeries] [-t tos]
           [-l flow_label] [-w waittime] [-z sendwait] [-UL] [-D]
           [-P proto] [--sport=port] [-M method] [-O mod_options]
           [--mtu] [--back]
           host [packet_len]
traceroute6  [options]
tcptraceroute  [options]
lft  [options]</pre>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-4, -6 明确强制使用IPv4或者IPv6，默认会根据制定的host名自动解析，根据结果来判断使用IPv6还是IPv4，如果同时支持，则默认使用IPv4<br />-I, --icmp 使用ICMP回应作为探针<br />-T, --tcp 使用TCP SYN作为探针<br />-d, --debug 启用Socket级别的调试信息<br /> Enable socket level debugging (when the Linux kernel supports it)<br />-f first_ttl, --first=first_ttl 第一次使用的TTL，默认1<br />-g gateway, --gateway=gateway 指定一个IP源路由选项<br />-i interface, --interface=interface 发送数据报使用的网络接口，默认根据路由表选择<br />-m max_ttl, --max-hops=max_ttl 最大跃点数（TTL），默认30<br />-n 显示结果时不把IP映射为主机名<br />-w waittime, --wait=waittime 等待响应的时间，默认5秒<br />-q 发送探针的数量<br />-s source_addr, --source=source_addr 手工指定源IP地址，默认根据网络接口的IP地址确定<br />-z sendwait, --sendwait=sendwait 发送探针报文的最小间隔，默认0<br />--mtu 沿着追踪路径检测最大传输单元（MTU）</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">1  * * *
 2  119.57.167.17  21.994 ms  21.737 ms  22.433 ms
 3  * * *
21  * * *
22  103.3.62.198  336.846 ms *  342.442 ms
#从第一跳一直检测到目标主机，或者超过最大跃点数限制
# 默认发送三个探针，出现星号表示超时前，当没有收到前跳对应的节点的回应报文</pre>
</td>
</tr>
<tr>
<td class="blog_h2">mtr</td>
<td>
<p>mtr是一个包含traceroute和ping功能的单一网络诊断工具，mtr命令启动后，会诊断当前主机和目标主机之间的网络状况，它会特意利用较低TTL发送封包，突然增加的数据报丢失率或者响应时间通常提示较差的网络状况或者网络负载过高。支持IP ECHO、UDP、TCP三种类型的封包
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">mtr  [-hvrctglspeniuTP46]   [--help]   [--version]   
     [--report]   [--report-wide]   [--report-cycles COUNT]   
     [--curses]   [--split]   [--raw]  [--mpls]  [--no-dns]  
     [--show-ips]  [--gtk] [--address IP.ADD.RE.SS] 
     [--interval SECONDS] [--psize BYTES | -s BYTES] [--tcp] 
     [--port PORT] [--timeout SECONDS] HOSTNAME [PACKETSIZE]</pre>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-r --report 以报告模式运行，将运行-c指定的次数，然后打印统计信息并退出<br />-w --report-wide 宽格式，不会截断主机名<br />-c --report-cycles COUNT ping包的数量，每个ping的周期为1秒<br />-s --psize BYTES 发送数据报的字节数，包含IP和ICMP头<br />-n --no-dns 显示数字的IP地址，不去查DNS<br />-b --show-ips 同时显示主机名和IP地址<br />-o --order FIELDORDER 设置输出列的顺序，例如 -o "LSD NBAW" 字符含义如下：L丢失率；D丢弃包数；R接收包数；S发送包数；N最新RTT；B最小RTT；W最大RTT；V标准偏差；G几何平均数；J当前抖动；M平均抖动；X最差抖动；I到达间隔抖动<br />-l --raw 使用原始输出格式<br />-a --address IP.ADD.RE.SS 绑定输出数据包到某个网络接口<br />-i SECONDS --interval SECONDS ICMP ECHO请求的发送间隔<br />-u 使用UDP数据报代替ICMP ECHO<br />-T --tcp 使用TCP SYN 包代替ICMP ECHO<br />-P PORT --port PORT 目标TCP端口<br />--timeout SECONDS 放弃连接之前保持TCP套接字打开的秒数<br />-4 -6 仅仅使用 IPv4或者 IPv6</p>
</td>
</tr>
<tr>
<td class="blog_h2">ping</td>
<td>
<p>发送因特网控制报文协议（ICMP）的ECHO_REQUEST数据报到网络上的主机<br /><span style="background-color: #c0c0c0;">格式：</span><br />ping <br />　　[-aAbBdDfhLnOqrRUvV] [-c count] [-F flowlabel] [-i interval] [-I interface] <br />　　[-l preload] [-m mark] [-M pmtudisc_option] [-N nodeinfo_option] [-w deadline] <br />　　[-W timeout] [-p pattern]　[-Q tos] [-s packetsize] [-S sndbuf] [-t ttl] <br />　　[-T timestamp option] [hop ...] destination</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-c count 发送count个数据报后，停止发送，如果指定-w，会等待所有应答返回，直到超时<br />-D 在每一行的开始打印时间戳 Print timestamp (unix time + microseconds as in gettimeofday) before each line.<br />-f 只显示点号，当发送一个数据报时，打印一个点，收到一个响应时，打印一个退格键，如果不指定-i，则尽可能快的发送数据报<br />-i interval 两次发送数据报时的间隔，默认1秒<br />-I interface 指定地址或者网络接口的名称，数据报从该接口发出<br />-n 在输出中使用数字，不解析为主机名<br />-O 打印未到达的响应，一般与-D联用<br />-q 安静模式，只在开始和结束时打印统计信息<br />-r 跳过默认的路由表，直接往目标主机发送报文 <br />-s packetsize 发送的数据字节数，默认56，加上8字节的ICMP头，共计64字节<br />-w deadline 指定超时的秒数，ping在退出前会等待</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">ping -f blog.gmem.cc</pre>
</td>
</tr>
<tr>
<td class="blog_h2">whois</td>
<td>执行DNS注册信息查询</td>
</tr>
<tr>
<td class="blog_h2">curl</td>
<td>
<p>向服务器传输、从服务器读取数据。支持DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3,<br /> POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET和TFTP等协议
<p><span style="background-color: #c0c0c0;">格式：</span> <pre class="crayon-plain-tag">curl [options] [URL...]</pre></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p># 显示进度条<br />-0 使用HTTP1.0<br />--http1.1 使用HTTP1.1<br />--http2.0 使用HTTP2.0<br />-1 使用TLSv1<br />-2 使用SSLv2<br />-3 使用SSLv3<br />-4 使用IPv4<br />-6 使用IPv6<br />-a 在用于FTP/SFTP上传时，附加到目标文件，而非替换它<br />-A 设置UserAgent<br />--anyauth 自动识别身份验证方式<br />-b 发送Cookie信息<br />-c 保存Cookie信息<br />-d 指定请求体参数<br />-D 输出Header<br />-e 设置Referer<br />-G 强制以GET方法提交<br />-H 设置请求头<br />-L 自动处理重定向<br />-X 指定使用的HTTP方法<br />--resolve host:port:ip 覆盖系统默认DNS解析<br />-w 指定输出的内容，可以用%{variable_name}格式指定输出变量。支持的变量包括：</p>
<p style="padding-left: 30px;">content_type  MIME类型<br />http_code  响应状态码<br />time_namelookup 到解析域名成功所消耗的时间<br />time_connect 到建立TCP连接所消耗的时间<br />time_total  总计消耗时间</p>
<p>-s  安静模式，不显示进度和错误信息<br />-v  显示冗长的调试性信息</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 输出响应到控制台
curl https://blog.gmem.cc
# 输出响应到文件
curl -o index.html https://blog.gmem.cc

# 设置HTTP头
curl -H "Host: ci.gmem.cc"  https://localhost

# HTTPS覆盖服务器DNS名
curl -sv --resolve git.pacloud.io:443:127.0.0.1 https://git.pacloud.io

# 下载文件到当前目录
curl -O https://www.gmem.cc/100.bin

# 通过SFTP下载
curl -u alex sftp://bj.gmem.cc/path/to/file

# 通过HTTP上传
curl --insecure -u "USER:PSWD" --data-binary "@/path/to/file" http://gmem.cc

# 获取HTTP头
curl -I http://www.gmem.cc

# 访问FTP服务器，列出文件和目录
curl ftp://ftp.gmem.cc --user username:password
# 上传文件到FTP服务器
curl -T file.zip ftp://ftp.gmem.cc/ --user username:password

# 设置Referer
curl -e "https://blog.gmem.cc/" https://gmem.cc

# POST
curl -d "name=alex&amp;password=111" http://blog.gmem.cc/login

# 保存Cookie
curl -c cookie.txt https://gmem.cc
# 发送Cookie
curl -b "key=value"  https://gmem.cc
curl -b cookie.txt https://gmem.cc

# 保存头
curl -D header.txt url

# 获取状态码

# 指定HTTP方法
curl -X PUT -d arg=val -d arg2=val2 localhost:8080

# 覆盖DNS解析
curl --resolve httpbin.k8s.gmem.cc:443:10.102.172.91 https://httpbin.k8s.gmem.cc/status/418

# 指定客户端证书
curl --cacert /home/alex/Documents/puTTY/ca.crt --cert alex.cert --key alex.key ... 

# 仅仅输出响应状态码
curl -s -o /dev/null -I -w "%{http_code}" http://10.0.11.11

# 设置UA
curl -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17" url
curl -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36" url</pre>
</td>
</tr>
<tr>
<td class="blog_h2">wget</td>
<td>
<p>非交互的从web或ftp站点上取得或下载文件
<p><span style="background-color: #c0c0c0;">格式：</span><br />wget [option]... [URL]...</p>
<p><span style="background-color: #c0c0c0;">基本选项：</span><br />-b --background 启动后转入后台运行<br /><span style="background-color: #c0c0c0;">日志选项：</span><br />-d --debug 打印调试输出<br />-q --quiet 安静模式，没有任何输出<br />-v --verbose 冗长模式，输出更多信息<br />-nv --non-verbose 关掉冗长模式，但不是安静模式<br />-o --output-file=LOG 把日志写到LOG文件中<br />-a --append-output=LOG 把日志追加到FILE文件中<br /><span style="background-color: #c0c0c0;">输入选项：</span><br />-i --input-file=FILE 读取输入文件，并下载其中的URL<br />-F --force-html 把输入文件当作HTML格式文件对待<br />-B --base=URL 基地址，用于解析输入文件中的相对地址<br /><span style="background-color: #c0c0c0;">下载选项：</span><br /> --bind-address=a 指定本地使用地址或主机名<br />-t --tries=NUMBER 设定最大尝试连接次数，0表示无限制<br />-O --output-document=F 把下载内容写到文件F中<br />-nc --no-clobber 不要覆盖存在的文件<br /> --backups=backups 备份被覆盖的文件，backups指定最大备份数量<br />-c --continue 接着下载没下载完的文件，断点续传<br /> --progress=dot/bar 设定进度条标记<br />-N --timestamping 不要重新下载文件除非比本地文件更新<br />-S --server-response 打印服务器的响应头<br />-T --timeout=SECONDS 设定响应超时的秒数<br />-w --wait=SECONDS 两次尝试之间间隔SECONDS秒<br />-Y --proxy=on/off 打开或关闭代理<br />-Q --quota=NUMBER 设置下载的容量限制，可以附带K/M/G单位<br /> --limit-rate=RATE 限定每秒下载输率，可以附带K/M/G单位<br />-4 --inet4-only 只使用IPv4<br />-6 --inet6-only 只使用IPv6<br /> --user=user<br /> --password=password 使用FTP或者HTTP用户名及密码<br /> --ask-password 手工输入密码<br /> --no-iri 禁用国际化的URL，默认禁用<br /><span style="background-color: #c0c0c0;">目录选项：</span><br />-nd --no-directories 当递归的下载时不创建目录层次，将导致所有文件存放在当前目录<br />-x --force-directories 与-nd相反，强制创建相应的目录层次<br />-nH --no-host-directories 禁止生成主机前缀目录，默认的http://gmem.cc会创建gmem.cc/目录<br /> --cut-dirs=number 忽略number层递归的远程目录的创建<br />-P --directory-prefix=p 下载文件保存到p/目录下<br /><span style="background-color: #c0c0c0;">HTTP选项：</span><br /> --default-page=name URL目录默认地址，用于代替index.html<br />-E --adjust-extension 根据Mime类型来调整文件扩展名<br /> --http-user=user<br /> --http-password=pswd 覆盖user/password，提供HTTP验证用户密码<br /> --no-http-keep-alive 关闭HTTP的keep-alive特性，默认的，连续下载多个文件会使用同一TCP连接<br /> --no-cache 禁用服务器端缓存，将发送no-cache请求头<br /> --no-cookies 禁用cookies<br /> --load-cookies file 从文件载入cookies<br /> --save-cookies file 保存cookiles<br /> --keep-session-cookies 让--save-cookies同时保存会话级cookies，默认会话级cookie在会话结束即失效<br /> --ignore-length 忽略响应头Content-Length<br /> --header=header-line 设置请求头，可以指定该选项多次<br /> --max-redirect=number 最大重定向次数<br /> --proxy-user=user<br /> --proxy-password=pswd 代理服务器验证使用<br /> --referer=url 设置请求头Referer: url<br /> --save-headers 保存服务器发送的请求头道文件<br />-U --user-agent=agent 指定请求头User-Agent<br /> --post-data=string<br /> --post-file=file 使用POST发送所有请求，并把请求体设置为参数指定的内容<br /> --method=HTTP-Method 指定使用的HTTP方法，例如POST或者GET<br /><span style="background-color: #c0c0c0;">HTTPS选项：</span> <br /> --secure-protocol=p 使用的安全协议，例如auto, SSLv2, SSLv3, TLSv1, PFS<br /> --https-only 递归下载时，仅下载HTTPS链接<br /> When in recursive mode, only HTTPS links are followed.<br /> --no-check-certificate 不验证服务器证书，也不要去URL的主机名与证书匹配<br /> --certificate=file 指定客户端证书所在的文件<br /> --certificate-type=type 客户端证书的类型，例如PEM,DER<br /> --private-key=file 指定私钥文件，允许私钥与证书存放在不同文件<br /> --private-key-type=type 私钥类型，例如PEM,DER<br /> --ca-certificate=file 指定CA证书文件<br /> --ca-directory=directory 指定CA证书的目录（PEM格式）<br /> --random-file=file<br /><span style="background-color: #c0c0c0;">递归下载：</span><br />-r, --recursive 递归下载<br />-l, --level=NUMBER 最大递归深度<br /> --delete-after 在下载完毕后局部删除文件<br />-k, --convert-links 转换非相对链接为相对链接<br />-K, --backup-converted 在转换文件X之前，将之备份为 X.orig<br />-m, --mirror 等价于 -r -N -l inf -nr.<br />-p, --page-requisites 下载为了展示HTML页面所必须的所有文件，例如图片<br />-A, --accept=LIST 分号分隔的被接受扩展名的列表<br />-R, --reject=LIST 分号分隔的不被接受的扩展名的列表<br />-D, --domains=LIST 分号分隔的被接受域的列表<br /> --exclude-domains=LIST 分号分隔的不被接受的域的列表<br /> --follow-ftp 跟踪HTML文档中的FTP链接<br /> --follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表<br />-G, --ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表<br />-H, --span-hosts 当递归时转到外部主机<br />-L, --relative 仅仅跟踪相对链接<br />-I, --include-directories=LIST 允许目录的列表<br />-X, --exclude-directories=LIST 不被包含目录的列表<br />-np, --no-parent 不要追溯到父目录</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#下载JDK7
wget 
  --no-cookies 
  --no-check-certificate 
  --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" 
   "http://download.oracle.com/otn-pub/java/jdk/7u67-b01/jdk-7u67-linux-x64.tar.gz" 
   -O jdk-7u67-linux-x64.tar.gz
#下载Tomcat7
wget 
   "http://mirrors.hust.edu.cn/apache/tomcat/tomcat-7/v7.0.61/bin/apache-tomcat-7.0.61.tar.gz" 
   -O apache-tomcat-7.0.61.tar.gz</pre>
</td>
</tr>
<tr id="network-command-route">
<td class="blog_h2">route</td>
<td>
<p>显示或修改内核路由表信息
<p><span style="background-color: #c0c0c0;">格式：</span><br /> route [-CFvnee]<br /> route [-v] [-A family] add [-net|-host] target [netmask Nm] [gw Gw] [metric N] [mss M] [window W] [irtt I] [reject] [mod] [dyn] [reinstate] [[dev] If]<br /> route [-v] [-A family] del [-net|-host] target [gw Gw] [netmask Nm] [metric N] [[dev] If]<br /> route [-V] [--version] [-h] [--help]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-A family 使用指定的地址组，例如inet<br />-F 在内核的FIB路由表上操作，默认值 <br />-C 在内核的路由表缓存上操作<br />-n 显示基于数字的地址，而不是尝试将其解析为符号主机名 <br />-e 使用netstat(8)格式显示路由表<br />-ee 显示所有输出列<br />del 删除一条路由<br />add 添加一条路由<br />target 路由的目标网络或者主机，支持IP和主机名<br />-net 指定目标类型是网络<br />-host 指定目标类型是主机<br />netmask NM 添加网络的路由时使用，子网掩码<br />gw GW 通过网关（路由器）来路由，路由器必须直接可达<br />metric M 设置路由表的metric字段<br />mss M 设置通过此路由的TCP连接的最大段大小(Maximum Segment Size，MSS)，单位字节，默认为设备的MTU减去头部<br />window W 设置通过此路由的TCP连接的TCP窗口大小（TCP window size），单位字节，一般仅在AX.25 网络使用<br />irtt I 设置通过此路由的TCP连接的初始往返时间（initial round trip time，irtt），单位毫秒，一般仅在AX.25 网络使用<br />reject 安装一个阻断路由，用于强制一个路由查找失败<br />mod, dyn, reinstate 安装一个动态或者修改的路由，主要用于诊断用途<br />dev If 强制路由域指定的网络接口关联。否则内核会自己尝试判断使用哪个网络接口，如果该选项位于命令行尾部，dev字样可以省略</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#添加一个普通的环回条目，使用子网掩码255.0.0.0，关联到网卡lo
route add -net 127.0.0.0 netmask 255.0.0.0 dev lo
#添加一个路由，使本地网络192.56.76.0通过网卡eth0访问
route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
#添加一条默认路由（默认路由在其它路由不匹配时生效）
#通过此路由的数据报都发往路由器mango-gw
#通往mango-gw的静态路由必须被事先设置
route add default gw mango-gw
#添加一条针对网络10.x.x.x的拒绝路由
route add -net 10.0.0.0 netmask 255.0.0.0 reject

#删除当前默认路由，默认路由的目的显示为 "default" 或者 0.0.0.0
route del default
#删除经有网关docker通向网络172.27.0.0/16的路由 
route del -net 172.27.0.0 netmask 255.255.0.0 gw docker</pre>
<p><span style="background-color: #c0c0c0;">输出：</span><br />包含以下列：<br />Destination 目标网络或者主机<br />Gateway 网关地址，如果没有设置，则为*（0.0.0.0）<br />Genmask 目的（即路由到的）网络的子网掩码。目标是H显示255.255.255.255，默认路由显示0.0.0.0。之所以叫Genmask使因为此字段显示了此路由的覆盖性（Generality）<br />Flags 标记，包括<br />　　U (该路由可用)<br />　　H (目标是主机)<br />　　G (使用网关，该标记非常重要，用于区分是直接还是间接路由，前者不需要G)<br />　　R (动态路由的复原路由)<br />　　D (该路由由重定向报文创建)<br />　　M (该路由已被重定向报文修改)<br />　　A (通过addrconf添加的路由)<br />　　C (缓存条目)<br />　　! (拒绝路由)</p>
<p>Metric　到目标的“距离”，使用hops计数表示<br />Ref　正在使用路由的活动进程个数<br />Use　显示的是通过该路由发送的分组数<br />Iface　数据报发往的网络接口<br />MSS　通过此路由的TCP连接的最大段大小<br />Window　通过此路由的TCP连接的TCP窗口大小<br />irtt　通过此路由的TCP连接的初始往返时间，内核使用该值猜测理想TCP协议参数</p>
</td>
</tr>
<tr>
<td class="blog_h2">netcat<br />nc</td>
<td>
<p>通过TCP和UDP在网络中读写数据</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 端口扫描：扫描172.31.100.7的21-25之间的端口
# -z 连接成功后立即关闭
# -v 输出详细信息
# -n 不要进行DNS反查
nc -z -v -n 172.31.100.7 21-25

# 开启服务器
nc -l 1567
# 开启客户端
nc 172.31.100.7 1567

# 文件传输
nc -l 1567 &lt; file.txt
nc -n 172.31.100.7 1567 &gt; file.txt

# 磁盘克隆
dd if=/dev/sda | nc -l 1567
nc -n 172.31.100.7 1567 | dd of=/dev/sda</pre>
</td>
</tr>
<tr>
<td>socat</td>
<td>
<p>多用途的套接字转发器，能够建立两个双向套接字，并在其间中继数据
<p>socat命令实例的生命周期分为以下阶段：</p>
<ol>
<li>init阶段：解析选项、初始化日志</li>
<li>open阶段：打开第一个地址，然后打开第二个地址。该步骤通常是非阻塞的</li>
<li>transfer阶段：通过select()监控两个流的文件描述符上的读写，并进行中继</li>
<li>closing阶段：当某一个流到达EOF，该阶段启动，传输EOF到另外一个流</li>
</ol>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">socat [options] &lt;address&gt; &lt;address&gt;</pre>
<p>-d 打印更多信息，指定-d多次，则信息更详细，最多4个<br />-D 进入transfer模式之前，打印文件描述符信息<br />-lf 打印日志到文件而非stderr<br />-x 同时将中继的数据写入到stderr<br />-u 非双向模式，从第一个地址读，写入第二个<br />-U 非双向模式，从第二个地址读，写入第一个</p>
<p><span style="background-color: #c0c0c0;">地址规格：</span></p>
<p style="padding-left: 30px;">地址类型关键字:[地址参数1:地址参数...][,地址选项1,地址选项2...]</p>
<p>地址类型关键字大小写不敏感，对于某些地址类型，关键字可以省略</p>
<p>如果地址规格以数字开头，则被假设为FD（原始文件描述符）地址，如果在第一个:或,之前出现 /，则假设关键字为GOPEN（一般性文件打开）</p>
<p><span style="background-color: #c0c0c0;">地址类型：</span></p>
<p>CREATE:&lt;filename&gt;  以create()打开指定的路径，并使用FD进行写（以create打开的不支持读）操作<br />EXEC:&lt;command-line&gt;  创建子进程，通过execvp()调用一个程序<br />FD:&lt;fdnum&gt;  打开文件描述符<br />GOPEN:&lt;filename&gt; 打开文件<br />OPEN:&lt;filename&gt;  使用open()调用打开文件，针对UDS会出错<br />IP-SENDTO:&lt;host&gt;:&lt;protocol&gt;  打开原始IP套接字，进行发送<br />IP4-SENDTO:&lt;host&gt;:&lt;protocol&gt;<br />IP6-SENDTO:&lt;host&gt;:&lt;protocol&gt;<br />IP-RECVFROM:&lt;protocol&gt;  打开原始IP套接字，进行接收<br />IP4-RECVFROM:&lt;protocol&gt;<br />INTERFACE:&lt;interface&gt;  连接到网卡，使用链路层通信<br />STDIN：打开标准输入<br />STDOUT：打开标准输出<br />TCP:&lt;host&gt;:&lt;port&gt; 连接到TCP服务器<br />TCP-LISTEN:&lt;port&gt; 打开TCNP端口并监听<br />UDP:&lt;host&gt;:&lt;port&gt; 发送到UDP服务器<br />UDP-LISTEN:&lt;port&gt; 打开UDP端口并监听<br />UNIX-CONNECT:&lt;filename&gt;  连接到UDS套接字<br />UNIX-LISTEN:&lt;filename&gt; 监听UDS套接字<br />ABSTRACT-CONNECT:&lt;string&gt;  和UNIX类似，仅仅针对抽象命名空间<br />ABSTRACT-LISTEN:&lt;string&gt;<br />ABSTRACT-SENDTO:&lt;string&gt;<br />ABSTRACT-RECVFROM:&lt;string&gt;<br />ABSTRACT-RECV:&lt;string&gt;<br />ABSTRACT-CLIENT:&lt;string&gt;</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 从文件读取信息
socat - /tmp/hello
# 写入信息到文件
echo hello | socat - /tmp/hello

# 连接到远程端口
socat - TCP:192.168.1.252:3306
# 监听端口
socat TCP-LISTEN:7000 -

# 监听抽象UDS
socat ABSTRACT-LISTEN:xtables -

# 端口转发 
socat -d -d -lf /var/log/socat.log 
  # 监听端口         绑定地址           
  TCP4-LISTEN:15672,bind=192.168.1.252,reuseaddr,fork 
  # 转发到的地址
  TCP4:172.17.0.15:15672</pre>
</td>
</tr>
<tr>
<td class="blog_h2">netstat</td>
<td>
<p>显示当前网络的统计和信息， 比如网络连接、路由表、网卡统计、组播关系
<p><strong><span style="background-color: #c0c0c0;">格式：</span></strong></p>
<pre class="crayon-plain-tag">netstat [address_family_options] [--tcp|-t] [--udp|-u] [--raw|-w] [--listening|-l] [--all|-a] [--numeric|-n] [--numeric-hosts] [--numeric-ports] [--numeric-users] [--symbolic|-N] [--extend|-e[--extend|-e]] [--timers|-o] [--program|-p] [--verbose|-v] [--continuous|-c]
netstat {-route|-r} [address_family_options] [--extend|-e[--extend|-e]] [--verbose|-v] [--numeric|-n] [--numeric-hosts] [--numeric-ports] [--numeric-users] [--continuous|-c]
netstat {-interfaces|-i} [--all|-a] [--extend|-e[--extend|-e]] [--verbose|-v] [--program|-p] [--numeric|-n] [--numeric-hosts] [--numeric-ports] [--numeric-users] [--continuous|-c]
netstat {-groups|-g} [--numeric|-n] [--numeric-hosts] [--numeric-ports] [--numeric-users] [--continuous|-c]
netstat {-masquerade|-M} [--extend|-e] [--numeric|-n] [--numeric-hosts] [--numeric-ports] [--numeric-users] [--continuous|-c]
netstat {-statistics|-s} [--tcp|-t] [--udp|-u] [--raw|-w]
netstat {-version|-V}
netstat {-help|-h} address_family_options: [-4] [-6] [--protocol={inet,unix,ipx,ax25,netrom,ddp}[,...]] [--unix|-x] [--inet|--ip] [--ax25] [--ipx] [--netrom] [--ddp]</pre>
<p><strong><span style="background-color: #c0c0c0;">子命令描述：</span></strong><br />本命令打印Linux网络系统的信息，信息的类型由第一个参数控制：<br />(none) 默认的，显示开启的Socket的信息，如果不指定任何地址族（address families）则所有配置的地址族均显示<br />--route , -r 显示内核路由表<br />--groups , -g 显示IPv4、IPv6组播关系<br />--interfaces, -i 显示网卡的表格<br />--masquerade , -M 显示masqueraded connections（Linux下类似于one-to-many的NAT的功能，允许内网机器通过单台连接到互联网机器上网）<br />--statistics , -s 显示各协议的统计信息</p>
<p><strong><span style="background-color: #c0c0c0;">选项：</span></strong><br />-A, --protocol=family 只显示选定的地址族（Socket种类），逗号分隔，包括inet, unix, ipx, ax25, netrom, ddp<br />--wide , -W 不截断IP地址<br />-n ,--numeric 显示数字的地址、端口、用户名，不指定可能显示主机名、知名端口名称<br />--numeric-hosts 显示数值的地址，不影响端口和用户名的解析<br />--numeric-ports 显示数值的端口，不影响主机和用户名的解析<br />--numeric-users 显示数值的用户ID，不影响主机和用户名的解析<br />--protocol=family , -A 指定地址族，逗号分隔<br />-c, --continuous 每秒刷新一次<br />-e, --extend 显示额外的信息，使用两次显示最大化信息<br />-o, --timers 包含和网络计时器相关的信息<br />-p, --program 显示每个Socket归属的PID和程序名<br />-l, --listening 仅显示监听Socket<br />-a, --all 显示监听和非监听Socket<br />-F 打印来自FIB的路由信息<br />--verbose , -v 显示冗长信息<br />-C 打印来自路由缓存的路由信息</p>
<p><span style="background-color: #c0c0c0;"><strong>输出：</strong></span></p>
<p><span style="background-color: #c0c0c0;">--- 默认输出 ---</span></p>
<pre class="crayon-plain-tag">root@gmem:~# netstat
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 li759-7.members.l:59042 74.125.23.1:xmpp-client ESTABLISHED
tcp        0      0 localhost:48305         localhost:64001         TIME_WAIT
tcp        0    268 li759-7.members.lin:ssh 119.57.167.18:2065      ESTABLISHED
tcp        0      0 li759-7.members.li:http 119.57.167.18:5058      FIN_WAIT2

Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  11     [ ]         DGRAM                    8076     /dev/log
unix  2      [ ]         DGRAM                    8344
unix  3      [ ]         STREAM     CONNECTED     7974     @/com/ubuntu/upstart</pre>
<p><span style="background-color: #c0c0c0;">Active Internet connections (TCP, UDP, raw)</span><br />Proto　Socket使用的协议(tcp, udp, raw) <br />Recv-Q　通过该Socket接收到的，未被用户程序拷贝的字节数<br />Send-Q　通过该Socket接收到的，未被远程主机确认的字节数<br />Local Address　本地通信地址、端口<br />Foreign Address　远程通信地址、端口<br />State Socket的状态：<br />　　ESTABLISHED Socket包含一个建立的连接<br />　　SYN_SENT 正在尝试建立连接<br />　　SYN_RECV 已经从网络接收到连接请求<br />　　FIN_WAIT1 Socket被关闭，并且连接正在关闭<br />　　FIN_WAIT2 连接被关闭，Socket正在等待远端（remote end ）的关闭动作<br />　　TIME_WAIT Socket正在等待接收（于关闭后）仍然在网络上的数据包<br />　　CLOSE Socket没有被使用<br />　　CLOSE_WAIT 远端（remote end ）已经关闭，等待Socket的关闭<br />　　LAST_ACK 远端（remote end ）已经关闭，Socket被关闭，等待确认<br />　　LISTEN Socket正在监听传入连接<br />　　CLOSING 双方Socket都在关闭，但是本端仍有数据没发送<br />　　UNKNOWN 状态未知<br />User　Socket所有者的用户名或者UID<br />PID/Program　斜线分隔Socket所有者的PID和程序名</p>
<p><span style="background-color: #c0c0c0;">Active UNIX domain Sockets</span><br />Proto　Socket使用的协议(通常unix) <br />RefCnt　引用计数（附着到该Socket的进程数）<br />Flags　包括： SO_ACCEPTON (ACC)， SO_WAITDATA (W)， SO_NOSPACE (N)<br />Type Socket类型<br />　　SOCK_DGRAM Socket以数据报（无连接）模式使用<br />　　SOCK_STREAM Socket以数据流（有连接）模式使用<br />　　SOCK_RAW  原始Socket<br />　　SOCK_RDM 仅可靠传输消息<br />　　SOCK_SEQPACKET 顺序数据包<br />　　UNKNOWN <br />Path　附着到该Socket的进程路径</p>
<p><span style="background-color: #c0c0c0;">--- --route输出 ---</span></p>
<pre class="crayon-plain-tag">root@gmem:~# netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         gw-li759.linode 0.0.0.0         UG        0 0          0 eth0
106.185.46.0    *               255.255.255.0   U         0 0          0 eth0</pre>
<p>Destination　目标网络或者主机<br /> Gateway 　网关地址<br /> Genmask 　目标网络的子网掩码<br /> Flags　标记<br /> MSS 　通过此路由的TCP连接的最大段大小<br /> Window 　通过此路由的TCP连接的TCP窗口大小<br /> irtt 　通过此路由的TCP连接的初始往返时间<br /> Iface　所使用的网络接口</p>
<p><span style="background-color: #c0c0c0;">--- --groups输出 ---</span></p>
<pre class="crayon-plain-tag">root@gmem:~# netstat --groups
IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
lo              1      all-systems.mcast.net
eth0            1      all-systems.mcast.net
lo              1      ip6-allnodes</pre>
<p>Interface　参与组播的网络接口<br />RefCnt　参与到此多播组的进程数量<br />Group　多播组的名称或地址</p>
<p><strong><span style="background-color: #c0c0c0;">举例：</span></strong></p>
<pre class="crayon-plain-tag">#列出所有处于监听状态的TCP端口，使用数字来显示地址与端口
#如果不指定n参数，可能会以“知名端口”名来显示端口，不利于直观的查看
#例如8080端口可能显示为sunrpc
netstat -ltn
netstat -a  #列出所有端口
netstat -at #列出所有tcp端口
netstat -au #列出所有udp端口 
netstat -l  #列出所有处于监听状态的 Sockets
netstat -s  #显示所有端口的统计信息
netstat -st # 显示TCP端口的统计信息
netstat -p  #在输出中显示PID和进程名称
netstat -antp -4 #显示所有IPv4的监听、非监听Socket，并且不显示主机名
netstat -r  #显示核心路由信息
netstat -ap | grep ssh   #搜索程序运行的端口
netstat -an | grep ':80' #搜索占用端口的进程
netstat -i  #显示网络接口列表</pre>
</td>
</tr>
<tr>
<td class="blog_h2">tcpdump</td>
<td>
<p>网络包嗅探器。把匹配指定expression的包头都显示出来。在tcpdump运行结束后，会报告以下内容：<br />捕获的包个数（tcpdump接收被处理的包个数）<br />内核丢弃的包个数（由于缺少缓冲区）
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<p>tcpdump <br />　　[ -AbdDefhHIJKlLnNOpqRStuUvxX ] [ -B buffer_size ] [ -c count ]<br />　　[ -C file_size ] [ -G rotate_seconds ] [ -F file ]<br />　　[ -i interface ] [ -j tstamp_type ] [ -m module ] [ -M secret ]<br />　　[ -P in|out|inout ]<br />　　[ -r file ] [ -V file ] [ -s snaplen ] [ -T type ] [ -w file ]<br />　　[ -W filecount ]<br />　　[ -E spi@ipaddr algo:secret,... ]<br />　　[ -y datalinktype ] [ -z postrotate-command ] [ -Z user ]<br />　　[ expression ]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-A 以ASCII形式打印每个包（除去连接层头部）<br />-B 设值操作系统捕获缓冲区大小，单位KB<br />-c 接收到指定数量的包后退出<br />-C 在把原始包写入文件前，检查当前文件是否大于file_size，如果是，关闭并打开新文件。后续文件的名称依据-w选项确定，后缀以1开始的序号，file_size的单位是100万字节<br />-D 打印可捕获包的网络接口的列表，对于每个接口，打印一个数字、接口名称，可能附加一个文字描述<br />-e 在dump的每一行打印链路层头部，可以用于为以太网、IEEE 802.x等协议打印MAC地址<br />-F 使用文件作为过滤表达式（filter expression）的输入。如果指定该参数，命令行提供的表达式被忽略<br />-h 打印tcpdump、libpcap版本和用法，并退出<br />-i 指定需要监听的网络接口<br />-j 设置时间戳类型为tstamp_type<br />-J 列出网络接口支持的时间戳类型并退出<br />-K 不去尝试验证IP、TCP、UDP的校验和<br />-L 列出网卡已知的数据链路类型，并退出<br />-n 不把地址（主机地址、端口等）转换为名称<br />-N 不去打印主机名所属域部分<br />-P 选择需要捕捉的包的类型（发送还是接收），有效的值包括in、out、inout<br />-p 不要把网卡改为混杂模式<br />-q 快速模式，打印较少的协议信息<br />-r 从文件中读取包（文件是通过-w选项创建的）<br />-S 打印绝对（而不是相对）的TCP序列号<br />-t 不在每一行上打印时间戳<br />-tt 在每一行打印未格式化的时间戳<br />-ttt 打印与前一行的时间偏移量，以微秒为单位<br />-tttt 使用默认格式打印时间戳<br />-ttttt 打印与第一行的时间偏移量，以微秒为单位<br />-u 打印未编码的NFS句柄<br />-v 冗余输出IP包的选项字段被打印，同时启用额外的包完整性检查，例如验证IP、ICMP头的校验和。使用-w时每10秒打印捕获的包个数<br />-vv 更冗余的输出<br />-vvv 更更冗余的输出<br />-w 打印原始包到文件，而不是解析、打印到标准输出<br />-W 与-C联用，限制文件的数量，如果超过了，循环覆盖第一个文件<br />-x 当解析并打印时，除了打印包的头部，还以HEX形式打印包除去链路层头的数据部分<br />-xx 当解析并打印时，除了打印包的头部，还以HEX形式打印包的数据部分<br />-X 当解析并打印时，除了打印包的头部，还以HEX、ASCII形式打印包除去链路层头的数据部分<br />-XX 当解析并打印时，除了打印包的头部，还以HEX、ASCII形式打印包的数据部分<br />-y 在捕获包到datalinktype时设置datalinktype</p>
<p>关于表达式<br />表达式用于过滤哪些包需要被输出，如果不指定表达式，则所有包被输出，表达式语法参考pcap-filter</p>
<p><span style="background-color: #c0c0c0;">关于iptables：</span></p>
<p>对于<span style="background-color: #c0c0c0;">出站流量来说，tcpdump是最后一个看见流量的软件</span>，也就是说，只要tcpdump看到流量，就说明本机没有软件层次上的防火墙措施。<span style="background-color: #c0c0c0;">如果iptables DROP掉了包，则tcpdump不会抓到包</span></p>
<p style="padding-left: 30px;">iptables ⇨ tcpdump ⇨ 网络接口 ⇨网线</p>
<p>对于<span style="background-color: #c0c0c0;">入站流量来说，tcpdump是第一个看见流量的软件</span></p>
<p style="padding-left: 30px;">网线  ⇨ 网络接口 ⇨ tcpdump ⇨ netfilter/iptables</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 要在Vmware Guest上使用tcpdump，需要在Host上设置网卡的权限
sudo chmod a+rw /dev/vmnet8

# 打印来自、发往主机：zircon的包
tcpdump host zircon
# 打印 zircon与vm1或者vm2之间的包，注意花括号需要转义
tcpdump host zircon and \( vm1 or vm2 \)
# 打印zircon与除vm3以外所有主机的IP包
tcpdump ip host zircon and not vm3
# 打印所有通过网关snup的FTP流量
tcpdump 'gateway snup and (port ftp or ftp-data)'
# 打印既不是来源于、也不是发送到本地网络的IP数据报
tcpdump ip and not net 192.168.0.0/16
# 打印主机192.168.0.201与本机端口7777的IP数据报
tcpdump ip host 192.168.0.201 and port 7777 -n -p
# 对于每一个非本地主机参与的TCP连接，打印起始（SYN）与结束（FIN）包
tcpdump 'tcp[tcpflags] &amp; (tcp-syn|tcp-fin) != 0 and not src and dst net 192.168.0.0/16'
# 打印来自或者去往80端口的IPv4的HTTP包
tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&amp;0xf)&lt;&lt;2)) - ((tcp[12]&amp;0xf0)&gt;&gt;2)) != 0)'
# 打印通过网关snup，且大于576字节的IP数据报
tcpdump 'gateway snup and ip[2:2] &gt; 576'
# 打印不是通过以太网广播、多播的IP广多播包
tcpdump 'ether[0] &amp; 1 = 0 and ip[16] &gt;= 224'
# 打印非echo请求应答（ping）的ICMP包
tcpdump 'icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply'

# 监听所有网络接口
tcpdump -i any port 2379</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ssh</td>
<td>
<p>OpenSSH客户端
<p><span style="background-color: #c0c0c0;">格式：</span><br />ssh <br />　　[-1246AaCfgKkMNnqsTtVvXxYy] <br />　　[-b bind_address] [-c cipher_spec] [-D [bind_address:]port] <br />　　[-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]<br />　　[-L  [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec] <br />　　[-O ctl_cmd] [-o option] [-p port] [-Q cipher | cipher-auth | mac | kex | key]<br />        [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] <br />        [-w local_tun[:remote_tun]] <br />        [user@]hostname [command]</p>
<p><span style="background-color: #c0c0c0;">描述：</span><br />SSH客户端用于<span style="background-color: #c0c0c0;">登陆到远程机器，并在远程机器上执行命令</span>。支持在两个不相互信任的机器之间，通过不安全网络建立安全连接。<br />如果提供command选项，那么会在远程机器上执行命令而不是登陆为Shell<br />SSH客户端亦可充当<span style="background-color: #c0c0c0;">SOCKS代理服务器</span>，进行<span style="background-color: #c0c0c0;">端口转发</span>，或者实现<span style="background-color: #c0c0c0;">反向连接（到内网机器）</span></p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-1 强制ssh使用协议1<br />-2 强制ssh使用协议2<br />-4 强制ssh仅使用IPv4<br />-6 强制ssh仅使用IPv6<br />-q 安静模式，服务器的Banner不会显示<br />-b 仅在多IP机器上使用，指定连接的源地址<br />-C 要求对所有数据进行压缩。压缩算法于gzip一致<br />-c 指定连接的加密算法<br />-D 指定本地应用级的端口转发，发往本地bind_address:port的套接字连接一律通过SSH隧道转发。应用协议用来确定在远程机器上连接到何处，支持的协议是SOCKS4、SOCKS5，SSH充当SOCKS服务器<br />-E 将调试日至转发到文件而不是stderr<br />-F 指定备选的用户级别的配置文件，会导致全局配置文件<pre class="crayon-plain-tag">/etc/ssh/ssh_config</pre> 被忽略，默认用户级别配置文件为<pre class="crayon-plain-tag">~/.ssh/config</pre> <br />-f 让ssh在执行命令前转到后台运行。如果配置项ExitOnForwardFailure=true，则启用此选项的ssh客户端会在所有远程端口转发成功建立后才传入后台<br />-g 允许远程主机连接到本地已转发（forwarded）端口<br />-I 指定ssh使用的PKCS#11共享库<br />-i 指定用于公钥身份验证的私钥文件。对于协议1，默认文件<pre class="crayon-plain-tag">~/.ssh/identity</pre> ，对于协议2，默认文件<pre class="crayon-plain-tag">~/.ssh/id_dsa</pre> 、<pre class="crayon-plain-tag">~/.ssh/id_ecdsa</pre> 、<pre class="crayon-plain-tag">~/.ssh/id_ed25519</pre> 、<pre class="crayon-plain-tag">~/.ssh/id_rsa</pre> 。可以在配置文件中针对每个主机设置私钥文件<br />-L 将本机[bind_address:]port转发到远程host:hostport<br />-l 登陆到远程机器时使用的用户名<br />-N 不执行远程命令，在仅仅用ssh进行端口转发时指定<br />-n 重定向标准输入到/dev/null，当ssh在后台运行时，必须指定，指定-f时自动指定此选项<br />-o 覆盖配置文件中的选项，例如-o "IdentityFile=gmem.key"<br />-p 远程主机的监听端口<br />-R 反向端口转发，远程机器（服务器）[bind_address:]port被自动转发到本地机器host:hostport。该选项启用后，ssh在远程机器（服务器）上开启port的监听，并把针对远程机器bind_address地址port端口的请求全部通过SSH隧道转发到本地。忽略bind_address或者将其指定为*则绑定远程机器的所有IP地址<br />注意：如果服务器使用的是OpenSSH，必须启用</p>
<pre class="crayon-plain-tag">GatewayPorts yes</pre>
<p>否则总是绑定到127.0.0.1<br />-T 不分配伪终端（pseudo-tty）</p>
<p>-t 强制分配伪终端<br />-v 冗长模式，打印调试信息</p>
<p><span style="background-color: #c0c0c0;">端口转发功能：</span></p>
<p>SSH客户端支持三种端口转发（SSH隧道）：</p>
<p>-L 本机网络接口:本机端口:远程网络接口:远程端口。正向转发，本机可以通过本机端口访问远程服务<br />-R 远程网络接口:远程端口:本机网络接口:本机端口。反向转发，远机可以通过本机端口访问本机服务<br />-D 本机网络接口:本机端口。动态转发，本地产生一个Socks代理，通过代理的请求，发送到远机的相同端口。例如本地代理端口8087，本地客户端访问xxxx端口都会被转发给远程主机的xxxx端口</p>
<p><span style="background-color: #c0c0c0;">注意：</span><br />服务器端是OpenSSH时，默认情况下root不能通过密码登录到SSH，需要修改：</p>
<pre class="crayon-plain-tag">PermitRootLogin yes</pre>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 基于公钥认证机制登陆
ssh -i ~/Documents/puTTY/gmem.key root@vpn.gmem.cc

# 启用正向端口转发，将本机2222端口转发到192.168.104.91:22
ssh -nNTf -L 0.0.0.0:2222:192.168.104.91:22 -o "ExitOnForwardFailure yes" root@192.168.104.91

# 启用反向端口转发
ssh -nNT -R vpn.gmem.cc:2022:localhost:22 root@vpn.gmem.cc
    -o "ExitOnForwardFailure yes"   # 转发失败则退出remote port forwarding failed for listen...

# 连接到目标机器，执行两个命令，然后退出
ssh -i /root/Documents/puTTY/gmem.key root@tk.gmem.cc "uname;echo OK"</pre>
</td>
</tr>
<tr>
<td class="blog_h2">sshpass</td>
<td>
<p>通过命令行传递SSH密码
<p>执行下面的命令安装此工具：</p>
<pre class="crayon-plain-tag">sudo apt-get install sshpass</pre>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">sshpass -p password ssh root@host</pre>
</td>
</tr>
<tr>
<td class="blog_h2">scp</td>
<td>
<p>安全拷贝（远程文件拷贝）
<p><span style="background-color: #c0c0c0;">格式：</span><br />scp <br />　　[-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] <br />　　[-l limit] [-o ssh_option] [-P port] [-S program] <br />　　[[user@]host1:]file1 ... <br />　　[[user@]host2:]file2</p>
<p><span style="background-color: #c0c0c0;">描述：</span><br />该命令使用SSH来进行网络上文件的拷贝，使用与SSH一样的身份验证并提供与SSH一样的安全性保证，如果需要身份验证，scp会提示输入密码。<br />可以将本地文件拷贝到远程服务器，反之亦可，在两台远程服务器之间拷贝文件也是支持的。<br />当目标文件存在时，scp会覆盖其内容，但是保留inode；如果目标文件不存在，则首先创建一个空白文件，并把内容填充进去</p>
<p>Windows下可以安装WinSSHD以支持SCP。</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-1 强制scp使用协议1<br />-2 强制scp使用协议2<br />-4 强制scp仅使用IPv4<br />-6 强制scp仅使用IPv6<br />-B 执行批处理模式（阻止密码输入提示）<br />-C 启用压缩<br />-c cipher 指定用于加密传输数据的密钥<br />-F ssh_config 指定传输给ssh的配置文件<br />-i identity_file 指定ssh私钥文件，用于基于公钥的的身份验证<br />-l limit 限制使用带宽（Kbit/s）<br />-P port 指定远程主机的端口<br />-p 保留原始文件的修改时间、访问时间等信息<br />-q 安静模式，禁用进度、诊断信息<br />-r 递归的复制整个目录，自动以树遍历顺序跟踪符号连接的目标<br />-S program 用于加密连接的程序<br />-v 冗长模式，导致scp和ssh打印调试信息</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 将远程文件复制到本机当前目录（注意Windows路径的转写）
scp Suigintou@192.168.0.89:/e:/Software/Developer/pdtenv/Tomcat7.tar.gz  .
# 复制目录下所有sh文件，当前目录同名文件自动覆盖
scp root@192.168.0.90:/usr/local/Kingsmart/Tomcat7/*.sh .
# 将本地文件复制到远程机器
scp -i ~/gmem.key /tmp/gmem.sql root@tk.gmem.cc:/tmp/gmem.sql</pre>
</td>
</tr>
<tr>
<td class="blog_h2">sftp</td>
<td>
<p>SFTP客户端。该客户端使用SSH协议进行通信
<p><span style="background-color: #c0c0c0;">选项：<br /></span>很多SSH、SCP命令的配置可以使用，这里不列出<br />-D sftp_server_path  直接连接到本地SFTP服务器</p>
<p><span style="background-color: #c0c0c0;">交互式命令：</span><br />bye  exit  退出<br />cd   修改服务器的当前目录<br />chgrp  修改服务器文件的组<br />chmod  修改服务器文件的权限<br />chown  修改服务器文件的所有者<br />df  显示当前目录所在文件系统的使用情况信息。-h人类可读，-i显示inode信息<br />get [-afPpr] remote-path [local-path]<br />下载远程文件到本地，如果local-path不指定则文件名由远程给出。remote-path可以包含通配符以匹配多个文件，local-path可以是目录。-a 尝试断点续传，-f在传输完成后调用fsync，-P/-p拷贝权限和访问时间信息，-r递归下载子目录<br />lcd  修改本地的当前目录<br />lls  列出本地文件<br />lmkdir  在本地创建目录<br />ln  创建符号连接<br />lpwd  打印本地当前目录<br />ls  列出远程文件<br />lumask  设置本地umask<br />mkdir  创建远程目录<br />progress  显示进度信息<br />put [-fPpr] local-path [remote-path]   上传本地文件到服务器<br />pwd  显示当前目录<br />reget [-Ppr] remote-path [local-path]  断点续传<br />rename oldpath newpath  重命名<br />rm path  删除文件<br />rmdir path  删除目录</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 使用公钥认证，连接到交互式客户端
sftp -o "IdentityFile=/root/Documents/puTTY/gmem.key" root@tokyo.gmem.cc

# 进入指定本地目录
lcd /var/lib
# 进入指定的远程目录
cd /var/lib

# 列出远程文件
ls

# 下载远程目录到本地
get -ar mysql

# 修改远程文件的组、权限、所有者
chgrp grp /path/to
chmod mode /path/to
chown own /path/to


# 退出交互式客户端
exit</pre>
</td>
</tr>
<tr>
<td class="blog_h2">nmap</td>
<td>
<p>执行网络探测与端口扫描<br />该工具可以用来快速扫描大型网络
<p><span style="background-color: #c0c0c0;">格式：</span><br />nmap [Scan Type...] [Options] {target specification}</p>
<p><span style="background-color: #c0c0c0;">目标选项：</span><br />可以指定主机名、IP地址或者网络，例如：scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254<br />-iL　从文件中读取目标<br />-iR　随机选取目标<br />--exclude　排除目标<br />--excludefile　从文件中读取排除目标<br /><span style="background-color: #c0c0c0;">目标发现选项:</span><br />-sL　简单的列出待扫描的目标<br />-sn　使用PING扫描，禁用端口扫描<br />-Pn　认为所有目标主机在线<br />-PS/PA/PU/PY[portlist]　对端口列表执行TCP SYN/ACK,UDP或SCTP扫描<br />-PE/PP/PMICMP　执行echo, timestamp,或者netmask request探针<br />-PO[protocol list]　执行IP协议PING<br />-n/-R　从不/总是进行DNS查找<br />--dns-servers　指定逗号分隔的DNS列表<br />--system-dns　使用操作系统DNS<br />--traceroute　跟踪每个主机的路由情况<br /><span style="background-color: #c0c0c0;">扫描技术选项:</span><br />-sS/sT/sA/sW/sM　执行TCP SYN/Connect()/ACK/Window/Maimon扫描<br />-sU　执行UDP扫描<br />-sN/sF/sX　执行TCP Null, FIN, Xmas扫描<br />--scanflags　定制TCP扫描标记<br />-sI　执行空闲扫描<br />-sO　IP协议扫描<br /><span style="background-color: #c0c0c0;">端口选项：</span><br />-p　仅扫描指定的端口，合法的实例：-p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9<br />-F　快速扫描，比标准扫描探测更少的端口<br />-r　顺序的扫描端口<br />--top-ports num　扫描num个最知名的端口<br /><span style="background-color: #c0c0c0;">服务检测：</span><br />-sV　探测打开的端口，以确定服务的类型与版本<br /><span style="background-color: #c0c0c0;">操作系统检测：</span><br />-O　启用操作系统检测<br /><span style="background-color: #c0c0c0;">输出选项：</span><br />-oN/-oX/-oS/-oG file　输出一般格式,XML格式,kIddi3,Grepable格式到文件<br />--reason　显示当口处于某个状态的原因<br />--open　仅显示打开状态的端口<br />--packet-trace　显示所有收发的数据包<br />--append-output　附加到输出文件的尾部而不是覆盖<br />-v　增加输出的冗长级别<br />-vv进一步增加输出的冗长级别</p>
<p><span style="background-color: #c0c0c0;">其它选项：</span><br />-6　启用IPv6扫描<br />--send-eth　发送原始以太网数据桢<br />--send-ip　发送IP数据报<br />-A　启用操作系统探测、版本探测、路由跟踪</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#对gmem.cc进行全面扫描
nmap -v -A gmem.cc
#alex@amethystine:~$ nmap -v -A gmem.cc
#Starting Nmap 6.40 ( http://nmap.org ) at 2015-05-21 15:15 CST
#NSE: Loaded 110 scripts for scanning.
#NSE: Script Pre-scanning.
#执行PING扫描
#Initiating Ping Scan at 15:15
#Scanning gmem.cc (106.185.46.7) [2 ports]
#Completed Ping Scan at 15:15, 0.07s elapsed (1 total hosts)
#执行DNS解析
#Initiating Parallel DNS resolution of 1 host. at 15:15
#Completed Parallel DNS resolution of 1 host. at 15:15, 0.01s elapsed
#Initiating Connect Scan at 15:15
#扫描端口，可以看到该服务器可能开启了MySQL、Apache、SSH服务
#Scanning gmem.cc (106.185.46.7) [1000 ports]
#Discovered open port 80/tcp on 106.185.46.7
#Discovered open port 22/tcp on 106.185.46.7
#Discovered open port 1723/tcp on 106.185.46.7
#Discovered open port 3306/tcp on 106.185.46.7
#Discovered open port 3690/tcp on 106.185.46.7
#Completed Connect Scan at 15:16, 13.28s elapsed (1000 total ports)
#扫描应用服务
#Initiating Service scan at 15:16
#Scanning 5 services on gmem.cc (106.185.46.7)
#Completed Service scan at 15:16, 15.58s elapsed (5 services on 1 host)
#NSE: Script scanning 106.185.46.7.
#Initiating NSE at 15:16
#Completed NSE at 15:16, 2.77s elapsed
#Nmap scan report for gmem.cc (106.185.46.7)
#Host is up (0.10s latency).
#rDNS record for 106.185.46.7: li759-7.members.linode.com
#Not shown: 995 closed ports
#PORT     STATE SERVICE  VERSION
#探测到SSH服务
#22/tcp   open  ssh      (protocol 2.0)
#| ssh-hostkey: 1024 2c:89:42:27:6d:12:b0:17:72:79:3f:ad:51:e0:04:ed (DSA)
#| 2048 1b:1e:2d:e0:10:c1:89:62:4e:c2:bc:7f:dd:ae:67:a1 (RSA)
#|_256 62:5f:c1:16:c7:42:b6:46:0d:d9:69:b0:88:c1:4b:6d (ECDSA)
#探测到Apache服务
#80/tcp   open  http     Apache httpd 2.4.7 ((Ubuntu))
#|_http-methods: POST OPTIONS GET HEAD
#|_http-title: Site doesn't have a title (text/html)
#探测到VPN服务
#1723/tcp open  pptp     linux (Firmware: 1)
#探测到MySQL服务
#3306/tcp open  mysql    MySQL 5.5.35-1ubuntu1
#| mysql-info: Protocol: 10
#| Version: 5.5.35-1ubuntu1
#| Thread ID: 8572
#| Some Capabilities: Long Passwords, Connect with DB, Compress, ODBC, Transactions, Secure Connection
#| Status: Autocommit
#|_Salt: &amp;a81/9l7
#探测到SVN服务
#3690/tcp open  svnserve Subversion
#NSE: Script Post-scanning.
#Initiating NSE at 15:16
#Completed NSE at 15:16, 0.00s elapsed
#Read data files from: /usr/bin/../share/nmap
#Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
#Nmap done: 1 IP address (1 host up) scanned in 31.95 seconds


#对本地局域网进行PING扫描
nmap -v -sn 192.168.0.1-254</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ifconfig</td>
<td>
<p>查看或者配置网络接口
<p><span style="background-color: #c0c0c0;">描述：</span><br />该命令用于配置内核常驻（kernel-resident）的网络接口，可以在系统启动时建立网络连接，如果不给定任何参数，该命令会显示当前活动的网络接口的列表</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />ifconfig [-v] [-a] [-s] [interface]<br />ifconfig [-v] interface [aftype] options | address ...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a　显示所有网络接口，包括已经停用的<br />-s　显示简短列表，类似netstat -i<br />interface　网络接口的名称<br />aftype　地址族，用于解析和现实所有协议地址，支持inet (TCP/IP, default), inet6 (IPv6), ax25 (AMPR Packet Radio), ddp (Appletalk Phase 2), ipx (Novell IPX) 以及 netrom (AMPR Packet radio)<br />up　该标记导致网络接口被激活，如果为网络接口分配了IP地址，则自动隐含该标记<br />down　该标记导致网络接口被停用<br />[-]arp　启用或者停用该网络接口的APR协议支持 <br />[-]promisc　启用或者停用网络接口的混杂模式，如果启用，网络上所有数据报被该网络接口接收<br />[-]allmulti　启用或者停用网络接口的全组播模式，如果启用，网络上所有多播包被该网络接口接收<br />metric N　在计算数据包转发次数时，所要加上的数目<br />mtu N　设置最大传输单元<br />dstaddr addr　设置PPP协议远端的IP地址<br />netmask addr　设置该网络接口的子网掩码<br />add addr/prefixlen　为网络接口分配一个IPv6地址<br />del addr/prefixlen　为网络接口移除一个IPv6地址<br />[-]broadcast [addr]　设置该网络接口的广播地址，或者清除该网络接口的IFF_BROADCAST标记<br />[-]pointopoint [addr]　启用点对点模式，与另外一台计算机直连<br />hw class address　如果设备驱动支持，可以设置该网络接口的硬件地址<br />multicast　设置该网络接口的多播标记<br />address　设置分配给该网络接口的IP地址</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#设置eth1的广播地址
ifconfig eth1 broadcast 192.168.0.255
#设置eth0的IP地址
ifconfig eth0 192.168.0.90
#停用eth1
ifconfig eth1 down</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ip</td>
<td>
<p>显示或者配置路由、网卡、策略路由或者隧道
<p>关于Linux路由的更多知识，参考：<a href="/network-faq#route">Linux网络知识集锦</a></p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 如果不清楚对象支持哪些命令，可以 ip object help
ip [ OPTIONS ] OBJECT { COMMAND | help }
ip [ -force ] -batch filename</pre>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-V, -Version 显示版本并退出<br />-b, -batch &lt;FILENAME&gt; 批处理模式，从指定的文件或者标准输入读取命令并调用，第一个错误导致退出<br />-force 批处理模式中出现错误时不退出<br />-s, -stats, -statistics 输出更多的信息，该选项使用多次会导致更多的信息被打印<br />-l, -loops &lt;COUNT&gt; 命令 ip addr flush 最大循环次数，默认0表示循环直到所有地址被移除<br />-f, -family &lt;FAMILY&gt; 指定使用的地址族<br />-4 等价于 -family inet<br />-6 等价于 -family inet6<br />-B 等价于 -family bridge<br />-D 等价于 -family decnet<br />-I 等价于 -family ipx<br />-0 等价于 -family link<br />-o, -oneline 每个记录单独一行显示，换行符替换为反斜杠（'\'）<br />-r, -resolve 使用系统的DNS解析器来打印主机地址</p>
<p><span style="background-color: #c0c0c0;">对象：</span><br />所有对象名称都支持前缀简写，例如address可以简写为addr或者a<br />address 设备的协议（IPv4或者IPv6）地址<br />addrlabel 协议地址标签<br />l2tp 第二层隧道协议，在IP协议上建立以太网隧道<br />link 网络设备，其add子命令用于添加虚拟设备（virtual link）<br />maddress 多播地址<br />monitor 监听网络连接信息<br />mroute 多播路有缓存条目<br />mrule 多播路有策略数据库中的规则<br />neighbour 管理ARP和NDISC缓存条目<br />netns 管理网络名字空间<br />route 路由表条目<br />rule 路由策略数据库中的规则<br />tunnel IP协议隧道<br />tuntap  TUN/TAP设备，TUN/TAP分别是模拟网络层/链路层的内核虚拟设备<br />xfrm 管理IPSec策略</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 给网络接口eth0分配一个IP地址，设置会在系统重启后丢失
ip addr add 192.168.0.89 dev eth0
# 显示IP地址、MAC地址等信息
ip addr show
# 仅仅显示特定网络接口的信息
ip addr show dummy0

# 移除IP地址
ip addr del 192.168.0.89/24 dev eth0
# 禁用网络接口
ip link set eth1 down
# 启用网络接口
ip link set eth1 up

# 显示所有设备
ip link ls
# 显示指定类型的设备
ip link show type bridge|veth|tap
# 显示详细信息，例如设备类型
ip -d link show
# 6: kube-ipvs0: &lt;BROADCAST,NOARP&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default 
#  link/ether 2e:be:57:1f:f0:d5 brd ff:ff:ff:ff:ff:ff promiscuity 0 
#  dummy addrgenmode eui64 
#  类型
# 10: cali11f1d8af43c@if4: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1440 qdisc noqueue state UP mode DEFAULT group default 
#  link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3 promiscuity 0 
#  veth addrgenmode eui64 
#  类型

# 添加一个网桥
ip link add br0 type bridge
# 删除一个网桥
ip link delete br0 type bridge

# 添加一个macvtap设备，其lower device为eth0
ip link add link eth0 name macvtap0 type macvtap
# 为macvtap设置MAC地址并启动
ip link set macvtap0 address 1a:46:0b:ca:bc:7b up

# 添加一个TAP设备，归属于root用户
ip tuntap add tap0 mode tap user root

# 添加一个veth对，同属默认网络命名空间
ip link add veth0 type veth peer name veth1

# 设置tap0的master设备为br0，即把tap0束缚（enslave）到br0。用于把一个虚拟/真实设备连接到网桥
# 被连接到网桥的设备，其状态必须为up
ip link set tap0 master br0
# 释放tap0的被束缚状态。用于解除一个虚拟/真实网络设备到网桥的连接
ip link set tap0 nomaster

# 显示路由表
ip route show
# 显示指定路由表的内容
ip route list table 133

# 添加静态路由
ip route add 10.10.20.0/24 via 192.168.0.1 dev eth0  # via网关的间接路由
ip route add 10.0.0.0/8 dev br0                      # 直接路由
# 移除静态路由
p route del 10.10.20.0/24

# 添加默认路由（网关）
ip route add default via 192.168.0.1

# 添加网络命名空间
ip netns add test
# 将设备移动到网络命名空间
ip link set veth1 netns test
# 在网络命名空间下执行命令
ip netns exec test ip link show veth1

# 添加策略路由，具有123标记的封包，查找100表
ip rule add fwmark 123 lookup 100

# 列出策略路由
ip -f inet rule list
# 0:      from all lookup local 
# 具有0x539的封包，查找133表
# 32765:  from all fwmark 0x539 lookup 133 
# 32766:  from all lookup main 
# 32767:  from all lookup default</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ip route</td>
<td>
<p>管理路由表
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 列出或刷新选择器匹配的路由
ip route { list | flush } SELECTOR

# 保存和恢复路由
ip route save SELECTOR
ip route restore

# 查询路由
ip route get ADDRESS [ from ADDRESS iif STRING  ] [ oif STRING ] [ tos TOS ]

# 增删改换路由
ip route { add | del | change | append | replace } ROUTE


# SELECTOR语法
[ root PREFIX ] [ match PREFIX ] [ exact PREFIX ] [ table TABLE_ID ] [ proto RTPROTO ] [ type TYPE ] [ scope SCOPE ]

# ROUTE语法
ROUTE = [ TYPE ] PREFIX [ tos TOS ] [ table TABLE_ID ] [ proto RTPROTO ] [ scope SCOPE ] [ metric METRIC ]
        [ [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] [ onlink | pervasive ] OPTIONS FLAGS [ nexthop NH ] ... ]
# 其中TYPE包括
[ unicast | local | broadcast | multicast | throw | unreachable | prohibit | blackhole | nat ]</pre>
<p><span style="background-color: #c0c0c0;">路由类型：</span></p>
<ol>
<li>unicast，此路由条目描述到达PREFIX所覆盖的目的地址的路径</li>
<li>unreachable，PREFIX覆盖的网络不可达，发送ICMP消息host unreachable</li>
<li>blackhole，PREFIX覆盖的网络不可达，静默的丢弃</li>
<li>prohibit，PREFIX覆盖的网络不可达，发送ICMP消息communication administratively prohibited</li>
<li>local，目的地指向本机，包被环回并本地递送</li>
<li>broadcast，目的地是广播地址，包作为link broadcasts发送</li>
</ol>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 添加一个local类型的路由到100表中，此路由让一切路由都走lo网卡
ip route add local 0.0.0.0/0 dev lo table 100 </pre>
</td>
</tr>
<tr>
<td>ip rule</td>
<td>
<p>管理路由策略数据库。典型的路由算法仅仅根据目的地址来决定如何路由，如果需要更加高级的路由策略 —— 例如基于源地址、IP协议、传输协议端口甚至协议载荷，则需要使用该命令。这种高级路由叫做policy routing
<p>为了实现策略路由，原先的基于目的地址的、以最长匹配规则排序的路由表，被替换为路由策略数据库（RPDB）。通过执行数据库中的一些规则，来决定如何路由</p>
<p>每个策略路由规则由选择器SELECTOR和动作断言ACTION PREDICTATE构成。<span style="background-color: #c0c0c0;">RPDB按照规则的优先级降序（数字越小优先级越高）的被扫描</span>，每个规则的SELECTOR针对封包的{source address, destination address, incoming interface, tos, fwmark}进行匹配判断，如果匹配成功，则ACTION被执行。</p>
<p>ACTION PREDICTATE可以<span style="background-color: #c0c0c0;">返回成功</span>，这种情况下它要么给出路由目的地址，要么给出失败提示，且RPDB查找终止。否则（不成功）则继续匹配下一条RPDB规则</p>
<p>在内核启动时，它会初始化具有三条规则的RPDB：</p>
<ol>
<li>优先级0；选择器：匹配任何封包；动作：查找路由表local（ID 255）。local是一个特殊的路由表，包含本地地址、广播地址的高优先级的控制路由。此规则不能删除或修改</li>
<li>优先级RPDB；选择器：匹配任何封包；动作：查找路由表main（ID 254）。main表是一个普通的路由表，包含所有非策略性路由规则。此表可以被删除或覆盖</li>
<li>优先级32767；选择器：匹配任何封包；动作：查找路由表default（ID 253）。default表是空白的，用于没有匹配到的封包的后处理</li>
</ol>
<p>每个RPDB条目都包含一些额外的属性，例如每个规则都有向路由表的指针。NAT和masquerading规则包含一个属性，用于选择转换/掩码的IP地址。</p>
<p>RPDB包含以下几种类型的规则：</p>
<ol>
<li>unicast，返回规则所引用的路由表中的一个条目</li>
<li>blackhole，安静的丢弃封包</li>
<li>unreachable，生成一个Network is unreachable错误</li>
<li>prohibit，生成一个Communication is administratively prohibited错误</li>
<li>nat，将源地址转换为其它值</li>
</ol>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">ip [ OPTIONS ] rule  { COMMAND | help }
ip rule  [ list | add | del | flush ] SELECTOR ACTION</pre>
<p>其中SELECTOR可以是（以下项中的多个）：</p>
<ol>
<li>from PREFIX，匹配源地址的前缀</li>
<li>to PREFIX，匹配目的地址的前缀</li>
<li>tos TOS，匹配Type of Service</li>
<li>fwmark FWMARK[/MASK]，匹配防火墙标记，也就是iptables的MARK</li>
<li>iif STRING，匹配封包的来源网卡，这意味着你可以为本地发起的、转发的封包设置完全独立的路由规则</li>
<li>oif STRING，匹配封包的出口网卡，仅仅对来自本地的、绑定到特定设备的套接字有效</li>
<li>pref NUMBER，优先级，同义词priority、order</li>
</ol>
<p>其中ACTION可以是：</p>
<ol>
<li>table TABLE_ID，表ID可以是local | main | default | NUMBER，如果匹配规则，则到哪个表中查找路由</li>
<li>nat ADDRESS</li>
<li>prohibit | reject | unreachable</li>
<li>realms [SRCREALM/]DSTREALM ]</li>
</ol>
<p>ACTION后面可以跟着SUPPRESSOR：[ suppress_prefixlength NUMBER ] [ suppress_ifgroup GROUP ]</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 添加一个规则，匹配标记为1的封包，到100表中查找
ip rule add fwmark 1 lookup 100</pre>
</td>
</tr>
<tr>
<td class="blog_h2">bridge</td>
<td>
<p>显示/操控网桥地址和设备
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 显示所有连接到网桥的网络接口（端口，Port）的信息
bridge link 
bridge link show dev eth0

# 在端口上设置网桥相关的属性
bridge link set
  dev eth0           # 针对哪个端口
  cost COST          # STP路径成本
  priority PRIO      # STP端口优先级
  state STATE        # 端口状态：0禁用 1STP监听 2 STP学习 3STP转发 4STP阻塞
  root_block on|off  # 端口是否为STP的根端口
  hairpin on|off     # 流量是否可以从接收它的端口发回去，默认否，流量不会从接收端口转发回去



# FDB包含了网桥每个端口（连接）上已知的MAC地址
# 添加FDB项
bridge fdb add
  LLADDR             # MAC地址
  dev DEV            # MAC地址关联的网络接口
  dst IPADDR         # MAC地址所在的VXLAN隧道端点(VTEP)的IP地址
  vni VNI            # VXLAN VNI网络标识符（VXLAN段ID），连接到VTEP是需要
  port PORT          # 远程VTEP的UDP端口
  via DEVICE         # 为了将数据报送达远程VTEP，需要通过的设备

  self               # 关联到软件FDB（默认）
  embedded           # 关联到offloaded FDB
  router             # 关联到路由器

# 显式FDB项
bridge fdb show



# MDB包含端口上已知的IP多播组地址
# 添加MDB项
bridge mdb add      
  dev DEV            # 组播地址关联的接口
  port PORT          # 连接到此端口上的link是多播组成员
  grp GROUP          # 组播地址</pre>
</td>
</tr>
<tr>
<td>brctl</td>
<td>
<p>操控网桥
<p><span style="background-color: #c0c0c0;">格式：</span><pre class="crayon-plain-tag">brctl [commands]</pre></p>
<p><span style="background-color: #c0c0c0;">子命令：</span></p>
<pre class="crayon-plain-tag">addbr     	    &lt;bridge&gt;  # 新建一个网桥
delbr     	    &lt;bridge&gt;  # 删除一个网桥
addif     	    &lt;bridge&gt; &lt;device&gt; # 将一个网卡接入网桥
delif     	    &lt;bridge&gt; &lt;device&gt; # 从网桥中删除一个设备
setbridgeprio	    &lt;bridge&gt; &lt;prio&gt; # 设置网桥优先级
setfd     	    &lt;bridge&gt; &lt;time&gt; # 设置转发延迟
setmaxage 	    &lt;bridge&gt; &lt;time&gt; # 设置报文最大生命周期
show      	    [ &lt;bridge&gt; ] # 显示网桥列表
showmacs  	    &lt;bridge&gt; # 显示MAC列表
showstp   	    &lt;bridge&gt; # 显示网桥的STP信息
stp       	    &lt;bridge&gt; {on|off} # 开关STP</pre>
<p><span style="background-color: #c0c0c0;">示例：</span> </p>
<pre class="crayon-plain-tag"># 停止eth0
sudo ip link set eth0 down

# 添加eth0到libvirt网桥，使用桌面系统时要禁用NM对eth0的管理
sudo brctl addif virbr0 eth0

# 为网桥添加一个新IP，此IP原来属于eth0
sudo ip addr add 10.0.1.1/24 dev virbr0

# 启动eth0
sudo ip link set eth0 up</pre>
</td>
</tr>
<tr>
<td class="blog_h2">iwconfig</td>
<td>
<p>支持与网线网络接口相关的配置
</td>
</tr>
<tr>
<td class="blog_h2">iwlist</td>
<td>
<p>获取无线网络的详细信息</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">iwlist wlan1 scan</pre>
</td>
</tr>
<tr>
<td class="blog_h2">wpa_cli</td>
<td>
<p>WPA命令行客户端
<p>举例：</p>
<pre class="crayon-plain-tag"># 查看当前无线网络的详细信息
sudo wpa_cli status
# Selected interface 'wlan1'
# bssid=e8:ed:f3:fc:b3:bf
# ssid=dangdang-ydgw
# id=0
# mode=station
# pairwise_cipher=CCMP
# group_cipher=CCMP
# key_mgmt=WPA2-PSK
# wpa_state=COMPLETED</pre>
</td>
</tr>
<tr>
<td class="blog_h2">ifup</td>
<td>
<p>激活一个网络接口。该命令与下面两个命令与/etc/network/interfaces文件一起工作
</td>
</tr>
<tr>
<td class="blog_h2">ifdown</td>
<td>
<p>停用一个网络接口</p>
</td>
</tr>
<tr>
<td class="blog_h2">ifquery </td>
<td>
<p>解析一个网络接口的配置</p>
</td>
</tr>
<tr>
<td class="blog_h2">iptables</td>
<td>参考<a href="/iptables">重温Iptables</a></td>
</tr>
<tr>
<td class="blog_h2">ipset</td>
<td>
<p>IP Sets的管理工具</p>
<p>在iptables中，如果我们去匹配多个无规律的IP地址的话就会写入多条iptables的规则，当如果需要匹配几百甚至上千个IP地址的话，那么性能就会受到严重的影响。IP Sets可以解决这个问题</p>
<p>IP Sets是内核框架，可以被ipset命令管理。根据类型的不同，一个IP Set可以存放多个IP地址、网络号、TCP/UDP端口、MAC地址、网络接口名，以及所有这些的组合。当用一个条目来比对，看是否匹配IP Set时，速度极快</p>
<p>应用场景：</p>
<ol>
<li>存储一个IP地址集合，并且和一个Iptables规则进行匹配</li>
<li>动态的基于IP地址、端口更新iptables规则，而不影响性能</li>
<li>表述一个复杂的规则集，然后在一个iptables规则中引用</li>
</ol>
<p><span style="background-color: #c0c0c0;"><strong>子命令：</strong></span></p>
<pre class="crayon-plain-tag"># 创建具有指定名称、指定类型的IPSet
# 其中TYPENAME格式为：method:datatype[,datatype[,datatype]]
ipset create SETNAME TYPENAME [ CREATE-OPTIONS ]
# 创建一个名为test的hash:ip类型（以哈希形式存储的IP）的IPSet，新条目的默认超时300s（超时后自动移除）。
ipset create test hash:ip timeout 300
# 创建hash:net类型的IPSet
ipset create foo hash:net
# 创建hash:ip,port类型的IPSet
ipset create bar hash:ip,port


# 添加一个条目到IPSet
ipset add SETNAME ADD-ENTRY [ ADD-OPTIONS ]
# 向test中添加条目192.168.0.1，超时60s
ipset add test 192.168.0.1 timeout 60
# 添加到hash:net类型的IPSet
ipset add foo 192.168.0.0/24
# 添加到hash:ip,port类型的IPSet
ipset add foo 192.168.1.0/24,80-82
ipset add foo 192.168.1.1,udp:53
ipset add foo 192.168.1.1,vrrp:0
ipset test foo 192.168.1.1,80


# 从IPSet中删除一个条目
ipset del SETNAME DEL-ENTRY [ DEL-OPTIONS ]
# 测试一个条目是否属于IPSet
ipset test SETNAME TEST-ENTRY [ TEST-OPTIONS ]
# 删除指定的IPSet，如果不指定名称，删除所有IPSet
ipset destroy [ SETNAME ]
# 列出指定的IPSet的头信息和条目列表
ipset list [ SETNAME ]
# 保存IPSet到文件
ipset save [ SETNAME ]
# 从文件恢复IPSet
ipset restore
# 刷空IPSet的条目
ipset flush [ SETNAME ]
# 重命名IPSet
ipset rename SETNAME-FROM SETNAME-TO
# 交换IPSet的内容
ipset swap SETNAME-FROM SETNAME-TO</pre>
<p><strong><span style="background-color: #c0c0c0;">CREATE/ADD选项</span></strong></p>
<p style="padding-left: 30px;">timeout 指定条目的超时<br />nomatch 对于hash:net类型，如果添加新条目是指定此选项，进行匹配时就好像该条目并没有添加一样<br />comment  指定注释</p>
<p><strong><span style="background-color: #c0c0c0;">IPSet类型：</span></strong></p>
<p style="padding-left: 30px;">bitmap:ip  使用一段内存存储IPv4主机或网络地址，最多存储65536个条目<br />bitmap:ip,mac  存储IP+MAC地址对<br />bitmap:port 存储端口<br />hash:ip 使用哈希表存储主机地址或网络地址，0 IP不能存储进来<br />hash:net 存储不同大小的网络地址<br />hash:net,net 存储一对网络地址<br />hash:ip,port 存储IP地址和端口</p>
<p><strong>在iptables中使用：</strong></p>
<p>你可以使用匹配扩展set来引用IPSet：</p>
<pre class="crayon-plain-tag">ipset create bannedip hash:ip
# 如果封包的源地址属于Ipset bannedip，则丢弃封包
iptables -I INPUT -m set --match-set bannedip src -j DROP
ipset add bannedip 4.5.6.7
ipset add bannedip 1.2.3.4</pre>
</td>
</tr>
<tr>
<td class="blog_h2">iptables-save</td>
<td>
<p>导出iptables配置到标准输出
</td>
</tr>
<tr>
<td class="blog_h2">iptables-restore</td>
<td>
<p>从文件恢复iptables配置</p>
</td>
</tr>
<tr>
<td class="blog_h2">conntrack</td>
<td>
<p>此命令提供netfilter的连接跟踪系统的全功能用户空间接口，代替/proc/net/ip_conntrack的功能。使用该命令你可以检索、列出、探查、维护Linux内核的连接跟踪子系统，例如：</p>
<ol>
<li>Dump出完整或过滤后的连接列表</li>
<li>从状态表中删除连接</li>
<li>添加新的连接</li>
<li>监控连接跟踪事件</li>
</ol>
<p>连接跟踪系统在内部，维护两个表：</p>
<ol>
<li>conntrack：默认表，包含当前被追踪的、经过本系统的连接。除非你使用iptables的<pre class="crayon-plain-tag">NOTRACK</pre>目标，否则所有连接都在其中</li>
<li>expect：期望表，连接跟踪期望（Connection tracking expectations ）用于“expect“ RELATED连接到一个现有的连接。Expectation主要被connection tracking helpers（也叫Application level gateways, ALGs）使用，处理FTP、SIP、H.323这样的复杂协议</li>
</ol>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 列出
conntrack -L [table] [options] [-z]
# 检索
conntrack -G [table] parameters
# 删除
conntrack -D [table] parameters
# 创建
conntrack -I [table] parameters
# 更新
conntrack -U [table] parameters
# 显示实时事件
conntrack -E [table] [options]
# 刷出整个表
conntrack -F [table]
# 显示计数器
conntrack -C [table]
# 显示内核连接跟踪系统的统计信息
conntrack -S</pre>
<p><span style="background-color: #c0c0c0;">一般选项：</span></p>
<p>-z, --zero  读取后自动清零计数器，仅仅和<pre class="crayon-plain-tag">-L</pre>联用<br />-o, --output [extended,xml,timestamp,id,ktimestamp]  以特定格式显示输出<br />-e, --event-mask [ALL|NEW|UPDATES|DESTROY][,...]过滤事件类型，和<pre class="crayon-plain-tag">-E</pre>联用<br />-b, --buffer-size 设置Netlink套接字缓冲大小</p>
<p><span style="background-color: #c0c0c0;">过滤器参数：</span></p>
<p>-s, --orig-src IP_ADDRESS 最初通信方向的源地址<br />-d, --orig-dst IP_ADDRESS 最初通信方向的目的地址<br />-r, --reply-src IP_ADDRESS 应答方向的源地址<br />-q, --reply-dst IP_ADDRESS 应答方向的目的地址 <br />-p, --proto PROTO L4协议类型，例如TCP UDP<br />-f, --family PROTO  L3协议族ipv4, ipv6，仅仅和<pre class="crayon-plain-tag">-L</pre>联用。默认IPv4<br />-t, --timeout TIMEOUT 指定超时<br />-m, --mark MARK[/MASK] 连接跟踪标记ctmark<br />-c, --secmark SECMARK 连接跟踪selinux安全标记<br />-u, --status [ASSURED|SEEN_REPLY|FIXED_TIMEOUT|EXPECTED|UNSET][,...]  连接跟踪状态<br />-n, --src-nat 仅仅匹配SNAT连接<br />-g, --dst-nat 仅仅匹配DNAT连接<br />-j, --any-nat 匹配任何NAT连接<br />-w, --zone 根据连接跟踪Zone过滤，参考iptables的CT目标<br />--tuple-src 指定expectation的源地址元组<br />--tuple-dst IP_ADDRESS 指定expectation的目的地址元组<br />--mask-src IP_ADDRESS   指定expectation的源地址掩码<br />--mask-dst IP_ADDRESS 指定expectation的目的地址掩码</p>
<p><span style="background-color: #c0c0c0;">TCP过滤参数：</span></p>
<p>--sport, --orig-port-src  最初通信方向的源端口<br />--dport, --orig-port-dst 最初通信方向的目的端口<br />--reply-port-src 应答方向的源端口<br />--reply-port-dst  应答方向的目的端口<br />--state [NONE | SYN_SENT | SYN_RECV | ESTABLISHED | FIN_WAIT | CLOSE_WAIT | LAST_ACK | TIME_WAIT | CLOSE | LISTEN]  TCP状态</p>
<p><span style="background-color: #c0c0c0;">UDP过滤参数：</span></p>
<p>--sport, --orig-port-src 最初通信方向的源端口<br />--dport, --orig-port-dst 最初通信方向的目的端口<br />--reply-port-src 应答方向的源端口<br />--reply-port-dst 应答方向的目的端口</p>
<p><span style="background-color: #c0c0c0;">ICMP过滤参数：</span></p>
<p>--icmp-type TYPE  ICMP类型<br />--icmp-code CODE ICMP代码<br />--icmp-id ID  ICMP ID</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 以 /proc/net/ip_conntrack 格式显示连接跟踪表
conntrack -L
conntrack -L -o extended
conntrack -L -o xml

# 仅仅列出IPv6
conntrack -L -f ipv6 -o extended

# 列出SNAT连接
conntrack -L --src-nat

# 列出事件，显示时间戳
conntrack -E -o timestamp
# 注意，输出中包含ASSURED字样的，表示此连接不会被消除，即使达到最大可跟踪连接数量限制后

# 删除所有源地址1.2.3.4的连接
conntrack -D -s 1.2.3.4

# 标记所有源地址1.2.3.4的连接
conntrack -U -s 1.2.3.4 -m 1 </pre>
</td>
</tr>
<tr>
<td class="blog_h2">pen</td>
<td>
<p>负载均衡器，支持基于TCP的协议的负载均衡，例如HTTP、SMTP
<p>此LB能够跟踪每个请求，并将其转发给客户端上一次访问的服务器（会话绑定），这种LB算法比RR更加高级</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">pen options [host:]port
  h1[:p1[:maxc1[:hard1[:weight1[:prio1]]]]] 
  [h2[:p2[:maxc2[:hard2[:weight2[:prio2]]]]]] ...</pre>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-C 指定控制端口，此端口接收发送给LB的命令<br />-F 指定配置文件<br />-H 为HTTP请求添加X-Forwarded-For头<br />-b 将不响应服务器列入黑名单的持续时间，单位秒<br />-T 会话跟踪持续时间，单位秒<br />-S 最大服务器数<br />-C 最大客户端数<br />-e host:port 如果所有服务器不可用，使用该紧急服务器<br />-f 在前台运行<br />-n 使用非阻塞IO<br />maxc1 服务器1的最大客户端数量<br />hard1  服务器1的最大客户端数量（硬限制）<br />weight1 此服务器的权重，用于基于权重的负载均衡算法<br />prio1 此服务器的优先级，用于基于优先级的负载均衡算法</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 监听所有网络接口的18000，转发给目标主机的8000，在前台运行，启动调试日志
pen -fd 0.0.0.0:18000 172.27.170.143:8000</pre>
</td>
</tr>
<tr>
<td class="blog_h2">snmp</td>
<td>
<p>snmp包提供了若干命令，它们的<span style="background-color: #c0c0c0;">格式</span>如下：
<pre class="crayon-plain-tag">snmp_command 
  -u bootstrap -l authPriv -a MD5 
  -x DES -A temp_password -X temp_password 
  
  remote_host snmp_sub_command_or_options</pre>
<p>它们<span style="background-color: #c0c0c0;">公共的选项</span>如下：</p>
<p>-v 指定需要使用的SNMP协议的版本<br />-c 指定SNMPv1、v2使用的团体名<br />-u 指定用于身份验证的用户名<br />-l 指定连接的安全性级别：noAuthNoPriv 不验证不加密；authNoPriv 验证不加密；authPriv 验证加密<br />-a 身份验证协议：MD5、SHA<br />-x 数据加密协议：DES、AES<br />-A 身份验证短语<br />-X 数据加密短语</p>
<p><span style="background-color: #c0c0c0;">命令举例：</span></p>
<pre class="crayon-plain-tag">#执行SNMP get操作，显示系统信息
snmpget 
  -u bootstrap -l authPriv -a MD5 
  -x DES -A password -X password 
  192.168.0.90 1.3.6.1.2.1.1.1.0</pre>
</td>
</tr>
<tr>
<td class="blog_h2">justniffer</td>
<td>
<p>这是一个TCP包嗅探器，它能够重建TCP流，并且对请求/应答风格的协议做了优化。justniffer还能重建HTTP内容并保存到磁盘
<p><span style="background-color: #c0c0c0;">安装：</span></p>
<pre class="crayon-plain-tag">sudo add-apt-repository ppa:oreste-notelli/ppa 
sudo apt-get update
sudo apt-get install justniffer</pre>
<p><span style="background-color: #c0c0c0;">格式： </span></p>
<pre class="crayon-plain-tag">justniffer 
  [ [-i interface] or [-f &lt;tcpdump file&gt;] ]  [-F]
  [-p &lt;packet filter&gt;]  [-u or -x] 
  [ [-r] or [-l &lt;log format&gt;] or [-a &lt;log format&gt;]  ] 
  [-c &lt;config file&gt;]  [-e &lt;external program&gt;]  [-U &lt;user&gt; ]   
  [-n &lt;not-found&gt; ]  [-s &lt;max concurrent tcp streams&gt; ]  
  [-d &lt;max concurrent IP fragments&gt; ]</pre>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-i 需要嗅探的接口名称，例如eth0<br />-f 从文件中读取tcpdump，用于离线分析。目标文件必须是pcap文件（tcpdump、wireshark等产生）。jusstniffer需要完整的dump，而默认的tcpdump只会收集每个包的前64字节，因此你需要<pre class="crayon-plain-tag">tcpdump -i eth0 -s 0 -w file.cap</pre> <br />-F 强制从文件读取tcpdump，即使dump不是完整的<br />-p 包过滤器，pcap filter格式，例如<pre class="crayon-plain-tag">"tcp port 23"</pre> 、<pre class="crayon-plain-tag">"host 8.8.8.8 and tcp port 80"</pre> <br />-l 日志格式，你可以使用很多<pre class="crayon-plain-tag">%</pre> 开头的占位符。如果不指定使用CLF（Common Log Format，大部分Web服务器的日志格式）<br />-a 附加日志格式到CLF后面<br />-r 显示原始流，等价于<pre class="crayon-plain-tag">-l %request%response</pre> <br />-s 最大并发的TCP流数量<br />-d 最大并发的IP分片数量<br />-x 使用HEX编码不可打印字符<br />-e 对于请求/应答模式的协议，通过管道，对每个请求/应答调用外部命令<br />-c 从文件读取配置</p>
<p><span style="background-color: #c0c0c0;">日志格式：</span></p>
<p>%close.originator([not applicable string])<br />连接关闭方：client,server,timedout，如果不可用，显示入参内的字符串（不指定使用短横线）<br />%close.time<br />从最后一个响应到关闭，消耗的时间<br />%close.timestamp[2]([format])<br />被关闭时间戳（close timestamp）替换，可以指定时间格式，如果有后缀2，则显示seconds.microseconds格式<br />%connection<br />连接持续性指示符，unique请求/应答是TCP连接中唯一的；start是第一个；last是最后一个；continue是中间的 <br />%connection.time([not applicable string])<br />从连接建立到现在，流逝的时间<br />%connection.timestamp[2]([format])<br />被连接时间戳（connection timestamp）替换，可以指定时间格式<br />%dest.ip<br />被目标地址替换<br />%dest.port<br />被目标端口替换<br />%source.ip<br />被源地址替换<br />%source.port<br />被源端口替换<br />%request<br />完整的请求，可能多行，可能包含不可打印字符<br />%request.timestamp[2]([format])<br />被请求时间戳替换<br />%request.size<br />请求的大小，包含请求头<br />%request.line<br />用于HTTP，请求行，例如"GET /index.html HTTP/1.1"<br />%request.method<br />用于HTTP，请求方法，例如POST<br />%request.url<br />用于HTTP，请求地址<br />%request.protocol<br />请求的协议<br />%request.header<br />请求头，多行。可以使用%request.header.*访问单个HTTP请求头<br />%response<br />完整的响应，可能多行，可能包含不可打印字符<br />%response.size<br />响应的大小，包含响应头<br />%response.timestamp[2]([format])<br />响应时间戳<br />%response.line<br />用于HTTP，响应行<br />%response.protocol<br />响应协议<br />%response.code<br />用于HTTP，响应状态码<br />%response.message<br />用于HTTP，响应状态码的字符串表示<br />%response.header<br />响应头，多行。可以使用%response.header.*访问单个HTTP响应头<br />%session.requests<br />被TCP会话的请求序列号替换<br />%session.time<br />被TCP会话lifespan替换<br />%tab<br />打印一个TAB<br />%streams<br />打印当前TCP流的数量<br />%newline<br />打印换行符<br />%% <br />打印 %</p>
<p><span style="background-color: #c0c0c0;">时间戳格式：</span><br />%Y   年<br />%m  月，01-12<br />%d   日，01-31<br />%H   时，00-23<br />%M  分，00-59<br />%S   秒，00-60<br />%F   等价于 %Y-%m-%d<br /> %T  等价于 %H:%M:%S</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 嗅探针对repository.sonatype.org的HTTP请求，打印请求URL和响应状态码
sudo justniffer -i wlan1 -p "host repository.sonatype.org and port 80"
    -l "%connection.timestamp(%F %T) %source.ip:%source.port %request.url %response.code"</pre>
</td>
</tr>
<tr>
<td class="blog_h2">setserial</td>
<td>
<p>获取或者设置Linux的串口信息。<br />在正常的初始化过程中，仅仅1-4号串口基于默认I/O端口和IRQ值初始化，要初始化额外的串口或者把1-4号串口修改为非标准配置，可以使用该程序
<p><span style="background-color: #c0c0c0;">格式：</span><br />setserial [ -abqvVWz ] device [ parameter1 [ arg ] ] ...<br />setserial -g [ -abGv ] device1 ...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />device 需要操作的设备，例如/dev/ttyS0, /dev/ttyS1, /dev/ttyS2, /dev/ttyS3<br />-a  获取串口设备信息时，打印所有可用信息<br />-b 获取串口设备信息时，打印概要配置信息<br />-g 获取串行端口的配置信息<br />-q 打印较少的信息<br />-v 打印冗长信息，输出额外的状态信息<br />-z 在设置串口标志位前置零</p>
</td>
</tr>
<tr>
<td class="blog_h2">ethtool</td>
<td>
<p>查看以太网接口的信息，例如连接速度。示例：</p>
<p><pre class="crayon-plain-tag">ethtool eth0
ethtool eth0 | grep Speed </pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">系统管理命令</span></div>
<table class="fixed-word-wrap full-width" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">users</td>
<td>显示所有的登录的用户</td>
</tr>
<tr>
<td class="blog_h2">last</td>
<td>显示最近登陆的用户列表</td>
</tr>
<tr>
<td class="blog_h2">groups</td>
<td>列出用户（如果不指定用户则默认当前用户）和他所属于的组，例如<pre class="crayon-plain-tag">groups alex</pre> </td>
</tr>
<tr>
<td class="blog_h2">id</td>
<td>
<p>显示用户标识符信息，举例：</p>
<pre class="crayon-plain-tag">id alex
#输出，gid显示了用户的主要组（Primary group），默认主要组和用户名相同
#uid=1000(alex) gid=1000(alex) groups=1000(alex),4(adm),27(sudo)</pre>
</td>
</tr>
<tr>
<td class="blog_h2">useradd</td>
<td>
<p>创建一个新用户或更新默认新用户信息
<p><span style="background-color: #c0c0c0;">格式：</span><br />useradd [options] LOGIN<br />useradd -D<br />useradd -D [options]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-b 指定基目录，基目录/账户名就是家目录，基目录默认/home<br />-c  password文件中的注释字段<br />-d 指定家目录<br />-e 账户被禁用的日期，格式YYYY-MM-DD<br />-f 密码过期后，账户被彻底禁用之前的天数。0 表示立即禁用，-1 表示禁用这个功能<br />-g 用户初始登陆组（主要组）的组名或ID。组名必须已经存在。组号码必须指代已经存在的组。如果不指定该参数，默认会创建一个和用户名字一样的组，该行为受/etc/login.defs中USERGROUPS_ENAB参数的控制：该参数为no时，新用户被添加到<pre class="crayon-plain-tag">/etc/default/useradd</pre> 中GROUP变量指定的组<br />-G 用户所属附属组，逗号分隔<br />-M 不创建家目录<br />-N 不创建同名组，而是加入到-g指定的组或者使用/etc/default/useradd中的GROUP变量<br />-p 加密过的密码<br />-r 创建一个系统账户。不在/etc/shadow中保存密码年龄信息、不创建家目录，UID在SYS_UID_MIN到SYS_UID_MAX之间选择<br />-s 用户使用的登录Shell<br />-u 基于数字的用户ID，必须非负整数，一般要求唯一<br />-U, --user-group 创建与用户同名的群组</p>
</td>
</tr>
<tr>
<td class="blog_h2">userdel</td>
<td>
<p>删除用户</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-f  强制删除，甚至在用户当前正在登录的场景下<br />-r  删除用户的家目录及其中的文件</p>
</td>
</tr>
<tr>
<td class="blog_h2">usermod</td>
<td>
<p>修改用户帐号。 可以修改密码、 组身份、 截止日期等。修改后的用户可能需要<span style="background-color: #c0c0c0;">重新登录</span>才能生效</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a  添加用户到额外的组中，仅和-G联用<br />-c password文件中的注释字段<br />-d 为用户指定新的家目录<br />-e 账户被禁用的日期，格式YYYY-MM-DD<br />-f 密码过期后，账户被彻底禁用之前的天数。0 表示立即禁用，-1 表示禁用这个功能<br />-G 用户所属附属组，逗号分隔<br />-l  修改用户的登录名<br />-L  锁定一个用户的密码<br />-U 解锁一个用户的密码<br />-m  移动家目录到新的位置</p>
<p><span style="background-color: #c0c0c0;"><span style="background-color: #c0c0c0;">示例：</span></span></p>
<pre class="crayon-plain-tag"># 把alex附加到ssl-cert组
sudo usermod -a -G ssl-cert alex</pre>
</td>
</tr>
<tr>
<td class="blog_h2">gpasswd</td>
<td>
<p>管理配置文件/etc/group和/etc/gshadow<br /><span style="background-color: #c0c0c0;"><span style="background-color: #c0c0c0;">示例：</span></span>
<pre class="crayon-plain-tag"># 从ssl-cert组中删除alex
sudo gpasswd -d alex ssl-cert</pre>
</td>
</tr>
<tr>
<td class="blog_h2">groupmod</td>
<td>修改指定组</td>
</tr>
<tr>
<td class="blog_h2">newgrp</td>
<td>修改当前用户的GID</td>
</tr>
<tr>
<td class="blog_h2">passwd</td>
<td>
<p>设置、修改、或者删除用户的密码。普通用户只能修改自己的密码，root则可以修改任何用户的密码。
<p><span style="background-color: #c0c0c0;">格式：</span><br />passwd [options] [LOGIN]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a 仅和-S联用，显式所有用户的状态<br />-d 删除用户密码<br />-e 立即使密码过期<br />-i INACTIVE 用于在密码过期INACTIVE天后自动禁用用户，用户将不能登陆<br />-l 锁定，禁止修改密码<br />-m 修改密码的最小时间间隔（天）<br />-S 显示账户的状态信息<br />-u 解锁，允许修改密码<br />-x 密码最长有效期限</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 修改root密码
sudo passwd root
# 系统会提示输入新密码Enter new UNIX password:  </pre>
</td>
</tr>
<tr>
<td class="blog_h2">who</td>
<td>显示本系统上的登陆用户</td>
</tr>
<tr>
<td class="blog_h2">w</td>
<td>
<p>显示哪些人登录了，他们在做什么
<p><span style="background-color: #c0c0c0;">格式：<br /></span></p>
<pre class="crayon-plain-tag">w [options] user [...]</pre>
<p> <span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 第一行：当前时间，系统运行时间，登录用户数，1、5、15分钟负载
 22:23:26 up 60 days, 21:06,  1 user,  load average: 0.04, 0.01, 0.00
#用户     终端名    远程主机         登录时间  空闲时间 JCPU   PCPU 当前执行的命令行
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/1    123.120.25.149   17:09    0.00s  0.06s  0.00s w
# JCPU：所有附着在此TTY上的进程所消耗的时间
# PCPU：WHAT指定的那个进程消耗的时间</pre>
</td>
</tr>
<tr>
<td class="blog_h2">logname</td>
<td>显示当前用户的名称，类似的命令：whoami</td>
</tr>
<tr>
<td class="blog_h2">hostname</td>
<td>显示系统的主机名字</td>
</tr>
<tr>
<td class="blog_h2">hostid</td>
<td>显示主机的 32 位的16 进制ID</td>
</tr>
<tr>
<td class="blog_h2">uname</td>
<td>显示操作系统的信息，例如内核版本</td>
</tr>
<tr>
<td class="blog_h2">arch</td>
<td>显示当前系统的指令集架构，相当于uname -m</td>
</tr>
<tr>
<td class="blog_h2">id</td>
<td>打印当前进程的真实和有效用户ID、组ID</td>
</tr>
<tr>
<td class="blog_h2">tty</td>
<td>显示当前登录终端的设备映射文件</td>
</tr>
<tr>
<td class="blog_h2">init</td>
<td>init命令是所有进程的父进程。在系统启动的最后一步调用</td>
</tr>
<tr>
<td class="blog_h2">runlevel</td>
<td>显示当前和最后的运行级别</td>
</tr>
<tr>
<td class="blog_h2">reboot</td>
<td>重新启动主机</td>
</tr>
<tr>
<td class="blog_h2">shutdown</td>
<td>关闭主机，类似的还有halt</td>
</tr>
<tr>
<td class="blog_h2">trap</td>
<td>捕获信号并设置信号处理逻辑，参考<a href="/bash-study-note/#trap">Bash学习笔记</a></td>
</tr>
<tr>
<td class="blog_h2">pgrep,pkill</td>
<td>
<p>查找或杀死进程<br />这两个命令用于根据进程的名称或者其他属性来查找、杀死进程
<p><span style="background-color: #c0c0c0;">格式：</span><br />pgrep [options] pattern<br />pkill [options] pattern</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-signal　仅pkill，需要发送给目标进程的信号<br />-c　打印匹配的进程数量而不是输出详细信息<br />-d　仅pgrep，指定用于分隔进程ID的字符串<br />-f　对整个命令行进行匹配而不是进程名称<br />-g　仅选择有效GID与该选项指定的GID匹配的进程，多个GID用逗号分隔<br />-G　仅选择真实GID与该选项指定的GID匹配的进程，多个GID用逗号分隔<br />-l　仅pgrep，连同PID一起列出进程名称<br />-a　仅pgrep，连同PID一起列出进程完整命令行<br />-n　仅选择最新启动的进程<br />-o　仅选择最早启动的进程<br />-P　仅选择父进程是该选项指定的PID的进程<br />-u　仅选择有效UID与该选项指定的UID匹配的进程，多个UID用逗号分隔<br />-U　仅选择真实UID与该选项指定的UID匹配的进程，多个UID用逗号分隔<br />-v　取反匹配<br />-w　仅pgrep，显示所有线程的id<br />-x　仅选择名称（如果指定-f则是命令行）完全匹配模式的进程<br />pattern　用于匹配名称或者命令行的正则表达式</p>
<p><span style="background-color: #c0c0c0;">退出状态：</span><br />0　一个或更多的进程被匹配 <br />1　无进程匹配 <br />2　语法错误 <br />3　其它严重错误</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#立即杀死所有MySQL和Java进程
sudo pkill -9 '(mysql|java)'
#列出MySQL进程的PID和命令行路径
pgrep -a mysql</pre>
</td>
</tr>
<tr>
<td class="blog_h2"><a id="service"></a>service</td>
<td>
<p>启动或停止一个System V系统服务（/etc/init.d中的脚本）或者Upstart Job（/etc/init中的配置文件）
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">service SCRIPT COMMAND [OPTIONS]
# 显示所有服务的状态
service --status-all</pre>
<p>SCRIPT要么对应<pre class="crayon-plain-tag">/etc/init.d/SCRIPT</pre> ，要么对应<pre class="crayon-plain-tag">/etc/init/SCRIPT.conf</pre> ，如果这两个目录都存在SCRIPT则Upstart Job的优先级更高</p>
<p>可以调用哪些COMMAND则取决于脚本，service将COMMAND和OPTIONS直接传递给对应的脚本</p>
<p>每个脚本至少应该定义start、stop命令，COMMAND restart相当于先运行stop，紧接着运行start</p>
</td>
</tr>
<tr>
<td class="blog_h2">monit</td>
<td>
<p>监控进程、程序、文件的状态。可以完成自动重启</p>
<p>参考<a href="/monit-under-monit">Ubuntu下使用monit</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">chkconfig</td>
<td>
<p>新近的Ubuntu版本已经不支持该命令，使用<span style="background-color: #c0c0c0;">update-rc.d</span>命令代替之</p>
<p>显示和管理在启动过程中所开启的服务，该命令提供简单的方式管理/etc/rc[0-6].d目录中的符号链接<br />chkconfig可以添加、移除、列出、修改服务，以及检查服务的启动状态<br />如果不指定--level，on/off选项应用于2-5运行级别，reset则应用于所有级别<br />注意：每个服务可以包含start、stop脚本，当切换运行级别时，已经启动的服务不会重新启动，已经停止的服务也不会重新停止</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">chkconfig [--list] [--type type][name]
chkconfig --add name
chkconfig --del name
chkconfig --override name
chkconfig [--level levels] [--type type] name &lt;on|off|reset&gt;
chkconfig [--level levels] [--type type] name</pre>
<p><span style="background-color: #c0c0c0;">选项：</span><br />--level levels 指定操控的运行级别<br />--add name 添加一个服务，服务脚本必须预先存放为/etc/init.d/name<br />--del name 删除一个服务，/etc/rc[0-6].d中的符号链接全部删除<br />--list name 列出服务在各运行级别的启停信息</p>
</td>
</tr>
<tr>
<td class="blog_h2">sysv-rc-conf</td>
<td>
<p>提供图形界面来管理System V风格的启动脚本：</p>
<pre class="crayon-plain-tag">sudo apt-get install sysv-rc-conf
sudo sysv-rc-conf </pre>
</td>
</tr>
<tr>
<td class="blog_h2">update-rc.d</td>
<td>
<p>安装或者移除System V风格的初始化脚本的符号链接
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">update-rc.d [-n] [-f] name remove
update-rc.d [-n] name defaults
update-rc.d [-n] name disable|enable [ S|2|3|4|5 ]</pre>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-n  不做任何事情，只显示会做什么<br />-f 强制移除符号链接，即使/etc/init.d/name仍然存在</p>
</td>
</tr>
<tr>
<td class="blog_h2"><a id="initctl"></a>initctl</td>
<td>
<p>管理Upstart任务，通过此命令管理员可以与基于Upstart的init进程交互。某些发行版中，此命令可以和service替换使用，例如<pre class="crayon-plain-tag">initctl start srv</pre> 效果等同于<pre class="crayon-plain-tag">service srv start</pre> </p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">initctl [OPTION]...  COMMAND [OPTION]...  ARG...</pre>
<p><span style="background-color: #c0c0c0;">子命令：</span><br />start JOB [KEY=VALUE]...   启动Job的新实例。KEY=VALUE用于指定Job的环境变量，对于多实例的Job通常基于环境变量设置它的实例名<br />stop JOB [KEY=VALUE]...  请求停止一个Job实例<br />restart JOB [KEY=VALUE]...  重启一个Job实例，首先将其Goal设置为stop，然后设置Goal为start<br />reload JOB [KEY=VALUE]...  发送SIGHUP信号给运行中的Job实例<br />status JOB [KEY=VALUE]...  显示Job的状态<br />list  列出已知的Job及其实例<br />emit EVENT [KEY=VALUE]...  请求发布EVENT类型的事件，KEY=VALUE用于指定事件中的环境变量，这些变量会自动export到受事件影响的Job的脚本代码中</p>
</td>
</tr>
<tr>
<td class="blog_h2"><a id="systemctl"></a>systemctl</td>
<td>
<p>控制和管理基于Systemd的服务，以及Systemd init机制本身。</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># NAME 服务的名称，必须是unitName.unitType，例如mysql.service
# 如果unitType为service，则.unitType部分可以省略
systemctl [OPTIONS...] COMMAND [NAME...]</pre>
<p><span style="background-color: #c0c0c0;">子命令：</span><br />start  启动一个服务， systemctl start x.service等价于sysvinit的service x start，下同<br />stop  停止一个服务<br />restart  停止，然后再启动一个服务。即使执行命令之前，服务没有在运行，它也会被启动<br />reload  如果支持，用于重新载入一个服务<br />condrestart  如果服务正在运行，则重启之<br />status  打印服务的状态<br />list-unit-files   列出所有Unit定义，附加--type=service则仅列出服务<br />enable  允许服务自启动，systemctl enable x.service等价于sysvinit的chkconfig x on<br />disable  禁止服务自启动<br />is-enabled  检查服务是否可以自启动<br />daemon-reload  重新加载Systemd的配置<br />reboot  重启机器<br />poweroff 关机<br />suspend  待机<br />hibernate  休眠<br />hybrid-sleep 混合休眠模式（同时休眠到硬盘并待机）</p>
<p>示例：</p>
<pre class="crayon-plain-tag"># 列出所有正在运行的单元
systemctl list-units
# 列出所有单元，包括没有配置文件或者启动失败的
systemctl list-units --all
# 列出所有没有运行的单元
systemctl list-units --all --state=inactive
# 列出所有加载失败的单元
systemctl list-units --failed
# 列出所有正在运行的服务
systemctl list-units --type=service

# 查看蓝牙服务的状态
systemctl status bluetooth.service

# 查看单元是否启用、禁用
# enabled 允许自动启动
# disabled 不允许自动启动
# static 该单元的配置不包含install段，只能作为其它配置文件的依赖
# masked 该单元禁止被设置为自启动
systemctl list-unit-files

# 启动MySQL服务
systemctl mysql start
# 杀死Apache服务的所有进程
ystemctl kill apache.service 

# 让Systemd加载最新的配置文件
sudo systemctl daemon-reload</pre>
</td>
</tr>
<tr>
<td class="blog_h2">journalctl</td>
<td>
<p>查询systemd日志，即systemd-journald.service写入磁盘的日志内容。如果调用时不添加任何参数，则输出所有已经收集的日志内容。你可以指定多个FIELD=VALUE格式过滤条件，例如_SYSTEMD_UNIT=tomcat8.service。如果：
<ol>
<li>多个过滤条件针对不同的FIELD，则这些条件进行AND</li>
<li>多个过滤条件针对相同的FIELD，则这些条件进行OR</li>
<li>在两组过滤条件之间添加 + 符号，则前后条件进行OR</li>
</ol>
<p>所有用户被授权访问自己私有的日志。默认的仅仅root和systemd-journal组的用户能够访问系统日志、其它用户的日志</p>
<p><span style="background-color: #c0c0c0;">格式：</span>journalctl [OPTIONS...] [MATCHES...]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-r  反转输出顺序，最新的日志最先显示<br />-n NUM  显示最新的NUM条日志，默认10<br />-f 持续追加日志到标准输出，类似于tail -f<br />-k, --dmesg 仅显示内核消息，等价于_TRANSPORT=kernel<br />-u, --unit=UNIT|PATTERN，根据systemd配置单元过滤，等价于_SYSTEMD_UNIT=*<br />--user-unit= 显示特定用户会话单元（session unit）的消息，等价于_UID=*<br />-p 根据优先级过滤消息，可以指定数字或者级别名称，0-7分别为emerg,alert,crit,err,warning,notice,info,debug。可以指定范围，例如 3..4。等价于PRIORITY=*<br />--since=, --until= 显示指定时间之后，或者之前的消息，日期格式：2012-10-30 18:17:16，如果时间部分省略默认00:00:00，日期还可以使用yesterday、today等单词<br />--system  仅仅显示内核和系统消息<br />--user  仅仅显示当前用户的配置单元的消息<br />-M  仅仅显示本机上一个正在运行的容器的消息，必须指定容器名</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 显示tomcat8服务今天的日志
journalctl -u tomcat8 --since today
# 显示指定进程的日志
journalctl _PID=12558
# 显示特定程序的日志
journalctl /usr/bin/bash
# 显示最新的20条日志
journalctl -n 20
# 持续跟踪新的日志写入
journalctl -f
# 查看磁盘用量
journalctl --disk-usage
# 删除老的日志，直到磁盘占用空间不大于1G
sudo journalctl --vacuum-size=1G
# 删除老的日志，保留最近1年的
sudo journalctl --vacuum-time=1years

# 显示Kubelet的INFO及其一上级别的信息
journalctl -u kubelet -f  -p 0..7

# 指定时间范围
journalctl --since "2018-08-16 18:30:00" --until "2018-08-16 18:40:0"</pre>
</td>
</tr>
<tr>
<td class="blog_h2">systemd-analyze</td>
<td>
<p>分析系统的启动耗时
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 查看启动耗时的概要信息
systemd-analyze         
# 查看每个服务的启动耗时信息，按照耗时从高到低排列
systemd-analyze blame</pre>
</td>
</tr>
<tr>
<td class="blog_h2">hostnamectl</td>
<td>
<p>查看或者设置主机名信息
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 设置主机名
sudo hostnamectl set-hostname</pre>
</td>
</tr>
<tr>
<td class="blog_h2">localectl</td>
<td>
<p>进行本地化配置
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 设置语言和编码方式
sudo localectl set-locale LANG=en_GB.utf8
# 设置键盘映射方式
sudo localectl set-keymap en_GB </pre>
</td>
</tr>
<tr>
<td class="blog_h2">timedatectl</td>
<td>
<p>时区、日期、时间设置
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 列出所有可用时区
timedatectl list-timezones 
# 设置时区
sudo timedatectl set-timezone America/New_York
# 设置日期、时间，格式分别为YYYY-MM-DD、HH:MM:SS
sudo timedatectl set-time "2016-11-11"
sudo timedatectl set-time "16:16:00"</pre>
</td>
</tr>
<tr>
<td>chronyc</td>
<td>
<p>时钟同步命令
<p>示例：</p>
<pre class="crayon-plain-tag"># 查看 NTP 服务器的在线和离线状态
chronyc activity

# 检查 NTP 访问是否对特定主机可用
chronyc accheck

# 该命令会显示有多少 NTP 源在线/离线
chronyc activity

# 手动添加一台新的 NTP 服务器
chronyc add server

# 在客户端报告已访问到服务器
chronyc clients

# 手动移除 NTP 服务器或对等服务器
chronyc delete

# 手动设置守护进程时间
chronyc settime

# 校准时间服务器，显示系统时间信息
chronyc tracking

# 检查 NTP 访问是否对特定主机可用
chronyc accheck

# 查看时间同步源
chronyc sources -v

# 查看时间同步源状态
chronyc sourcestats -v

# 立即进行时钟同步
chronyc -a makestep</pre>
</td>
</tr>
<tr>
<td class="blog_h2">loginctl</td>
<td>
<p>查看当前登录的用户或会话
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 显示登录的会话
loginctl list-sessions
# 显示登录的用户
loginctl list-users</pre>
</td>
</tr>
<tr>
<td class="blog_h2">su</td>
<td>
<p>改变用户身份，以另外一个账户登录，如果不加参数，则改变为root
<p><span style="background-color: #c0c0c0;">格式：</span><br />su [options] [username]<br /><span style="background-color: #c0c0c0;">选项：</span><br />-c,  指定需要执行的命令<br />-, -l, --login 提供类似于username直接登陆的执行环境，如果使用“-”，必须作为最后一个options<br />-s, --shell 需要调用的Shell<br />-m, -p  保留当前的环境变量，除了$PATH、$IFS</p>
</td>
</tr>
<tr>
<td class="blog_h2">sudo</td>
<td>
<p>以root或其他用户身份来执行一个命令。只能应用于程序，不能应用于Bash内置命令，例如：<pre class="crayon-plain-tag">sudo cd /var/lib/mysql</pre>不会达到预期效果。</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br /> sudo -h | -K | -k | -V<br /> sudo -v [-AknS] [-g group] [-h host] [-p prompt] [-u user]<br /> sudo -l [-AknS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-E 提示需要保留当前环境变量<br />-e 编辑一个或者多个文件，而不是执行命令<br />-g 指定主要组（primary group）而不是使用目标用户的主要组<br />-H 设置HOME环境变量为目标用户的HOME目录<br />-i 执行用户的默认登陆Shell，这意味着.profile、.login等会被读取并执行<br />-u 指定目标用户</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 以root身份执行命令，不永久提升
sudo ls /dev
# 永久提升当前用户为root，不执行命令
sudo -i
exit     #需要退出root的登陆才能返回当前用户</pre>
</td>
</tr>
<tr>
<td class="blog_h2">mount</td>
<td>
<p>挂载一个文件系统， 通常都用来安装外部设备， 比如软盘或CDROM。Unix系统中所有可访问的文件，都组织在单棵树中，这棵树的根叫做  / 。树中的文件可以分布在多个设备上。mount命令的职责就是将设备附到文件系统树的某个节点上。
<p>大部分设备都由特殊的设备文件来代表。例外情况如NFS，它由gmem.cc:/dir这样的URI表示。引用设备文件时，也可以使用LABEL或UUID。</p>
<p>注意：</p>
<ol>
<li>作为挂载点的目录，必须预先创建</li>
<li>如果挂载点目录中有文件，挂载成功后，此文件不可见</li>
<li>上述不可见的文件，在umount后重新出现</li>
<li>挂载后，挂载点于目录文件系统根目录具有一致的Unix权限</li>
</ol>
<p><span style="background-color: #c0c0c0;">绑定挂载</span></p>
<p>从内核2.4开始，允许将文件系统树的某一部分，重新挂载到另外一个位置：</p>
<p style="padding-left: 30px;">mount --bind olddir newdir<br />mount -B olddir newdir</p>
<p>在fstab中，进行绑定挂载的格式是：/olddir /newdir none bind</p>
<p>执行绑定挂载后，olddir的内容可以在newdir处访问，它们一模一样。</p>
<p>绑定挂载也可以用来挂载单个文件。</p>
<p>默认情况下，绑定挂载不会处理子挂载，也就是说，/olddir的某个子目录被作为挂载点的情况，其内容无法体现在/newdir中，要解决此问题，可以使用：</p>
<p style="padding-left: 30px;">mount --rbind olddir newdir</p>
<p><span style="background-color: #c0c0c0;">移动操作</span></p>
<p>从2.5.1开始，支持将一个挂载的子树移动到另外一个位置：</p>
<p style="padding-left: 30px;">mount --move olddir newdir<br />mount -M olddir newdir</p>
<p>注意olddir必须是一个挂载点</p>
<p><span style="background-color: #c0c0c0;">共享子树</span></p>
<p>从2.6.15开始，可以将挂载、及其子挂载标记为shared, private, slave 或 unbindable。可以在容器技术中使用：</p>
<p style="padding-left: 30px;">mount --make-shared mountpoint<br /> mount --make-slave mountpoint<br /> mount --make-private mountpoint<br /> mount --make-unbindable mountpoint</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />mount -t type device dir<br />mount [-lhV]<br />mount -a [-fFnrsvw] [-t vfstype] [-O optlist]<br />mount [-fnrsvw] [-o option[,option]...] device|dir<br />mount [-fnrsvw] [-t vfstype] [-o options] device dir</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-a　--all 挂载所有fstab中包括的所有文件系统<br />-f　--fake 执行所有步骤，除了系统调用，用于和-v联用来观察mount命令的具体行为<br />-r　--read-only 挂载为只读模式，与-o ro同义<br />-w　--rw 挂载为读写模式，与-o rw同义<br />-L 　label 挂载具有指定标签的分区<br />-U 　uuid 挂载具有指定UUID的分区<br />-t　 --types vfstype 指示文件系统的类型，如果不指定，mount命令会尝试猜测文件系统的类型<br />-o　--options 使用逗号分隔的选项，例如mount LABEL=mydisk -o noatime,nouser</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#把第一块硬盘的第3个分区挂载为/kssi 
mount /dev/hda3 /kssi 
#挂载一个iso镜像文件 
mount -o loop /home/alex/unbunt-14.02.iso /mnt/cdrom</pre>
</td>
</tr>
<tr>
<td class="blog_h2">umount</td>
<td>
<p>卸载一个当前已经 mount 的文件系统
<p>举例：</p>
<pre class="crayon-plain-tag">#根据设备路径卸载 
umount /dev/hda3 
#根据挂载目录卸载 
umount /kssi</pre>
</td>
</tr>
<tr>
<td class="blog_h2">getconf </td>
<td>
<p>查询系统配置变量
<p><span style="background-color: #c0c0c0;">格式：</span><br />getconf -a<br />getconf [-v specification] system_var<br />getconf [-v specification] path_var pathname</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a 显示所有配置变量及其值<br />system_var 系统配置变量的名称，在sysconf中定义<br />path_var 由pathconf定义的系统配置变量</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#获取内存页的大小
getconf PAGESIZE</pre>
</td>
</tr>
<tr>
<td class="blog_h2">mkswap</td>
<td>创建一个交换分区或文件，随后必须马上使用swapon来启用</td>
</tr>
<tr>
<td class="blog_h2">swapon</td>
<td rowspan="2">启用/禁用交换分区或文件，通常在启动和关机的时候才有效</td>
</tr>
<tr>
<td class="blog_h2">swapoff</td>
</tr>
<tr>
<td class="blog_h2">mke2fs</td>
<td>创建ext2文件系统</td>
</tr>
<tr>
<td class="blog_h2">tune2fs</td>
<td>调整ext2文件系统。 可以用来修改文件系统参数</td>
</tr>
<tr>
<td class="blog_h2">dumpe2fs</td>
<td>打印非常详细的文件系统信息</td>
</tr>
<tr>
<td class="blog_h2">tune2fs </td>
<td>
<p>显示或调整ext2/ext3/ext4文件系统的参数
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#查看硬盘分区的块大小
tune2fs -l /dev/xvda | grep 'Block size'</pre>
</td>
</tr>
<tr>
<td class="blog_h2">du</td>
<td>递归的显示磁盘文件的使用状况。默认从当前工作目录递归</td>
</tr>
<tr>
<td class="blog_h2">hdparm</td>
<td>列出或修改硬盘参数</td>
</tr>
<tr>
<td class="blog_h2">fdisk</td>
<td>
<p>在存储设备上创建和修改一个分区表。该命令理解DOS风格的分区表、BSD|SUN风格的disklables。fdisk不理解GUID分区表（GPTs），它也不是为了大型分区设计的，你可以使用GNU parted代替它
<p>硬盘可以被划分为1-N个逻辑磁盘——分区（partitions），关于分区的信息记录在分区表中，分区表位于磁盘的0扇区</p>
<p>Linux至少需要一个分区，作为其<span style="background-color: #c0c0c0;">根文件系统</span>。Linux可以使用交换文件或者交换分区，然而后者效率高，因此Linux系统的第二个分区常常是<span style="background-color: #c0c0c0;">swap</span>分区。在Intel兼容硬件上启动机器的BIOS最多只能访问磁盘的前1024个柱面（cylinders），因此，大磁盘用户常常划分从若干MB的小分区，挂载到<span style="background-color: #c0c0c0;">/boot</span>，存储内核镜像、一部分启动时需要的辅助文件</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">fdisk [-uc] [-b sectorsize] [-C cyls] [-H heads] [-S sects] device
fdisk -l [-u] [device...]
fdisk -s partition...</pre>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-b sectorsize    指定磁盘的扇区大小<br />-c[=mode]    指定兼容模式：dos或nondos（默认）<br />-l 列出指定设备的分区表并退出<br />-s partition...    打印指定的每个分区的尺寸（按block计）</p>
<p><span style="background-color: #c0c0c0;">示例 - 在线修改分区表：</span></p>
<pre class="crayon-plain-tag"># 这个例子中，我们希望扩展根分区的大小
# 目标机器是一台虚拟机，我们首先扩展了它的磁盘尺寸
qemu-img resize xenial-base.qcow2 256G


# 但是查看磁盘空间，发现没有变化。这是因为分区表的问题
df -H
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        33G  2.6G   29G   9% /


# 开始修改分区表

# 首先关闭swap
swapoff -a

# 查看当前分区表
fdisk -l /dev/vda
# 可以看到磁盘是256G
Disk /dev/vda: 256 GiB, 274877906944 bytes, 536870912 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x873848e8

Device     Boot    Start      End  Sectors  Size Id Type
/dev/vda1  *        2048 65107967 65105920   31G 83 Linux
/dev/vda2       65110014 67106815  1996802  975M  5 Extended
/dev/vda5       65110016 67106815  1996800  975M 82 Linux swap / Solaris
# 但是分区表仅仅用到 67106815 * 512 / 1024 / 1024 / 1024 = 32GiB，也就是未扩展前的磁盘大小

# 进入交互模式
fdisk /dev/vda

# 依次删除所有分区
fdisk /dev/vda
Command (m for help): d
Partition number (1,2,5, default 5):   5        # 删除分区5，交换分区
Command (m for help): d
Partition number (1,2,5, default 5):   2        # 删除分区2
Command (m for help): d                         # 不需要选择，删除分区1

# 重新创建主分区
Command (m for help): n 
Select (default p): p                           # 创建主分区
Partition number (1-4, default 1):  1           # 创建根分区vda1
First sector (2048-536870911, default 2048):    # 保持第一扇区大小不变
Last sector, +sectors or +size{K,M,G,T,P} (2048-536870911, default 536870911): +252G
                                                # 填写+252G 留下4G给交换分区
# 重新创建交换分区
Command (m for help): n                         # 然后依次回车
Command (m for help): t                         # 修改分区2的类型
Partition number (1,2, default 2): 2
Partition type (type L to list all types): 82   # 修改为交换分区

# 保存更改
Command (m for help): w

# 由于根分区正在被使用，因此分区表修改但未重新载入
The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).

# 不需要重启，使用下面的命令告知内核，载入新的分区表
partprobe

# 可以看到分区表正常了
fdisk -l /dev/vda
Disk /dev/vda: 256 GiB, 274877906944 bytes, 536870912 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x873848e8

Device     Boot     Start       End   Sectors  Size Id Type
/dev/vda1            2048 528484351 528482304  252G 83 Linux
/dev/vda2       528484352 536870911   8386560    4G 82 Linux swap / Solaris

# 工作仍然没有结束，df -H 可以看到文件系统大小仍然是旧的
df -H
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        33G  2.6G   29G   9% /# 

# 更新文件系统大小
resize2fs /dev/vda1

# 重新创建交换分区
mkswap /dev/vda2
Setting up swapspace version 1, size = 4 GiB (4293914624 bytes)
no label, UUID=bb073528-4aa6-4af1-8bbd-78267bc90336

# 将新的UUID，替换fstab中的旧的
vim /etc/fstab

# 启动swap
swapon -a</pre>
</td>
</tr>
<tr>
<td class="blog_h2">badblocks</td>
<td>检查存储设备的坏块</td>
</tr>
<tr>
<td class="blog_h2">lsblk</td>
<td>列出所有块设备，将以树状结构显示磁盘和分区</td>
</tr>
<tr>
<td class="blog_h2">blkid</td>
<td>显示各分区的UUID</td>
</tr>
<tr>
<td class="blog_h2">lsusb</td>
<td>列出所有USB总线和使用USB 的设备</td>
</tr>
<tr>
<td class="blog_h2">usbmodules</td>
<td>输出连接USB 设备的驱动模块的信息</td>
</tr>
<tr>
<td class="blog_h2">mkbootdisk</td>
<td>创建启动盘</td>
</tr>
<tr>
<td class="blog_h2">dump,restore</td>
<td>备份和还原文件系统</td>
</tr>
<tr>
<td class="blog_h2">quota</td>
<td>显示用户或组的磁盘配额</td>
</tr>
<tr>
<td>setquota</td>
<td>设置用户或组的磁盘配额</td>
</tr>
<tr>
<td class="blog_h2">ulimit</td>
<td>
<p>设置使用系统资源的上限
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-a：显示目前资源限制的设定<br />-c ：设定core文件的最大值，单位为区块<br />-d ：程序数据节区的最大值，单位为KB<br />-f ：shell所能建立的最大文件，单位为区块<br />-H：设定资源的硬性限制，也就是管理员所设下的限制<br />-m ：指定可使用内存的上限，单位为KB<br />-n ：指定同一时间最多可开启的文件数<br />-p ：指定管道缓冲区的大小，单位512字节<br />-s ：指定栈的上限，单位为KB<br />-S：设定资源的弹性限制<br />-t ：指定CPU使用时间的上限，单位为秒<br />-u ：用户最多可开启的程序数目<br />-v ：指定可使用的虚拟内存上限，单位为KB</p>
</td>
</tr>
<tr>
<td class="blog_h2">lsmod</td>
<td>列出所有安装的内核模块</td>
</tr>
<tr>
<td class="blog_h2">insmod</td>
<td>强制一个内核模块的安装</td>
</tr>
<tr>
<td class="blog_h2">rmmod</td>
<td>强制卸载一个内核模块</td>
</tr>
<tr>
<td class="blog_h2">modinfo</td>
<td>输出一个可装载的内核模块的信息</td>
</tr>
<tr>
<td class="blog_h2">modprobe</td>
<td>
<p>从Linux内核中添加或者删除模块。该命令智能的进行模块的安装/卸载，为了便利，不区分模块名字中的-和_。该命令会查找<pre class="crayon-plain-tag">/lib/modules/`uname -r`</pre> 目录，找到所有模块和相关文件</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag">modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b] [modulename] [module params...]
modprobe [-r] [-v] [-n] [-i] [modulename...]
modprobe [-c]
modprobe [--dump-modversions] [filename]</pre>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a    安装命令行中所有指定的模块<br />-r    移除而不是安装模块</p>
<p>注意，重启后效果消失。要永久修改内核模块加载，使用/etc/modules文件</p>
</td>
</tr>
<tr>
<td class="blog_h2">sync</td>
<td>强制写入所有需要更新的 buffer 上的数据到硬盘上</td>
</tr>
<tr>
<td class="blog_h2">ldd</td>
<td>显示一个可执行文件的共享库的依赖关系</td>
</tr>
<tr>
<td class="blog_h2">rsync</td>
<td>
<p>可以在多台主机之间进行文件同步</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 本地同步
rsync [OPTION...] SRC... [DEST]

# 远程同步
rsync [OPTION...] [USER@]HOST:SRC... [DEST]     # PULL
rsync [OPTION...] SRC... [USER@]HOST:DEST       # PUSH</pre>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-a  归档模式，等价于-rlptgoD<br />-r  递归处理目录<br />-l   不跟随符号链接<br />-p 保留文件模式<br />-t  保留修改时间<br />-g  保留文件的group信息<br />-o  保留文件的owner信息<br />-D  --devices --specials 保留设备文件、保留特殊文件<br />-z  传输数据时进行压缩<br />-P 等价于 --partial --progress<br />--partial  保留部分传输的文件<br />--progress 显示传输进度<br />--delete  删除目标目录中比源目录多余的文件<br />--inplace  就地更新，不改变目标文件的inode<br />-t, --times  保持修改时间不变<br />-c, --checksum 基于校验和判断哪些文件需要传输，而非修改时间+尺寸<br />-I, --ignore-times 判断哪些文件需要传输时，仅仅考虑尺寸，不考虑修改时间<br />-v 显示更多日志<br />-e 指定使用的远程Shell<br />-i 为所有更新输出变更摘要<br />-q 安静模式</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 使用密钥登陆到bj.gmem.cc，然后将本地的www目录拷贝到bj的/var/目录下
rsync -avz -e "ssh -i  /home/alex/Documents/puTTY/gmem.key" --progress www root@bj.gmem.cc:/var/

# 使用校验和来确认文件是否需要同步
rsync -Pav -e "ssh -i /root/Documents/puTTY/gmem.key" --checksum \
    root@hk.gmem.cc:/var/www/html/blog/wp-content/uploads /var/www/html/blog/wp-content</pre>
</td>
</tr>
<tr>
<td class="blog_h2">stat</td>
<td>显示一个文件或目录的详细信息，例如尺寸、修改时间等</td>
</tr>
<tr>
<td class="blog_h2">logger</td>
<td>
<p>附加一个用户产生的消息到系统日志，系统日志默认/var/log/syslog
<p><span style="background-color: #c0c0c0;">格式：</span><br />logger [-dhisV] [-f file] [-n server] [-P port] <br />[-p pri] [-t tag] [-u socket] [message]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-d, --udp 使用UDP而不是TCP<br />-i, --id 记录进程的ID<br />-f, --file file 将文件的内容记录到系统日志，不得与命令行message联用<br />-n, --server server 通过UDP将日志写到远程syslog服务器<br />-P, --port port UDP端口，默认514<br />-p, --priority priority 设定消息进入日志的优先级，使用facility.level的格式，默认user.notice<br />-s, --stderr 同时将消息写出到标准错误<br />-t, --tag tag 为每行日志输出附加标签<br />-u, --socket socket 写到指定的套接字而不是内置的syslog例程</p>
</td>
</tr>
<tr>
<td class="blog_h2">logrotate</td>
<td>管理系统的日志文件。可以在合适的时候轮换、压缩、删除</td>
</tr>
<tr>
<td class="blog_h2">strip</td>
<td>从可执行文件中去掉调试符号引用。这样做可以减小尺寸， 但是就不能调试了</td>
</tr>
<tr>
<td class="blog_h2">lscpu</td>
<td>
<p>打印CPU架构相关信息，示例：</p>
<pre class="crayon-plain-tag"># 显示CPU当前的时钟频率
lscpu | grep MHz </pre>
</td>
</tr>
<tr>
<td class="blog_h2">lshw</td>
<td>打印硬件信息</td>
</tr>
<tr>
<td class="blog_h2">dmidecode</td>
<td>
<p>解码DMI（即SMBIOS，System Management BIOS）信息为人类可读的格式。DMI表包含了系统硬件信息，包括序列号、BIOS版本
<p>信息依据DMI类型分段输出到屏幕上</p>
</td>
</tr>
<tr>
<td class="blog_h2">sysctl</td>
<td>
<p>在运行时配置内核参数，这些内核参数位于/proc/sys/</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />sysctl [-n] [-e] variable ...<br />sysctl [-n] [-e] [-q] -w variable=value ...<br />sysctl [-n] [-e] [-q] -p &lt;filename&gt;<br />sysctl [-n] [-e] -a<br />sysctl [-n] [-e] -A</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />variable 内核参数名（键），可以使用点号.代替/<br />variable=value 设置内核参数的值<br />-n 打印值时禁止打印键<br />-e 忽略未知键错误<br />-N 仅打印名称 <br />-q 不把设置的值打印到标准输出<br />-w 执行修改操作<br />-p 从/etc/sysctl.conf加载内核参数设置，设置为-表示从标准输入读取<br />-a 显示当前变量的所有值</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">sysctl -a
sysctl -n kernel.hostname
sysctl -w kernel.domainname="example.com"</pre>
</td>
</tr>
<tr>
<td class="blog_h2">dpkg-reconfigure</td>
<td>
<p>重新配置一个已经安装的包，<span style="background-color: #c0c0c0;">举例：</span>
<pre class="crayon-plain-tag"># 重新配置数字证书，可以禁用某些证书
dpkg-reconfigure ca-certificates</pre>
</td>
</tr>
<tr>
<td class="blog_h2">update-ca-certificates</td>
<td>
<p>根据配置文件 /etc/ca-certificates.conf 的内容，更新/etc/ssl/certs下的受信任证书的符号链接
</td>
</tr>
<tr>
<td>update-icon-caches</td>
<td>更新GTK图标缓存：<pre class="crayon-plain-tag">sudo update-icon-caches /usr/share/icons/*</pre></td>
</tr>
<tr>
<td class="blog_h2">openssl</td>
<td>
<p>OpenSSL的命令行工具，OpenSSL是一个实现了 SSL(v2/v3) 和TLS (v1) 的开源组件<br /><span style="background-color: #c0c0c0;">格式：</span><br />openssl command [ command_opts ] [ command_args ]</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 生成密钥并自签名（根证书）
openssl genrsa -out ca.key 2048
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 3650 \
  -subj "/C=CN/ST=BeiJing/L=BeiJing/O=Gmem Studio/OU=IT Support/CN=Gmem SHA256 CA"

# 单个命令同时生成密钥和证书，-nodes禁止 Enter PEM pass phrase 提示
openssl req -nodes -new -x509 -keyout server.key -out server.crt \
  -subj "/CN=Teleport Self-signed Certificate" -addext "subjectAltName = IP:82.157.67.96"

# 解码证书，以查看其内容
openssl x509 -in ca.crt -text -noout
# 解码证书请求，以查看其内容
openssl req -in k8s.csr -text -noout

# 解除私钥密码，将私钥转换为原始RSA Key
openssl rsa -in ca-withpass.key -out ca.key

# 转换为PKCS#8格式的私钥
# 这种格式的私钥以 -----BEGIN PRIVATE KEY----- 开头
# 包含私钥 + OID，OID说明了密钥类型
# 老版本的OpenSSL使用PKCS#1（传统OpenSSL格式），该格式以
# -----BEGIN RSA PRIVATE KEY----- 开头

# PKCS#1 转换为 PKCS#8 
openssl pkcs8 -topk8 -in ~/Documents/puTTY/ca.key -out rootCA-key.pem  -nocrypt
# PKCS#8 转换为 PKCS#1


# 生成非对称秘钥对
openssl genrsa -out www.gmem.cc.key 2048
#生成证书请求（生成网站证书时，Common Name应当是你的域名，不要加端口）
openssl req -new -sha256 -key www.gmem.cc.key -out www.gmem.cc.csr \
  -subj "/C=CN/ST=BeiJing/L=BeiJing/O=Gmem Studio/OU=IT Support/CN=*.gmem.cc"

# 对证书请求签名
openssl x509 -req -days 3650 -in www.gmem.cc.csr -CA ca.crt 
    -CAkey ca.key -CAcreateserial -out www.gmem.cc.crt

# 从证书中导出公钥
openssl x509 -inform pem -in www.gmem.cc.crt -pubkey


# 从Chrome 58开始，不支持仅仅依据CN字段来识别网站主机
# 必须在证书中使用SAN
openssl genrsa -out k8s.key 2048
export SAN_CFG=$(printf "\n[SAN]\nsubjectAltName=DNS:*.k8s.eb.dapp.com,DNS:*k8s.eb.mid,DNS:k8s.eb.dapp.com,DNS:k8s.eb.mid")
openssl req -new -sha256 -key k8s.key -out k8s.csr \
    -subj "/C=CN/ST=BeiJing/O=Beijing Dangdang Information Technology Co., Ltd./CN=TECH-DIGITAL Kubernetes Cluster " \
    -reqexts SAN -config &lt;(cat /etc/ssl/openssl.cnf &lt;(echo $SAN_CFG))

openssl x509 -req -sha256 -in k8s.csr  -out k8s.crt -CA ca.crt -CAkey ca-nopass.key -CAcreateserial -days 3650 \
     -extensions SAN -extfile &lt;(echo "${SAN_CFG}")
# 不使用配置文件生成带有SAN的请求
openssl req  -addext ... 
# 不使用配置文件进行签名
openssl x509 -extfile &lt;(printf "subjectAltName = DNS:localhost, IP:127.0.0.1, IP:82.157.67.96")
# 不使用配置文件进行签名，签名中间CA证书
cat "$(openssl version -d | sed 's/.*"\(.*\)"/\1/g')/openssl.cnf" | sed "s|^\(dir\s*=\s*\)\(.*\)|\1$PWD|g" &gt; openssl.cnf
openssl ca -extensions v3_ca -notext -in installer.csr -out installer.crt -cert ../ca/ca.crt -keyfile ../ca/ca.key -days 3650 -policy policy_anything -batch -create_serial -config openssl.cnf</pre>
</td>
</tr>
<tr>
<td class="blog_h2">cvt</td>
<td>
<p>计算VESA标准的CVT模式代码（mode lines），<span style="background-color: #c0c0c0;">举例：</span>
<pre class="crayon-plain-tag">cvt 1600 900 60
# 输出如下：
# # 1600x900 59.95 Hz (CVT 1.44M9) hsync: 55.99 kHz; pclk: 118.25 MHz
# Modeline "1600x900_60.00"  118.25  1600 1696 1856 2112  900 903 908 934 -hsync +vsync</pre>
</td>
</tr>
<tr>
<td class="blog_h2">xrandr</td>
<td>
<p>注：该命令的修改在重启后失效，要永久生效，参考<a href="/ubuntu-faq#add-modeline">添加自定义的显示模式</a>
<p>RandR扩展的原生命令行接口。RandR（Resize and Rotate）是X11协议的扩展，为屏幕根窗口提供改变大小、旋转、反射效果的能力，同时支持设置刷新率，<span style="background-color: #c0c0c0;"><span style="background-color: #c0c0c0;">举例：</span></span></p>
<pre class="crayon-plain-tag"># 查询当前连接到的显示器，显示分辨率和刷新率等信息
xrandr -q
# 输出
# Screen 0: minimum 1 x 1, current 1600 x 900, maximum 8192 x 8192
# Virtual1 connected primary 1600x900+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
#   1280x768      60.00 +  59.87  
#   ...
#   640x480       59.94  
#   1600x900_600.00 599.89* 
# Virtual2 disconnected (normal left inverted right x axis y axis)

# 新建一个显示模式              这里的内容参考cvt 1600 900 60命令的输出
xrandr --newmode "1600x900"  1837.25  1600 1760 1936 2272  900 903 908 1348 -hsync 命令+vsync

# 为显示器Virtual1添加刚刚新建的模式
xrandr --addmode Virtual1 1600x900

# 立即以指定模式输出
xrandr --output Virtual1 1600x900</pre>
</td>
</tr>
<tr>
<td class="blog_h2">dd</td>
<td>
<p>转换并拷贝文件，该命令根据参数转换数据的格式，并且把输入文件写出到输出文件中
<p><span style="background-color: #c0c0c0;">参数：</span><br />bs=BYTES 每次读写的最大字节数<br />cbs=BYTES 每次转换的字节数<br />conv=CONVS 根据逗号分隔的CONVS符号列表来执行转换<br />count=N 读取N个输入块，块大小为bs<br />ibs=BYTES 每次最多读取的字节数，默认512<br />if=FILE 从目标文件读取，而不是标准输入<br />iflag=FLAGS 根据逗号分隔的FLAG符号列表来读取<br />obs=BYTES 每次最多写入的字节数，默认512<br />of=FILE 写出到文件，而不是标准输出<br />oflag=FLAGS 根据逗号分隔的FLAG符号列表来写出<br />seek=N 在输出文件开始处，跳过N个obs字节的块<br />skip=N  在输入文件开始处，跳过N个ibs字节的块<br />status=WHICH 禁止输出什么信息到标准错误，none禁止所有；noxfer禁止传输状态<br />注意：N和BYTES可以添加后缀：KB（1000）；K（1024）；MB（1000*1000）；M（1024*1024）...等等</p>
<p><span style="background-color: #c0c0c0;">CONVS符号：</span><br />ascii 从EBCDIC到ASCII<br />ebcdic 从ASCII到EBCDIC<br />ibm 从ASCII到备选EBCDIC<br />block 填充空格，使换行符结尾的记录达到cbs大小<br />unblock 把记录结尾的空格改为换行符<br />lcase 转换为小写<br />ucase 转换为大写<br />sparse 对于NUL输入块，尝试seek而不是write<br />swab 交换每对输入字节<br />sync 填充NUL，让每个输入块达到ibs大小，与block/unblock联用，则填充空格而非NUL<br />excl 如果输出文件已经存在，则失败<br />nocreat 不去创建输出文件<br />notrunc 不去截断输出文件<br />noerror 出现读错误后，仍然继续<br />fdatasync 完毕前，同步数据到磁盘<br />fsync 同上，同时同步元数据</p>
<p><span style="background-color: #c0c0c0;">FLAG符号：</span><br />append 仅用于oflag，附加模式<br />direct 使用direct I/O<br />directory 除非是一个目录，否则失败<br />dsync 使用同步I/O<br />sync 使用他不I/O，包括元数据<br />nonblock 使用非阻塞I/O<br />noatime 不更新文件的访问时间<br />nocache 丢弃缓存数据<br />nofollow 不跟随符号链接</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 导出整个磁盘为镜像文件，注意if选择设备文件（而不是分区）
sudo dd bs=4M of=~/Downloads/raspberrypi-alex.img if=/dev/mmcblk0
# 导入镜像到磁盘，目标磁盘必须大于等于镜像，目标磁盘的所有分区必须预先unmount
sudo dd bs=4M if=~/Downloads/raspberrypi-alex.img of=/dev/mmcblk0&amp;nbsp;

# 写swap分区
sudo dd if=/dev/zero of=/swapfile bs=1M count=1024</pre>
</td>
</tr>
<tr>
<td class="blog_h2">cpufreq-info</td>
<td>
<p>检索cpufreq内核信息
<p><span style="background-color: #c0c0c0;">选项：</span><br />-c  CPU  显示指定CPU（序号）的信息<br />-e  打印调试信息<br />-f   依据cpufreq core得到CPU当前运作频率<br />-w  从硬件获取CPU运作频率，需要ROOT<br />-l    获得硬件限制的CPU频率范围<br />-d   得到使用的cpufreq内核驱动<br />-p   获得当前使用的cpufreq策略<br />-g   列出可用的governors，governors类似于Windows的电源计划：<br />          ondemand    按需快速动态调整CPU频率，会极速的在最大最小频率之间调整<br />          conservative   类似于ondemand，但是频率按时间的变化曲线会比较平缓<br />          performance   运行于最大频率<br />          powersave    运行于最小频率<br />          userspace   运行于用户指定的频率<br />-a   查看哪些CPU以相同的硬件频率运行<br />-s    如果可用，获得cpufreq统计信息<br />-y    显示最大的CPU频率变更延迟时间<br />-m  输出人类可读格式</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">cpufreq-info -p
# 800000 2901000 userspace 

cpufreq-info -g
# conservative ondemand userspace powersave performance</pre>
</td>
</tr>
<tr>
<td class="blog_h2">cpufreq-set</td>
<td>
<p>修改CPU频率设置的小工具
<p><span style="background-color: #c0c0c0;">选项：</span><br />-c CPU    指定操作的CPU的序号，如果不指定，针对0号操作<br />-d    governor可以选择的最低频率<br />-u    governor可以选择的最高频率<br />-g GOV    切换使用的governor<br />-f    手工设置频率，需要加载governor：userspace<br />-r    修改所有硬件相关的CPU，2.6.29+忽略此选项</p>
<p>注意：设置在重启后丢失，要持久化，可以修改<pre class="crayon-plain-tag">/etc/init.d/cpufrequtils</pre> <span style="color: #111111;">脚本</span></p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 设置为最大性能
sudo cpufreq-set -g performance
# 设置为省电模式
sudo cpufreq-set -g powersave

# 设置0号CPU的频率
sudo cpufreq-set -f 2901000</pre>
</td>
</tr>
<tr>
<td class="blog_h2"><a id="nsenter"></a>nsenter</td>
<td>
<p>在其它进程的命名空间中，执行程序。可进入的命名空间包括：mount、UTS、IPC、network、PID、user。
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 如果不指定program，则运行${SHELL}，默认/bin/sh
nsenter [options] [program [arguments]]</pre>
<p> <span style="background-color: #c0c0c0;">选项：</span></p>
<p>-t 进入哪个PID的命名空间<br />-m, --mount[=file] 进入mount命名空间，如果指定file，则从文件读取命名空间，否则使用PID的mount命名空间<br />-u 进入UTS命名空间<br />-i 进入IPC命名空间<br />-n 进入网络命名空间<br />-p 进入PID命名空间<br />-U 进入User命名空间<br />-G, --setgid gid 进入命名空间后，使用的GID，进入User命名空间总是需要设置GID，默认自动设置为0<br />-S, --setuid uid 进入命名空间后，使用的UID，进入User命名空间总是需要设置UID，默认自动设置为0<br />--preserve-credentials 进入User命名空间后不改变凭证信息，默认行为是设置为0，并且清楚补充组（supplementary groups）<br />-r 设置root目录<br />-w 设置工作目录</p>
</td>
</tr>
<tr>
<td>unshare </td>
<td>
<p>运行应用程序，且不共享父进程的某些命名空间。</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<p>unshare [options] program [arguments]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-m 取消共享mount命名空间<br />-u 取消共享UTS命名空间<br />-i 取消共享IPC命名空间<br />-n 取消共享网络命名空间<br />-p 取消共享PID命名空间<br />-U 取消共享User命名空间</p>
</td>
</tr>
<tr>
<td>lvm</td>
<td>
<p>逻辑卷管理器（Logical Volume Manager）用于管理“逻辑卷”，专注于<span style="background-color: #c0c0c0;">可弹性的扩缩文件系统的大小</span>，它能够把多个物理分区合并为一个逻辑的卷。</p>
<p><span style="background-color: #c0c0c0;">概念：</span></p>
<p>Physical Volume，PV，物理卷：可供存储LVM的块设备，例如一块磁盘、一个MBR/GPT分区、一个回环文件、一个被内核映射的设备。PV包含一个特殊的LVM头</p>
<p>Volume Group，VG，卷组：一组物理卷，可以作为逻辑卷的容器。物理块从VG中分配出来，分配给LV</p>
<p>Logic Volume，LV，逻辑卷：也叫虚拟卷，由单个VG中的很多物理块组成。行为上类似于物理块设备，你可以在其上创建文件系统</p>
<p>Physical Extent，PE，物理块：VG中最小的连续取于（默认4MiB），多个PE组成LV</p>
<p>下面示例一个由两块磁盘构成的LV：</p>
<pre class="crayon-plain-tag">Physical disks

  Disk1 (/dev/sda):
     _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    |Partition1 50 GiB (Physical volume) |Partition2 80 GiB (Physical volume)     |
    |/dev/sda1                           |/dev/sda2                               |
    |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |

  Disk2 (/dev/sdb):
     _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    |Partition1 120 GiB (Physical volume)                 |
    |/dev/sdb1                                            |
    |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|




LVM logical volumes

  Volume Group1 (/dev/MyVolGroup/ = /dev/sda1 + /dev/sda2 + /dev/sdb1):
     _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    |Logical volume1 15 GiB  |Logical volume2 35 GiB      |Logical volume3 200 GiB              |
    |/dev/MyVolGroup/rootvol |/dev/MyVolGroup/homevol     |/dev/MyVolGroup/mediavol             |
    |_ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|</pre>
<p><span style="background-color: #c0c0c0;">命令：</span></p>
<pre class="crayon-plain-tag"># 列出可作为物理卷的设备
lvmdiskscan

# 创建物理卷，此命令将会在设备上创建LVM头
pvcreate /dev/sda2

# 显示物理卷列表
pvdisplay
# 获取所有物理卷
pvs

# 分区增大后，使用下面的命令可以自动扩充PV的大小
pvresize /dev/sda1

# 缩小PV
pvresize --setphysicalvolumesize 40G /dev/sda1

# 移动PV上的数据
pvmove /dev/sdb1
# 指定目标
pvmove /dev/sdb1 /dev/sdf1

# 删除PV，解除LVM头，LVM不会再将其看作是PV
pvremove /dev/sdb1



# 创建卷组
# vgcreate &lt;volume_group&gt; &lt;physical_volume&gt;
vgcreate VolGroup00 /dev/sda2

# 扩大卷组，覆盖（添加）其它物理卷
vgextend VolGroup00 /dev/sdb1

# 你也可以在创建卷组是指定多个物理卷
vgcreate VolGroup00 /dev/sda2 /dev/sdb1 /dev/sdc

# 重命名卷组
vgrename /dev/vg02 /dev/my_volume_group

# 显示卷组列表
vgdisplay
# 获取所有卷组
vgs

# 从VG移除PV
vgreduce myVg /dev/sdb1
# 移除所有空的PV
vgreduce --all vg0



# 创建逻辑卷
# lvcreate -L &lt;size&gt; &lt;volume_group&gt; -n &lt;logical_volume&gt;
lvcreate -L 10G VolGroup00 -n lvolhome
# 随后你可以通过 /dev/VolGroup00/lvolhome 访问此逻辑卷

# 限制逻辑卷仅能位于指定的物理卷上
lvcreate -L 10G VolGroup00 -n lvolhome /dev/sdc1

# 使用卷组中所有剩余空间的LV
lvcreate -l 100%FREE VolGroup00 -n lvolhome

# 显示逻辑卷列表
lvdisplay
# 获取所有逻辑卷
lvs

# 重命名逻辑卷
lvrename /dev/vg02/lvold /dev/vg02/lvnew
# 或者
lvrename vg02 lvold lvnew

# 改变LV大小
# 可能需要先通知物理卷的大小改变了
pvresize /dev/vdb
# 修改逻辑卷大小
lvresize -L +10G --resizefs MyVolGroup/mediavol
lvresize -L 15G --resizefs MyVolGroup/mediavol
lvresize -l +100%FREE --resizefs cinder-volumes/cinder-volumes-pool

# 删除逻辑卷，需要先umount，lsblk获取挂载点列表
lvremove VolGroup00/lvolhome

# 改变逻辑卷属性
vgchange 
  # 激活（ay）或禁用（an）逻辑卷，只有激活的才能作为块设备
  -a|--activate y|n|ay</pre>
</td>
</tr>
<tr>
<td>dmsetup</td>
<td>
<p>Device Mapper的用户空间工具，可以实现低级别的逻辑卷管理
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 为指定的设备销毁非活动表槽（table slot）中的表
dmsetup clear device_name

# 创建具有指定名称的卷
# 如果操作成功，则设备出现在表中，并且为活动设备创建文件/dev/mapper/device_name
dmsetup create device_name [-u|--uuid uuid] [--addnodeoncreate|--addnodeonresume] 
  # 如果提供table或table_file则加载表并激活，否则从stdin读取表（除非-n）
  [-n|--notable|--table {table|table_file}] [--readahead {[+]sectors|auto|none}]

# 列出设备的live table引用的设备列表
dmsetup deps [-o options] [device_name]

# 打印设备的简要信息
dmsetup info [device_name]
# 格式化打印
dmsetup info -c|-C|--columns [--count count] [--interval seconds] [--nameprefixes] 
  [--noheadings] [-o fields] [-O|--sort sort_fields] [--separator separator] [device_name]

# 将table加载到设备的inactive table  slot
dmsetup load device_name [--table {table|table_file}]

# 列出设备名称
dmsetup ls [--target target_type] [--exec command] [--tree] [-o options]

# 向设备发送消息
dmsetup message device_name sector message

# 确保设备位于/dev/mapper的设备文件节点正确
dmsetup mknodes [device_name]

# 移除设备，移除后dmsetup不能再看见它。正在使用中的设备无法移除
# 使用 --force 会替换掉表，并让所有IO失败
# 使用 --deferred 可以延迟打开中的设备的删除，最后一个用户关闭设备后执行移除
dmsetup remove [-f|--force] [--retry] [--deferred] device_name

# 删除所有设备定义，也就是重置驱动
dmsetup remove_all [-f|--force] [--deferred]

# 重命名设备
dmsetup rename device_name new_name
dmsetup rename device_name --setuuid uuid

# 恢复设备，如果加载了inactive table，则表变为live，退出的IO将重新排队等待处理
dmsetup resume device_name [--addnodeoncreate|--addnodeonresume] [--noflush] 
  [--nolockfs] [--readahead {[+]sectors|auto|none}]

# 管理IO统计信息
dmsetup stats command [options]

# 输出状态信息
dmsetup status [--target target_type] [--noflush] [device_name]

# 暂停设备，任何已经被设备映射的、尚未完毕的IO操作会被flush，
# 后续IO操作将被推迟处理，直到从暂停中恢复
dmsetup suspend [--nolockfs] [--noflush] device_name

# 输出设备的当前的表信息
dmsetup table [--target target_type] [--showkeys] [device_name]

# 打印当前加载的目标信息
dmsetup targets

# 等待直到事件计数器超过event_nr
dmsetup wait [--noflush] device_name [event_nr]

# 等待进行中的IO完成，然后将表替换为新的，让所有新IO请求失败
# 如果操作成，设备的表中引用的所有设备被释放
dmsetup wipe_table device_name [-f|--force] [--noflush] [--nolockfs]</pre>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 创建空白文件
# 声明大小10G，实际占用空间（seek）4K
dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M
# 声明大小100M
dd if=/dev/zero of=/tmp/meta.img bs=1K count=1 seek=100K

# 映射为虚拟（loopback）块设备
losetup /dev/loop10 /tmp/meta.img
losetup /dev/loop11 /tmp/data.img

# 基于上述块设备，创建一个mapped device（thin-provisioning池）
dmsetup create thinpool0 \
#          数据设备起始扇区
#            数据设备结束扇区 * 512 = 10G      
#                               元数据设备
#                                           数据设备     
  --table "0 20971522 thin-pool /dev/loop10 /dev/loop11 \
# 最小可以分配的扇区数
#    最少可用的扇区阈值
#          有1个附加参数
#            附加参数，跳过用0填充的块 
  128 65536 1 skip_block_zeroing"

# 设备 /dev/mapper/thinpool0 现在可用

# 在thinpool0上创建一个thin-provisioning卷
#                                        标识符
dmsetup message thinpool0 0 "create_thin 0"
dmsetup create thinvol0 --table "0 2097152 thin /dev/mapper/thinpool0 0"

# 设备 /dev/mapper/thinvol0 现在可用

# 格式化
mkfs.ext4 /dev/mapper/thinvol0

# 挂载
mkdir /tmp/thinvol
mount /dev/mapper/thinvol0 /tmp/thinvol



# 创建快照
#                                        新设备ID（快照设备）
#                                          旧设备ID，就是上文的thinvol
dmsetup message thinpool0 0 "create_snap 1 0"
# 创建快照设备
dmsetup create thinvol-snap0 \
  --table "0 2097152 thin /dev/mapper/thinpool0 1"</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">诊断与监控命令</span></div>
<table class="full-width fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">df</td>
<td>
<p>使用列表的形式显示文件系统的使用状况
<p><span style="background-color: #c0c0c0;">格式：</span><br />df [OPTION]... [FILE]...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a, --all 包含虚拟文件系统<br />-B, --block-size=SIZE 按SIZE缩放长度单位，-BM表示以1,048,576字节为单位<br />--total 进行总计<br />-h, --human-readable 以可读方式显示容量<br />-H, --si 与-h相似，但是单位转换时使用1000而不是1024<br />-i, --inodes 显示inode信息而不是block用量<br />-k 等价于 --block-size=1K<br />-l, --local 仅显示本地文件系统<br />--no-sync 在获取容量使用情况前不使用sync操作，默认行为<br />--output[=FIELD_LIST] 使用FIELD_LIST来指定输出列<br />-P, --portability 使用POSIX输出格式<br />--sync 在获取容量使用情况前使用sync操作<br />-t, --type=TYPE 限制文件系统的类型<br />-T, --print-type 打印文件系统类型<br />-x, --exclude-type=TYPE 排除文件系统类型</p>
<p><span style="background-color: #c0c0c0;">输出：</span><br />Filesystem　文件系统（分区）<br />1K-blocks　 总计块数量<br />Used　已使用块数量<br />Available　 可用块数量<br />Use%　占用百分比<br />Mounted on　挂载点</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">alex@amethystine:~$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda1       81405680 7703348  69544124  10% /
none                   4       0         4   0% /sys/fs/cgroup
udev             3041916       4   3041912   1% /dev
tmpfs             610540    1624    608916   1% /run
none                5120       0      5120   0% /run/lock
none             3052692     152   3052540   1% /run/shm
none              102400      32    102368   1% /run/user</pre>
</td>
</tr>
<tr>
<td class="blog_h2">du</td>
<td>
<p>估算磁盘使用状况。该命令会递归的统计目录下每个文件的磁盘占用情况
<p><span style="background-color: #c0c0c0;">格式：</span><br />du [OPTION]... [FILE]...</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-0, --null 使用空字符而不是换行符结束输出的每一行<br />-a, --all 统计所有文件，而不仅仅是目录<br />--apparent-size 统计文件尺寸而不是磁盘占用空间，前者<br />-B, --block-size=SIZE 打印输出前，对尺寸进行缩放<br />-b, --bytes 等价于--apparent-size --block-size=1<br />-c, --total 生成总计信息<br />-D, --dereference-args 仅解引用命令行参数列出的文件的符号链接<br />-d, --max-depth=N 仅打印不超过N级子目录的统计信息<br />-h, --human-readable 使用便于阅读的方式打印输出，例如1K 234M 2G<br />-L, --dereference 解引用所有符号链接<br />-l, --count-links 对于硬链接，出现多次时，分别统计大小<br />-P, --no-dereference 默认，不解引用任何符号连接<br />-S, --separate-dirs 不包含子目录的大小<br />--si 类似于-h，但是使用1000而不是1024作为倍率<br />-s, --summarize 仅显示每个参数的统计信息<br />-t, --threshold=SIZE 如果为负数，则排除大小小于SIZE的条目；如果为正数，则排除大小大于SIZE的条目<br />--time 显示目录和文件的最后修改时间<br />--time-style=STYLE 时间显示格式，参考date命令的日期格式<br />-X, --exclude-from=FILE 排除FILE模式匹配的文件<br />exclude files that match any pattern in FILE</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#显示最大的文件或目录
for i in G M K; do du -ah | grep [0-9]$i | sort -nr -k 1; done | head -n 11
#显示最大子目录
du -cks * | sort -rn | head -10</pre>
</td>
</tr>
<tr>
<td class="blog_h2">lsof</td>
<td>
<p>列出打开的文件<br />这里的文件可能是：普通文件、目录、特殊块文件、特殊字符文件、库、流、网络文件（套接字、NFS文件）<br />如果不提供任何选项lsof会列出当前活动进程打开的所有文件
<p><span style="background-color: #c0c0c0;">格式：</span><br />lsof <br />　　[ -?abChKlnNOPRtUvVX ] [ -A A ] [ -c c ] [ +c c ] [ +|-d d ] <br />　　[ +|-D D ] [ +|-e s ] [ +|-f [cfgGn] ] [ -F [f] ] [ -g [s] ] <br />　　[ -i [i] ] [ -k k ] [ +|-L [l] ] [ +|-m m ] [ +|-M ] [ -o [o]]<br />　　[ -p s ] [ +|-r [t[m]] ] [ -s [p:s] ] [ -S [t] ] [ -T [t] ] <br />　　[ -u s ] [ +|-w ] [ -x [fl] ] [ -z [z] ] [ -Z [Z] ] [ -- ] [names]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a　各选项指定的条件进行逻辑与<br />-b　避免调用可能导致阻塞的内核函数，例如lstat/readlin/stat<br />-c　c 选择执行的命令以c字符开始的进程，可指定多次（逻辑或）<br />+d　s 列出打开的目录s及其直接包含的文件的实例<br />-d　s 指定需要从输出中排除的文件描述符，逗号分隔，例如'cwd,1,3'，可指定多次（逻辑或）<br />+D　D 列出打开的目录D及其目录层次下任何文件的实例<br />-i　i 列出所有Internet地址匹配i的文件，如果不指定i则列出所有网络文件<br />　　Internet地址格式：[46][protocol][@hostname|hostaddr][:service|port]<br />　　46指定IP地址版本<br />　　protocol指定协议名称，例如TCP/UDP<br />　　hostname指定主机名<br />　　hostaddr指定主机地址<br />　　service指定/etc/services中的服务名称<br />　　port指定端口或者端口的列表<br />-l　禁止将UID转换为用户名<br />-n　禁止将网络地址转换为主机名<br />-P　禁止将端口号转换为知名端口<br />-N　选择NFS文件的列表<br />-p　s包含或选择指定PID打开的文件，例如-p 1022,^3391<br />-R　在PPID列显示父进程ID<br />-U　选择UNIX domain Socket文件<br />-u　列出指定用户打开的文件<br />--　用于指示选项的结束<br />names　需要被列出的文件的路径名</p>
<p><span style="background-color: #c0c0c0;">输出：</span><br />COMMAND　关联到进程的UNIX命令的前9个字符<br />PID　打开文件的进程的ID<br />TID　关联的任务ID<br />PPID　打开文件的进程的父进程的ID<br />PGID　进程组ID<br />USER　进程所属用户的ID或者名称<br />FD　显示文件描述符或者：<br />　　　cwd 当前工作目录<br />　　　err FD信息错误<br />　　　jld FreeBSD的jail目录<br />　　　ltx 共享库文本（代码或数据）<br />　　　Mxx HEX内存映射类型xx<br />　　　m86 DOS合并映射文件<br />　　　mem 内存映射文件<br />　　　mmap 内存映射设备<br />　　　pd 父目录<br />　　　rtd 根目录<br />　　　txt 程序文本（代码或数据）<br />　　　v86 VP/ix映射文件<br />　　FD之后会跟随文件打开方式字符：<br />　　　r 读访问<br />　　　w 写访问<br />　　　u 读写访问<br />　　　 空格表示模式未知且无锁定<br />　　　- 模式未知且有锁定<br />　　打开方式后会跟随一个锁定方式字符：<br />　　　r 部分文件读锁定<br />　　　R 全部文件读锁定<br />　　　w 部分文件写锁定<br />　　　W 全部文件写锁定<br />　　　u 任意长度的读写锁定<br />　　　U 未知类型的锁定<br />　　　 空格表示无锁定<br />TYPE　关联文件的节点类型：GDIR, GREG, VDIR, VREG，或者：<br />　　　IPv4 IPv4套接字<br />　　　IPv6 IPv6套接字<br />　　　ax25 Linux AX.25套接字<br />　　　inet Internet domain 套接字<br />　　　rte AF_ROUTE 套接字<br />　　　sock 未知domain的套接字<br />　　　unix UNIX domain套接字<br />　　　x.25 HP-UX x.25套接字<br />　　　BLK 特殊块文件<br />　　　CHR 特殊字符文件　　<br />　　　DEL 已删除的Linux映射文件　<br />　　　DIR 目录<br />　　　DOOR VDOOR文件<br />　　　FIFO FIFO特殊文件<br />　　　KQUEUE BSD风格的内核事件队列文件<br />　　　LINK 符号链接<br />　　　MPB 多路复用的块文件<br />　　　MPC 多路复用的字符文件<br />　　　PIPE 管道<br />NAME　挂载点的名称、或者文件依存的文件系统或者字符、块设备名称；<br />　　　或者网络文件的本地/远程IP地址：localip:port -&gt; remoteip:port<br />　　　或者本地或远程NFS的挂载点名称；</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#查看占用8080端口的进程
lsof  -i  :8080
#列出所有TCP网络连接信息
lsof  -i tcp
#root打开的全部txt文件
lsof -a -u root -d txt
#如果umount文件系统失败，可以查看谁在使用
lsof /media/kssi
#列出alex打开的所有文件
lsof -u alex
#列出apache或者mysqld打开的文件
lsof -c mysqld -c apache
#列出alex执行的mysql打开的文件
lsof -u alex -c mysql
#列出进程打开的文件
lsof -p 1223,3328</pre>
</td>
</tr>
<tr>
<td class="blog_h2">fuser</td>
<td>
<p>找出使用文件或者套接字的进程<br />在默认显示模式下，每个文件名后面会有一个字母来表示访问类型：<br />c　当前目录 <br />e　可执行文件 <br />f　打开文件 <br />F　打开文件以写入 <br />r　根目录 <br />m　内存映射文件或者共享库
<p><span style="background-color: #c0c0c0;">格式：</span><br />fuser [-fuv] [-a|-s] [-4|-6] [-c|-m|-n space] <br />　　　[ -k [-i] [-M] [-w] [-SIGNAL] ] name ...<br />fuser -l<br />fuser -V</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-a　--all　显示命令行指定的所有文件，默认只会显示最少被一个进程使用的文件<br />-c　-m --mount NAME 指定一个被挂载的文件系统，该文件系统所有文件被列出<br />-k　--kill 杀死访问文件的进程，除非指定-SIGNAL，否则会使用SIGKILL信号，fuser不会杀死自己<br />-i　--interactive 在杀死进程前要求用户确认<br />-l　--list-signals 列出所有支持的信号名称<br />-w　仅仅杀死执行写访问的进程<br />-n　--namespace SPACE 指定不同的命名空间：默认file，其它包括udp、tcp<br />-SIGNAL 杀死进程时发送的信号<br />-u　--user 在PID后面附加用户名<br />-v　--verbose 冗长模式<br />-4　--ipv4 仅搜索IPv4套接字<br />-6　--ipv6 仅搜索IPv6套接字</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag">#杀死所有访问/home文件系统的进程
fuser -km /home
#查看使用631端口的进程
fuser -v 631/tcp</pre>
</td>
</tr>
<tr>
<td class="blog_h2">dstat</td>
<td>
<p>收集系统资源统计信息的综合性工具<br />该工具可以用来替换vmstat/iostat/ifstat
<p><span style="background-color: #c0c0c0;">格式：</span><br />dstat [-afv] [options..] [delay [count]]</p>
<p><span style="background-color: #c0c0c0;">选项/输出：</span></p>
<p>-c --cpu 启用CPU统计：<br />      usr　用户态时间占比<br />      sys　内核态时间占比<br />      idl　空闲时间占比<br />      wai　等待时间占比<br />      hiq　硬件中断次数<br />      siq　软件中断次数</p>
<p>-C 0,3,total 包含CPU0、CPU3和总计的CPU使用情况</p>
<p>&nbsp;</p>
<p>-d --disk 启用磁盘使用情况统计，单位字节</p>
<p>-D total,xvda 包含总计、xvda磁盘使用情况</p>
<p>&nbsp;</p>
<p>-g, --page 启用换页统计情况<br />      in　换入的页个数<br />      out　换出的页个数</p>
<p>&nbsp;</p>
<p>-i --int 启用中断次数的统计</p>
<p>&nbsp;</p>
<p>-l --load 启用平均负载统计<br />      1m 一分钟平均负载<br />      5m 五分钟平均负载<br />      15m 十五分钟平均负载<br />所有类UNIX系统在内核中计算负载，<span style="background-color: #c0c0c0;">完全空闲的计算机负载为0，单CPU的饱和负载为1，8核CPU的饱和负载为8</span>。每个使用/等待CPU的进程导致负载+1，每个终结的进程导致负载-1，如果你<span style="background-color: #c0c0c0;">发动1000个死循环线程，负载就会高达千级别</span>。大部分UNIX系统仅仅统计Running或者Runnable（等待被调度）进程，但是<span style="background-color: #c0c0c0;">Linux也将不可中断睡眠的进程（通常在等待磁盘活动完成）统计进来</span>。这意味着，在Linux系统中，卡死的磁盘IO（例如NFS太慢、硬盘坏道、USB 1.0）可能导致负载畸高的同时CPU占用较低</p>
<p>当单CPU的<span style="background-color: #c0c0c0;">负载达到0.7，需要警惕；持续大于1.0，需要尽快找到原因并降下来</span>；大于5.0意味着系统问题严重，接近死机</p>
<p>在单CPU系统中，平均负载1.73 0.60 7.98意味着：</p>
<ol>
<li>最近1分钟，系统超载73%。如果CPU每分钟内最多处理100进程，这意味着最近一分钟它需要处理的进程为173，需要再用一分钟才能处理完</li>
<li>（CPU、磁盘、内存...）速度快1.73倍，它就可以处理完最近一分钟调度的工作</li>
<li>最近5分钟，系统平均有40%的时间空闲</li>
<li>最近15分钟，系统超载698%，7.98的可运行进程</li>
</ol>
<p>&nbsp;</p>
<p>-m --mem 启用内存统计<br />      used　已经使用的内存<br />      buff　系统缓冲区（块设备I/O缓冲）占用内存<br />      cach　已缓存（在内存中的页）的内存<br />      free　空闲的内存</p>
<p>&nbsp;</p>
<p>-n --net 启用网络统计，单位字节<br />      recv　接收数据量<br />      send　发送数据量</p>
<p>-N eth0,total 包含eth0、总计的网络统计</p>
<p>&nbsp;</p>
<p>-p --proc 启用进程统计<br />      run　处于可运行（不一定占用CPU）状态的占比<br />      blk　不可中断睡眠（阻塞）状态的占比<br />      new　新启动进程占比</p>
<p>&nbsp;</p>
<p>-r, --io 启用I/O请求统计，针对所有块设备的IO请求次数<br />      read　读请求<br />      writ　写请求</p>
<p>&nbsp;</p>
<p>-s, --swap<br />      used　已使用的交换分区<br />      free　空闲的交换分区</p>
<p>-S swap1,total 包含swap1、总计的交换分区统计</p>
<p>&nbsp;</p>
<p>-t, --time 输出系统时间</p>
<p>-T, --epoch 输出1970到现在的秒数</p>
<p>&nbsp;</p>
<p>-y, --sys 启用系统统计<br />      int　中断次数<br />      csw　上下文切换次数</p>
<p>&nbsp;</p>
<p>--aio 启用异步I/O统计</p>
<p>&nbsp;</p>
<p>--fs 启用文件系统统计</p>
<p>&nbsp;</p>
<p>--ipc 启用进程间通信统计<br />      msg　消息队列<br />      sem　信号量<br />      shm　共享内存</p>
<p>&nbsp;</p>
<p>--lock 启用文件锁定统计<br />      pos　posix锁<br />      lck　flock锁<br />      rea　读锁<br />      wri　写锁</p>
<p>&nbsp;</p>
<p>-raw 启用原始套接字统计</p>
<p>--socket 启用套接字统计<br />      tot　统计<br />      tcp　TCP<br />      udp UDP<br />      raw RAW<br />      frg IP分片</p>
<p>--tcp 启用TCP统计<br />      lis　处于监听状态的个数<br />      act　建立的连接个数<br />      syn　处于SYN阶段的连接个数<br />      tim　本端已关闭，正在等待网络上数据报的连接个数　<br />      clo　关闭的连接个数</p>
<p>--udp 启用UDP统计enable udp stats (listen, active)<br />      lis　处于监听状态的个数<br />      act　活动的个数</p>
<p>--unix 启用UNIX套接字统计</p>
<p>&nbsp;</p>
<p>--list 列出内部外部插件的名称<br />-a --all 等价于-cdngy，默认开启此选项<br />-f, --full 完整格式，单独显示各网卡和硬盘<br />-v, --vmstat 等价于 -pmgdsc -D total<br />--float 强制显示浮点数<br />--integer 强制显示整数<br />--bw, --blackonwhite 如果Terminal背景为白色，使用该选项调整配色<br />--nocolor 禁用配色<br />--noheaders 禁用重复的表头显示<br />--noupdate 禁止立即刷新，如果delay大于1<br />--output file 输出CSV格式到文件</p>
<p>delay 刷新延迟，默认1秒<br />count 退出前刷新的次数，默认无限</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 各项系统指标，每分钟显示新行
dstat -tlcmsgdrnpy 60
# 网络相关指标
dstat -tn --socket --tcp --udp --unix 60</pre>
</td>
</tr>
<tr>
<td class="blog_h2">vmstat</td>
<td>
<p>显示虚拟内存的统计信息<br /><span style="background-color: #c0c0c0;">输出：</span><br />procs组：<br />　　r表示等待CPU时间的进程数<br />　　b表示处于不可中断睡眠（等待I/O）的线程数<br />memory组：<br />　　swpd交换到磁盘的块数<br />　　free表示空闲内存块数<br />　　buff表示用于缓冲区的块数<br />　　cache表示用于系统缓存的块数<br />swap组：<br />　　si表示每秒交换进内存的块数<br />　　so表示每秒交换出内存的块数，通常不应该超过10<br />io组：<br />　　bi表示每秒读出块数<br />　　bo表示每秒写入块数<br />system组：<br />　　in表示每秒中断数<br />　　cs表示每秒上下文切换数<br />cpu组： <br />　　us表示运行用户代码（非内核）占比<br />　　sy表示运行系统（内核）代码占比<br />　　id表示空闲占比，wa表示等待I/O占比<br />　　st在虚拟化时表示从虚拟机“偷来”的时间占比
<p>注意：统计按块为单位，GNU/Linux一般为1024字节</p>
</td>
</tr>
<tr>
<td class="blog_h2">pidstat</td>
<td>
<p>报告进程（任务）的统计信息</p>
<p><span style="background-color: #c0c0c0;">格式：</span><pre class="crayon-plain-tag">pidstat options interval count</pre></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-C comm，仅仅显示其命令行包含指定字样的任务的统计信息<br /><span style="background-color: #c0c0c0;">-d 报告磁盘I/O统计信息</span><br /><span style="background-color: #c0c0c0;">-r 报告页面错误和内存使用</span><br /><span style="background-color: #c0c0c0;">-s 报告栈使用情况</span><br /><span style="background-color: #c0c0c0;">-u  报告CPU使用情况</span><br /><span style="background-color: #c0c0c0;">-w 报告上下文切换情况</span><br />-I 在SMP环境下，提示任务的CPU用量应该被除以CPU总数<br />-l 显示进程的完整命令<br />-p 逗号分割，需要报告的PID<br />-T TASK|CHILD|ALL，TASK表示报告每个进程本身、CHILD表示报告进程+子进程，ALL表示同时报告TASK和CHILD<br />-t 同时显示进程的线程的统计信息</p>
</td>
</tr>
<tr>
<td class="blog_h2">free</td>
<td>显示系统的空闲内存</td>
</tr>
<tr>
<td class="blog_h2">iotop</td>
<td>
<p>显示磁盘使用最高的进程，动态刷新</p>
<p>输出列包括在采样周期内：每个进程/线程读写所占用的带宽、每个线程在等待换页、等待IO所消耗的时间百分比（相对于自己的总时间，因此可能有多个进程同时高达99%），每个进程的IO优先级（类型/级别）也会显示</p>
<p>同时输出的还包括统计信息：</p>
<p style="padding-left: 30px;">总磁盘读写，进程/内核线程与内核块设备子系统之间的总计带宽<br />实际磁盘读写，内核块设备子系统与底层硬件（HDD/SSD）之间的实际带宽</p>
<p>由于IO重排序、缓存的存在，实际磁盘读写、总磁盘读写会不一样</p>
<p>快捷键：Left Right切换排序方式、r倒序显示、p切换进程/线程模式、o仅仅显示实际正在IO的进程、i调整IO优先级（ionice）</p>
</td>
</tr>
<tr>
<td class="blog_h2">iostat</td>
<td>
<p>显示输入输出统计信息。该命令的数据源是/proc/diskstats：</p>
<pre class="crayon-plain-tag">cat /proc/diskstats 
   8       0 sda 72447 130694 1438586 11132 14 0 8 88 0 10068 11148
   8       1 sda1 311 4798 7160 36 0 0 0 0 0 28 36
   8       2 sda2 71622 125896 1427320 11052 0 0 0 0 0 9956 10980
   8       3 sda3 364 0 2906 36 1 0 8 0 0 36 36
   8      16 sdb 12186 206560 907224 63264 6085 523283 4234488 2478428 0 74192 2541680
   8      17 sdb1 12008 206560 905800 63048 6028 523283 4234488 2474144 0 69692 2537180</pre>
<p>一共11个字段，除了字段9之外，都从系统启动以来一直累加：</p>
<ol>
<li>rd_ios：读操作的次数。</li>
<li>rd_merges：合并读操作的次数。如果两个读操作读取相邻的数据块时，可以被合并成一个，以提高效率</li>
<li>rd_sectors：读取的扇区数量</li>
<li>rd_ticks：读操作消耗的时间（以毫秒为单位）。每个读操作从__make_request()开始计时，到end_that_request_last()为止，<span style="background-color: #c0c0c0;">包括了在队列中等待的时间</span></li>
<li>wr_ios：写操作的次数</li>
<li>wr_merges：合并写操作的次数</li>
<li>wr_sectors：写入的扇区数量</li>
<li>wr_ticks：写操作消耗的时间（以毫秒为单位）</li>
<li>in_flight：当前未完成的I/O数量。在I/O请求进入队列时该值加1，在I/O结束时该值减1。是I/O请求进入队列时，而不是提交给硬盘设备时，因此不考虑IO请求合并</li>
<li>io_ticks：该设备用于处理I/O的墙上时间，注意io_ticks与rd_ticks和wr_ticks的区别，rd_ticks和wr_ticks是把<span style="background-color: #c0c0c0;">每一个I/O所消耗的时间累加</span>在一起，因为硬盘设备通常可以并行处理多个I/O，所以rd_ticks和wr_ticks往往会比墙上大。而io_ticks表示该设备有I/O（即非空闲）的时间，不考虑I/O有多少，只考虑有没有。在实际计算时，in_flight)不为零的时候io_ticks保持计时，in_flight为零的时候io_ticks停止计时</li>
<li>time_in_queue：对io_ticks的加权值，用当前的I/O数量in-flight乘以墙上时间。虽然该字段的名称是time_in_queue，但<span style="background-color: #c0c0c0;">并不真的只是在队列中的时间，其中还包含了硬盘处理I/O的时间</span>。iostat在计算avgqu-sz时会用到这个字段</li>
</ol>
<p>更多信息参考：<a href="http://linuxperf.com/?p=156">http://linuxperf.com/?p=156</a></p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-c 显示CPU利用情况报告<br />-d 显示设备利用情况报告<br />-g 限制指定设备组的统计信息<br />-h 让报告更加易读<br />-k 以KB为单位<br />-m 以MB为单位<br />-p 显示指定的块设备和它的分区的统计信息</p>
<p><span style="background-color: #c0c0c0;">输出：</span><br />rrqm/s wrqm/s <br />表示每秒被合并（OS把队列中的多个逻辑读写请求分组到单个物理读写）的读、写请求<br />r/s w/s <br />每秒发送到设备的读写请求数<br />rsec/s wsec/s 每秒读写的扇区数（扇区是磁盘读写的基本单位，磁盘表面的同心圆曰磁道，磁道的上512字节的弧段曰扇区。现代磁盘的扇区大小可能为4K。Windows下磁盘最小逻辑管理单位叫簇，由若干个扇区组成。NTFS文件系统默认簇大小为4K）<br />rkB/s wkB/s <br />每秒读写的KB数<br />avgrq-sz <br />平均每次设备I/O操作的数据大小 (扇区)<br />avgqu-sz <br />在设备队列中等待的请求数（平均I/O队列长度）<br />await <br />平均每次设备I/O操作的等待的毫秒数<br />svctm <br />平均每次设备I/O操作的服务请求的毫秒数（排除队列等待时间），<span style="background-color: #c0c0c0;">该指标没有价值，可以忽略</span><br />%util <br />一秒中有百分之多少的时间用于I/O操作，或者说一秒中有多少时间I/O队列是非空的。对于串行处理请求的设备，此值接近100%意味着设备能力饱和；对于<span style="background-color: #c0c0c0;">RAID阵列或现代SSD则不能体现出设备的性能限制</span></p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 每秒刷新
iostat -xd 1

# 显示磁盘SDC的TPS、每秒读写KB数
iostat 1 -d -h -y -k -p sdc</pre>
</td>
</tr>
<tr>
<td class="blog_h2">top </td>
<td>
<p>显示CPU占用最高的进程，动态刷新
<p><span style="background-color: #c0c0c0;">命令：</span></p>
<p>d 刷新延迟 H 显示线程 B 粗体显示 z 彩色显示 <br />l 系统负载  t 处理器状态 1按核显示处理器状态 m 内存和交换文件<br />c 显示完整命令 i 显示空闲任务 R 反转排序  V树状显示</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-PN1,N2... 仅仅显示指定进程<br />-H 显示每个线程</p>
<p><span style="background-color: #c0c0c0;">输出：</span></p>
<p>CPU状态：us非niced用户进程耗时，sy内核进程耗时，ni被niced用户进程耗时，id空闲的时间，wa等待IO操作完成消耗的时间，hi处理硬件中断的时间，si处理软件中断的时间，st当前vm被hypervisor偷去的时间</p>
<p>wa应该<span style="background-color: #c0c0c0;">在绝大部分时间内都是0</span>，它表示CPU花在等待IO的时间，如果长时间超过1%提示磁盘IO性能不足</p>
<p><span style="background-color: #c0c0c0;">其它：</span></p>
<p>关于系统负载（load average）：</p>
<ol>
<li><span style="background-color: #c0c0c0;">状态为运行（R）和不可中断睡眠（D）的进程，贡献系统负载</span></li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h2">mpstat</td>
<td>
<p>报告CPU相关的统计信息</p>
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<p>mpstat [ -A ] [ -u ] [ -V ] [ -I { keyword [,...] | ALL } ] [ -P { cpu [,...] | ON | ALL } ] [ interval [ count ] ]</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-A 等价于 -u -I ALL -P ALL<br />-P 指定需要报告的CPU<br />-u 报告CPU用量</p>
<p><span style="background-color: #c0c0c0;">输出：</span></p>
<p>CPU CPU序号，all表示所有CPU的均值<br />%usr 执行用户空间代码消耗的CPU时间占比<br />%nice 执行用户空间并且使用nice优先级的代码耗时占比<br />%sys 执行内核空间代码消耗的CPU时间占比，不包含处理硬件/软件中断的时间<br />%iowait 系统中存在进行中的IO请求，同时CPU空闲的时间占比<br />%irq 处理硬件中断的CPU时间占比<br />%soft 处理软件中断的CPU时间占比<br />%steal  虚拟CPU消耗在非自愿等待的、同时Hypervisor用来执行其它虚拟处理器的时间占比<br />%guest 执行虚拟CPU的时间占比<br />%gnice 执行niced客户机的CPU时间占比<br />%idle 空闲，且没有进行中IO请求的时间占比</p>
<p>注意：</p>
<ol>
<li>%usr + %nice + %sys + %iwoait + %irq + %soft + %steal + %guest + %gnice + %idle = 100%</li>
<li>%iowait不代表CPU不能工作，也不代表存在IO瓶颈。判定是否存在IO瓶颈，应当结合iostat的avserv/avwait/avque等指标</li>
</ol>
</td>
</tr>
<tr>
<td class="blog_h2">atop</td>
<td>
<p>交互式的系统负载监视器，它能够直观的监控最重要的系统硬件资源的使用情况，还能显示哪些进程应该为高资源占用负责</p>
<p>注意：</p>
<ol>
<li>必须启用内核特性storage accounting才能按进程显示磁盘负载</li>
<li>必须安装netatop内核模块才能按进程显示网络负载</li>
</ol>
<p>程序界面每10秒</p>
</td>
</tr>
<tr>
<td class="blog_h2">htop</td>
<td>
<p>显示CPU占用最高的进程，动态刷新，比top提供更多的信息</p>
<p><span style="background-color: #c0c0c0;">状态条：</span></p>
<p>CPU：蓝色代表低优先级线程；绿色代表正常优先级线程；红色代表内核线程</p>
<p>MEM：绿色代表已使用内存页；蓝色代表缓冲(Buffer)页；黄色代表缓存(Cache)页</p>
<p>Load average：包含3个，分别为最近1/5/15分钟的平均负载。所谓负载使用浮点数表示，如果服务器具有4个核心，那么负载达到4.00相当于满载，如果负载达到20.00意味着严重超载，进程等待CPU时间将过长</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-d　刷新间隔，单位秒<br />-C　不使用配色<br />-p　--pid=PID,PID... 仅显示给定的PID<br />-s　--sort-key COLUMN 指定排序列<br />-u　--user=USERNAME 仅显示指定用户的进程</p>
<p><span style="background-color: #c0c0c0;">输出：</span><br />Command 完整的进程命令行<br />PID 进程ID<br />PPID 父进程ID<br />PGRP 进程组ID<br />SESSION (SESN) 进程会话ID<br />TTY_NR (TTY) 进程关联的中断<br />TPGID 控制进程的中断的前台进程组的ID<br />STATE (S) 进程状态：<br />　　S 休眠进程（idle）<br />　　R 运行中的进程<br />　　D 不可中断的等待（I/O）<br />　　Z 僵尸（等待父进程读取其退出转变港台）</p>
<p>PROCESSOR (CPU) 　进程最后在哪个CPU执行<br />NLWP 　进程包含的线程数量<br />NICE (NI) 　进程的nice值，从19到-20（数字越小优先级越高）<br />PERCENT_CPU (CPU%) 　进程占用的CPU百分比<br />UTIME (UTIME+) 　用户态CPU执行时间<br />STIME (STIME+)　 内核态CPU执行时间<br />TIME (TIME+)　 用户态、内核态时间总和<br />CUTIME 　子进程的用户态CPU执行时间<br />CSTIME 　子进程的内核态CPU执行时间<br />PRIORITY (PRI)　 内核给进程的内部优先级<br />PERCENT_MEM（MEM%）　 进程占用的内存百分比，基于驻留集RES计算<br />M_SIZE (VIRT)　 整个程序的虚拟内存大小，包含没有映射到内存的页。单位KB<br />M_RESIDENT (RES)　 驻留集尺寸，实际占用物理内存大小<br />M_SHARE (SHR) 　共享页的尺寸，即RES中与其它进程共享的部分<br />M_TRS (CODE) 　进程代码段尺寸，即程序可执行代码的大小<br />M_LRS (LIB) 　进程使用的库占用的尺寸<br />M_DRS (DATA) 　进程数据段+栈的尺寸<br />M_DT (DIRTY) 　进程脏页数量<br />ST_UID (UID) 　进程所有者的UID<br />TARTTIME 　进程启动以来流逝的时间<br />RCHAR (RD_CHAR)　 进程已经读取的字节数<br />WCHAR (WR_CHAR) 　进程已经写入的字节数<br />SYSCR (RD_SYSC)　 read系统调用的次数<br />SYSCW (WR_SYSC) 　write系统调用的次数<br />RBYTES (IO_RBYTES)　 read调用读取的字节数<br />WBYTES (IO_WBYTES) 　write调用写入的字节数<br />IO_READ_RATE (IORR) 　read调用速率，单位KB/s<br />IO_WRITE_RATE (IOWR)　 write调用速率，单位KB/s<br />IO_RATE (IO) 前两者之和，单位KB/s<br />CNCLWB (IO_CANCEL) 　取消的write字节数</p>
</td>
</tr>
<tr>
<td class="blog_h2">ps</td>
<td>
<p>报告一个当前进程的状态快照</p>
<p><span style="background-color: #c0c0c0;">格式：</span><br />ps [options]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br /><span style="background-color: #c0c0c0;">简单进程选择：</span><br />-A -e 选择所有进程<br />-a 选择除了作为session leader的进程和未关联Terminal的进程<br />a  BSD风格的“仅当前用户的”约束<br />-d 选择除了作为session leader的进程<br />-N --deselect 选择所有不满足条件的进程<br /><span style="background-color: #c0c0c0;">列表进程选择：</span><br />这些选项支持单个以空格或逗号分隔的参数，并且可以指定多次<br />-p --pid 按PID选择，100、-100、-p 100这三种语法都可以<br />-C 根据命令名称选择<br />-G --Group 根据真实GID选择<br />-g --group 根据有效GID选择<br />--ppid 根据父进程PID选择<br />-s --sid 根据会话ID选择<br />-t --tty 根据tty选择，-t -用于选择没有关联到任何终端的进程<br />-U 根据真实UID选择<br />-u 根据有效UID选择</p>
<p><span style="background-color: #c0c0c0;">输出格式控制：</span><br />-f 输出完整格式<br />-F 输出特别完整格式<br />v 显示虚拟内存信息<br />Z 显示寄存器信息<br />-o 使用用户自定义格式<br />--no-headers  不输出表头</p>
<p><span style="background-color: #c0c0c0;">自定义格式：</span></p>
<p>%cpu  CPU用量<br />%mem 内存用量<br />args  命令行及其参数，别名command</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 显示完整的命令行
ps -ef    # 标准语法
ps aux    # BSD语法

# 显示所有JVM的命令行
jcmd -l | awk '{system("ps --no-headers -p "$1 " -o command " ) }'</pre>
</td>
</tr>
<tr>
<td class="blog_h2">pstree</td>
<td>
<p>显示进程树
<p>格式：</p>
<p><span style="background-color: #c0c0c0;">选项：</span></p>
<p>-a 显示命令行参数<br /> -h 高亮当前进程及其祖先<br /> -H PID 高亮指定进程及其祖先<br /> -l  不要截断过长的行<br /> -n 根据PID对输出排序<br /> -p  显示PID，后面跟PID则仅仅显示子树</p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 显示以进程24741为根节点的子树
pstree -an -p 24741</pre>
</td>
</tr>
<tr>
<td> exportfs</td>
<td>
<p>修改了NFS导出配置后，使用该命令生效：
<pre class="crayon-plain-tag"># 执行导出
exportfs -ar

# 查看导出
exportfs -v</pre>
<p>导出配置示例：</p>
<pre class="crayon-plain-tag">/srv/nfs4          10.0.0.0/8(rw,sync,no_subtree_check,crossmnt,fsid=0)
/srv/nfs4/fast     10.0.0.0/16(rw,sync,no_subtree_check) 10.1.0.0/16(rw,sync,no_subtree_check)
/srv/nfs4/slow     10.0.0.0/16(rw,sync,no_subtree_check) 10.1.0.0/16(rw,sync,no_subtree_check)</pre>
</td>
</tr>
<tr>
<td class="blog_h2">iftop</td>
<td>
<p>显示网络使用最高的套接字，动态刷新
<p><span style="background-color: #c0c0c0;">格式：</span><br />iftop -h | [-nNpblBP] [-i interface] <br />[-f filter code] [-F net/mask] [-G net6/mask6]</p>
<p><span style="background-color: #c0c0c0;">选项：</span><br />-h 打印帮助<br />-n 不进行DNS查找<br />-N 不解析著名端口名<br />-p 运行在混杂模式，不直接发送给目标接口的流量也被统计<br />-P 启用端口显示<br />-l 显示并统计本地IPv6数据报，默认不显示<br />-b 不显示流量的bar图<br />-B 使用bytes/s作为单位而不是bits/sec<br />-i interface 监听指定网络接口上的流量<br />-f filter code 设置过滤器<br />-F net/mask 指定流量分析的IPv4网络范围<br />-G net6/mask6 指定流量分析的IPv6网络范围<br />-c config file 指定配置文件</p>
<p><span style="background-color: #c0c0c0;">输出：</span><br />顶部显示瞬时速度的标尺，其下面是占用网络带宽最高的套接字列表：</p>
<pre class="crayon-plain-tag">#按p可以显示或者隐藏端口号
#按1,2,3,4可以按对应的列排序，按&lt;&gt;分别根据源、目标排序
#按P暂停刷新，按o可以冻结当前顺序
#按f可以动态修改过滤器
#默认显示最近10秒流量占用最高的套接字
#从foo到bar最近1/10/40秒平均速率
foo.com  =&gt;  bar.com      1Kb  500b   100b
                 &lt;=       2Mb    2Mb    2Mb
                 
#底部显示发送（TX）、接收（RX）和合计的信息
#包括bar图、累计（cum）、峰值（peak）以及1/10/40统计
TX:    cum: 27.5MB   peak: 141kb  rates: 2.67kb  4.87kb  18.3kb
RX:         2.18MB         129kb         1.66kb  2.19kb  13.7kb
TOTAL:      29.7MB         174kb         4.33kb  7.06kb  32.1kb</pre>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">iftop -nNBPi eth0</pre>
</td>
</tr>
<tr>
<td class="blog_h2">dmesg</td>
<td>打印内核诊断信息，或者操控内核ring buffer</td>
</tr>
<tr>
<td class="blog_h2">uptime</td>
<td>显示已经系统运行的时间</td>
</tr>
<tr>
<td class="blog_h2">sysbench</td>
<td>
<p>一个跨平台、多线程、模块化的性能测试工具。可以测试：文件I/O性能、内存分配和传输速率、POSIX线程性能、数据库服务器性能等方面的内容
<p><span style="background-color: #c0c0c0;">命令格式：</span></p>
<pre class="crayon-plain-tag">sysbench [common-options] --test=name [test-options] command</pre>
<p><span style="background-color: #c0c0c0;">通用选项：</span></p>
<p>--num-threads 工作线程总数，默认1<br />--max-requests 最大请求次数，默认10000，0表示无限<br />--max-time 测试最大执行的秒数<br />--thread-stack-size 线程的栈大小，默认32K<br />--init-rnd 是否在测试启动前，从定时器初始化随机数生成器<br />--test 指定测试模式<br />--debug 打印调试信息<br />--validate 如果可能，验证测试结果<br />--percentile 保留测试数据的百分比，默认95%，意味着耗时最长的5%测试数据被丢弃<br />--batch 批量模式，周期性的产生测试结果<br />--batch-delay 两次执行之间延迟的秒数</p>
<p><span style="background-color: #c0c0c0;">测试模式：</span></p>
<p>cpu 通过寻找质数，来测试CPU性能<br />  --cpu-max-primes 寻找质数的最大数量<br />threads 用于测试内核在大量线程竞争一组互斥量时的调度性能<br />  --thread-locks 互斥量的数量，默认8<br />  --thread-yield  每次请求执行lock/yield/unlock循环的数量，默认1000<br />fileio 根据多种I/O工作负载，测试I/O性能。分为prepare、run两个阶段。prepare阶段创建一系列待测试文件<br />  --file-test-mode 测试模式：seqwr顺序写、seqrewr顺序重写、seqrd顺序读、rndrd随机读、rndwr随机写、rndrw随机读写<br />  --file-io-mode  I/O模式：sync同步、async异步、fastmmap快速内存映射、slowmmap内存映射<br />  --file-num 创建的文件数量，默认128<br />  --file-block-size 读写时的块大小，默认16K<br />  --file-total-size 总计文件大小，默认2G<br />  --file-async-backlog 每个线程可以有多少异步操作排队<br />  --file-fsync-freq 在多少次请求后，执行fsync()<br />  --file-fsync-all 是否在每次操作后执行fsync()，默认no<br />  --file-fsync-end 是否在测试结束后执行fsync()，默认yes<br />  --file-fsync-mode fsync模式：fsync、fdatasync<br />  --file-merged-requests 最多合并多少I/O请求，默认不合并</p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># CPU性能测试
sysbench --test=cpu --cpu-max-prime=1000000 --num-threads=8 run
# I/O性能测试
sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw prepare
sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw run
sysbench --num-threads=16 --test=fileio --file-total-size=3G --file-test-mode=rndrw cleanup
# MySQL数据插入
sysbench --mysql-host=127.0.0.1 --mysql-user=root --mysql-password=pswd --tables=1 \
    --table_size=100000000000 oltp_read_write prepare</pre>
</td>
</tr>
<tr>
<td class="blog_h2">fio</td>
<td>
<p>fio是测试IOPS的工具，用来对硬件进行压力测试和验证
<p><span style="background-color: #c0c0c0;">安装：</span></p>
<pre class="crayon-plain-tag">wget https://codeload.github.com/axboe/fio/zip/master
unzip master
rm -rf master
cd fio-master/
./configure
make &amp;&amp; make install</pre>
<p><span style="background-color: #c0c0c0;">格式：</span><pre class="crayon-plain-tag">fio [options] [jobfile]...</pre></p>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag">filename=/dev/sdb1   # 测试文件名称，通常选择需要测试的盘的data目录
direct=1             # 测试过程绕过机器自带的buffer。使测试结果更真实
rw=randwrite         # 测试随机写的I/O
rw=randrw            # 测试随机写和读的I/O
bs=16k               # 单次io的块文件大小为16k
bsrange=512-2048     # 同上，提定数据块的大小范围
size=5G              # 本次的测试文件大小为5g，以每次4k的io进行测试
numjobs=30           # 本次的测试线程为30个
runtime=1000         # 测试时间1000秒，如果指定则将5g文件分4k每次写完即结束
ioengine=psync       # io引擎使用psync方式
rwmixwrite=30        # 在混合读写的模式下，写占30%
group_reporting      # 关于显示结果的，汇总每个进程的信息
lockmem=1G           # 只使用1g内存进行测试
zero_buffers         # 用0初始化系统buffer
nrfiles=8            # 每个进程生成文件的数量

# 顺序读
fio -filename=/dev/rbd1 -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -group_reporting -name=test
# 顺序写
fio -filename=/dev/rbd1 -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -group_reporting -name=test
#随机读
fio -filename=/dev/rbd1 -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -group_reporting -name=test
# 随机写
fio -filename=/dev/rbd1 -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -group_reporting -name=test
# 混合随机读写
fio -filename=/dev/rbd1 -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -group_reporting -name=test -ioscheduler=noop</pre>
</td>
</tr>
<tr>
<td class="blog_h2">iperf3</td>
<td>
<p>网络吞吐量测试工具
<p><span style="background-color: #c0c0c0;">格式：</span></p>
<pre class="crayon-plain-tag"># 服务器
iperf3 -s [ options ]
# 客户端
iperf3 -c server [ options ]</pre>
<p><span style="background-color: #c0c0c0;">示例：</span></p>
<pre class="crayon-plain-tag"># 启动一个服务器
iperf3 -s
# 连接到服务器进行网速测试
iperf3 -c 10.0.1.1</pre>
</td>
</tr>
<tr>
<td>sysdig<a id="sysdig"></a></td>
<td>参考<a href="/sysdig">使用Sysdig进行系统性能分析</a></td>
</tr>
</tbody>
</table>
<div id="dev-commands" class="blog_h1"><span class="graybg">开发相关命令</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 100px; text-align: center;"> 命令</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2">pkg-config</td>
<td>
<p>参考<a href="/linux-programming-faq#pkgcfg">Linux编程知识集锦</a>
</td>
</tr>
<tr>
<td class="blog_h2">ldconfig</td>
<td>
<p>参考<a href="/linux-programming-faq#ldconfig">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">ldd</td>
<td>
<p>参考<a href="/linux-programming-faq#ldd">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">objdump</td>
<td>
<p>参考<a href="/linux-programming-faq#objdump">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">gcc</td>
<td>
<p>参考<a href="/linux-programming-faq#gcc">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">gdb</td>
<td>
<p>参考<a href="/linux-programming-faq#gdb">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">gdbserver</td>
<td>
<p>参考<a href="/linux-programming-faq#gdbserver">Linux编程知识集锦</a></p>
</td>
</tr>
<tr>
<td class="blog_h2">adb</td>
<td>参见<a href="/android-faq#adb">Android知识集锦</a></td>
</tr>
<tr>
<td class="blog_h2">keytool</td>
<td>参见<a href="/android-faq#keytool">Android知识集锦</a></td>
</tr>
<tr>
<td class="blog_h2">jarsigner</td>
<td>参见<a href="/android-faq#jarsigner">Android知识集锦</a></td>
</tr>
<tr>
<td class="blog_h2">apktool</td>
<td>参见<a href="/android-faq#apktool">Android知识集锦</a></td>
</tr>
<tr>
<td class="blog_h2">dex2jar</td>
<td>参见<a href="/android-faq#dex2jar">Android知识集锦</a></td>
</tr>
<tr>
<td class="blog_h2">cmake</td>
<td>参见<a href="/cmake-study-note#cmake">CMake学习笔记</a></td>
</tr>
<tr>
<td>readelf</td>
<td>
<p>参考<a href="/linux-programming-faq#readelf">Linux编程知识集锦</a></p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">多媒体命令</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2"><a id="ffmpeg"></a>ffmpeg</td>
<td>
<p>ffmpeg是一个高性能的音视频编码器，它能够进行文件格式转换，也能够从源捕获音视频。ffmpeg能够任意的转换采样率、实时改变视频尺寸</p>
<p>ffmpeg读取任意数量的输入文件（可以是普通文件、管道、网络流、输入设备），写出到任意数量的输出文件中。每个文件可以是不同类型的视频、音频、字幕、附件、数据，但是受限于容器格式，不一定所有文件都支持。输入文件和输出文件的对应关系，可以自动映射，也可以利用-map选项手工映射。你应该先指定所有输入文件，然后开始指定输出文件</p>
<p>某些选项需要引用输入文件，你可以使用从0开始的索引引用之</p>
<p><strong>基本流程</strong></p>
<p>转码的处理过程如下：</p>
<p style="padding-left: 30px;">输入文件 ⇨ (demuxer) ⇨ 解码后的数据包 ⇨ (decoder ) ⇨ 解码后的帧 <br />                                            ⇨ (encoder) ⇨ 编码后的数据包 ⇨ (muxer) ⇨ 输出文件</p>
<p>ffmpeg调用libavformat库（其中包含demuxers）来读取输入文件，并获得其中的编码数据包。如果有多个输入文件，ffmpeg会尝试跟踪任意输入流中最低的时间戳，以确保这些文件同步</p>
<p>编码数据包随后被送给decoder（除非为目标流选择流streamcopy），decoder产生非压缩的帧（原始video / PCM 音频）。这些非压缩帧可能被过滤器处理</p>
<p>过滤器处理过后，帧被传递给encoder，然后送给muxer，最终写到目标文件中</p>
<p><strong>过滤器</strong></p>
<p>在重新编码解码帧之前，ffmpeg可以对原始音频、视频进行处理，这通过调用libavfilter库完成。若干个过滤器链条组成一个filter graph。过滤器图被分为简单（simple）、复杂（complex）两类</p>
<p>简单过滤器图仅仅有一个输入、一个输出，且它们的类型相同。简单过滤器图通过-filter选项，在每个流上设置（-vf、-af分别对应视频、音频过滤）。简单过滤器图示例：输入 ⇨ 反交错  ⇨ 缩放  ⇨ 输出</p>
<p>某些过滤器会改变帧属性，但是不改变其内容。例如fps过滤器，它改变帧的数量，但是不会操作帧内容。又例如setpts过滤器，仅仅设置时间戳</p>
<p>复杂过滤器图可以有N个输入、N个输出。这种过滤器图通过-filter_complex（-lavfi）选项配置，此选项是全局的。复杂过滤器图的例子如overlay，它可以把两个视频重叠输出</p>
<p><strong>流拷贝</strong></p>
<p>设置 -codec copy，则仅仅从demux输入文件，然后mux到输出文件中。流拷贝非常快，但是由于诸多因素可能不正常工作</p>
<p><strong>流选择</strong></p>
<p>默认的ffmpeg仅仅包含每个类型（视频、音频、字幕）中的单个输入流，并把它们输出到每个输出文件中。如果有多个同类型的输入流，它会选择其中最好的：</p>
<ol>
<li>对于视频：选择最高分辨率</li>
<li>对于音频：选择通道数最多的</li>
<li>对于字幕：选择第一个</li>
</ol>
<p>要禁用上面这种默认特性，选用-vn/-an/-sn/-dn选项，或者通过-map进行细粒度控制</p>
<p><strong>命令格式</strong></p>
<pre class="crayon-plain-tag">ffmpeg [options] [[infile options] -i infile]... {[outfile] outfile}...

# 示例
# 获取本地摄像头视频流，以MPEG1编码后通过HTTP发送
ffmpeg -s 640x480 -f video4linux2 -i /dev/video0 -f mpegts -codec:v mpeg1video -b 800k -r 30 http://127.0.0.1:8800/12345
# 把MP4转换为vp8编码格式，加快编码速度
ffmpeg -i Lightheaded.mp4 -c:v vp8 -c:a libopus -speed 4 lightheaded.webm
# 缩放视频帧，iw、ih分别表示输入宽度、高度
ffmpeg -i Lightheaded.webm -vf scale=iw/2:ih/2 Lightheaded1.webm

# 裁剪视频中352x288大小的区域，此区域的左上角坐标为256x64，跳过开头的一分钟
ffmpeg -i lightheaded.webm -c:v vp8 -vf "crop=352:288:256:64" -ss 60 lightheaded1.webm
# 实时预览上述裁剪的效果
ffplay -i lightheaded.webm -vf "crop=352:288:256:64" -ss 60

# 同时指定多个过滤器
-vf "scale=iw/2:ih/2, crop=352:288:64:64"

# 基于H.264编码
ffmpeg -f video4linux2 -i /dev/video0 -framerate 15 -video_size 640x480
                        # 使用x264库作为编码器              # 使用Baseline Profile
       -pix_fmt yuv420p -c:v libx264 -b:v 600k -bufsize 600k -vprofile baseline 
       # 零延迟优化
       -tune zerolatency -f rawvideo -   # 输出原始视频（没有容器封装）到标准输出</pre>
<p><strong>辅助类选项</strong></p>
<p>-buildconf 显示此软件的构建选项<br />-formats 显示支持的文件格式，例如mp3 mp4<br />-muxers 显示支持的混流器<br />-demuxers 显示支持的分流器<br />-devices  显示支持的设备类型，例如video4linux2（本质上是一系列驱动和API标准）<br />-codecs 显示支持的编码算法<br />-decoders 显示可用的解码器<br />-encoders 显示可用的编码器<br />-bsfs 显示可用的比特流过滤器<br />-protocols 显示支持的输入输出协议，例如http rtsp rtmp<br />-filters 显示支持的过滤器<br />-pix_fmts 显示支持的像素格式<br />-layouts 显示支持的音频通道布局<br />-sample_fmts 显示支持的音频采样<br />-colors 显示可用的颜色名称及其HEX代码<br />-sources  显示可用输入设备列表<br />-hwaccels 显示可用的硬件加速方式</p>
<p><strong>全局选项</strong></p>
<p>-loglevel loglevel 设置日志级别，等价于 -v。合法值quiet,panic,fatal,error,warning,info,verbose,debug,trace<br />-report 生成一个报告<br />-hide_banner 禁止打印ffmpeg的banner<br />-max_alloc bytes 单个分配的块的最大尺寸<br />-y  如果输出文件存在，覆盖之<br />-n 绝不覆盖已经存在的输出文件，如果目标文件存在则退出<br />-ignore_unknown 忽略未知媒体类型<br />-filter_threads 非复杂性过滤线程数量<br />-filter_complex_threads 复杂性过滤线程数量<br />-stats 在编码时打印进度/统计信息<br />-max_error_rate 最大错误率，0-1之间<br />-bits_per_raw_sample number 每个原始采样的对应的比特数<br />-vol volume 改变音量，255是正常音量<br />-speed N 加快编码速度，速度越快质量越差<br />-progress url  发送进度信息到指定的URL<br />-stdin 允许基于标准输入的交互，-nostdin明确的禁用之</p>
<p><strong><span class="graybg">文件级选项</span></strong></p>
<p>-f fmt 适用IO。强制文件格式，例如mpegts<br />-i url 适用I。输入文件的URI<br />-c[:stream_specifier] codec  适用IO，可针对单个流。指定使用的编码算法，等价于 -codec <br />-pre preset 预设名称<br />-stream_loop 适用I。输入循环次数，-1表示无限循环<br />-map_metadata outfile[,metadata]:infile[,metadata] 根据源文件为目标文件设置元数据<br />-t duration 适用IO。录制或者转码指定时长的音频/视频<br />-to time_stop 适用O。停止录制或者转码的时间点（position）<br />-fs limit_size 适用O。按字节设置文件最大尺寸<br />-ss time_off 适用IO。设置开始时间偏移。指定秒数或者<pre class="crayon-plain-tag">hh:mm:ss[.xxx]</pre><br />-sseof time_off 适用IO。设置相对于EOF的开始时间偏移。0为EOF时间点，数字越小越靠前<br />-itsoffset offset  适用I。设置输入文件的时间偏移<br />-seek_timestamp 使用-ss时允许基于时间戳进行seek<br />-timestamp time 适用O。在容器中设置录制时间戳，now表示当前时间<br />-metadata string=string 添加元数据<br />-target type 适用O。目标类型，例如vcd、svcd、dvd、dv、dv50<br />-frames framecount 适用O，可针对单个流。设置输出帧的最大数量，超过数量后不再输出<br />-filter filter_graph 适用O，可针对单个流。设置流的filtergraph<br />-filter_script 适用O，可针对单个流。从文件读取filtergraph脚本<br />-pre[:stream_specifier] preset_name 适用O，可针对单个流。为匹配的流指定预设。这样可以快捷的调整处理速度。可选值ultrafast,superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo<br />-tune 根据输入类型来优化输出。可选值film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency<br />-attach filename  适用O。附加文件到输出，少数容器例如MKV支持，可以附加字幕<br />-discard  适用I。可以在混流器中丢弃某些流或者某些帧</p>
<p><strong>视频选项</strong></p>
<p>-vframes number 适用O。输出视频帧数量，等价于-frames:v<br />-r rate 适用IO，可针对单个流。设置帧率，可以指定HZ值、fraction、所写<br />-s [:stream_specifier] size 适用IO，可针对单个流。设置帧大小，WxH或者缩写，例如640x480<br />-aspect [:stream_specifier] 适用O，可针对单个流。aspect 设置纵横比，例如16:9 1.3333<br />-bits_per_raw_sample number  每个原始采样的对应的比特数<br />-vn 禁用视频录制，即丢弃输入的视频部分<br />-vcodec codec 适用O。强制使用编码算法，等价于-codec:v。取值copy则不进行重新编码<br />-vf filter_graph 适用O。设置视频过滤器<br />-b bitrate 视频比特率，等价于 -b:v<br />-dn 禁用数据<br />-pix_fmt[:stream_specifier] format (input/output,per-stream) 设置像素格式，如果指定的格式不被支持，ffmpeg会打印警告并选取编码器的最佳像素格式<br />+pix_fmt 类似上面，但是如果像素格式不支持，会退出<br />-force_key_frames[:stream_specifier] time[,time...] 适用O，可针对单个流。在指定的时间戳强制输出关键帧<br /> -force_key_frames[:stream_specifier] expr:expr 适用O，可针对单个流。在指定的时间戳强制输出关键帧<br />-hwaccel[:stream_specifier] hwaccel 适用I，可针对单个流。选择硬件加速机制。取值：none不进行硬件加速；auto自动选择硬件加速方法；vda使用Apple的VDA硬件加速；vdpau使用Unix下的VDPAU硬件加速；dxva2使用DirectX硬件加速；使用Intel的QuickSync硬件加速<br />-hwaccel_device[:stream_specifier] hwaccel_device 适用I，可针对单个流。选择硬件加速设备。</p>
<p><strong>音频选项</strong></p>
<p>-aframes number 适用O。输出音频帧数量<br />-aq quality 适用O。设置音频质量（依赖于codec）<br />-ar rate 适用IO，可针对单个流。设置采样频率，单位Hz<br />-ac channels 设置音频通道数<br />-an 禁用音频，即丢弃输入的音频部分<br />-acodec codec 强制使用编码算法。取值copy则不进行重新编码<br />-vol 改变音量，255是正常音量<br />-af filter_graph 设置音频过滤器<br />-ab bitrate 音频比特率，等价于-b:a</p>
<p><strong>字幕选项</strong><br />-s size 设置帧大小，WxH或者缩写<br />-sn 禁用字幕<br />-scodec codec 强制使用编码算法<br />-stag fourcc/tag 强制字幕tag/fourcc<br />-fix_sub_duration 修复字幕持续时间<br />-canvas_size size 设置画布大小，WxH或者缩写<br />-spre preset  设置字幕预设</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">发行版特殊命令</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-weight: normal; line-height: 19px;">命令 </span></span></td>
<td style="text-align: center;"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-weight: normal; line-height: 19px;"> 说明</span></span></td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h2"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="line-height: 19px;">rpm</span></span></td>
<td><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;">Redhat及其衍生版本下的包管理器，详见：<a href="/centos-faq#rpm">CentOS知识集锦</a></span></span></td>
</tr>
<tr>
<td class="blog_h2"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="line-height: 19px;">yum </span></span></td>
<td><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;">基于rpm的便捷的软件包管理工具，详见：<a href="/centos-faq#yum">CentOS知识集锦</a></span></span></td>
</tr>
<tr>
<td class="blog_h2"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="line-height: 19px;">apt-get</span></span></td>
<td><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;"><span style="font-family: Ubuntu, Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;">APT包处理工具，详见：<a href="/ubuntu-faq#apt-get">Ubuntu知识集锦</a></span></span></td>
</tr>
<tr>
<td>update-alternatives</td>
<td>
<p>属于dpkg的实用工具，能够维护基于符号链接确定的“默认命令”</p>
<p>在同一个Debian系统中安装多个相同或相似的功能是允许的，例如可以安装多个文本编辑器。但是对于程序来说，它却不知道选择哪个编辑器</p>
<p>Debian的备选系统（Alternatives system）用于解决此问题，系统中一个一般性的名字被多个提供可替换功能的文件共享，备选系统和管理员共同决定某个时刻到底哪个文件生效。例如ed和nvi同时安装到系统中，则备选系统默认会将一般性名字/usr/bin/editor指向nvi，管理员可以覆盖此默认值</p>
<p>一般性名字并非直接指向选中备选的符号链接，它直接指向的是备选目录中的文件，后者指向实际的备选文件</p>
<p>有时候保持一组备选的同步选中很有用，假设你安装了多个版本的vi，则man页/usr/share/man/man1/vi应该指向/usr/bin/vi所指向的vi版本。update-alternatives可以使用master/slave链接来处理这个问题。如果master变更，则关联的slave会相应的变更（改变备选）。<span style="background-color: #c0c0c0;">master + 加上它关联的slaves构成链接组（link group）</span></p>
<p>每个链接组必然处于两种状态之一：</p>
<ol>
<li>自动：当包安装或移除时，备选系统自动决定是否更新链接</li>
<li>手工：链接必须由管理员通过该命令手工变更</li>
</ol>
<p>每个备选都具有优先级（priority），在自动模式下，链接组成员将指向最高优先级的备选</p>
<p><strong><span style="background-color: #c0c0c0;">命令格式</span></strong></p>
<pre class="crayon-plain-tag"># 安装一组备选到系统中
# link master link的一般性名称
# name 位于备选目录中的符号链接的名称
# path master link引入的备选的所在位置
# priority 优先级
# --slave slave link列表，可以指定多次
--install link name path priority [--slave link name path]...
# 示例：
# 将gcc的备选设置为gcc-5，g++自动跟随着变更
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 1 \
                    --slave /usr/bin/g++ g++ /usr/bin/g++-5


# 将path设置为name的一个备选，name为备选目录中的名称
--set name path

# 移除一个备选，以及它关联的slave links，name为备选目录中的名称
--remove name path

# 移除所有备选
--remove-all name

# 针对所有备选调用 --config
--all

# 将name的备选的链接组设置为自动模式。这导致最高优先级的备选对应的
# master + slaves链接被启用
--auto name

# 显示链接组的信息
--display name
# 类似，输出易于机器解析
--query name

# 列出所有链接组及其状态
--get-selections
# 示例：
sudo update-alternatives --get-selections
# c89                            auto     /usr/bin/c89-gcc
# c99                            auto     /usr/bin/c99-gcc
# cc                             auto     /usr/bin/gcc
# go                             auto     /usr/bin/gccgo-go

# 显示链接组的所有目标
--list name
# 示例：
# update-alternatives --list gcc
# /usr/bin/gcc-4.8
# /usr/bin/gcc-4.9
# /usr/bin/gcc-5


# 显示链接组的所有备选，允许用户交互式的选中备选
--config name</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">JVM相关命令</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 100px; text-align: center;"><span style="font-weight: normal; line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">命令</span></span></td>
<td style="text-align: center;"><span style="font-weight: normal; line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">说明</span></span></td>
</tr>
</thead>
<tbody>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jps"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jps </span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">显示指定系统内所有的HotSpot虚拟机进程</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jstat"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jstat</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">用干收集HotSpot虚拟机各方面的运行数据</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jinfo"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jinfo</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">实时地査看和调整虚拟机的各项参数</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jmap"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jmap</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">生成虚拟机的内存转储快照</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jhat"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jhat</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">分析 heapdump 文件</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jstack"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jstack</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">显示虚拟机的线程快照</span></span></td>
</tr>
<tr>
<td><a href="/jvm-options-and-performance-tuning#jcmd"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;"><span style="color: #333333; font-family: Ubuntu, 微软雅黑;">jcmd</span></span></a></td>
<td><span style="line-height: 19px;"><span style="font-family: Ubuntu, 微软雅黑;">1.7引入的通用命令行工具</span></span></td>
</tr>
</tbody>
</table>
<p>&nbsp;
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-command-faq">Linux命令知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-command-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux IO知识集锦</title>
		<link>https://blog.gmem.cc/io-faq</link>
		<comments>https://blog.gmem.cc/io-faq#comments</comments>
		<pubDate>Sat, 23 Sep 2006 11:26:26 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=35549</guid>
		<description><![CDATA[<p>IO编程 参考： Linux文件I/O编程 Linux编程知识集锦 Linux知识集锦 终端 简介 在UNIX的世界里，Terminal、TTY、Console这些术语是紧密相关的，一般可以认为Terminal就是TTY，Console则是指物理存在的Terminal。 终端 终端（Terminal）起源于大型主机的时代，由于昂贵的造价，多名工作人员必须共享主机——通过一根电缆线，一端连接着主机，另外一端连接着电传打字机（TTY），这个TTY就是终端——基于文本的输入/输出环境。 Linux中某些TTY是内核专门用于特定硬件的，例如： 键盘输入 + 文本模式显示器输出 通过串口线进行的输入输出 其它的一些TTY，被称为伪（不存在对应的硬件）终端（pseudo-ttys），这类终端由一类称为终端仿真器（terminal emulators）的应用程序（例如 Xterm、 Screen、SSH）提供。伪终端常常关联到一个Shell会话。 控制台 控制台（Console）通常是指直接连接到机器的主终端（primary terminal）。 <a class="read-more" href="https://blog.gmem.cc/io-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/io-faq">Linux IO知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">IO编程</span></div>
<p>参考：</p>
<ol>
<li><a href="/linux-programming-file-io">Linux文件I/O编程</a></li>
<li><a href="/linux-programming-faq">Linux编程知识集锦</a></li>
<li><a href="/linux-faq">Linux知识集锦</a></li>
</ol>
<div class="blog_h1"><span class="graybg"><a id="terminal"></a>终端</span></div>
<div class="blog_h2"><span class="graybg">简介</span></div>
<p>在UNIX的世界里，Terminal、TTY、Console这些术语是紧密相关的，<span style="background-color: #c0c0c0;">一般可以认为Terminal就是TTY</span>，<span style="background-color: #c0c0c0;">Console则是指物理存在的Terminal</span>。</p>
<div class="blog_h3"><span class="graybg">终端</span></div>
<p>终端（Terminal）起源于大型主机的时代，由于昂贵的造价，多名工作人员必须共享主机——通过一根电缆线，一端连接着主机，另外一端连接着电传打字机（TTY），这个TTY就是终端——基于文本的输入/输出环境。</p>
<p>Linux中<span style="background-color: #c0c0c0;">某些TTY是内核专门用于特定硬件</span>的，例如：</p>
<ol>
<li>键盘输入 + 文本模式显示器输出</li>
<li>通过串口线进行的输入输出</li>
</ol>
<p><span style="background-color: #c0c0c0;">其它的一些TTY，被称为伪（不存在对应的硬件）终端（pseudo-ttys）</span>，这类终端由一类称为终端仿真器（terminal emulators）的应用程序（例如 Xterm、 Screen、SSH）提供。伪终端常常关联到一个<a href="/linux-signals-processes-and-sessions#session">Shell会话</a>。</p>
<div class="blog_h3"><span class="graybg">控制台</span></div>
<p>控制台（Console）通常是指直接连接到机器的主终端（primary terminal）。</p>
<p><span style="background-color: #c0c0c0;">在Linux、FreeBSD之类的系统中，控制台对应了多个TTYs</span>，例如Ubuntu会自动创建<span style="background-color: #c0c0c0;">虚拟控制台（virtual console ），tty1-tty6</span>，它们都可以通过控制台键盘快捷键启用。</p>
<div class="blog_h2"><span class="graybg">设备文件</span></div>
<div class="blog_h3"><span class="graybg">/dev/console</span></div>
<p>系统主控制台，内核代码<pre class="crayon-plain-tag">printk()</pre>总是向此控制台写入消息。第一个非内核进程，也就是<span style="background-color: #c0c0c0;">init，也将/dev/console作为标准输入、输出、错误</span>。</p>
<p>/dev/console是一个<span style="background-color: #c0c0c0;">可以通过内核参数进行重定向的虚拟设备集</span>。你可以在grub.conf中配置<pre class="crayon-plain-tag">console=ttyS0</pre>，这样<a href="/libvirt-study-note#console-to-ttys0">/dev/console就重定向到了/dev/ttyS0</a>。你还可以在grub.conf中多次配置console参数，这样内核<span style="background-color: #c0c0c0;">主控制台的输出被传播到多个设备</span>。</p>
<div class="blog_h3"><span class="graybg">/dev/tty</span></div>
<p>这是一个别名，对应打开/dev/tty的那个进程所关联的（物理，虚拟，伪）控制台。打开/dev/tty进行读写，不需要root权限。</p>
<p>需要注意，cron之类批处理服务启动的进程，<span style="background-color: #c0c0c0;">没有关联的控制台，因而也就没有可用的/dev/tty</span>。这类进程的<pre class="crayon-plain-tag">ps -ef</pre>输出的TTY列显示为<pre class="crayon-plain-tag">?</pre>。</p>
<div class="blog_h3"><span class="graybg">/dev/ttyn</span></div>
<p>原本含义是连接到服务器的串口终端设备。现在/dev/tty0总是表示当前控制台，而/dev/tty1…对应Ctrl + Alt + F1…切换到的那些虚拟控制台。 </p>
<div class="blog_h3"><span class="graybg">/dev/ttySn</span></div>
<p>代表连接到服务器的串口终端设备。有段时间串口设备被称为终端设备，因为那时串口设备的主要用途就是用作终端。</p>
<div class="blog_h1"><span class="graybg">内核IO</span></div>
<div class="blog_h2"><span class="graybg">关于IOWait</span></div>
<p>在top输出中，IOWait，也就是%wa，是CPU空闲时间的子类别（不包含在%idle），它是<strong><span style="background-color: #c0c0c0;">由于等待IO完成</span></strong>导致的。</p>
<p>精确的说，IOWait是<span style="background-color: #c0c0c0;">消耗在接收、处理硬件中断的时间 —— 以占据处理器tick的百分比计算。</span></p>
<p>解决高IOWait的方法：</p>
<ol>
<li>更好的硬件</li>
<li>保证系统有足够的缓存，作为页缓存</li>
<li>保证磁盘利用率小于80%，避免碎片化</li>
<li>编程时使用缓冲</li>
</ol>
<div class="blog_h2"><span class="graybg">IO调度器</span></div>
<p>如果没有IO调度器，内核只能简单的将请求转发给磁盘，这会导致磁头反复移动，性能非常差。从2.6版本开始，Linux内核支持选择IO调度器，以便基于不同的工作负载来优化IO性能。</p>
<p>IO调度器通过以下技术来优化磁盘访问性能：</p>
<ol>
<li>请求合并：将相邻（磁道）的请求合并，减少寻道</li>
<li>电梯：根据请求针对的物理位置，进行排序，尽可能单个方向进行寻道</li>
<li>优先级：优先请求重要请求</li>
</ol>
<p>IO调度器必须要考虑请求饿死的情况，确保任何请求最终能得到处理。</p>
<div class="blog_h3"><span class="graybg">查看设置</span></div>
<p>执行下面的命令，查看当前使用何种IO调度器：</p>
<pre class="crayon-plain-tag">cat /sys/block/sda/queue/scheduler</pre>
<div class="blog_h3"><span class="graybg">修改设置</span></div>
<pre class="crayon-plain-tag">echo noop &gt; /sys/block/hda/queue/scheduler</pre>
<div class="blog_h3"><span class="graybg">修改默认值</span></div>
<p>编辑/boot/grub/grub.conf文件，在内核命令行添加<pre class="crayon-plain-tag">elevator=noop</pre>，表示默认使用noop调度器。</p>
<div class="blog_h3"><span class="graybg">调度器对比</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">IO调度器</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>noop</td>
<td>仅仅实现请求合并功能</td>
</tr>
<tr>
<td>as</td>
<td>
<p>老版本2.6内核的默认调度器。实现请求合并、单向电梯、读写请求批处理，并且尝试进行一些预测性读</p>
<p>对于数据库、存储系统来说，可能导致性能的不稳定</p>
</td>
</tr>
<tr>
<td>deadline</td>
<td>
<p>实现请求合并、单向电梯，并且为所有IO操作强加了一个deadline，避免了饿死现象</p>
<p>由于在Linux中<span style="background-color: #c0c0c0;">写操作立即返回（使用Page cache）</span>，因此该调度器<span style="background-color: #c0c0c0;">更加优先考虑读请求</span> —— 只要写请求的deadline尚未到达</p>
<p>对于数据库系统、TCQ感知磁盘、高性能磁盘，可以选择该调度器</p>
</td>
</tr>
<tr>
<td>cfq</td>
<td>
<p>实现请求合并、电梯。倾向于在一段时间内，服务特定设备的所有用户相同的IO请求次数</p>
<p>对于<span style="background-color: #c0c0c0;">多用户系统更加高效</span></p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">文件和文件系统</span></div>
<div class="blog_h2"><span class="graybg">一些基础概念</span></div>
<div>
<ol>
<li>依据POSIX标准，文件系统至少维护文件的以下属性：文件类型、硬链接计数、长度（字节）、设备ID、inode、文件所有者UID、文件所属GID、若干时间戳</li>
<li>Inode：索引节点。内核为每个文件分配的符号，具有唯一标识符，存放在文件系统特殊的数据块中。文件元数据（创建日期、修改日期、访问权限文件大小、文件位置等）存放于其中。删除文件时，系统会将指向目标Inode的计数减1，如果计数为0，则表示该节点及其指向的数据不再被使用，磁盘上相应的位置会被标记为可用空间</li>
<li>硬链接：一个指向其他文件（不能目录）的指针，不能跨文件系统。与原文件Inode一致，本质上是等同的关系，相当于别名</li>
<li>软链接（符号链接）：具有独立的Inode。目标文件移走了则无法访问。可以跨文件系统、可以指向目录</li>
<li>交换分区：swap分区，虚拟内存专用的单独分区</li>
</ol>
</div>
<div class="blog_h2"><span class="graybg">万物皆文件</span></div>
<p>Linux对文件的定义与Windows很不相同，在Linux中，设备、进程等概念均映射到文件。这个特性和JNDI等目录服务比较类似，相当于任何资源均使用一个URI表示。实际上，在Linux中任何支持通过文件API操作的事物，都被作为文件看待，包括网络服务、内核组件、基于内存的虚拟磁盘等。</p>
<p>Linux甚至还有一个“环回（Loopback）”文件系统，允许把单个文件当作文件系统，挂载到目录树中：</p>
<pre class="crayon-plain-tag">mount -o loop /home/alex/unbunt-14.02.iso /mnt/cdrom</pre>
<div class="blog_h2"><span class="graybg">挂载</span></div>
<div class="blog_h3"><span class="graybg">绑定挂载</span></div>
<pre class="crayon-plain-tag">mount --bind /proc/self/ns/net mynetns</pre>
<p><span style="background-color: rgb(192, 192, 192);">绑定挂载具有打开被绑定者的文件描述符的效果</span>，你甚至可以<span style="background-color: rgb(192, 192, 192);">绑定挂载到自己，这种情况下，文件既是挂载目标，也是挂载点</span>。尝试删除会提示Device or resource busy，需要先umount，再删除。</p>
<p>绑定挂载后，你可以删除掉目标，此时挂载点的内容保持不变，底层文件依然存在。</p>
<div class="blog_h2"><span class="graybg">静态文件系统信息</span></div>
<p>配置文件/etc/fstab包含了静态文件系统信息，用于自动化磁盘分区的挂载过程。</p>
<p>所谓挂载，就是准备好物理分区，并将其分配到文件系统目录树中的某个节点（挂载点）的过程。</p>
<p>fstab具有一下特点：</p>
<ol>
<li>fstab文件一般可以用于：内部设备、光驱设备、网络共享设备（例如：samba、nfs、sshfs）。可拔插设备（例如U盘）通常不编写在fstab</li>
<li>fstab可以在启动时自动挂载某个分区</li>
<li>如果设备或者分区没有在fstab中列出，那么只有ROOT才能进行mount</li>
</ol>
<p>fstab中每行可以具有以下列（以Ubuntu为例说明）：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 列</td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>file system</td>
<td>
<p>包含文件系统的/设备/分区，支持以下形式：</p>
<p>Ubuntu中，默认使用UUID识别分区，例如UUID=xxx.yyy.zzz，可以使用sudo blkid命令列出UUID<br />其它可选方式：<br />设备：/dev/sdxy<br />标签：LABEL=label<br />网络标识：<br />Samba : //server/share<br />NFS : server:/share<br />SSHFS : sshfs#user@server:/share</p>
</td>
</tr>
<tr>
<td>mount point</td>
<td>
<p>挂载点，根文件系统中的一个节点。交换分区（swap）没有挂载点。挂载点名称中不得有空格。</p>
<p>默认挂载点是/media，其它任何位置都是可以的，例如/mnt。挂载点必须首先使用mkdir创建</p>
</td>
</tr>
<tr>
<td>type</td>
<td>
<p>文件系统的类型，例如：</p>
<p>auto、vfat（FAT分区）、ntfs, ntfs-3g（NTFS分区）、ext4, ext3, ext2,、udf,iso9660（光盘分区）、swap（交换分区）</p>
</td>
</tr>
<tr>
<td>options</td>
<td>
<p>对应mount命令的挂载选项</p>
<p>可以使用默认选项（defaults = rw, suid, dev, exec, auto, nouser, and async）</p>
<p>常用选项：</p>
<p>sync/async 所有的I/O应当同/异步完成<br />auto 文件系统在启动时自动挂载 <br />noauto 文件系统不会自动挂载，必须手工调用mount<br />dev/nodev 解释或不解释文件系统中的字符或者块特殊设备<br />exec/noexec 允许或不允许执行文件系统上的可执行文件<br />suid/nosuid - 允许或禁止suid、sgid位的操作<br />ro 挂载为只读<br />rw 挂载为读写<br />user 允许任何用户挂载该文件系统<br />nouser 仅仅允许ROOT来挂载该文件系统 <br />_netdev 这是一个网络设备，需要在网络启动后挂载，仅对nfs文件系统有效</p>
</td>
</tr>
<tr>
<td>dump</td>
<td>是否启用分区备份，一般否0</td>
</tr>
<tr>
<td>pass</td>
<td>系统启动时检查分区错误的顺序，根设备应该是1，其它2，0禁止检查</td>
</tr>
</tbody>
</table>
<p>fstab示例内容如下：</p>
<pre class="crayon-plain-tag">root@localhost:~# cat /etc/fstab
#                               
  proc          /proc           proc    defaults                       0       0
  /dev/xvda     /               ext4    noatime,errors=remount-ro      0       1
  /dev/xvdb     none            swap    sw                             0       0
# / was on /dev/sda1 during installation
  UUID=66f7abf6-d861-4e46-a255-e6b8094c8c42 / ext4 errors=remount-ro 0 1
# swap was on /dev/sda5 during installation
UUID=a7122024-5385-48c3-b536-b9fefb25cd73  none swap sw 0 0
/dev/fd0 /media/floppy0 auto rw,user,noauto,exec,utf8 0 0</pre>
<div class="blog_h2"><span class="graybg">文件类型</span></div>
<div class="blog_h3"><span class="graybg">Shell配色</span></div>
<p>在Ubuntu默认配色方案中：</p>
<p><span style="font-weight: bold; color: #729fcf;">蓝色</span>表示：目录<br /> <span style="font-weight: bold; color: #8ae234;">绿色</span>表示：可执行文件<br /> <span style="font-weight: bold; color: #ef2929;">红色</span>表示：压缩文件<br /> <span style="font-weight: bold; color: #33e2e2;">淡蓝色</span>表示：链接文件<br /> <span style="font-weight: bold; color: #797979;">灰色</span>表示：其它文件<br /> <span style="font-weight: bold; color: #ef2929; background-color: #000;">红色黑背景</span>表示：链接存在问题<br /> <span style="font-weight: bold; color: #f8e94f;">黄色</span>表示：设备文件<br /> <span style="font-weight: bold; background-color: #4e9a06;">绿色背景</span>表示：权限大开</p>
<div>下面是在Ubuntu的Terminal程序中显示效果的截图：</div>
<div class="blog_h2"><span class="graybg">访问权限</span></div>
<p>使用 ls -l命令，以列表形式显示当前目录下的文件时，首列将显示每个文件的属性，如下图：</p>
<p><img class="aligncenter  wp-image-6067" src="https://blog.gmem.cc/wp-content/uploads/2012/07/ll.png" alt="ll" width="90%" /></p>
<p>可以看到，文件属性显示为10位字母，其结构如下：</p>
<p style="padding-left: 30px;"><span style="background-color: #c0c0c0;"> {1位文件类型标记} {3位所有者访问权限} {3位文件所在组用户的访问权限} {3位所有其它用户访问权限}</span></p>
<div class="blog_h3"><span class="graybg">文件类型标记位</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">符号</td>
<td style="text-align: center;">含义</td>
</tr>
</thead>
<tbody>
<tr>
<td>-</td>
<td>普通文件</td>
</tr>
<tr>
<td>d</td>
<td>目录</td>
</tr>
<tr>
<td>l</td>
<td>符号链接</td>
</tr>
<tr>
<td>c</td>
<td>字符设备节点。设备文件和<span style="background-color: #c0c0c0;">I/O设备、集成到内核的设备驱动</span>有关，程序访问设备文件，则直接访问其关联的设备</td>
</tr>
<tr>
<td>b</td>
<td>块设备节点</td>
</tr>
<tr>
<td>p</td>
<td>命名管道。管道和套接字用于进程间通信</td>
</tr>
<tr>
<td>s</td>
<td>套接字</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">文件模式</span></div>
<p>所谓文件模式（Mode），是指<span style="background-color: #c0c0c0;">9个控制读、写、执行的位，附加3个影响可执行文件运行方式的标记位</span>组成的文件访问权限描述符。</p>
<div><span style="text-decoration: underline;"><strong>普通文件的9个权限位的含义为</strong></span>:</div>
<div style="padding-left: 30px;">r: 查看文件内容<br />w: 修改文件<br />x: 如同命令一样执行文件</div>
<div><span style="text-decoration: underline;"><strong>目录文件的9个权限位的含义为</strong></span>:</div>
<div style="padding-left: 30px;">r: 列出目录内容<br />w: 在目录中增删文件<br />x: 访问目录中的文件（查看目录下文件名、文件大小、修改时间等）</div>
<div><span style="text-decoration: underline;"><strong>3个附加位的含义为</strong></span>：</div>
<div style="padding-left: 30px;">setuid：执行该文件时，进程的有效UID被设置为文件的所有者，而不是执行该文件的用户</div>
<div style="padding-left: 30px;">setgid：执行该文件时，进程的有效GID被设置为文件的所属组，而不是当前进程组的GID</div>
<div style="padding-left: 30px;">粘滞位：如果目录上设置了粘滞位，那么除了root、目录所有者、目录下文件所有者外，任何人不能删除、重命名其中的文件。对于可执行文件，该标志提示内核，在执行完毕后，保留程序在内存中（已经过时，现在使用共享代码页的方式）</div>
<p>注意：<span style="background-color: #c0c0c0;">挂载文件系统时，可以禁用setuid、setgid</span>，这种情况下即使设置了这些位，也没有效果。 </p>
<p>一般使用8进制的4/2/1分别表示读/写/执行，下表描述的顺序与文件属性中9位权限的排列顺序相同：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 10%; text-align: center;">分类</td>
<td style="width: 30%; text-align: center;" colspan="3">所有者</td>
<td style="width: 30%; text-align: center;" colspan="3">文件所在组的用户</td>
<td style="width: 30%; text-align: center;" colspan="3">所有其它用户</td>
</tr>
</thead>
<tbody>
<tr>
<td>数值</td>
<td>4</td>
<td>2</td>
<td>1</td>
<td>4</td>
<td>2</td>
<td>1</td>
<td>4</td>
<td>2</td>
<td>1</td>
</tr>
<tr>
<td>含义</td>
<td>r</td>
<td>w</td>
<td>x</td>
<td>r</td>
<td>w</td>
<td>x</td>
<td>r</td>
<td>w</td>
<td>x</td>
</tr>
</tbody>
</table>
<p>对于没有设置的权限位，显示为短横线：“-”。此外，3个附加位为被设置时，执行位的x显示为其它字符：</p>
<ol>
<li>如果setuid位被设置，则所有者的x显示为s</li>
<li>如果setgid位被设置，则所属群的x显示为s</li>
<li>如果粘滞位被设置，则其它人的x显示为t</li>
<li>如果设置了setuid、setgid或者粘滞位，但是对应的x没有设置，则使用大写显示相应的S、T</li>
</ol>
<div class="blog_h3"><span class="graybg">chmod命令</span></div>
<p>命令格式： chmod [-vR] mode filename</p>
<div>-v 显示权限改变的详细资料</div>
<div>-R 表示对当前目录下的所有文件和子目录进行相同的权限更改</div>
<div>mode：表示权限设置字串,格式为[ugoa] [+-=] [rwx]</div>
<div style="padding-left: 30px;">u 表示文件的拥有者； g 表示文件所属群组； o 表示其他人 ；a 表示包含以上三者；</div>
<div style="padding-left: 30px;">+ 表示增加权限； - 表示取消权限； = 表示唯一设置权限</div>
<div style="padding-left: 30px;">r 表示有读取的权限； w 表示有写入的权限；x 表示有执行的权限；s表示设置setuid或者setgid位</div>
<p>举例：</p>
<pre class="crayon-plain-tag">#给文件所有者增加执行权限
chmod u+x filename
#设置为：所有者可以读写，其他人没有任何权限
chmod 600 filename

# 设置setuid位
chmod u+s /usr/bin/qemu-ifup 
# 设置setgid位
chmod g+s /usr/bin/qemu-ifup
# 同时设置setuid setgid位
chmod a+s /usr/bin/qemu-ifup

# 设置粘滞位
chmod o+t tmp</pre>
<div class="blog_h3"><span class="graybg">chown命令</span></div>
<p>就像每个用户都归属于一个主要组，任何Linux文件都具有一个<span style="background-color: #c0c0c0;">所有者</span>和<span style="background-color: #c0c0c0;">关联的组</span>。</p>
<p>命令格式： chown  [-vR]  [user] [:group] filename</p>
<p>举例：</p>
<pre class="crayon-plain-tag">#把blog目录及其子目录、文件的所有者改为root，所属群组改为www-data
chown -vR root:www-data blog
#把www目录的所有者改为www-data
chown www-data www</pre>
<div class="blog_h3"><span class="graybg">chgrp命令</span></div>
<p>命令格式： chown  [-vR]  group  filename</p>
<p>举例：</p>
<pre class="crayon-plain-tag">#把www目录的所属群组改为www-data 
chgrp www-data www</pre>
<div class="blog_h1"><span class="graybg">存储</span></div>
<div class="blog_h2"><span class="graybg">LVM</span></div>
<p>逻辑分卷管理器（Logical Volume Manager）是一种可用在Linux内核的分卷管理器，支持管理硬盘驱动器或者其它大容量存储设备。</p>
<p>LVM<span style="background-color: #c0c0c0;">简化了存储管理</span>，它利用Linux内核的device-mapper来实现<span style="background-color: #c0c0c0;">存储系统的虚拟化</span>（系统分区独立于底层硬件）。你可以基于LVM实现<span style="background-color: #c0c0c0;">存储空间的抽象化</span>，并在上面建立虚拟分区（virtual partitions）。这样，扩大/缩小分区变得更加简便，增删分区时也不用担心硬盘没有足够的<span style="background-color: #c0c0c0;">连续空闲空间</span>。</p>
<p>LVM牵涉到以下基本概念：</p>
<ol>
<li>物理卷Physical volume (PV)：可以在上面建立卷组的媒介，<span style="background-color: #c0c0c0;">物理卷可以是硬盘分区，也可以是硬盘本身</span>。物理卷包括一个特殊的header，其余部分被切割为一块块的物理区域（physical extents）</li>
<li>卷组Volume group (VG)：将一组物理卷收集为一个管理单元。<span style="background-color: #c0c0c0;">卷组被整体的作为存储卷使用，就像单个磁盘一样。一个卷组可以包含多块硬盘</span></li>
<li>逻辑卷Logical volume (LV)：即虚拟分区，由物理区域（physical extents）组成。你可以把逻辑卷看作普通分区</li>
<li>物理区域Physical extent (PE)：硬盘可供指派给逻辑卷的最小单位（通常为4MB）。物理区域可以分配给任何逻辑卷</li>
</ol>
<p>LVM的优势是：</p>
<ol>
<li>可以让多个硬盘看起来像是一个大硬盘</li>
<li>可以创建跨越多块硬盘的分区</li>
<li><span style="color: #222222;">可以创建小的逻辑卷，在空间不足时再动态调整它的大小</span></li>
<li><span style="color: #222222;">调整逻辑卷大小时可以不用考虑逻辑卷在硬盘上的位置，不用担心没有可用的连续空间</span></li>
<li><span style="color: #222222;">可以在线（已挂载）对逻辑卷和卷组进行创建、删除、调整大小等操作</span></li>
<li><span style="color: #222222;">无需重新启动服务，就可以将服务中用到的逻辑卷在线、动态迁移至别的硬盘上</span></li>
<li><span style="color: #222222;">允许创建快照，可以保存文件系统的备份</span></li>
</ol>
<p><span style="color: #222222;">这些优势使得LVM对服务器存储的管理非常有用。</span></p>
<div class="blog_h2"><span class="graybg">RAID</span></div>
<p>独立硬盘冗余阵列（RAID, Redundant Array of Independent Disks）的简写。分为多种类型：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 45px; text-align: center;">级别</td>
<td style="width: 80px; text-align: center;">冗余盘数</td>
<td style="width: 100px; text-align: center;">空间利用率</td>
<td style="width: 45px; text-align: center;">性能</td>
<td style="width: 60px; text-align: center;">可靠性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>0</td>
<td>100%</td>
<td>最高</td>
<td>最低</td>
<td>数据没有副本。同时从N块盘写入、读出 </td>
</tr>
<tr>
<td>1</td>
<td>N/2</td>
<td>50%</td>
<td>低</td>
<td>最高</td>
<td>每块数据都有一份副本，读取性能可倍增 </td>
</tr>
<tr>
<td>3</td>
<td>1</td>
<td>( N - 1 ) / 2</td>
<td>较高</td>
<td>较低</td>
<td>
<p>一块磁盘专门存放数据校验信息，N-1块盘做成RAID 0</p>
<p><span style="color: #1a1a1a;">最多允许坏一块盘。N最少为3</span></p>
</td>
</tr>
<tr>
<td>5</td>
<td>1</td>
<td>( N - 1 ) / 2</td>
<td>较高</td>
<td>较低</td>
<td>
<p>相当于一块磁盘的容量存放校验信息，但是信息分散存放在所有磁盘上</p>
<p><span style="color: #1a1a1a;">最多允许坏一块盘。</span><span style="color: #1a1a1a;">N</span><span style="color: #1a1a1a;">最少为3</span></p>
</td>
</tr>
<tr>
<td>6</td>
<td>2</td>
<td>( N - 2 ) / 2</td>
<td>较低</td>
<td>较高</td>
<td>在RAID 5的基础上，又增加了一种校验码。最多允许坏两块盘</td>
</tr>
</tbody>
</table>
<p>对于RAID 1 - 6 ，两个阵列可以再组成RAID0，从而形成RAID 10 、RAID 50 ……<span style="background-color: #c0c0c0;">其中RAID 50提供了接近RAID 10性能、可用性以及接近RAID 5成本的特性</span>，具有较好的整体性价比优势。</p>
<div class="blog_h2"><span class="graybg">RAID5</span></div>
<div class="blog_h3"><span class="graybg">高坏盘率问题</span></div>
<p>RAID5只能提供一块磁盘的容错率，服务器硬盘的年坏盘率为1.7%-8.6%。这么高的概率，让短时间内坏掉两块盘的情况成为可能。</p>
<p>虽然使用RAID6能容忍两块坏盘，但是<span style="background-color: #c0c0c0;">RAID 6 需要生成两组校验码，资源耗费比RAID5大不少</span>。</p>
<div class="blog_h3"><span class="graybg">重建时间漫长</span></div>
<p>当前磁盘容量越来越大，出现坏盘后，插入新的热备盘进行数据重建，耗时想当长。以SATA 1TB盘为例，单盘速度100MB/s，重建全速进行也要耗费3小时。这3个小时里面，RAID5是没有数据保护能力的。</p>
<div class="blog_h3"><span class="graybg">写惩罚</span></div>
<p>RAID5对随机IO的处理是它的一大弱势，<span style="background-color: #c0c0c0;">对于随机读写不仅仅性能会下降</span>，而且由于它的特点会导致<span style="background-color: #c0c0c0;">写性能额外的损失</span>。</p>
<p>RAID5是把<span style="background-color: #c0c0c0;">数据划分成一个个条带（Stripe）</span>来处理。只有整个条带数据的写入才能产生校验信息，如果只写条带的一部分，则需要从磁盘把整个条带读取出来以计算校验信息。这导致一次写操作可能需要两次IO才能完成。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/io-faq">Linux IO知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/io-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux网络知识集锦</title>
		<link>https://blog.gmem.cc/network-faq</link>
		<comments>https://blog.gmem.cc/network-faq#comments</comments>
		<pubDate>Fri, 22 Sep 2006 12:21:20 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Network]]></category>
		<category><![CDATA[Linux知识]]></category>
		<category><![CDATA[Linux编程]]></category>
		<category><![CDATA[网络编程]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=6337</guid>
		<description><![CDATA[<p>网络编程 参考： Linux网络编程 Linux编程知识集锦 Linux知识集锦 Bonding 网络接口绑定（Network Interface Bonding）是Linux下的一项技术，它能够将多块物理网卡绑定为单一的逻辑网卡，从而实现： 带宽增加 提供容错能力，防止一根网线损坏的情况 也叫Teaming、 Link Aggregation Groups（LAG）。 启用 你需要先安装bonding内核模块，并且用modprobe查看bonding驱动是否被加载： [crayon-69e29b2725c10557718713/] 创建 你需要先安装两块物理NIC，然后使用下面的命令将它们bond为新的逻辑接口：  [crayon-69e29b2725c14749973591/] 或者永久化的配置： <a class="read-more" href="https://blog.gmem.cc/network-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/network-faq">Linux网络知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">网络编程</span></div>
<p>参考：</p>
<ol>
<li><a href="/linux-network-programming">Linux网络编程</a></li>
<li><a href="/linux-programming-faq">Linux编程知识集锦</a></li>
<li><a href="/linux-faq">Linux知识集锦</a></li>
</ol>
<div class="blog_h1"><span class="graybg">Bonding</span></div>
<p>网络接口绑定（Network Interface Bonding）是Linux下的一项技术，它能够将多块物理网卡绑定为单一的逻辑网卡，从而实现：</p>
<ol>
<li>带宽增加</li>
<li>提供容错能力，防止一根网线损坏的情况</li>
</ol>
<p>也叫Teaming、 Link Aggregation Groups（LAG）。</p>
<div class="blog_h2"><span class="graybg">启用</span></div>
<p>你需要先安装bonding内核模块，并且用modprobe查看bonding驱动是否被加载：</p>
<pre class="crayon-plain-tag">sudo modprobe bonding
lsmod | grep bond</pre>
<div class="blog_h2"><span class="graybg">创建</span></div>
<p>你需要先安装两块物理NIC，然后使用下面的命令将它们bond为新的逻辑接口： </p>
<pre class="crayon-plain-tag">sudo ip link add bond0 type bond mode 802.3ad
sudo ip link set eth0 master bond0
sudo ip link set eth1 master bond0</pre>
<p>或者永久化的配置：</p>
<pre class="crayon-plain-tag">auto bond0
iface bond0 inet static
	address 192.168.1.150
	netmask 255.255.255.0	
	gateway 192.168.1.1
	dns-nameservers 192.168.1.1 8.8.8.8
	dns-search domain.local
		slaves eth0 eth1
		bond_mode 0
		bond-miimon 100
		bond_downdelay 200
		bound_updelay 200</pre>
<div class="blog_h1"><span class="graybg">TCP协议</span></div>
<p>参考：<a href="/tcp-ip-study-note">TCP/IP协议栈学习笔记</a></p>
<div class="blog_h2"><span class="graybg"><a id="connstate"></a>连接状态</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">状态</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">LISTEN</td>
<td>仅仅对于服务器端存在，正在指定的端口上监听</td>
</tr>
<tr>
<td class="blog_h3">ESTABLISHED</td>
<td>三次握手完成，连接建立</td>
</tr>
<tr>
<td class="blog_h3">FIN_WAIT1</td>
<td> </td>
</tr>
<tr>
<td class="blog_h3">CLOSE_WAIT</td>
<td> </td>
</tr>
<tr>
<td class="blog_h3">FIN_WAIT2</td>
<td>
<p>在FIN_WAIT_2状态，本端已经发送FIN，对端已经ACK。除非进行半关闭，否则对端的应用层已经意识到需要进行关闭，并向本端发送FIN来关闭另一方向的连接，只有对端完成这个关闭，本端才从FIN_WAIT_2进入TIME_WAIT状态。</p>
<p>这也意味着，本端可能一直处于FIN_WAIT_2，对端则一直处于CLOSE_WAIT，为了防止处于FIN_WAIT_2的无限等待，TCP实现中使用定时器进行处理</p>
</td>
</tr>
<tr>
<td class="blog_h3">TIME_WAIT</td>
<td>
<p>TIME_WAIT又称2MSL等待状态。</p>
<p>每个TCP实现必须选择一个报文段最大生存时间（Maximum Segment Lifetime，MSL）—— 是任何报文段被丢弃前在网络内的最长时间。此外，由于IP数据报具有TTL，因此在网络上存在是有限制的。后者是基于跳数，而不是定时器。MSL的值通常是 30秒、1分钟或者2分钟。</p>
<p>对一个具体实现所给定的MSL值，处理的原则是：当TCP执行一个主动关闭，并发回最后一个ACK，该连接必须在TIME_WAIT状态停留的时间为2倍的MSL，这样可让TCP再次发送最后的ACK以防这个ACK丢失（另一端超时并重发最后的FIN）。</p>
<p>2MSL等待的结果是，2MSL期间定义连接的Socket（即：唯一标识Socket的四元组）不能再被使用。但是某些实现允许重用处于2MSL状态的端口（指定：SO_REUSEADDR）</p>
<p>服务器通常是被动关闭，不会进入TIME_WAIT状态</p>
</td>
</tr>
<tr>
<td class="blog_h3">CLOSED</td>
<td>不是一个真实的状态，作为状态图假想的起终点</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">网络地址转换</span></div>
<p>网络地址转换也称为网络掩蔽或者<span style="background-color: #c0c0c0;">IP掩蔽（Masquerading）</span>，是一种在IP封包通过路由器或防火墙时<span style="background-color: #c0c0c0;">重写源IP地址或目的IP地址</span>的技术。该技术普遍用于只有一个公有IP的局域网中，允许局域网中多台主机与公网上的主机进行通信。NAT 功能通常被集成到路由器、防火墙、ISDN路由器或者单独的NAT设备中。</p>
<div class="blog_h2"><span class="graybg">NAT工作机制</span></div>
<p>一个典型的局域网会使用一个专用网络来指定子网，最常用的是192.168.x.x，这个局域网中包含一个路由器，它占用一个IP地址（例如192.168.0.1）。路由器同时通过一个ISP提供的公有IP地址连接到因特网上。</p>
<p>当局域网上的主机需要和公网主机通信时，其发送IP封包，路由器将IP封包的源地址从专有地址192.168.0.x转换为公有地址（例如106.185.46.7），路由记住专有地址[:端口]与公有地址[:端口]的映射关系，当公网主机的IP封包到达时，利用此映射关系改写目的地址，进而转发给局域网主机。</p>
<p>上述的源地址、目的地址改写的过程即为NAT。</p>
<div class="blog_h2"><span class="graybg">NAT的优缺点</span></div>
<p>NAT的优势：</p>
<ol>
<li>在IPv4短缺的情况下，可以使多台主机共享一个IP地址接入因特网</li>
<li>隐藏内网计算机，避免受到来自外部网络的攻击</li>
</ol>
<p>NAT的缺点：</p>
<ol>
<li>某些协议无法正常工作，例如主动模式的FTP。这些协议需要引入<span style="background-color: #c0c0c0;">应用层网关（Application Layer Gateway，ALG）</span>才能正常工作</li>
</ol>
<div class="blog_h2"><span class="graybg">NAT的分类</span></div>
<div class="blog_h3"><span class="graybg">基本网络地址转换</span></div>
<p>亦可简称NAT或者静态NAT，<span style="background-color: #c0c0c0;">仅支持地址转换，不支持端口映射</span>。需要每一个连接对应一个公网IP地址，因此需要维护一个公网IP地址池。某些宽带路由器使用这种方式去指定一台局域网主机去接受所有外部连接，并称该机器为<span style="background-color: #c0c0c0;">DMZ主机</span>（并不是真正意义上的，因为DMZ主机必须与内网隔离）。</p>
<div class="blog_h3"><span class="graybg">网络地址端口转换</span></div>
<p>NAPT，该方式<span style="background-color: #c0c0c0;">支持端口映射</span>，允许多台内网主机共享一个公网IP地址，其包括两类转换功能：</p>
<ol>
<li>源地址转换：发起连接的内网计算机的IP地址将会被重写，使得内网主机发出的数据包能够到达外网主机</li>
<li>目的地址转换：被连接内网计算机的IP地址将被重写，使得外网主机发出的数据包能够到达内网主机</li>
</ol>
<p>这两种转换一般会一起使用，以支持双向通信。</p>
<p>NAPT需要维护一个NAT表，来基于内网IP/端口与公网IP/端口的对应关系，例如：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;"> 内网IP/端口</td>
<td style="text-align: center;"> 外网IP/端口</td>
</tr>
</thead>
<tbody>
<tr>
<td>192.168.0.89:6443</td>
<td>106.185.46.7:9200</td>
</tr>
<tr>
<td>192.168.0.90:8897</td>
<td>106.185.46.7:9201 </td>
</tr>
</tbody>
</table>
<p> <a href="/webrtc-server-basedon-kurento#glossary-stun">STUN</a>标准将NAPT分为以下几个子类型：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">类型 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>完全圆锥型NAT </td>
<td>一旦一个内部地址（iAddr:port1）映射到外部地址（eAddr:port2），<span style="background-color: #c0c0c0;">所有</span>发自iAddr:port1的包<span style="background-color: #c0c0c0;">都经由</span>eAddr:port2向外发送。<span style="background-color: #c0c0c0;">任意外部主机</span>（hAddr:*）都能通过给eAddr:port2发送封包到达iAddr:port1</td>
</tr>
<tr>
<td>地址受限圆锥型NAT</td>
<td>
<p>与完全圆锥形 NAT类似，但是外部主机（hAddr:*）能通过eAddr:port2给iAddr:port1发送封包的前提是，之前iAddr:port1<span style="background-color: #c0c0c0;">曾经发送</span>封包到hAddr:*。星号表示任意端口</p>
<p>在应用受限圆锥形NAT时，外网主机不能主动发起最初的通信，内网主机为了让外网主机能与之通信，主动发起连接的行为，被称为“打洞”</p>
</td>
</tr>
<tr>
<td>端口受限圆锥型NAT</td>
<td>与地址受限圆锥形NAT类似，但是附加了一个端口限制，即：外部主机（hAddr:port3）能通过eAddr:port2给iAddr:port1发送封包的前提是，之前iAddr:port1曾经发送封包到hAddr:port3</td>
</tr>
<tr>
<td>对称NAT（symmetric）</td>
<td>
<p>内部地址（iAddr:port1）向外部主机（hAddr:port3）发送封包，总是映射到同一个外部地址（eAddr:port2），不同的内部地址、外部主机地址组合总是映射到不同的外部地址。只有曾经收到过内部主机封包的外部主机，才能够把封包发回</p>
<p>一般注重安全性的大公司会启用对称NAT，以禁止P2P通信</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg"><a id="traversal"></a>NAT穿透</span></div>
<p>NAT-T技术主要解决两台同时处于NAT设备后面的局域网主机建立网络连接的问题。</p>
<p>两台都处于NAT后面的主机，无法知道对方映射的公网地址/端口，因此，一般的NAT-T技术都需要一个公共服务器作为媒介：要么在建立连接的时候需要用到该服务器，要么所有的数据都通过此公共服务器中继。</p>
<p>假设：</p>
<ol>
<li>位于NAT后的主机A（局域网IP：192.168.0.89，公网IP：106.185.46.7）</li>
<li>主机B（局域网IP：192.168.1.90，公网IP：106.185.46.10），以公网服务器S（106.185.46.1）为媒介（所谓<span style="color: #383838;">STUN服务器）</span>，进行NAT-T的过程可能如下（A与B是对称的）：</li>
<li>A连接S，获得映射地址（192.168.0.89:35330  =&gt;106.185.46.7:2250），将此地址信息告知S</li>
<li>B连接S，获得映射地址（192.168.1.90:35330 =&gt;106.185.46.10:3210），将此地址信息告知S</li>
<li>A和B分别获取对方的两个地址</li>
<li>A尝试连接B
<ol>
<li>如果B是完全圆锥型NAT，那么不论A是圆锥型、对称NAT，连接都立刻建立</li>
<li>如果B是地址或端口受限NAT，由于A之前从未与B通信过，因此无法建立连接
<ol>
<li>如果A是地址或端口受限NAT，与B的连接无法建立，但是A尝试发包到B的记录已经被A侧NAT记录。只要该记录没有过期，B就可以反过来向A发送数据，该数据 A必然收到，随后A和B即可双向通信</li>
<li>如果A是对称型NAT，B利用从S得到的关于A的信息（假设为192.168.0.89:35330 =&gt;106.185.46.7:2250 =&gt; 106.185.46.1:3387），尝试向106.185.46.7:2250发送信息，由于A的对称NAT的限制，此尝试必然失败。但是，此尝试在B侧留下了NAT记录。如果
<ol>
<li>B是地址受限NAT，那么A可以再次向B发送数据，这一次B必然收到</li>
<li>B是端口受限NAT，则无法通信</li>
</ol>
</li>
</ol>
</li>
<li><span style="background-color: #c0c0c0;">B是对称NAT的情况下，如果A是对称NAT或者端口受限圆锥型NAT</span>，则双方无法通信</li>
</ol>
</li>
</ol>
<div id="bridging" class="blog_h1"><span class="graybg">桥接</span></div>
<p><a id="bridging"></a>网桥（Bridge）是一种（可以是软件虚拟的）设备，该设备有<span style="background-color: #c0c0c0;">两个或者多个</span>网络接口，分别连接到多个局域网（不同网段）中。网桥将它连接的某个局域网发送的数据帧，统一转发到其它网络接口对应的局域网中，从而将多个网络在数据链路层无缝连接起来，就<span style="background-color: #c0c0c0;">好像是一个局域网</span>一样。任何真实设备（例如eth0）和虚拟设备（例如tap0）都可链接到网桥。</p>
<p>网桥与路由器不同，后者允许多个网络在保持独立的情况下能够相互通信。网桥的行为更像是一台虚拟的交换机，它能够透明的工作，其它主机不需要知道其存在。</p>
<p>Vmware中的桥接，和一般意义上的桥接原理是一样的，它让虚拟机网卡连接到一个虚拟的以太网交换机，虚拟以太网交换机与宿主机器的以太网卡通过一个“虚拟网桥”相连。</p>
<div class="blog_h2"><span class="graybg">Linux中的桥接</span></div>
<p>当你将一个网络接口（例如eth0）添加到Linux虚拟网桥（例如br0）时，你需要把IP地址从eth0移除，并添加到br0，这样才能保证网络正常工作。</p>
<p>这是因为，网桥br0负责处理本机eth0的入站流量，它应该响应ARP请求——这样它<span style="background-color: #c0c0c0;">才能把流量转发给网桥中的其它网络接口</span>，因而它拥有eth0的IP就很合理了（把网桥的MAC和IP关联）。</p>
<div class="blog_h3"><span class="graybg">Hairpin</span></div>
<p>默认情况下，Bridge<span style="background-color: #c0c0c0;">不允许包从收到包的端口发出</span>。例如，当Bridge从一个端口接收到广播报文后，它会将报文<span style="background-color: #c0c0c0;">向其它端口全部发出</span>，而不包括接收的那个端口。</p>
<p>你可以<span style="background-color: #c0c0c0;">在端口级别打开Hairpin模式</span>，这样，<span style="background-color: #c0c0c0;">从该端口进入网桥的包，还可以从此端口出去</span>。在NAT场景下，例如Docker的NAT网络，从容器访问它映射（80）到的主机端口（8080）时：</p>
<ol>
<li>容器访问hostip:8080</li>
<li>请求到达网桥，来自端口A</li>
<li>进入宿主机协议栈</li>
<li>DNAT转换为continerip:80</li>
<li>协议包又要从A端口发出</li>
</ol>
<p>为什么叫Hairpin，这是形象的描述流量的走向，就像最简单的U形发卡一样，封包经过网桥/路由处理后，从它来自的地方发回去。</p>
<div class="blog_h1"><span class="graybg">以太网监听</span></div>
<p>当一张网卡被设置为混杂（Promicuous）模式，它就会接收网络上所有的数据帧，从而可以实现监听。</p>
<div class="blog_h2"><span class="graybg">混杂模式网卡检测</span></div>
<p>有多种方式可以检测处于混杂模式的网卡，假设现在怀疑硬件地址为38-83-45-09-0A-60、IP地址为192.168.1.5的网卡处于混杂模式，可以：</p>
<ol>
<li>伪造ICMP报文：ECHO_REQUEST，即ping命令使用的报文，将其IP首部的目的地址设置为192.168.1.5，以太网帧首部的目的地址设置为虚假的地址00-00-00-00-00-00。如果192.168.1.5是正常的网卡，它将会检测硬件地址，发现与自己的地址不同，因而忽略此帧；反之，如果192.168.1.5处于混杂模式，则它不会做硬件地址的比较，报文直接交由上层处理，导致回复ICMP报文</li>
<li>以非广播方式在局域网内发送ARP请求，如果某个网卡回复之，则其可能处于混杂模式</li>
</ol>
<div class="blog_h2"><span class="graybg">监听防范</span></div>
<p>首先要保证局域网的安全，因此这种监听只能出现在局域网内，因此必须有以太局域网主机被攻破。</p>
<p>另外数据加密也是比较好的手段，如果监听到的报文是加密的，没有什么用。</p>
<p>使用交换机也是一种常见的方式，交换机工作在数据链路层，与工作在物理层的HUB不同。交换机通常会维护一个ARP数据库，记录每个交换机端口绑定的MAC地址，当报文到达交换机时，它<span style="background-color: #c0c0c0;">只会将其从匹配的端口发送出去</span>。交换机只在两种情况下广播报文：</p>
<ol>
<li>以太网帧的目的地址在本地ARP数据库中不存在</li>
<li>报文本身就是广播的</li>
</ol>
<p>交换机可以在很大程度上解决监听问题，但是不能防止ARP欺骗。</p>
<div class="blog_h1"><span class="graybg">隧道</span></div>
<p>所谓隧道（Tunneling），是指<span style="background-color: #c0c0c0;">使用一种网络协议，并将另外一个网络协议封装在其载荷（或称负载，Payload）部分</span>的技术。前者叫做隧道协议，后者叫做负载协议。使用隧道技术的目的一般是：</p>
<ol>
<li>在不兼容的网络上传送网络协议数据报</li>
<li>在不安全的网络上提供安全性保证</li>
<li>规避防火墙，被防火墙阻挡的协议可以封装在不被阻挡的协议中，例如HTTP</li>
</ol>
<p>通常，隧道协议往往位于负载协议的高层（例如PPTP，即点对点隧道协议，可以通过TCP来封装PPP，前者工作在传输层，而PPP是链路层协议），或者与负载协议处于同一层。</p>
<p>Linux L3的隧道技术，主要基于TUN设备实现。</p>
<p>常见的隧道协议包括：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 100px; text-align: center;">协议 </td>
<td style="text-align: center;"> 说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>IPsec</td>
<td>即互联网安全协定（Internet Protocol Security）。该协议通过对IP协议的分组进行加密、认证，来保护TCP/IP协议族的安全性</td>
</tr>
<tr>
<td>GRE</td>
<td>即通用路由封装（Generic Routing Encapsulation）。可以在虚拟的点对点链路中封装多种网络层协议</td>
</tr>
<tr>
<td>IP in IP</td>
<td>一种IP隧道协定，可以将IP封包封装进另外一个IP封包中</td>
</tr>
<tr>
<td>L2TP</td>
<td>
<p>即第二层隧道协议（Layer Two Tunneling Protocol）。是一种VPN的实现方式。L2TP本身不提供加密和验证功能，需要和某种安全协议搭配使用（例如IPsec）</p>
<p>L2TP封包使用UDP来传送。每个高层协议，例如PPP，都可以在L2TP隧道中建立一个L2TP会话，一个隧道里面可以包含多个会话</p>
</td>
</tr>
<tr>
<td>PPTP</td>
<td>即点对点隧道协议（Point to Point Tunneling Protocol）。是一种VPN的实现方式。PPTP使用TCP来创建控制通道，来发送控制命令，并使用GRE通道来封装PPP协议数据报，以发送数据。该协议的加密方式容易被破解</td>
</tr>
<tr>
<td>PPPoE</td>
<td>即基于以太网的点对点隧道协议（Point-to-Point Protocol over Ethernet）。是将PPP协议封装在以太网中的一种隧道协议</td>
</tr>
<tr>
<td>PPPoA</td>
<td>即基于异步传输模式的点对点隧道协议（Point-to-Point Protocol over ATM）</td>
</tr>
<tr>
<td>SSH</td>
<td>即安全外壳协议（Secure Shell Protocol）。是一项跨越多个层次的协议，为Shell提供安全的传输和使用环境。SSH提供了数据压缩的功能。</td>
</tr>
<tr>
<td>SOCKS</td>
<td>即套接字安全协议（Socket Secure）。用于通过一个代理服务器在客户端和服务器之间路由数据包</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">网关</span></div>
<p>在传统TCP/IP术语中，网络设备只分为网关、主机（亦称终端系统）两种，<span style="background-color: #c0c0c0;">前者能够在网络间传递数据报</span>。网关的数据处理一般只到达第三层（IP层）。网关与路由器在传统TCP/IP术语中往往是一个概念。</p>
<p>现代网络术语中，网关与路由器不同，<span style="background-color: #c0c0c0;">网关能够在不同协议之间移动数据</span>；而路由器则是在不同网络之间移动数据。例如，语音网关可以连接公共交换电话网（PSTN）与以太网，实现网络电话。</p>
<div class="blog_h1"><span class="graybg">多播</span></div>
<p>多播（MultiCast）也称为组播，包括链路层、网络层、应用层等多播技术。多播通常指IP多播（网络层）。</p>
<p>由于在IPv4网络中，<span style="background-color: #c0c0c0;">多播包可能不被路由</span>，因此你只能在局域网中使用多播技术。DVMRP、MOSPF 之类的技术解决此问题，但是你需要能够对客户端 - 服务器之间所有路由器进行配置，或者创建隧道。IPv6网络是强制支持IP多播的。</p>
<div class="blog_h1"><span class="graybg">网络虚拟化</span></div>
<p>网络虚拟化是一个很大的技术方向，可以包括硬件、软件或者相结合的实现方式。</p>
<p>Linux内核支持的网络虚拟化，主要包括两个方面：</p>
<ol>
<li>软交换机：Linux Bridge, OpenVSwitch</li>
<li>虚拟网络适配器：tun、tap、veth等</li>
</ol>
<div class="blog_h2"><span class="graybg">VLAN</span></div>
<p>VLAN（Virtual Local Area Network）是广泛应用的<span style="background-color: #c0c0c0;">网络虚拟化（在一套物理网络设备上虚拟出多个二层网络）</span>技术，它直接在Ethernet帧的头部加上<span style="background-color: #c0c0c0;">4个字节的VLAN Tag</span>，此Tag用于标识不同的二层网络。VLAN已经在大部分的网络设备和操作系统中得到了支持，它处理起来也比较简单，在读取Ethernet数据的时候，只需要根据EtherType相应的偏移4个字节就行。利用VLAN可以：</p>
<ol>
<li>将主机进行分组，即使主机不是连接到同一交换机上</li>
<li>可以对连接到相同交换机/网桥上的主机/客户机进行隔离，划分到不同子网。这样一个交换机就可以表现的像是多个独立交换机一样</li>
</ol>
<p>VLAN ID的范围是1 - 4095，对于规模较大的IT组织，需要谨慎规划子网，否则VLAN ID可能不够用。</p>
<div class="blog_h3"><span class="graybg">理解VLAN</span></div>
<p>假设你的IT环境下有一个物理交换机，连接着一系列物理主机，现在需要划分为三个独立网络。管理员配置交换机端口，将它们的VLAN ID分别设置为10 11 12，不同网络的主机分别连接到对应的端口。这种仅仅配置针对单个VLAND的端口，叫做access port。</p>
<p>随着规模的增大，一个交换机不够用了，管理员又引入一个交换机。这样，两个交换机必须都有一个端口，它允许任何以太网帧通过，而不管帧的VLAN ID，否则就无法联通了。这种不过滤VLAN ID的端口叫做trunk port。</p>
<p>在<span style="background-color: #c0c0c0;">虚拟化环境下，交换机的所有端口都会配置为trunk port。这样，每个端口（也就是主机）上都可以运行不同虚拟网络的VM</span>。</p>
<div class="blog_h2"><span class="graybg">VXLAN</span></div>
<p>VXLAN（Virtual eXtensible Local Area Network）是目前最热门的网络虚拟化/Overlay/虚拟隧道技术之一，VXLAN协议将<span style="background-color: #c0c0c0;">Ethernet帧封装在底层网络（Underlay）的三层报文（UDP）内，再加上8个字节的VXLAN header</span>，用来标识不同的二层网络：</p>
<p><a href="/wp-content/uploads/2019/04/106_net_005_vxlan.png"><img style="width: 100%;" src="/wp-content/uploads/2019/04/106_net_005_vxlan.png" alt="" /></a></p>
<p><span style="color: #1a1a1a;">VXLAN数据是经过<span style="background-color: #c0c0c0;">VTEP（VXLAN Tunnel EndPoint）封装和解封装</span>的，相应的VXLAN数据的外层UDP封包的IP（源、目的）地址就是VTEP（本机、对方）的IP地址，端口就是VTEP设备的端口（默认4789）。最外层的MAC地址用来实现VTEP之间的数据传递。每个物理节点上的所有虚拟机可以共享VTEP。VTEP可以由软件（例如OVS）或硬件实现。</span></p>
<p>VXLAN因为提出的较晚，在设备上的支持率不如VLAN，而且，VXLAN数据的封装解封装，要比VLAN复杂的多。但是它具有以下优势：</p>
<ol>
<li>VLAN ID数量限制：8个字节的VXLAN Header。其中的24bit用来标识（VNI，VXLAN Network Identifier）不同的二层网络，这样总共可以<span style="background-color: #c0c0c0;">标识1600多万个不同的二层网络</span></li>
<li>TOR交换机MAC地址表限制：在网络虚拟化之前，TOR交换机的一个端口连接一个物理主机对应一个MAC地址，但现在交换机的一个端口虽然还是连接一个物理主机但是可能进而连接<span style="background-color: #c0c0c0;">几十个甚至上百个虚拟机和相应数量的MAC地址</span>，MAC地址表记录在交换机的内存中，而交换机的内存是有限的。如果使用VXLAN，虚拟机的以太网帧被VTEP封装在UDP里面，<span style="background-color: #c0c0c0;">一个VTEP可以被一个物理主机上的所有虚拟机共用</span></li>
<li>灵活的虚机部署：采用VLAN网络的虚拟环境，不存在Overlay网络。虚拟机的网络数据，被打上VLAN Tag之后，直接在物理网络上传输，与物理网络上的VLAN是融合在一起的。这种实现机制的好处是：虚拟机能直接访问到物理网络的设备。而坏处是，无法突破物理网络的限制，通常<span style="background-color: #c0c0c0;">不同的VLAN网络，会被分配不同的IP地址段</span>，通过路由器或者其他的三层设备连接在一起，不同VLAN的虚拟机不能方便的进行L2通信。使用VXLAN后，由于基于UDP进行封装，因此可以在L2或L3网络上构建L2网络，这是一个独立于物理网络的Overlay network</li>
</ol>
<p>和普通隧道（例如ipip）不同，VXLAN是一对多的，而不是1：1的隧道协议。VXLAN设备可以像网桥那样，动态添加对端IP地址。严格来说，VXLAN模型中并没有隧道的物理实体。</p>
<p>配置和管理VXLAN，需要内核版本3.7以上的iproute2包，ip命令即此包的用户空间工具。</p>
<div class="blog_h3"><span class="graybg">工作原理</span></div>
<p><span style="color: #1a1a1a;">VXLAN报文的转发过程如下：</span></p>
<ol>
<li>原始报文经过源主机上的VTEP设备，被Linux内核添加VXLAN包头、外层UDP头，发送出去</li>
<li>对端VTEP设备接收到VXLAN报文，移除UDP头，然后根据VXLAN头决定发给哪个虚拟机</li>
</ol>
<p>在通信之前，需要回答以下三个问题：</p>
<ol>
<li>哪些VTEP需要加到同一VNI组</li>
<li>源虚拟机如何知道目的虚拟机的MAC地址</li>
<li>如何知道目的虚拟机在哪个节点</li>
</ol>
<p>问题1，通常由管理员进行配置。问题2/3本质上是一个问题 —— VXLAN通信双方如何感知彼此：</p>
<ol>
<li>内层报文，双方IP地址可以认为是已知</li>
<li>内层报文，对方MAC地址，需要实现一种ARP机制</li>
<li>VXLAN头，只需要知道<span style="background-color: #c0c0c0;">VNI，通常直接配置在VTEP上</span> —— 要么提前规划，要么根据内层报文自动生成</li>
<li>UDP头，需要知道源、目的地址/端口。源地址端口自动获取，目的端口一般默认4789，目的IP地址亦即目标主机的VTEP地址，可以通过两种方式得到：
<ol>
<li>组播：同一个VXLAN网络的VTEP加入到同一组播网络中，通过组播同步信息</li>
<li>控制中心：集中式保存需要的信息</li>
</ol>
</li>
</ol>
<div class="blog_h3"><span class="graybg">点对点VXLAN</span></div>
<p>这种配置下，两台机器构成一个VXLAN网络，每个机器上配置一个VTEP，VTEP之间通过它们的IP地址进行通信。</p>
<p>首先，需要添加VXLAN类型的接口：</p>
<pre class="crayon-plain-tag"># 接口类型  VNI  对端端口
ip link add vxlan0 type vxlan id 1 dstport 4789 
  # 对端Underlay地址  本地Underlay地址   物理网络接口
  remote 192.168.1.3 local 192.168.1.2 dev ens33</pre>
<p>为VTEP分配IP地址并启用之：</p>
<pre class="crayon-plain-tag"># 自动添加路由 172.17.1.0      0.0.0.0         255.255.255.0   U     0      0        0 vxlan0
ip addr add 172.17.1.2/24 dev vxlan0
ip link set vxlan0 up</pre>
<p>此时你可以看到如下FDB（Forwarding information base，也叫MAC表）表项： </p>
<pre class="crayon-plain-tag">bridge fdb
00:00:00:00:00:00 dev vxlan0 dst 192.168.1.3 via ens33 self permanent</pre>
<p>该项的含义是， 默认的VTEP对端地址为192.168.1.3。原始报文经过vxlan0接口后，内核会为其添加VXLAN头部，外部封装的UDP包的目的IP地址会被设置为192.168.1.3。</p>
<p>在对端，你需要进行对应的配置，VNI必须保持一致。 </p>
<div class="blog_h3"><span class="graybg">多播VXLAN</span></div>
<p>该模式需要Underlay网络支持组播。首先创建VTEP：</p>
<pre class="crayon-plain-tag"># VNI                    # 使用ens33上的多播组239.1.1.1进行信息交换
ip link add vxlan0 type vxlan id 1  local 192.168.1.2  group 239.1.1.1 dev ens33 dstport 4789</pre>
<p>和点对点模式类似的，你需要为VTEP配置IP地址并且启用之。此时，你可以看到如下FDB表项：</p>
<pre class="crayon-plain-tag">00:00:00:00:00:00 dev vxlan0 dst 239.1.1.1 via ens33 self permanent</pre>
<p>和点对点模式不同的是，dst字段的值设置为组播地址，而非对端Underlay IP地址。这个条目会导致默认情况下UDP头的目的地址被设置为组播地址239.1.1.1。</p>
<p>在所有参与到VXLAN的节点上，你需要进行对应的配置，VNI必须保持一致。</p>
<p>该模式下VTEP之间通信过程如下：</p>
<ol>
<li>主机1通过vxlan0发起ping 172.17.1.3报文到主机2的vxlan0</li>
<li>主机1内核发现目的地址和源地址在同一二层网络中，需要获取对方MAC地址，但是本地没有缓存，因此发送ARP查询请求</li>
<li>ARP报文源MAC地址为主机1的vxlan0的MAC地址，目的地址为广播地址255.255.255.255</li>
<li>内核将ARP报文封装到UDP中，设置VXLAN头。由于VTEP配置了多播组，同时<span style="background-color: #c0c0c0;">不知道目标VTEP在哪台主机上，因此会从239.1.1.1发送组播</span>报文</li>
<li>多播组中所有主机都会收到UDP报文，并根据VXLAN头发送给对应的VTEP</li>
<li>VTEP去掉VXLAN头，得到ARP请求报文，并<span style="background-color: #c0c0c0;">将源VTEP的MAC地址+源主机IP地址信息记录到FDB表</span>中</li>
<li>主机2发现ARP请求是针对自己的，因此生成ARP应答，并且单播（因为已经知道主机1的IP-MAC对应关系）给主机1的VTEP</li>
<li>ARP应答通过底层网络发送给主机1，解析后发送给vxlan0，VTEP解析ARP报文并更新VTEP缓存，同时根据报<span style="background-color: #c0c0c0;">文学习得到目的VTEP所在的主机地址，添加到自己的FDB表</span></li>
</ol>
<div class="blog_h3"><span class="graybg">VXLAN+桥接</span></div>
<p>实际情况下，每个主机都可能有几十甚至上百个虚拟机/容器，需要加入到同一个VLAN中，而<span style="background-color: #c0c0c0;">每个VLAN在一台主机上仅仅有一个VTEP</span>。</p>
<p>一个简单的解决办法是桥接。对于容器场景，可以用VETH Pair将容器连接到网桥，然后将VTEP也连接到网桥。VTEP通过物理网络相互联系。</p>
<div class="blog_h3"><span class="graybg">分布式控制中心</span></div>
<p>由于某些网络设备不支持多播，而且多播导致的不必要流量，<span style="background-color: #c0c0c0;">在生产环境下VXLAN多播模式用的很少</span>。</p>
<p>生产环境主要使用分布式控制中心架构，<span style="background-color: #c0c0c0;">在每个VTEP节点部署Agent，Agent联系控制中心，获取通信所需要的信息（FDB+ARP）</span>。</p>
<div class="blog_h2"><span class="graybg">Linux Bridge</span></div>
<p>L2设备，一种虚拟的网桥（Bridge），它是：</p>
<ol>
<li>一种<span style="background-color: #c0c0c0;">网络设备，因此可以配置IP地址、MAC地址</span></li>
<li>虚拟交换机，具有和物理交换机类似的能力</li>
</ol>
<p>普通网络设备只有两个Port，一端的数据会从另外一端出去。例如物理网卡，会从外部接收数据，发往内核协议栈，或者从内核协议栈接收数据发送到外部。</p>
<p>Bridge则不同，它具有多个Port，数据可以从任何端口进来，至于从哪个端口出去，原理类似于物理交换机 —— 要看MAC地址。</p>
<p>Bridge被广泛用于KVM/QEMU、容器技术。</p>
<p>下面的命令创建并启用一个网桥：</p>
<pre class="crayon-plain-tag">ip link add name br0 type bridge
ip link set br0 up

# 或者使用brctl命令
brctl addbr br0</pre>
<p>刚创建的网桥，一端连接着网络协议栈，其余什么都没有连接，因此没有任何功能。</p>
<p>下面我们创建veth对：</p>
<pre class="crayon-plain-tag">ip link add veth0 type veth peer name veth1
ip addr add 1.2.3.101/24 dev veth0
ip addr add 1.2.3.102/24 dev veth1
ip link set veth0 up
ip link set veth1 up


# 接受源自本地IP的ARP包
echo 1 &gt; /proc/sys/net/ipv4/conf/all/accept_local 
# 不进行源地址校验
echo 0 &gt; /proc/sys/net/ipv4/conf/all/rp_filter 
echo 0 &gt; /proc/sys/net/ipv4/conf/veth0/rp_filter 
echo 0 &gt; /proc/sys/net/ipv4/conf/veth1/rp_filter


# 现在测试是可以通的
#         由于目的地址是本地地址，默认会走lo，因此用 -I 强制指定PING请求的源地址/源接口
ping -c 1 -I veth0 1.2.3.102</pre>
<p>并且将其一端连接到网桥： </p>
<pre class="crayon-plain-tag">ip link set dev veth0 master br0
# 或者使用brctl命令
brctl addif br0 veth0</pre>
<p>使用下面的命令查看网桥上连接了哪些设备：</p>
<pre class="crayon-plain-tag">bridge link
# 9: veth0 state UP : &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 master br0 state forwarding priority 32 cost 2 

# 或者使用brctl命令
brctl show
# bridge name	bridge id		   STP enabled	interfaces
# br0		    8000.4a4c11893317	no		     veth0
# docker0		8000.02428b446c5b	no		
# virbr0		8000.deadbeef0000	no		     virbr0-nic</pre>
<p>现在：</p>
<ol>
<li>br0和veth0连接起来，<span style="background-color: #c0c0c0;">双向通道</span></li>
<li>协议栈和veth0是单向通道，<span style="background-color: #c0c0c0;">协议栈可以发数据给veth0</span>，<span style="background-color: #c0c0c0;">veth0从外部接受的数据不能发送给协议栈</span></li>
<li>br0的MAC地址变成veth0的<span style="background-color: #c0c0c0;">MAC地址 </span></li>
</ol>
<p>相当于<span style="background-color: #c0c0c0;">br0在veth0和协议栈之间做了拦截</span> —— 本来veth0发给协议栈的数据，全部转发给br0处理了。 </p>
<p>而且，veth0和veth1之间也不通了：</p>
<pre class="crayon-plain-tag">ping -c 1 -I veth0 1.2.3.102
# From 1.2.3.101 icmp_seq=1 Destination Host Unreachable</pre>
<p>原因是什么呢？下面在veth1、veth0、br0上分别抓包：</p>
<pre class="crayon-plain-tag">tcpdump -n -i veth0
# tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
# listening on veth0, link-type EN10MB (Ethernet), capture size 65535 bytes
# 22:37:16.291125 ARP, Request who-has 1.2.3.102 tell 1.2.3.101, length 28
# 22:37:16.291165 ARP, Reply 1.2.3.102 is-at ce:97:9b:c2:d1:ee, length 28

tcpdump -n -i veth1
# tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
# listening on veth1, link-type EN10MB (Ethernet), capture size 65535 bytes
# 22:37:16.291140 ARP, Request who-has 1.2.3.102 tell 1.2.3.101, length 28
# 22:37:16.291163 ARP, Reply 1.2.3.102 is-at ce:97:9b:c2:d1:ee, length 28


tcpdump -n -i br0
# tcpdump: WARNING: br0: no IPv4 address assigned
# tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
# listening on br0, link-type EN10MB (Ethernet), capture size 65535 bytes
# 22:37:16.291165 ARP, Reply 1.2.3.102 is-at ce:97:9b:c2:d1:ee, length 28
# 22:37:17.289325 ARP, Reply 1.2.3.102 is-at ce:97:9b:c2:d1:ee, length 28</pre>
<p>可以看到，veth0、veth1正常进行了ARP<span style="background-color: #ff9900;">请求</span>、<span style="background-color: #99cc00;">响应</span>，br0则仅仅监控到<span style="background-color: #cc99ff;">响应</span>。但是veth0却不能将ARP应答发给协议栈，因此无法通信。 具体分析：</p>
<ol>
<li>Ping命令发起ICMP请求，设置IP封包的源接口为veth0</li>
<li>内核接受到上述封包，发送到目的地址1.2.3.102之前，发现没有ARP缓存，无法生成L2帧</li>
<li>内核发送ARP请求，从veth0<span style="background-color: #ff9900;">发出</span></li>
<li>veth1接口接收到ARP请求，并正常<span style="background-color: #99cc00;">响应</span>之</li>
<li>veth0接收到<span style="background-color: #ff9900;">响应</span></li>
<li>veth0将响应<span style="background-color: #cc99ff;">通过交换机br0</span>发送，而非内核</li>
<li>内核没有接收到ARP响应，L2帧发送失败，Ping失败</li>
</ol>
<p>下面，把veth0的IP地址转让给br0：</p>
<pre class="crayon-plain-tag">ip addr del 1.2.3.101/24 dev veth0
ip addr add 1.2.3.101/24 dev br0</pre>
<p>现在，veth0没有IP地址了，因此协议栈在路由的时候，不会将数据包发送给veth0。这意味着，协议栈到veth0的单向通道也断了，<span style="background-color: #c0c0c0;">veth0单纯变成了连接br0和veth1的网线</span>。 </p>
<p>从br0 Ping veth1现在可以收到ICMP应答报文。报文流转路线：内核 - br0 - veth0 - veth1 - 内核 - veth1 - veth0 - br0 - 内核，但是却还不能联系到外部网络。另外，veth0仍然无法Ping通veth1，原因还是无法将ARP应答返回给内核。 </p>
<p>现在，再把物理网卡添加到网桥：</p>
<pre class="crayon-plain-tag">ip link set dev eth0 master br0</pre>
<p>现在eth0也和veth0一样，接受到的封包直接转发给br0，而不发给协议栈，自己变为一根网线。eth0、veth0无法Ping通网关，而br0则通过eth0这根网线连接到网关，veth1也可以通过br0 ping通网关。</p>
<p>这时，eth0已经成为一根网线，它上面配置IP没有意义，反而会影响路由表：</p>
<pre class="crayon-plain-tag">ip addr del 192.169.1.105/24 dev eth0</pre>
<p>eth0被删除后默认路由消失，重新添加默认路由后，可以从veth1连接外部网络。</p>
<div class="blog_h3"><span class="graybg">网桥和虚拟化</span></div>
<p>对于虚拟机来说，通常<span style="background-color: #c0c0c0;">通过tun/tap等网络设备，将虚拟机内的网卡连接到网桥</span>，再由网桥转发给出去。</p>
<p>对于容器来说，每个则通常使用veth对。容器的网关被桥接到br0，且网关设置为br0。容器IP封包发到br0后，进入宿主机协议栈。宿主机需要配置IP转发功能，可以把容器IP封包转发出去。由于容器、宿主机物理网络通常不是一个网段，因此转发出去之前通常需要NAT。</p>
<div class="blog_h3"><span class="graybg">网桥和混杂模式</span></div>
<p>混杂模式下，网卡会把所有接收到的流量交给协议栈处理，不管目的MAC地址是否匹配。</p>
<p>使用下面的命令，使网卡进入混杂模式：</p>
<pre class="crayon-plain-tag">ifconfig eth0 promisc</pre>
<p>使用下面的命令则退出混杂模式：</p>
<pre class="crayon-plain-tag">ifconfig eth0 -promisc</pre>
<p>需要注意的时，<span style="background-color: #c0c0c0;">加入到网桥后，设备自动进入混杂模式</span>。退出网桥后则自动退出混杂模式。另外加入网桥期间，设备无法退出混杂模式。  </p>
<div class="blog_h2"><span class="graybg">Bridge+VLAN</span></div>
<p>通过网桥，可以协助处理虚拟化/命名空间下的基于多VLAN的网络虚拟化。</p>
<div class="blog_h3"><span class="graybg">VLAN filtering</span></div>
<p>VLAN filtering是在3.8引入的特性，可以简化基于VLAN的网络虚拟化的配置复杂度。利用VLAN filtering，管理员不需要创建大量的VLAN、Bridge。<span style="background-color: #c0c0c0;">使用一个网桥，你就能够控制所有VLAN</span>。</p>
<p>这里我们看一个在虚拟化中广泛使用的网络拓扑：不同子网的虚拟机通过网桥，通过bond做负载均衡，最后连接到物理网卡。</p>
<p>没有VLAN filtering之前，拓扑图如下：</p>
<p><a href="https://cdn.gmem.cc/wp-content/uploads/2020/06/bridge_original.png"><img class="size-full wp-image-32969 aligncenter" src="https://cdn.gmem.cc/wp-content/uploads/2020/06/bridge_original.png" alt="bridge_original" width="538" height="370" /></a></p>
<p>创建此拓扑的步骤：</p>
<ol>
<li>创建bond设备，作为两张物理网卡的master：<br />
<pre class="crayon-plain-tag">ip link add bond0 type bond
ip link set bond0 type bond miimon 100 mode balance-alb

ip link set eth0 down
ip link set eth0 master bond0

ip link set eth1 down
ip link set eth1 master bond0

ip link set bond0 up </pre>
</li>
<li>在bond0上创建VLAN子接口：<br />
<pre class="crayon-plain-tag">ip link add link bond0 name bond0.2 type vlan id 2
ip link set bond0.2 up

ip link add link bond0 name bond0.3 type vlan id 3
ip link set bond0.3 up</pre>
</li>
<li>
<p>将VLAN子接口分别连接到一个网桥：</p>
<pre class="crayon-plain-tag">ip link add br0 type bridge
ip link set bond0.2 master br0
ip link set br0 up

ip link add br1 type bridge
ip link set bond0.3 master br1
ip link set br1 up </pre>
</li>
<li>
<p>将代表虚拟机的TAP设备也连接到网桥：
<pre class="crayon-plain-tag">ip link set guest_1_tap_0 master br0
ip link set guest_2_tap_0 master br0

ip link set guest_2_tap_1 master br1
ip link set guest_3_tap_0 master br1 </pre>
</li>
</ol>
<p>如果使用了VLAN filtering，就不需要创建bond的VLAN子接口，也仅需要一个网桥：
<p><a href="https://cdn.gmem.cc/wp-content/uploads/2020/06/bridge_current.png"><img class="size-full wp-image-32971 aligncenter" src="https://cdn.gmem.cc/wp-content/uploads/2020/06/bridge_current.png" alt="bridge_current" width="541" height="374" /></a></p>
<p>创建此拓扑的步骤：</p>
<ol>
<li>
<p> 创建bond设备：</p>
<pre class="crayon-plain-tag">ip link add bond0 type bond
ip link set bond0 type bond miimon 100 mode balance-alb
ip link set eth0 down
ip link set eth0 master bond0
ip link set eth1 down
ip link set eth1 master bond0
ip link set bond0 up</pre>
</li>
<li>
<p>创建网桥，启用VLAN Filtering： 
<pre class="crayon-plain-tag">ip link add br0 type bridge
ip link set br0 up
ip link set br0 type bridge vlan_filtering 1

ip link set bond0 master br0</pre>
</li>
<li>
<p> 将TAP连接到网桥：
<pre class="crayon-plain-tag">ip link set guest_1_tap_0 master br0
ip link set guest_2_tap_0 master br0

ip link set guest_2_tap_1 master br0
ip link set guest_3_tap_0 master br0</pre>
</li>
<li>为这些TAP接口、Bond接口设置VLAN filter：<br />
<pre class="crayon-plain-tag">bridge vlan add dev guest_1_tap_0 vid 2 pvid untagged master
bridge vlan add dev guest_2_tap_0 vid 2 pvid untagged master

bridge vlan add dev guest_2_tap_1 vid 3 pvid untagged master
bridge vlan add dev guest_3_tap_0 vid 3 pvid untagged master

bridge vlan add dev bond0 vid 2 master
bridge vlan add dev bond0 vid 3 master </pre>
</li>
</ol>
<div class="blog_h2"><span class="graybg">TUN/TAP</span></div>
<p>简单的理解：TUN/TAP就是针对用户空间程序而不是物理媒体的网络接口，这种网络接口的<span style="background-color: #c0c0c0;">一端连接着网络协议栈，另一端连接着用户空间程序</span>。不同于普通靠硬件板卡实现的设备，TUN/TAP全部用软件实现，并向运行于操作系统上的<span style="background-color: #c0c0c0;">软件提供与硬件的网络设备完全相同的功能</span>。
<p>TUN/TAP为用户空间程序提供数据包的收发功能，它可以呈现为简单的点对点设备或者以太网设备，与物理设备不同的是，它<span style="background-color: #c0c0c0;">从用户空间程序</span>接收数据包<span style="background-color: #c0c0c0;">而不是物理媒体</span>；同样的，它将数据包发送给用户空间程序，而不是物理媒体。操作系统通过TUN/TAP设备发送的数据包，被分发给关联到这些设备的用户空间程序中。用户空间程序也可以将数据包发给TUN/TAP设备，后者把数据包注入到操作系统的网络栈之中，模拟为来自外部的数据。</p>
<p>要使用TUN驱动，必须打开/dev/net/tun文件，并发起相应的ioctl()调用，来<span style="background-color: #c0c0c0;">向内核注册一个网络设备</span>，这些网络设备可以通过ifconfig看到，名字为tunXXX或者tapXXX（依据调用时的选项）。当关闭文件描述符时，这些网络设备连同相应的路由会消失。用户程序写入/dev/net/tun文件的数据，会写到内核网络协议栈；用户程序从/dev/net/tun文件读取时，则拿到内核发送给tun设备的IP封包。</p>
<p>要使用TAP驱动，基本和TUN完全相同，区别有几点：</p>
<ol>
<li>tun设备的/dev/tunX工作在L3，可以通过IP转发和物理网卡连通</li>
<li>tap设备的/dev/tapX工作在L2，可以和物理网卡进行桥接</li>
</ol>
<div class="blog_h3"><span class="graybg">TUN</span></div>
<p><span style="background-color: #c0c0c0;">TUN模拟了网络层（点对点）设备</span>，操作第三层数据包比如IP数据封包。普通的网卡通过网线收发数据包，但是 TUN 设备通过一个文件收发数据包。所有对这个文件的<span style="background-color: #c0c0c0;">写操作会通过 TUN 设备转换成一个数据包送给内核</span>；当内核发送一个包给 TUN 设备时，通过<span style="background-color: #c0c0c0;">读这个文件可以拿到数据包的内容</span>：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2010/05/tun.png"><img class="aligncenter size-full wp-image-26513" src="https://blog.gmem.cc/wp-content/uploads/2010/05/tun.png" alt="tun" width="590" height="266" /></a></p>
<p>TUN 设备的 /dev/tunX 文件收发的是 IP 层数据包，只能工作在 IP 层，<span style="background-color: #c0c0c0;">无法与物理网卡做 bridge</span>，但是可以通过三层交换（如 ip_forward）与物理网卡连通。</p>
<p>TUN的含义即隧道，实际上TUN的确和隧道技术有关系。tun/tap设备将协议栈的部分数据转发给用户空间程序，使之有处理数据包的机会，例如加密、压缩。VPN是最常见的应用场景。下面是基于TUN设备实现VPN的过程：</p>
<ol>
<li>本地应用构造一个数据包，发送给tun0所在网段（虚拟网络）的IP地址192.168.1.3</li>
<li>数据包到达内核协议栈后，根据目的IP地址判断需要从tun0发出</li>
<li>tun0的一端连接着内核协议栈，另一端则连接着用户空间应用 —— VPN程序</li>
<li>VPN程序对数据包进行再次封装，源地址设置为eth0，目的地址设置为eth0所在网段的VPN对端物理机IP地址</li>
<li>封装后数据包发送到协议栈，走eth0发送到VPN对端</li>
</ol>
<p>Linux的L3隧道技术都是基于TUN设备，包括ipip、GRE、sit、ISATAP、VTI等。</p>
<div class="blog_h3"><span class="graybg">TAP </span></div>
<p>TAP 等同于一个以太网设备，它操作第二层数据包如以太网数据帧。TAP的工作方式和TUN完全相同。</p>
<p>TAP 设备的 /dev/tapX 文件收发的是 MAC 层数据包，拥有 MAC 层功能，<span style="background-color: #c0c0c0;">可以与物理网卡做 bridge</span>，支持 MAC 层广播。</p>
<p>Libvirt创建的VM，会在宿主机上对应一个vnet*接口，这个接口桥接到网桥设备virbr*：</p>
<pre class="crayon-plain-tag">virbr0		8000.100000000000	no		virbr0-nic
							vnet0
							vnet1</pre>
<p>这些vnet接口，就是TAP设备，它关联到一个进程，即运行qemu-kvm模拟器的那个进程。qemu-kvm写入到此接口的数据，在宿主机看来，就好像从vnet这个网卡接收到的封包似的。反之亦然，qemu-kvm从此接口读取的数据，会写入到虚拟机的eth0，在虚拟机看来，就好像是从网卡接收到封包似的。</p>
<div class="blog_h2"><span class="graybg">VETH</span></div>
<p>也是虚拟的以太网设备，可以作为<span style="background-color: #c0c0c0;">网络命名空间的隧道，让两个网络命名空间可以通信</span>。VETH也能够作为独立网络设备使用。从效果上说，VETH就像是两个网络接口通过RJ45网线连接在一起似的。</p>
<p>VETH设备总是成对形式创建的：</p>
<pre class="crayon-plain-tag"># 创建一对veth，相互连接起来
ip link add veth0 type veth peer name veth1

# 查看veth对
ip link list
# 13: veth1@veth0: &lt;BROADCAST,MULTICAST,M-DOWN&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
#     link/ether 72:a4:a7:eb:e8:db brd ff:ff:ff:ff:ff:ff
# 14: veth0@veth1: &lt;BROADCAST,MULTICAST,M-DOWN&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
#     link/ether 46:ba:9b:5e:3a:27 brd ff:ff:ff:ff:ff:ff</pre>
<p><span style="background-color: #c0c0c0;">从一端发送的数据，会立即在另一端接收到</span>。任何一端Down掉或删除，则veth对的状态被破坏。</p>
<p>可以把veth放到其他网络命名空间：</p>
<pre class="crayon-plain-tag"># 创建一个新的网络命名空间
ip netns add test

# 移动veth1
ip link set veth1 netns test</pre>
<p>移动了以后，从当前命名空间看到的，Peer的名字会变化：</p>
<pre class="crayon-plain-tag"># 默认命名空间看veth1变为if13，表示Peer是test命名空间序号为13的网络接口
ip link show veth0
# 14: veth0@if13: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
#     link/ether 46:ba:9b:5e:3a:27 brd ff:ff:ff:ff:ff:ff link-netnsid 3

# test命名空间看veth0变为if14，和上面的序号14匹配
ip netns exec test ip link show veth1
# 13: veth1@if14: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
#    link/ether 72:a4:a7:eb:e8:db brd ff:ff:ff:ff:ff:ff link-netnsid 0</pre>
<p>在宿主机上，通过<span style="background-color: #c0c0c0;">设置路由</span>，可以让其他网络命名空间的流量通过veth对出站。 </p>
<div class="blog_h3"><span class="graybg">和TAP区别</span></div>
<p>VETH和TAP都是用来传递L2以太网帧的，<span style="background-color: #c0c0c0;">TAP/TUN常用于用于加密、VPN、隧道、虚拟机</span>，VETH常用于不同命名空间之间进行数据穿越。这有历史问题在里面。</p>
<p>下面是OpenStack创建了新的虚拟机vm0后的网络架构图：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2006/09/Openstack-VM-details.png"><img class="aligncenter size-full wp-image-27119" src="https://blog.gmem.cc/wp-content/uploads/2006/09/Openstack-VM-details.png" alt="openstack-vm-details" width="700" height="471" /></a></p>
<p>关于虚拟机vm0的网络模型，说明如下：</p>
<ol>
<li>虚拟机中的eth0，连接到vnet0，vnet0是Linux网桥<span style="background-color: #c0c0c0;">qbrXXX的TAP接口</span></li>
<li>Linux网桥qbrXXX连接到OVS网桥br-int，通过一对VETH qvbXXX - qv0XXX</li>
</ol>
<p>可以看到，通过TAP将虚拟机连接到第一个网桥qbrXXX，而VETH对则将第一个网桥连接到第二个。为什么需要两根RJ45网线呢？用其中一个不行么？</p>
<p>主要原因是遗留技术的存在：</p>
<ol>
<li>当KVM产生一个虚拟机，它<span style="background-color: #c0c0c0;">期望一个TAP接口连接到虚拟机的以太网端口</span>（eth0），这样KVM就得到一个FD，它在其上读写以太网帧。也就是说<span style="background-color: #c0c0c0;">TAP是去不掉</span>的</li>
<li>而VETH是相对新的技术，能够支持Linux Bridge、命名空间、Open vSwitch等技术</li>
</ol>
<div class="blog_h2"><span class="graybg">MacVLAN</span></div>
<p>有时我们需要一块物理网卡<span style="background-color: #c0c0c0;">绑定多个 IP 以及多个 MAC 地址</span>，虽然<span style="background-color: #c0c0c0;">绑定多个 IP 很容易（通过网卡别名，例如eth0:1），但是这些 IP 会共享物理网卡的 MAC 地址</span>，可能无法满足我们的设计需求，所以有了 macvlan设备，其工作方式如下：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2010/05/macvlan.png"><img class="aligncenter size-full wp-image-26511" src="https://blog.gmem.cc/wp-content/uploads/2010/05/macvlan.png" alt="macvlan" width="690" height="322" /></a></p>
<p>配合网络命名空间机制，可以在<span style="background-color: #c0c0c0;">不需要建立Bridge</span>的情况下，为虚拟机建立独立的网络栈：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2010/05/macvlan-network-ns.png"><img class="aligncenter size-full wp-image-26517" src="https://blog.gmem.cc/wp-content/uploads/2010/05/macvlan-network-ns.png" alt="macvlan-network-ns" width="650" height="252" /></a></p>
<p>&nbsp;</p>
<p>MacVLAN接口可以看做是物理以太网接口的N个虚拟子接口（sub interface），每个子接口都具有区别于父接口（parent interface）的MAC地址，并且可以像普通网卡一样分配IP地址。这种<span style="background-color: #c0c0c0;">虚拟网卡在逻辑上，和物理网卡具有对等的地位</span>。</p>
<p>父接口可以是一个物理接口（例如eth0），可以是一个 802.1q 的子接口（例如eth0.10），也可以是 bonding 接口。</p>
<p>除了虚拟化之外，Keepalived也通过MacVLAN来使用虚拟Mac地址。</p>
<p>需要注意，使用MacVLAN的虚拟机/容器，<span style="background-color: #c0c0c0;">和主机共享一个网段</span>。如果虚拟机/容器<span style="background-color: #c0c0c0;">需要和宿主机通信，需要额外创建一个子接口给宿主机</span>使用</p>
<p>MacVLAN支持5种模式：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 13%; text-align: center;">模式</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">bridge</td>
<td>
<p>类似于Linux Bridge，最常用。适合共享父接口的MacVLAN虚拟网卡直接进行通信的场景</p>
<p>这种模式下，共享父接口的虚拟网卡可以直接通信，不需要把流量发送到父接口外部</p>
<p>示例：</p>
<pre class="crayon-plain-tag"># 创建MacVLAN子接口，父接口为eth0，MAC地址默认自动分配，如需指定，可以 address 00:00:00:00:00:09
ip link add link eth0 name macv1 type macvlan mode bridge
ip link add link eth0 name macv2 type macvlan mode bridge

# if link可以看到新创建的两个子接口
5: macv1@eth0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/ether a6:6d:2c:6e:33:bf brd ff:ff:ff:ff:ff:ff
6: macv2@eth0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/ether 12:c9:36:fe:f8:6c brd ff:ff:ff:ff:ff:ff

# 移动到命名空间中
ip netns add net1
ip netns add net2
ip link set macv1 netns net1
ip link set macv2 netns net2

# 查看命名空间中的接口状态
ip netns exec net1 ip link
1: lo: &lt;LOOPBACK&gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 类似于VETH对的一端，移动到命名空间后，使用@if2指明父接口在宿主机网络命名空间的序号
5: macv1@if2: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1
    link/ether a6:6d:2c:6e:33:bf brd ff:ff:ff:ff:ff:ff link-netnsid 0

# 配置IP地址
ip netns exec net1 ip addr add dev macv1 10.0.0.200/16
ip netns exec net2 ip addr add dev macv2 10.0.0.201/16
ip netns exec net1 ip link set dev macv1 up
ip netns exec net2 ip link set dev macv2 up

# 通信
ip netns exec net1 ping 10.0.0.1    # OK
ip netns exec net1 ping 10.0.0.201  # OK
ip netns exec net1 ping 10.0.0.200  # 失败
ip net exec net1 ip link set lo up  # 原因是命名空间中lo默认DOWN，导致不能PING自己</pre>
</td>
</tr>
<tr>
<td class="blog_h3">VEPA</td>
<td>
<p>Virtual Ethernet Port Aggregator，虚拟以太网端口聚合，这是默认的模式
<p>所有虚拟接口发出的流量，不管目的地是什么（即使是共享父接口的兄弟虚拟接口），都发给父接口</p>
<p>需要连接父接口们的交换机支持<span style="background-color: #c0c0c0;">Hairpin模式（可以把某个端口发出的包反射回去）</span>，把源、目的地址都是本地MacVLAN虚拟接口地址的流量，发给相应接口</p>
<p>大部分交换机设备不支持Hairpin模式，但是Linux Bridge可以支持：</p>
<pre class="crayon-plain-tag"># 对于网桥br0的端口eth0，从此端口收到的包，允许（可能经过某种处理后）从此端口再发回去
brctl hairpin br0 eth0 on
# or
ip link set dev eth0 hairpin on</pre>
</td>
</tr>
<tr>
<td class="blog_h3">Private</td>
<td>类似于VEPA，但是增强了隔离能力，阻止共享父接口的MacVLAN虚拟接口之间的通信</td>
</tr>
<tr>
<td class="blog_h3">Passthrough</td>
<td>这种模式下一个父接口只能和单个MacVLAN虚拟接口绑定，并且子接口继承父接口的MAC地址</td>
</tr>
<tr>
<td class="blog_h3">Source</td>
<td>MacVLAN接口只接受指定源MAC地址的数据包 </td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">跨主机通信</span></div>
<p>位于两台机器上的MacVLAN虚拟网卡要实现通信，需要满足：
<ol>
<li>两台机器的父接口都处于混杂模式</li>
<li>两台主机的MacVLAN子网IP段不重叠</li>
</ol>
<div class="blog_h3"><span class="graybg">和父接口通信</span></div>
<p>在MacVLAN的虚拟网络中，父接口仅仅相当于一个交换机。对于进出子MacVLAN接口的数据包，物理网卡仅仅转发而不处理，这导致使用本机MacVLAN网卡的IP无法和物理网卡的IP进行通信。</p>
<div class="blog_h3"><span class="graybg">缺点</span></div>
<p>MacVLAN是将虚拟机、容器连接到物理网络的简单方法。但是它有以下缺点：</p>
<ol>
<li>每个虚拟接口都需要MAC地址，但是物理交换器支持的MAC地址数量会有限制，许多物理网卡支持的MAC地址数量也会有限制</li>
<li>IEEE 802.11（WiFi）不支持一个客户端有多个MAC地址</li>
<li>在云上环境，VPC往往对入站报文的MAC地址进行校验，Linux Bridge面临同样的问题</li>
</ol>
<p>面临这些缺点时，可以考虑使用IPVLAN。</p>
<div class="blog_h2"><span class="graybg">IPVLAN</span></div>
<p>类似于MacVLAN，都是在一个父接口下虚拟出多个子接口。不同之处是，<span style="background-color: #c0c0c0;">IPVLAN的所有子接口MAC相同，仅仅IP不同</span>。</p>
<p>IPVLAN从3.19开始支持，<span style="background-color: #c0c0c0;">比较稳定的版本需要4.2+</span>。Docker对老版本的支持存在缺陷。</p>
<p>某些DHCP服务器在分配IP地址时，以MAC地址作为机器的标识，这和IPVLAN无法协同工作。</p>
<p>IPVLAN支持L2、L3等模式，<span style="background-color: #c0c0c0;">一个父接口同时只能在一种模式下工作</span>。</p>
<p><span style="background-color: #c0c0c0;">IPVLAN无法从宿主机访问子接口的IP地址，也不能从子接口所在命名空间访问父接口的IP地址</span>。你可以额外创建一对veth，配合路由，来解决这个问题。</p>
<div class="blog_h3"><span class="graybg">L2模式</span></div>
<p>这种模式下，和MacVLAN工作方式很类似。父接口作为交换机，转发子接口的数据。<span style="background-color: #c0c0c0;">子接口直接加入父接口所在的二层网络</span>。</p>
<p>实验：</p>
<pre class="crayon-plain-tag">ip link add link eth0 ipvlan1 type ipvlan mode l2
ip link add link eth0 ipvlan2 type ipvlan mode l2

ip net add net1
ip net add net2

ip link set ipvlan1 netns net1
ip link set ipvlan2 netns net2

ip net exec net1 ip link set ipvlan1 up
ip net exec net2 ip link set ipvlan2 up

ip net exec net1 ip addr add 10.0.10.1/16 dev ipvlan1
ip net exec net2 ip addr add 10.0.10.2/16 dev ipvlan2

ip net exec net1 route add default dev ipvlan1
ip net exec net2 route add default dev ipvlan2

ip net exec net1 ip link set ipvlan1 up
ip net exec net2 ip link set ipvlan2 up

ip net exec net1 ip link set lo up
ip net exec net2 ip link set lo up</pre>
<div class="blog_h3"><span class="graybg">L3模式</span></div>
<p>这种模式下， 父接口类似于路由器。它在各个<span style="background-color: #c0c0c0;">虚拟网络和主机网络之间</span>进行不同网络报文的<span style="background-color: #c0c0c0;">路由转发</span>工作。<span style="background-color: #c0c0c0;"> 只要父接口相同，即使虚拟机/容器不在同一个网络，也可以互相ping通对方</span>，因为ipvlan会在中间做报文的转发工作。 L3模式下的虚拟接口不会接收到多播或者广播的报文，所有的<span style="background-color: #c0c0c0;"> ARP 过程或者其他多播报文都是在底层的父接口</span>完成的。</p>
<p>外部网络不会理解IPVLAN虚拟出来的网络，因此如果外部路由器上不配置适当路由规则，IPVLAN的虚拟IP无法被外部访问。 </p>
<p>实验：</p>
<pre class="crayon-plain-tag">ip netns add net1
ip netns add net2

# 添加L3模式的IPVLAN子接口
ip link add link eth0 ipvlan1 type ipvlan mode l3
ip link add link eth0 ipvlan2 type ipvlan mode l3

# 可以发现子接口的MAC地址和父接口相同
2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 10:00:00:00:00:07 brd ff:ff:ff:ff:ff:ff
5: ipvlan1@eth0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 10:00:00:00:00:07 brd ff:ff:ff:ff:ff:ff
6: ipvlan2@eth0: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 10:00:00:00:00:07 brd ff:ff:ff:ff:ff:ff

# 移动到网络命名空间
ip link set ipvlan1 netns net1
ip link set ipvlan2 netns net2</pre>
<p>注意，由于父子接口的MAC地址一样，因此不能通过DHCP分配IP地址，必须手工添加：</p>
<pre class="crayon-plain-tag">ip net exec net1 ip addr add 10.98.0.1/16 dev ipvlan1
ip net exec net1 ip link set dev ipvlan1 up
ip net exec net2 ip addr add 10.99.0.1/16 dev ipvlan2
ip net exec net2 ip link set dev ipvlan2 up

# 添加路由
ip net exec net1 route add default dev ipvlan1
ip net exec net2 route add default dev ipvlan2

ip net exec net1 ping 10.99.0.1  # OK

# 在宿主机的L2网络的其它主机上，必须有路由才能访问IPVLAN子接口
#                                        IPVLAN父接口IP
ip route add 10.98.0.0/16 dev virbr0 via 10.0.0.7
ip route add 10.99.0.0/16 dev virbr0 via 10.0.0.7</pre>
<div class="blog_h3"><span class="graybg">L3S模式</span></div>
<p>类似于L3模式，但是L3S模式下出入流量均<span style="background-color: #c0c0c0;">经过宿主机网络命名空间的三层网络（L2/L3模式则不经过）</span>，会被宿主机的netfilter框架过滤。</p>
<p>这意味着L3S可以支持kube-proxy，但是，它又会引入以下问题：</p>
<ol>
<li>当K8S服务的客户端、Pod位于同一节点时，访问服务的应答报文会走ipvlan datapath，接收不到</li>
<li>同样场景下，同一方向流量多次进出宿主机 conntrack，datapath复杂，和iptables/ipvs也存在兼容性问题</li>
</ol>
<p>可以为容器额外引入VETH（另外一端放在宿主机），通过路由设置，让K8S服务的流量从VETH出站，解决Service无法访问的问题。</p>
<div class="blog_h2"><span class="graybg">MacVTap</span></div>
<p>Macvtap是一个新式驱动，用于简化虚拟化网络桥接。它基于macvlan设备驱动，将macvlan和TAP的优点进行整合。它使用macvlan的方式来收发数据包，但是收到的数据包<span style="background-color: #c0c0c0;">不会交给独立网络栈处理，而是交给/dev/tapX文件</span>：</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2010/05/macvtap.png"><img class="aligncenter size-full wp-image-26521" src="https://blog.gmem.cc/wp-content/uploads/2010/05/macvtap.png" alt="macvtap" width="770" height="280" /></a></p>
<p>由于 macvlan是工作在 MAC 层的，所以 macvtap 也只能工作在 MAC 层，不会存在macvtun这样的设备。</p>
<p>一个macvtap端点（endpoint）是一个字符设备，它很大程度上遵循tun/tap ioctl接口，可以被KVM/QEMU或者其它支持tun/tap接口的hypervisors直接使用。<span style="background-color: #c0c0c0;">macvtap端点扩展一个既有网络接口（底层设备，lower device），具有自己的MAC地址</span>，位于与被扩展接口相同的网段。典型情况下，macvtap让宿主机、客户机一同出现在物理网络上。</p>
<div class="blog_h3"><span class="graybg">工作模式</span></div>
<p>类似于macvlan，一个macvtap可以工作在以下三种模式之一。这些模式定义了连接到同一lower device的macvtap端点之间的通信方式：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">模式</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>VEPA</td>
<td>
<p>即虚拟以太网端口聚合器（Virtual Ethernet Port Aggregator），这是默认的模式。从一个端点发送到另外一个端点的数据下发到lower device，进而发送到外部交换机，如果外部交换机支持hairpin模式，则以太网帧被发回lower device进而发送到目标端点。然而，大部分现代交换机都不支持hairpin模式，这意味着两个端点不能交换以太网帧（尽管它们可以通过TCP/IP路由器通信）</p>
</td>
</tr>
<tr>
<td>Bridge</td>
<td>直接连接端点，两个同时处于bridge模式的macvlan可以直接交换以太网帧。对于典型的交换机，这是最有用的模式</td>
</tr>
<tr>
<td>private</td>
<td>类似于VEPA，但是即使交换机支持hairpin，也被忽略，这种模式下端点之间绝不能相互通信</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">反向代理</span></div>
<p>亦称替代物（surrogate)，这类代理可以假扮Web服务器，接收发给 Web 服务器的真实请求，它可以向一个或者多个服务器索取内容，并返回给客户端，在客户端看来，好像反向代理就是真正的服务器。 反向代理有以下应用场景：</p>
<ol>
<li>反向代理可以缓存静态、动态内容，从而提高访问慢速Web服务器上公共内容时的性能，在此场景下反向代理被称为服务器加速器（Server accelerator）</li>
<li>隐藏原始服务器的存在和特性</li>
<li>为不支持SSL的服务器提供SSL支持</li>
<li>负载均衡：分散入站请求到多个服务器上</li>
<li>任何几个Web服务器需要通过同一IP地址访问的时候，可以将反向代理作为前置机</li>
</ol>
<p>&nbsp;</p>
<div class="blog_h1"><span class="graybg"><a id="transparent-proxy"></a>透明代理</span></div>
<p>所谓透明代理，它透明在：</p>
<ol>
<li>对于客户端，发起连接时连接的服务端是真实的服务器而不是代理服务器</li>
<li>对于服务器，收到的请求来自真实客户端而不是代理服务器</li>
</ol>
<p>必须具有某种机制，在客户端毫无知觉的情况下，将它的请求劫持给代理服务器处理。同样的，真实服务器发回的响应也需要直接发给代理服务器，而不是客户端。</p>
<p>实现透明代理，需要多方面的配合。客户端（C）、代理（P）、服务器（S）的部署架构会很大程度上影响技术实现。主要关注点包括：</p>
<ol>
<li>网络路由：目的地是S的IP包路由给P处理。如果S和P部署在一起（例如用于Sidecar部署的Envoy），通过Linux本身的能力就能达成</li>
<li>IP_TRANSPARENT：P必须启用此套接字选项</li>
</ol>
<div class="blog_h2"><span class="graybg">路由处理</span></div>
<p>当封包到达Linux内核时，它要么被路由，要么被丢弃，要么<span style="background-color: #c0c0c0;">被Linux本地处理 —— 如果目的地址匹配本地地址的话</span>。</p>
<p><span style="background-color: #c0c0c0;">什么是本地地址，是可以设置的</span>，甚至你可以设置为0.0.0.0/0 —— 任何地址都是本地地址，但这样设置会导致系统无法连接到任何远程地址，因为所有地址都是本地地址了…应当发给本地进程处理。幸运的是，<span style="background-color: #c0c0c0;">通过独立路由表，我们可以选择性的将一部分封包路由到本地</span>：</p>
<pre class="crayon-plain-tag"># 对于任何目的端口是53的封包，设置标记
iptables -t mangle -I PREROUTING -p udp --dport 5301 -j MARK --set-mark 1

# 然后配置路由表，强制将其从lo网卡发出，而不管目的地址是什么
ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100 </pre>
<p>注意，需要启用IP转发功能：</p>
<pre class="crayon-plain-tag">sysctl net.ipv4.conf.all.forwarding=1
sysctl net.ipv6.conf.all.forwarding=1</pre>
<p>某些情况下反向路径过滤器（reverse path filter）可能会丢弃你拦截的封包，禁用之：</p>
<pre class="crayon-plain-tag">sysctl net.ipv4.conf.eth0.rp_filter=0</pre>
<div class="blog_h2"><span class="graybg">IP_TRANSPARENT</span></div>
<p>套接字选项IP_TRANSPARENT可以<span style="background-color: #c0c0c0;">绑定不属于本机的IP地址</span>，<span style="background-color: #c0c0c0;">P需要启用此选项</span>。此选项的作用：</p>
<ol>
<li>作为S的客户端时，此选项允许P使用不属于本机的IP地址作为源IP（也就是C的地址）发起连接</li>
<li>作为C的服务器时，可以侦听（绑定到）不属于本机的IP地址（也就是S的地址）的连接请求。比如开启此IP_TRANSPARENT选项同时监听0.0.0.0.80，那么本机接收到的DST地址是192.168.0.189:80的TCP请求可被监听</li>
<li>接受被TPROXY重定向的连接和封包</li>
</ol>
<p>下面是一段基于<a href="https://github.com/ahupowerdns/simplesocket">simplesocket</a>的代码，说明了IP_TRANSPARENT如何指定任意源地址的。注意需要CAP_NET_ADMIN权限才能运行：</p>
<pre class="crayon-plain-tag"># 基于TCP/IP协议族的UDP套接字 
Socket s(AF_INET, SOCK_DGRAM, 0);
# 套接字选项，IP_TRANSPARENT
SSetsockopt(s, IPPROTO_IP, IP_TRANSPARENT, 1);
# 使用的源地址
ComboAddress local("1.2.3.4", 5300);
# 使用的目的地址
ComboAddress remote("198.41.0.4", 53);
# 绑定到源地址，注意此地址不是本机地址 
SBind(s, local);
# 发送UDP包
SSendto(s, "hi!", remote);</pre>
<p>使用tcpdump可以发现：</p>
<pre class="crayon-plain-tag"># tcpdump -n host 1.2.3.4
# 源地址不是本机
21:29:41.005856 IP 1.2.3.4.5300 &gt; 198.41.0.4.53</pre>
<div class="blog_h2"><span class="graybg">TPROXY</span></div>
<p>Iptables支持一个特殊的目标TPROXY，它能够拦截流量，并将其转发给任意特定的本地IP地址，同时为封包设置标记：</p>
<pre class="crayon-plain-tag"># 修改针对25端口的TCP流量封包
iptables -t mangle -A PREROUTING -p tcp --dport 25 -j TPROXY
  # 设置0x1/0x1标记
  --tproxy-mark 0x1/0x1 
  # 转发给127.0.0.1:10025，但是不改变IP封包头的任何信息
  --on-port 10025      # 默认原始目的端口
  --on-ip 127.0.0.1    # 默认接收到流量的网卡地址</pre>
<p>注意：</p>
<ol>
<li>--tproxy-mark，它确保此封包通过正确的路由表发出。参考第一小节的路由，此封包  xxx:xxx:127.0.0.1:10025，将从lo网卡出网络栈</li>
<li>TPROXY和REDIRECT不同，后者本质上是DNAT，会改变IP封包头的目的地址</li>
</ol>
<div class="blog_h2"><span class="graybg">SocketMatch</span></div>
<p>Iptables匹配扩展socket能够<span style="background-color: #c0c0c0;">匹配和本地套接字相关的封包</span>，它会进行Socket Hash查找，并且在以下条件下匹配：</p>
<ol>
<li>本地存在已经建立的套接字，匹配此封包</li>
<li>如果本地存在非零监听套接字，并且地址端口匹配封包的地址端口</li>
</ol>
<p>示例：</p>
<pre class="crayon-plain-tag">iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT

iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT

# 配合路由表</pre>
<div class="blog_h2"><span class="graybg">获取原始目的地址</span></div>
<p>使用IP_TRANSPARENT时：</p>
<ol>
<li>对于TCP连接，套接字的原始目的地址、端口可以通过getsockname()调用获得</li>
<li>对于UDP连接，你必须设置套接字选项IP_RECVORIGDSTADDR：<br />
<pre class="crayon-plain-tag">setsockopt (s, IPPROTO_IP, IP_RECVORIGDSTADDR, &amp;n, sizeof(int));</pre></p>
<p>然后，调用recvmsg()方法：</p>
<pre class="crayon-plain-tag">char orig_ip[32] = {0};
int orig_port = 0;
struct sockaddr_in *orig_addr;
for (cmsg = CMSG_FIRSTHDR(&amp;msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&amp;msg,cmsg)) {
    if (cmsg-&gt;cmsg_level == SOL_IP &amp;&amp; cmsg-&gt;cmsg_type == IP_ORIGDSTADDR) {
        orig_addr = (struct sockaddr_in *) CMSG_DATA(cmsg);
        transfer_sock_addr(orig_addr, orig_ip, 32, &amp;orig_port);
        break;
    }
}
if (cmsg == NULL) {
    printf("IP_ORIGDSTADDR not enabled or small buffer or I/O error");
    return;
} </pre>
</li>
</ol>
<div class="blog_h2"><span class="graybg">Istio的实例</span></div>
<p>参考<a href="/istio-study-note#tproxy-iptables-rules">Istio学习笔记</a>。
<div class="blog_h2"><span class="graybg">小结</span></div>
<ol>
<li>需要通过Iptables来对封包进行标记。时用TPROXY目标可以在标记的同时，转发给指定IP:PORT，但却又不改包头</li>
<li>需要为被标记的封包设置路由表，不管其目的地址是什么（0.0.0.0/0），总是发送到lo接口</li>
<li>lo接口接收到的封包，再次进入Iptables，这次不会走PREROUTING链</li>
<li>对于TPROXY，已经明确指定了接收者进程的IP:PORT，直接由此进程处理封包，前提是IP_TRANSPARENT被设置好</li>
<li>SocketMatch的作用是识别本地相关的套接字并标记，它不是必须的，TPROXY也能够做标记工作</li>
</ol>
<div class="blog_h1"><span class="graybg">环回</span></div>
<p>Loopback本来是通信学上的术语，和电信号、数字数据包的路由有关，将它们不做修改的路由给信号的发起者，主要用于测试通信基础设施。</p>
<div class="blog_h2"><span class="graybg">lo</span></div>
<p>在TCP/IP协议族的规范实现中，包含一个虚拟的网络接口，同一机器上的程序可以通过此虚拟接口进行网络通信，此接口完全是软件的，不会引起任何物理网卡的流量。程序发往lo接口的封包会简单的、<span style="background-color: #c0c0c0;">立即发回网络栈，就好像是从网络上收到的一样</span>。</p>
<p>如果路由将<span style="background-color: #c0c0c0;">出口网卡设置为lo（不管目的地址是什么），则报文交由本地的应用程序处理</span>，而不会真正路由出去。</p>
<div class="blog_h2"><span class="graybg">dummy</span></div>
<p>在Linux中，dummy类型的网络接口类似于lo，区别在于，你可以创建多个dummy类型的网络接口。</p>
<p>此外，对于lo接口，内核会自动添加目的地址是CIDR的路由：</p>
<pre class="crayon-plain-tag">local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1</pre>
<p> 而对于dummy接口则仅仅会添加针对其IP地址的路由。</p>
<div class="blog_h1"><span class="graybg">UDS</span></div>
<div class="blog_h2"><span class="graybg">简介</span></div>
<p>Socket API原本是为网络通讯设计的，但后来在socket的框架上发展出一种IPC机制，就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯（通过loopback地址127.0.0.1），但是<span style="background-color: #c0c0c0;">UNIX Domain Socket用于IPC更有效率：不需要经过网络协议栈，不需要打包拆包、计算校验和、维护序号和应答等</span>，只是将应用层数据<span style="background-color: #c0c0c0;">从一个进程拷贝到另一个进程</span>。这是因为，<span style="background-color: #c0c0c0;">IPC机制本质上是可靠的通讯</span>，而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供<span style="background-color: #c0c0c0;">面向流和面向数据包两种API接口，类似于TCP和UDP</span>，但是面向消息的UNIX Domain Socke<span style="background-color: #c0c0c0;">t也是可靠</span>的，消息既不会丢失也不会顺序错乱。</p>
<p>UNIX Domain Socket是全双工的，API接口语义丰富，相比其它IPC机制有明显的优越性，目前已成为<span style="background-color: #c0c0c0;">使用最广泛的IPC机制</span>，比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。</p>
<p>使用UNIX Domain Socket的过程和网络socket十分相似，也要<span style="background-color: #c0c0c0;">先调用socket()创建一个socket文件描述符</span>，address family指定为AF_UNIX，type可以选择SOCK_DGRAM或SOCK_STREAM，protocol参数仍然指定为0即可。</p>
<p>UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同，用结构体sockaddr_un表示：</p>
<pre class="crayon-plain-tag">struct sockaddr_un {
    sa_family_t     sun_family;             /*PF_UNIX或AF_UNIX */
    char    sun_path[UNIX_PATH_MAX];        /* 路径名 */
}; </pre>
<p>网络编程的socket地址是IP地址加端口号，而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径，这个socket文件由bind()调用创建，如果<span style="background-color: #c0c0c0;">调用bind()时该文件已存在，则bind()错误返回</span>。</p>
<div class="blog_h2"><span class="graybg">抽象命名空间</span></div>
<p>所谓抽象套接字命名空间（Abstract Socket Namespace）是Linux的特性，用于创建一个<span style="background-color: #c0c0c0;">不去绑定到文件系统中文件的UDS</span>，从而获得以下好处：</p>
<ol>
<li>不需要担心文件系统中的命名冲突</li>
<li>当完成套接字使用后，不需要关注unlink文件路径名的问题 —— 到套接字关闭后，抽象名称会自动删除</li>
<li>不需要在文件系统中创建对应路径，在chroot环境下可能有用，在没有缺陷操控特定文件系统路径时也有用</li>
</ol>
<p>要创建抽象UDS套接字，需要指定sub_path的第一个字节为\0字符。在netstat lsof等命令的输出中，\0显示为@符号</p>
<div class="blog_h1"><span class="graybg">交换机</span></div>
<p>现代以太网依赖于交换机进行帧的交换。它通常是具有多个端口的硬件盒子。如果目的地址对于交换机未知（它不知道关联到哪个端口），那么交换机会广播帧给所有端口。通过观察端口上的帧流量，交换机就能学习到端口-MAC对应关系，从而不再需要广播流量。交换机在FIB（forwarding information base，也叫forwarding table）中缓存对应关系。</p>
<p>交换机可以相互连接，从而构成更大的以太网。</p>
<div class="blog_h1"><span class="graybg"><a id="route"></a>路由</span></div>
<div class="blog_h2"><span class="graybg">目的地类型</span></div>
<div class="blog_h3"><span class="graybg">本机地址</span></div>
<p>Linux支持loopback设备，用于通过网络栈和主机自身进行通信。Linux能够和任何配置在本地网络接口上的IP进行通信，不管地址是不是在loopback设备上。</p>
<p>所有配置在本机网络接口的地址，即本地地址（Locally Hosted Addresses）。</p>
<div class="blog_h3"><span class="graybg">本地可达地址</span></div>
<p>这类地址来自和本机位于同一网段内的其它主机。这些主机通过交换机相互连接，地址本地可达（Locally Reachable Addresses）</p>
<div class="blog_h3"><span class="graybg">其它地址</span></div>
<p>所有其它地址，都必须依赖路由器（IP Routing Device）作为中介才能到达，并且，此路由器具有本地可达地址。</p>
<div class="blog_h2"><span class="graybg">跳/Hop</span></div>
<p>当封包从一个网络（经过路由器）到达另外一个网络，称为经过了一跳。跳的数量，即封包经过的中介路由器的数量。</p>
<div class="blog_h3"><span class="graybg">下一跳</span></div>
<p>对于一个路由路径，封包要经过的下一个网关（路由器），即下一跳。</p>
<div class="blog_h2"><span class="graybg">路由器</span></div>
<p>任何在两个网络之间接受/转发封包的设备，都是路由器。路由器至少是dual-homed的 —— 每个接口接入到一个网络，网络接口通常是NIC。Linux主机经常扮演路由器的角色，NIC经常是VLAN接口之类的虚拟设备。</p>
<div class="blog_h2"><span class="graybg">默认路由</span></div>
<p>默认路由，即目的地是0/0的路由，是最一般的路由——如果找不到和封包目的地更匹配的路由，则自动使用默认路由。</p>
<p>对于连接到因特网的主机来说，默认路由通常是本地可达的路由器，此路由器也叫默认网关。</p>
<div class="blog_h2"><span class="graybg">路由选择</span></div>
<p>路由选择的基本原则是逐跳（hop-by-hop）进行，<span style="background-color: #c0c0c0;">典型情况下以目的地址为选路的唯一标准</span>。Linux作为路由器时，可能<span style="background-color: #c0c0c0;">基于其它的封包特征进行选路，此所谓策略路由</span>。</p>
<p>基于目的地址选路时，<span style="background-color: #c0c0c0;">首先会查询路由缓存，然后查找主路由表</span>。如果<span style="background-color: #c0c0c0;">最近发送封包到了目的地址，则关于目的地址的路由会存在于缓存（本质上是一个hash表）</span>中。如果最近没有发送封包到目的地，则需要查询路由表，匹配的原则是最长前缀匹配（longest prefix match） —— 也就是尽量选择精确的路由。最长前缀匹配能够针对大规模网络的路由规则，被<span style="background-color: #c0c0c0;">更加精确的路由器（通常更靠近）或主机覆盖</span>。</p>
<div class="blog_h2"><span class="graybg">策略路由</span></div>
<p>从2.2开始，Linux支持基于多路由表（multiple routing tables）和路由策略数据库（routing policy database，RPDB）的策略路由。</p>
<p>策略路由的依据，是封包的某些属性，例如源地址、ToS标记、fwmark（仅在内核中存在的、位于表示封包的数据结构中的字段）、接收封包接口名，等等。</p>
<p>启用策略路由的情况下，选路算法如下：</p>
<ol>
<li>首先，仍然是查询路由缓存</li>
<li>基于优先级，来遍历RPDB。查找匹配的RPDB</li>
<li>对于每个匹配RPDB条目，使用最长前缀原则匹配封包目的地址，遍历条目对应的路由表</li>
<li>如果找到匹配路由，则终止迭代</li>
</ol>
<p>伪代码：</p>
<pre class="crayon-plain-tag"># 首先查找路由缓存
if packet.routeCacheLookupKey in routeCache :
    route = routeCache[ packet.routeCacheLookupKey ]
else
    # 然后按优先级遍历RPDB，此数据库中是一条条规则
    for rule in rpdb :
        # 如果封包匹配RPDB规则
        if packet.rpdbLookupKey in rule :
            # 则遍历RPDB规则对应的路由表
            routeTable = rule[ lookupTable ]
            # 根据最长前缀原则来匹配路由表中的路由
            if packet.routeLookupKey in routeTable :
                route = route_table[ packet.routeLookup_key ]</pre>
<p><span style="background-color: #c0c0c0;">使用策略路由时，单个路由表的使用逻辑保持不变</span>。</p>
<div class="blog_h2"><span class="graybg">路由缓存</span></div>
<p>路由缓存，又叫转发信息库（forwarding information base，FIB）。它是一个存放了最近使用的路由条目的哈希表，在查询路由表之前，会优先检查FIB。</p>
<p>FIB由内核独立于路由表维护，修改路由表可能不会立即反应到FIB，要避免此延迟，可以调用：</p>
<pre class="crayon-plain-tag">ip route flush cache</pre>
<p>FIB清空后，新的封包，或者<pre class="crayon-plain-tag">ip route get</pre>命令会触发路由表查找、重新填充缓存。 </p>
<p>FIB提供基于多种方式的哈希查找，包括：dst（目的地址）、src（源地址）、tos（服务类型）、fwmark（内核封包标记）、iif（入站网络接口）。</p>
<p>FIB的缓存条目上，存储了以下属性：cwnd、advmss（建议最大段大小）、src（Preferred Local源地址）、mtu、rtt、age、users、used。</p>
<div class="blog_h2"><span class="graybg">路由表</span></div>
<p>Linux 2.2 / 2.4支持多路由表，除了广泛使用的local / main表外，内核最多支持额外的252张路由表。联合多个路由表（主要基于目的地址）和RPDB（主要基于源地址），Linux内核实现了灵活的路由功能。</p>
<p>支持多重路由表的内核，使用0-255之间的整数来引用路由表。最基本的路由表是local（255）、main（252），路由表序号和名字的映射关系定义在<pre class="crayon-plain-tag">/etc/iproute2/rt_tables</pre>中。</p>
<p>下面的命令显示所有路由表的路由：</p>
<pre class="crayon-plain-tag">ip route show table all</pre>
<p>下面的命令显示特定路由表中的路由：</p>
<pre class="crayon-plain-tag">ip route show table main
ip route show table 2005</pre>
<div class="blog_h3"><span class="graybg">local表</span></div>
<p>这是一张<span style="background-color: #c0c0c0;">由内核维护的特殊表</span>，条目可以删除，但是需要注意风险。ip address和ifconfig命令会导致内核修改local表（通常也同时修改main表）。</p>
<p>local表的主要用途有两个：</p>
<ol>
<li>指定广播地址的规格，仅仅对于支持广播寻址的L2有用</li>
<li>用于<span style="background-color: #c0c0c0;">路由到本机IP地址</span></li>
</ol>
<p>local表中可以出现的路由类型包括：local、nat、broadcast。这些路由类型不会出现在其它表，其它路由类型不会出现在local表。</p>
<p>如果某个<span style="background-color: #c0c0c0;">网络接口具有多个IP地址，则每个IP地址都在local表中具有一个路由条目</span>。这是在Linux下为网络接口添加IP地址的正常附加效果。</p>
<div class="blog_h3"><span class="graybg">main表</span></div>
<p>命令route操控的是该表，如果ip route不指定目标表，则默认操作main表。</p>
<div class="blog_h2"><span class="graybg">路由条目</span></div>
<p>路由表可以包含任意多个（除了local表，它由内核维护）条目。</p>
<p>条目的形式为：</p>
<pre class="crayon-plain-tag">ip route { add | del | change | append | replace } ROUTE

ROUTE := NODE_SPEC [ INFO_SPEC ]
  #            路由类型  匹配CIDR           路由表              路由协议           范围
  NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ] [ table TABLE_ID ] [ proto RTPROTO ] [ scope SCOPE ] [ metric METRIC ]
    TYPE := [ unicast | local | broadcast | multicast | throw | unreachable | prohibit | blackhole | nat ]
    TABLE_ID := [ local| main | default | all | NUMBER ]
    RTPROTO := [ kernel | boot | static | NUMBER ]
    SCOPE := [ host | link | global | NUMBER ]

  INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ] ...
    #                     下一跳路由器地址             出口网卡名称    权重，在multipath路由中反应此路由的相对带宽或质量 
    NH := [ encap ENCAP ] [ via [ FAMILY ] ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS
      FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]
      NHFLAGS := [ onlink | pervasive ]
    OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ] rtt TIME ] [ rttvar TIME ] 
                     [ reordering NUMBER ] [ window NUMBER ] [ cwnd NUMBER ] [ ssthresh REALM ] [ realms REALM ] 
                     [ rto_min TIME ] [ initcwnd NUMBER ] [ initrwnd NUMBER ] [ features FEATURES ] [ quickack BOOL ] 
                     [ congctl NAME ] [ pref PREF ] [ expires TIME ]</pre>
<div class="blog_h3"><span class="graybg">路由类型</span></div>
<p>路由类型可以分为一下几种：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 150px; text-align: center;">类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>unicast</td>
<td>单播，最常见的路由条目，针对特定单一目的主机：<br />
<pre class="crayon-plain-tag">ip route add unicast 192.168.0.0/24 via 192.168.100.5
ip route add default via 193.7.255.1
ip route add unicast default via 206.59.29.193
ip route add 10.40.0.0/16 via 10.72.75.254</pre></p>
<p>如果ip route命令不指定路由条目类型，则默认是unicast </p>
</td>
</tr>
<tr>
<td>broadcast</td>
<td>用于支持广播地址的链路层设备，仅用在local表中，通常由内核管理：<br />
<pre class="crayon-plain-tag">ip route add table local broadcast 10.10.20.255 dev eth0 proto kernel scope link src 10.10.20.67 </pre>
</td>
</tr>
<tr>
<td>local</td>
<td>每当一个<span style="background-color: #c0c0c0;">IP地址添加到网络接口上时，内核自动在local表添加local类型的路由条目</span>：<br />
<pre class="crayon-plain-tag">ip route add 
  # 添加到local表    路由类型为local
  table local       local 
  # PREFIX，不带 / 为单一地址
  10.96.0.1 
  # 出口设备名称
  dev kube-ipvs0 
  # 内核自动添加的条目
  proto kernel 
  # 主机范围
  scope host 
  # 优先使用的源地址
  src 10.96.0.1</pre></p>
<p> 例如上面这个条目，是K8S的Kube Proxy IPVS模式下为kube-ipvs0配置10.96.0.1地址后，内核自动增加的条目</p>
</td>
</tr>
<tr>
<td>nat</td>
<td>当尝试配置无状态NAT时，内核添加这类条目到local表：<br />
<pre class="crayon-plain-tag">ip route add nat 193.7.255.184 via 172.16.82.184
ip route add nat 10.40.0.0/16 via 172.40.0.0</pre>
</td>
</tr>
<tr>
<td>unreachable</td>
<td>当匹配这类路由条目时，会返回ICMP unreachable消息：<br />
<pre class="crayon-plain-tag">ip route add unreachable 192.168.14.0/26 </pre>
</td>
</tr>
<tr>
<td>prohibit</td>
<td>当匹配这类路由条目时，会返回ICMP prohibited消息</td>
</tr>
<tr>
<td>blackhole</td>
<td>当匹配这类路由条目时，封包被丢弃，不会产生ICMP消息<br />
<pre class="crayon-plain-tag">ip route add blackhole 64.65.64.0/18 </pre>
</td>
</tr>
<tr>
<td>throw</td>
<td>
<p>导致针对当前路由表的查找立即失败，返回RPDB（可能继续匹配后续规则并查找相应的其它路由表）</p>
<pre class="crayon-plain-tag">ip route add throw 10.79.0.0/16</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">路由SCOPE</span></div>
<p>所谓route scope，是一个“指示符”，用来说明到目标网络的“距离”。 
<p>必须是定义在  /etc/iproute2/rt_scopes 中的名字或数字</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 150px; text-align: center;">取值</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>global<br />universe</td>
<td>
<p>此Scope提示目的地远于一跳</p>
<p>对于所有通过网关（gatewayed）的单播路由，这是默认值</p>
</td>
</tr>
<tr>
<td>link</td>
<td>
<p>此Scope提示目的地在本地网络上</p>
<p>对于直接单播路由、广播路由，这是默认值</p>
</td>
</tr>
<tr>
<td>host</td>
<td>
<p>此Scope提示目的地在本机上</p>
<p>对于local类型路由，这是默认值</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">路由协议标识符</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 150px; text-align: center;">取值</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>redirect</td>
<td>因为ICMP重定向而安装此路由条目</td>
</tr>
<tr>
<td>kernel</td>
<td>因为内核执行指定配置而安装此路由条目</td>
</tr>
<tr>
<td>boot</td>
<td>路由条目在启动期间安装，当路由守护进程启动后，会清除所有这类条目</td>
</tr>
<tr>
<td>static</td>
<td>管理员手工添加，用于覆盖动态路由。路由守护进程会遵从此条目，甚至同步给它的peers</td>
</tr>
<tr>
<td>ra</td>
<td>由路由发现协议安装</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">路由策略数据库</span></div>
<p>RPDB控制内核遍历多张路由表的顺序。RPDB中的每个条目，具有一个优先级，优先级是0-32767之间的数字，越小优先级越高。下面是一个示例的路由策略：</p>
<pre class="crayon-plain-tag">ip rule list
9:      from all fwmark 0x200/0xf00 lookup 2004 
10:     from all fwmark 0xa00/0xf00 lookup 2005 
100:    from all lookup local 
32766:  from all lookup main 
32767:  from all lookup default</pre>
<p>当封包达到后，假设没有路由缓存，内核会首先检查优先级为0的RPDB规则，如果匹配，则使用该规则指向的路由表，如果不匹配，则依次检查低优先级的规则。</p>
<p>RPDB中的规则类型有若干种，和路由类型对应：</p>
<pre class="crayon-plain-tag"># unicast 这是最常见的规则类型，也是默认的规则类型。会导致查找某个路由表
# 如果源地址是192.168.100.17则查找表5
ip rule add unicast from 192.168.100.17 table 5
# 如果源接口是eth7则查找表5
ip rule add unicast iif eth7 table 5
# 如果封包具有标记4则查找4
ip rule add unicast fwmark 4 table 4


# nat 用于正确实现无状态NAT，通常和nat类型的路由条目耦合在一起。这种条目
# 会导致内核重写出站封包的源地址
#  对于源地址是172.16.82.184的封包，将源地址改写为193.7.255.184
ip rule add nat 193.7.255.184 from 172.16.82.184
ip rule add nat 10.40.0.0 from 172.40.0.0/16


# unreachable 如果匹配，则立即应答 ICMP unreachable 给源地址
# 如果是来自eth2的、服务类型0xc0的封包
ip rule add unreachable iif eth2 tos 0xc0
ip rule add unreachable iif wan0 fwmark 5
ip rule add unreachable from 192.168.7.0/25


# prohibit 如果匹配，则立即应答 ICMP prohibit 给源地址
ip rule add prohibit from 209.10.26.51
ip rule add prohibit to 64.65.64.0/18
ip rule add prohibit fwmark 7


# blackhole 如果匹配，封包被静默的丢弃
ip rule add blackhole from 209.10.26.51
ip rule add blackhole from 172.19.40.0/24
ip rule add blackhole to 10.182.17.64/28</pre>
<div class="blog_h2"><span class="graybg">源地址选择</span></div>
<p>对于具有多重IP地址的主机来说，进行相互通信时必须选择正确的源IP地址。</p>
<p>出站封包的源地址选择的原则如下：</p>
<ol>
<li>如果应用程序已经在使用套接字，则源地址已经被选择过</li>
<li>应用程序<span style="background-color: #c0c0c0;">可以显式请求一个源地址，这个地址甚至可以不是本机地址。</span>很多应用程序支持选取源地址，例如：<br />
<pre class="crayon-plain-tag">nc -s $BINDADDR $DEST $PORT
socat - TCP4:$REMOTEHOST:$REMOTEPORT,bind=$BINDADDR </pre>
</li>
<li>内核进行选路操作，如果<span style="background-color: #c0c0c0;">匹配路由存在src参数，则内核会利用该参数作为源地址</span></li>
<li>如果没有src提示，则内核会根据目的地址，找到本机上<span style="background-color: #c0c0c0;">第一个配置了与该地址在同一个网段、或者与该目的地址的路由下一跳地址在同一网段的IP地址的网络接口，并用此网络接口的IP地址</span>作为源地址</li>
</ol>
<div class="blog_h2"><span class="graybg">ICMP和路由</span></div>
<div class="blog_h3"><span class="graybg">PMTU</span></div>
<p>Path MTU即（Path Maximum Transmission Unit），ICMP报文可以用于发现PMTU，它是整个路由路径中，最小的MTU。知道这个值以后，可以避免不必要的IP分片。</p>
<p>路由中任何一跳阻止ICMP报文，都会导致无法识别PMTU。</p>
<div class="blog_h3"><span class="graybg">ICMP重定向</span></div>
<p>ICMP重定向是路由器提示发送者，具有更好路由路径的一种机制。配置静态路由可以防止不期望的ICMP重定向，尽管ICMP重定向不会导致危险，但是在良好管理的网络中是不应当发生的。</p>
<div class="blog_h2"><span class="graybg"><a id="ecmp"></a>ECMP</span></div>
<p>ECMP是一个逐跳的基于流的负载均衡策略，当路由器发现<span style="background-color: #c0c0c0;">同一目的地址出现多个最优路径时，会更新路由表，为此目的地址添加多条规则，对应于多个下一跳</span>。ECMP的路径选择策略有多种方法：</p>
<ol>
<li>哈希，例如根据源IP地址的哈希为流选择路径</li>
<li>轮询，各个流在多条路径之间轮询传输</li>
<li>基于路径权重，根据路径的权重分配流，权重大的路径分配的流数量更多</li>
</ol>
<p>OSPF、ISIS、EIGRP、BGP等多种路由协议均支持ECMP。</p>
<p>ECMP是一种较为简单的负载均衡策略，其在实际使用中面临的问题也不容忽视：</p>
<ol>
<li>可能增加链路的拥塞：ECMP并没有拥塞感知的机制，只是将流分散到不同的路径上转发。对于已经产生拥塞的路径来说，很可能加剧路径的拥塞</li>
<li>非对称网络使用效果不好</li>
</ol>
<div class="blog_h1"><span class="graybg">防火墙</span></div>
<div class="blog_h2"><span class="graybg">iptables</span></div>
<p>参考：<a href="/iptables">重温iptables</a></p>
<div class="blog_h1"><span class="graybg">流量控制</span></div>
<div class="blog_h3"><span class="graybg">tc</span></div>
<p>参考<a href="/tc">基于tc的网络QoS管理</a></p>
<div class="blog_h1"><span class="graybg">Offloading</span></div>
<p>Offloading是Linux内核中一系列用于减轻CPU网络处理负担的技术。</p>
<p>随着技术的进步，网络接口的带宽越来越大。从早期的10Mbps发展到现在的10Gbps+，带宽增加了3个数量级。与此同时，网络中能够传递的单个封包的大小，受限于路径MTU。在简单的局域网环境下，可以使用Jumbo Frame将MTU提升到9KB，但是在复杂的因特网环境下，MTU一直都只能是1500左右。作为后果，带宽打满的情况下CPU单位时间需要处理的封包个数比早期多了3个数量级。现代处理器在处理1Gbps网络接口时，通常能够应对，不需要考虑Offloading。</p>
<p>尽管这些年CPU的性能也有很大的提升，包括频率升高和核心数增加，但是减少需要处理的封包数量对于提升性能仍然是很有价值的。每个封包都需要经过网络栈，处理过程相当复杂。</p>
<div class="blog_h2"><span class="graybg">TSO</span></div>
<p>TCP Segmentation Offload，允许内核发送一个很大的封包，例如64KB，然后由（支持TSO的）网络适配器将封包分割为合适大小的TCP段传输。</p>
<p>TSO能够平均减少发送单个封包的开销达40倍，对于以发送为主的工作负载，例如下载服务器，足以让10Gbps网络全速工作。</p>
<div class="blog_h2"><span class="graybg">GSO</span></div>
<p>Generic Segmentation Offload，更加一般的segmentation offloading机制，可以用于UDP协议。</p>
<p>即使在驱动层模拟GSO，也能提升性能。</p>
<div class="blog_h2"><span class="graybg">LRO</span></div>
<p>在接收端进行Offloading的技术出现较晚，一方面早期的网络流量均是下行为主，另一方面接收端的处理难度要大的多。你无法控制什么时候接收封包，这是由对端控制的。</p>
<p>Large Receive Offload类似于GSO，它允许网络接口将接收到的封包进行合并，让操作系统看到的封包变少。即使在驱动层模拟LRO，也能提升性能。LRO被Linux中10Gbps网卡驱动广泛的支持。LRO仅仅支持TCP/IPv4。</p>
<p>LRO存在缺陷，它只是简单的将看到的封包都合并起来，如果包头中有差异这些差异会丢失。这些信息的丢失会导致问题：</p>
<ol>
<li>如果Linux主机作为路由器运行，那么它任何时候都不应该改变包头中的信息</li>
<li>某些基于卫星网络的连接，依赖于特殊的头才能正常运作</li>
<li>Linux网桥无法工作，导致很多虚拟化场景无法使用LRO</li>
</ol>
<div class="blog_h2"><span class="graybg">GRO</span></div>
<p>Generic Receive Offload，解决了LRO的缺陷。GRO严格限制了封包合并的条件：</p>
<ol>
<li>MAC地址必须相同</li>
<li>仅允许很少一部分的TCP或IP头不同</li>
</ol>
<p>这些限制让合并后的封包能够被无损的重新分段。<span style="color: #000000;">GRO code可以用于重新分段。</span></p>
<p>GRO不限制于TCP协议。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/network-faq">Linux网络知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/network-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux知识集锦</title>
		<link>https://blog.gmem.cc/linux-faq</link>
		<comments>https://blog.gmem.cc/linux-faq#comments</comments>
		<pubDate>Fri, 22 Sep 2006 05:15:15 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Linux知识]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=5883</guid>
		<description><![CDATA[<p>Linux编程 参考：Linux编程知识集锦 Unix知识 Unix简介 Unix系统是指遵循特定规范的计算机操作系统，这个规范称为“单一UNIX规范”，定义了所有UNIX系统必须提供的系统函数的名称、接口、行为，该规范很大程度上是POSIX规范的超集。 类Unix系统都是可移植操作系统接口（POSIX）的实现，UNIX具有以下显著特点： 种IO轮询对比 简洁性：仅提供数百个目的明确的系统调用 所有的东西都被当做文件对待。这种抽象使对数据和对设备的操作是通过一套相同的系统调用接口来进行的：open、read、write、lseek和 close 系统内核和相关的系统工具软件是用C语言编写而成 进程的创建非常迅速，并且有一个非常独特的fork系统调用 提供了一套非常简单但又很稳定的进程间通信元语 Unix已经发展成为一个支持抢占式多任务、多线程、虚拟内存、换页、动态链接和 TCP/IP网络的现代化操作系统。Unix的不同变体被应用在大到数百个CPU的集群，小到嵌入式设备的各种系统上。 和Unix一样，Linux是单内核的操作系统，它与传统的Unix系统有以下不同点： Linux支持动态加载内核模块。尽管Linux内核是单内核，可是允许在需要的时候动态地卸除和加载部分内核代码 Limix支持对称多处理（SMP)机制，传统的Unix 并不支持这种机制 Linux内核可以抢占（preemptive)。与传统的Unix变体不同，Linux内核允许在内核运行的任务优先执行 Linux内核支持内核线程，它并不区分线程和其他的一般进程。对于内核来说，所有的进程都一样，只是其中的一些共享内存资源 常用工具 <a class="read-more" href="https://blog.gmem.cc/linux-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-faq">Linux知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">Linux编程</span></div>
<p>参考：<a href="/linux-programming-faq">Linux编程知识集锦</a></p>
<div class="blog_h1"><span class="graybg">Unix知识</span></div>
<div class="blog_h2"><span class="graybg">Unix简介</span></div>
<p>Unix系统是指遵循特定规范的计算机操作系统，这个规范称为“单一UNIX规范”，定义了所有UNIX系统必须提供的系统函数的名称、接口、行为，该规范很大程度上是POSIX规范的超集。</p>
<p>类Unix系统都是可移植操作系统接口（POSIX）的实现，UNIX具有以下显著特点：</p>
<p>种IO轮询对比</p>
<ol>
<li>简洁性：仅提供数百个目的明确的系统调用</li>
<li>所有的东西都被当做文件对待。这种抽象使<span style="background-color: #c0c0c0;">对数据和对设备</span>的操作是通过一套相同的系统调用接口来进行的：open、read、write、lseek和 close</li>
<li>系统内核和相关的系统工具软件是用C语言编写而成</li>
<li>进程的创建非常迅速，并且有一个非常独特的fork系统调用</li>
<li>提供了一套非常简单但又很稳定的进程间通信元语</li>
</ol>
<p>Unix已经发展成为一个支持<span style="background-color: #c0c0c0;">抢占式多任务、多线程、虚拟内存、换页、动态链接和 TCP/IP网络</span>的现代化操作系统。Unix的不同变体被应用在大到数百个CPU的集群，小到嵌入式设备的各种系统上。</p>
<p>和Unix一样，Linux是单内核的操作系统，它与传统的Unix系统有以下不同点：</p>
<ol>
<li>Linux支持<span style="background-color: #c0c0c0;">动态加载内核模块</span>。尽管Linux内核是单内核，可是允许在需要的时候动态地卸除和加载部分内核代码</li>
<li>Limix支持对称多处理（SMP)机制，传统的Unix 并不支持这种机制</li>
<li>Linux内核可以抢占（preemptive)。与传统的Unix变体不同，Linux内核允许在内核运行的任务优先执行</li>
<li>Linux内核支持内核线程，它<span style="background-color: #c0c0c0;">并不区分线程和其他的一般进程</span>。对于内核来说，所有的进程都一样，只是其中的一些共享内存资源</li>
</ol>
<div class="blog_h2"><span class="graybg">常用工具</span></div>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2010/05/linux-tools.png"><img class="aligncenter  wp-image-22515" src="https://blog.gmem.cc/wp-content/uploads/2010/05/linux-tools.png" alt="linux-tools" width="924" height="584" /></a></p>
<p>&nbsp;</p>
<div class="blog_h1"><span class="graybg">ELF</span></div>
<p>可执行/链接文件格式（Executable and Linkable Format，ELF）是一种可执行文件、对象代码、共享库、coredump的格式。ELF最初作为Unix的ABI规范发布在System V第四版上，现在是Unix-like的操作系统的标准二进制文件格式。</p>
<p>ELF格式具有灵活性、可扩展性、跨平台性：</p>
<ol>
<li>支持不同的（endiannesses）端：也就是说同时支持BE/LE</li>
<li>支持不同的地址尺寸，不限制指令集或CPU类型</li>
</ol>
<div class="blog_h2"><span class="graybg">分类</span></div>
<p>从使用上来说，主要的ELF文件的种类主要有三类：</p>
<ol>
<li>可执行文件（.out）：Executable File，包含代码和数据，是可以直接运行的程序。其<span style="background-color: #c0c0c0;">代码和数据都有固定的地址 （或相对于基地址的偏移 ）</span>，系统可根据这些地址信息把程序加载到内存执行</li>
<li>可重定位文件（.o文件）：Relocatable File，包含基础代码和数据，但<span style="background-color: #c0c0c0;">它的代码及数据都没有指定绝对地址</span>，因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件</li>
<li>共享对象文件（.so）：Shared Object File，也称动态库文件，包含了代码和数据，这些数据是在<span style="background-color: #c0c0c0;">链接时被链接器（ld）</span>和<span style="background-color: #c0c0c0;">运行时动态链接器（ld.so.l、libc.so.l、ld-linux.so.l）</span>使用的</li>
</ol>
<div class="blog_h2"><span class="graybg">格式</span></div>
<p>ELF文件由一个ELF头 + 数据组成，其中数据包括：</p>
<ol>
<li>Program header table：描述0-N个内存segments，<span style="background-color: #c0c0c0;">这些segment在运行时使用</span></li>
<li>Section header table：描述0-N个sections，这些section在<span style="background-color: #c0c0c0;">链接、重定位时使用</span></li>
<li>由上面两个表所引用的数据</li>
</ol>
<p>文件中的任何一个字节，最多被一个section所拥有。</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2021/02/elf.png"><img class="wp-image-36303 aligncenter" src="https://blog.gmem.cc/wp-content/uploads/2021/02/elf.png" alt="elf" width="311" height="345" /></a></p>
<div class="blog_h3"><span class="graybg">ELF头</span></div>
<p>使用下面的命令可以查看ELF头：</p>
<pre class="crayon-plain-tag"># readelf -h /usr/local/bin/cgdb 
ELF Header:
  # 魔法数字
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  # 当取值为0时，是非法类别，1是32位的目标，2是64位的目标
  Class:                             ELF64
  # 表示数据的编码，当为0时，表示非法数据编码，1表示高位在前，2表示低位在前
  Data:                              2's complement, little endian
  # 头版本号
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  # 该elf文件是针对哪个处理器架构的
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  # 表示程序的入口地址
  Entry point address:               0x405cb0
  # Program segment 头表的位置
  Start of program headers:          64 (bytes into file)
  # Section头表的位置
  Start of section headers:          1859208 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         39
  Section header string table index: 36</pre>
<div class="blog_h3"><span class="graybg">Section</span></div>
<p>在可从重定位的可执行文件中，Section header table描述了文件的组成，Section的位置等信息。通过下面的命令可以查看Section信息：</p>
<pre class="crayon-plain-tag">readelf -S /usr/local/bin/mc
There are 14 section headers, starting at offset 0x1c8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000   # 代码段
       0000000000969856  0000000000000000  AX       0     0     16
  [ 2] .rodata           PROGBITS         0000000000d6b000  0096b000
       0000000000402661  0000000000000000   A       0     0     32
  [ 3] .shstrtab         STRTAB           0000000000000000  00d6d680
       00000000000000a5  0000000000000000           0     0     1
  [ 4] .typelink         PROGBITS         000000000116d740  00d6d740
       0000000000006f04  0000000000000000   A       0     0     32
  [ 5] .itablink         PROGBITS         0000000001174648  00d74648
       0000000000002668  0000000000000000   A       0     0     8
  [ 6] .gosymtab         PROGBITS         0000000001176cb0  00d76cb0
       0000000000000000  0000000000000000   A       0     0     1
  [ 7] .gopclntab        PROGBITS         0000000001176cc0  00d76cc0
       00000000006298c2  0000000000000000   A       0     0     32
  [ 8] .go.buildinfo     PROGBITS         00000000017a1000  013a1000
       0000000000000020  0000000000000000  WA       0     0     16
  [ 9] .noptrdata        PROGBITS         00000000017a1020  013a1020
       000000000006032c  0000000000000000  WA       0     0     32
  [10] .data             PROGBITS         0000000001801360  01401360   # 数据段
       0000000000034020  0000000000000000  WA       0     0     32
  [11] .bss              NOBITS           0000000001835380  01435380   # BSS段
       0000000000030dd0  0000000000000000  WA       0     0     32
  [12] .noptrbss         NOBITS           0000000001866160  01466160
       0000000000004148  0000000000000000  WA       0     0     32
  [13] .note.go.buildid  NOTE             0000000000400f9c  00000f9c
       0000000000000064  0000000000000000   A       0     0     4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)</pre>
<p>在编译器将一个一个.o文件链接成一个可以执行的elf文件的过程中，同时也生成了一个Section Header 表。这个表记录了各个Section所处的区域，这个表的每个Header包含以下字段：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">字段</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>sh_name</td>
<td>Section的名字，4字节，指向字符串表的索引</td>
</tr>
<tr>
<td>sh_type</td>
<td>
<p>Section的类型，4字节。下面是一些例子</p>
<p style="padding-left: 30px;">1 程序数据，例如.text .data .rodata<br />2 符号表<br />3 字符串表<br />4 带附加的重定位项<br />5 符号哈希表<br />6 动态链接信息<br />7 提示性信息<br />8 无数据程序空间（bss）<br />9 无附加的重定位项<br />10 保留<br />11 动态链接符号表<br />14 构造函数数组<br />15 析构函数数组</p>
</td>
</tr>
<tr>
<td>sh_flags</td>
<td>Section标记，每位对应一个标记。32位4字节，64位8字节</td>
</tr>
<tr>
<td>sh_addr</td>
<td>程序执行时，Section所在的虚拟地址。32位4字节，64位8字节</td>
</tr>
<tr>
<td>sh_offset</td>
<td>Section在文件内的偏移。32位4字节，64位8字节</td>
</tr>
<tr>
<td>sh_size</td>
<td>Section的大小（字节数）。32位4字节，64位8字节</td>
</tr>
<tr>
<td>sh_link</td>
<td>指向其它Section的索引。4字节</td>
</tr>
<tr>
<td>sh_info</td>
<td>额外信息。4字节</td>
</tr>
<tr>
<td>sh_addralign</td>
<td>Section载入内存时如何对其。32位4字节，64位8字节</td>
</tr>
<tr>
<td>sh_entsize</td>
<td>Section内每条记录的字节数。32位4字节，64位8字节</td>
</tr>
</tbody>
</table>
<p>如上，Section Header一共占用大小：32位40字节，64位64字节。</p>
<div class="blog_h3"><span class="graybg">Segment</span></div>
<p>链接视图由Sections组成，而可执行的文件的内容由Segments组成：</p>
<ol>
<li>每个Segments可以包含多个Sections</li>
<li>每个Sections可以属于多个Segments</li>
<li>Segments之间可以有重合的部分</li>
</ol>
<p>执行下面的命令可以得到Segment信息：</p>
<pre class="crayon-plain-tag"># readelf -l /usr/local/bin/mc

Elf file type is EXEC (Executable file)
Entry point 0x468ee0
There are 7 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000188 0x0000000000000188  R      1000
  NOTE           0x0000000000000f9c 0x0000000000400f9c 0x0000000000400f9c
                 0x0000000000000064 0x0000000000000064  R      4
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000096a856 0x000000000096a856  R E    1000        # R读W写E执行
  LOAD           0x000000000096b000 0x0000000000d6b000 0x0000000000d6b000
                 0x0000000000a35582 0x0000000000a35582  R      1000
  LOAD           0x00000000013a1000 0x00000000017a1000 0x00000000017a1000
                 0x0000000000094380 0x00000000000c92a8  RW     1000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
  LOOS+5041580   0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000         8

 # Segment和Section的映射关系，M:N
 Section to Segment mapping:
  Segment Sections...
   00     
   01     .note.go.buildid 
   02     .text .note.go.buildid 
   03     .rodata .typelink .itablink .gosymtab .gopclntab 
   04     .go.buildinfo .noptrdata .data .bss .noptrbss 
   05     
   06</pre>
<p>这些信息和程序的加载执行相关。 </p>
<div class="blog_h2"><span class="graybg">程序内存</span></div>
<p>Section中某些部分会<a href="/linux-kernel-study-note-vol3#process-address-space">映射到进程地址空间</a>。 <span style="background-color: #c0c0c0;">Stack、Heap等区域则是运行时动态在内存中创建的，在ELF中没有体现</span>。下面是一个C语言编译出的程序的典型内存布局：<a href="https://blog.gmem.cc/wp-content/uploads/2021/02/memorylayout.png"><img class="size-full wp-image-36319 aligncenter" src="https://blog.gmem.cc/wp-content/uploads/2021/02/memorylayout.png" alt="memorylayout" width="496" height="400" /></a></p>
<p>从下往上，内存地址从小变大。因此栈是向低地址方向增长的。</p>
<div class="blog_h1"><span class="graybg"><a id="endianness"></a>Endian</span></div>
<div class="blog_h2"><span class="graybg">字节序</span></div>
<p>就是字节的顺序：多字节数据类型，其<span style="background-color: #c0c0c0;">高、低字节，在内存中是如何分布</span>（地址高低）的。类似的衍生到，在<span style="background-color: #c0c0c0;">网络上，高、低字节谁先传输</span>。</p>
<p><span style="background-color: #c0c0c0;">字节内部不存在顺序问题</span>，大部分处理器以相同的顺序处理bit。</p>
<div class="blog_h3"><span class="graybg">内存</span></div>
<p>几乎所有机器中，多字节数据类型，都在内存中连续存储。如果一个int类型（4字节）x的地址位0x100，那么它的四个字节应该存储在0x100, 0x101, 0x102, 0x103 这些<span style="background-color: #c0c0c0;">内存地址上（内存寻址的精度是字节）</span>。</p>
<p>这4个字节的分布方式有两种：</p>
<ol>
<li>小端（little-endian，LE）：int的最低位字节，存放在最小内存地址上。依次排列其它字节。小端的处理器包括<span style="background-color: #c0c0c0;">x86</span>、MOS Technology 6502、Z80、VAX、PDP-11</li>
<li>大端（big-endian，BE）：int的最低位字节，存放在最大内存地址上。依次排列其它字节。大端的处理器包括Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC</li>
</ol>
<p>ARM、PowerPC（除PowerPC 970外）、DEC Alpha、SPARC V9、MIPS、PA-RISC及IA64的字节序是可配置的。</p>
<div class="blog_h3"><span class="graybg">网络</span></div>
<p>由于不同结构的机器，可能通过网络进行通信，因此必须对字节顺序进行规定，否则会出现混乱。<span style="background-color: #c0c0c0;">网络传输一般采用大端序</span>，也被称之为网络字节序，或网络序。IP协议中定义大端序为网络字节序。</p>
<div class="blog_h2"><span class="graybg">位序</span></div>
<p>一般用于描述串口设备的传输顺序，网络协议中只有数据链路层会涉及到。</p>
<p>使用小端（先传低bit）的协议包括：RS-232、RS-422、RS-485、USB、以太网。其中以太网，在字节序上遵从大端要求，但是每个字节内部，先传递低bit。</p>
<p>使用大端（先传高bit）的协议包括：I2C、SPI、莫尔斯电码</p>
<p>所谓命名空间，是Linux系统资源的隔离机制。进程可以加入到命名空间，并共享其中的系统资源。命名空间是容器技术的基础之一。</p>
<p>对于外面的进程，命名空间中的资源不可见的， 反之，内部的进程感觉自己拥有<span style="background-color: #c0c0c0;">完整的全局资源</span>。举例来说，PID是一种系统资源，每个命名空间都可以拥有自己的、值为1的PID，而不会出现混乱。</p>
<p>大部分类型的命名空间，创建时都需要CAP_SYS_ADMIN能力（Capability，即权限）。所谓能力就是细粒度化的Linux系统（Root）权限，例如CAP_KILL允许进程向任意进程发送信号，CAP_SYS_TIME允许进程修改系统时间。</p>
<div class="blog_h1"><span class="graybg"><a id="namespace"></a>Linux命名空间</span></div>
<div class="blog_h2"><span class="graybg">/proc/PID/ns目录</span></div>
<p>每个进程都有这样的一个目录，里面存放若干符号连接，提示进程所属的各种命名空间：</p>
<pre class="crayon-plain-tag">ls -la /proc/self/ns
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 ipc -&gt; ipc:[4026531839]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 mnt -&gt; mnt:[4026531840]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 net -&gt; net:[4026531968]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 pid -&gt; pid:[4026531836]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 user -&gt; user:[4026531837]
lrwxrwxrwx 1 alex alex 0 Nov 16 20:32 uts -&gt; uts:[4026531838]</pre>
<p>如果两个进程指向同一符号连接（方括号内的inode相同），则它们属于同一命名空间。</p>
<p>只要/proc/PID/ns下的文件<span style="background-color: #c0c0c0;">被打开，则相应的命名空间就会存在，即使命名空间内没有任何进程</span>。 下面的挂载命令即可保证当前进程的网络命名空间持续存在：</p>
<pre class="crayon-plain-tag">touch mynetns
sudo mount --bind /proc/self/ns/net mynetns 

# 绑定挂载具有打开被绑定者的文件描述符的效果
# 你甚至可以绑定挂载到自己，这种情况下，文件既是挂载目标，也是挂载点
# 尝试删除会提示Device or resource busy，需要先umount，再删除

# 绑定挂载后，你可以删除掉目标，此时挂载点的内容保持不变，底层文件依然存在</pre>
<div class="blog_h2"><span class="graybg">Cgroup命名空间</span></div>
<p>该命名空间虚拟化进程的Cgroups视图，也就是通过<pre class="crayon-plain-tag">/proc/[pid]/cgroup</pre>、<pre class="crayon-plain-tag">/proc/[pid]/mountinfo</pre>看到的Cgroup路径。</p>
<p>每个Cgroup命名空间<span style="background-color: #c0c0c0;">具有自己的Cgroup根目录集合</span>，文件/proc/[pid]/cgrpup中的路径，都是相对于这些根目录。在<pre class="crayon-plain-tag">clone</pre>或<pre class="crayon-plain-tag">unshare</pre>进程时，你可以指定<pre class="crayon-plain-tag">CLONE_NEWCGROUP</pre>标记，这会导致<span style="background-color: #c0c0c0;">进程当前的cgroups目录变为新命名空间的cgroup根目录</span>。此规则对于cgroups v1 v2均适用。</p>
<p>当你通过/proc/[pid]/cgrpup查看目标进程都归属于哪个Cgroup时，该文件的每一行的第3字段相对于<span style="background-color: #c0c0c0;">当前（读取Cgroup文件的）进程</span>的对应Cgroup子系统根目录。如果目标进程所属Cgroup目录在当前进程Cgroup对应子系统根目录之外，则第3字段中会出现<pre class="crayon-plain-tag">../</pre>表示上级目录。</p>
<p>上面这段规则很拗口，我们结合例子看： </p>
<pre class="crayon-plain-tag"># 在freezer子系统中创建一个子组
mkdir -p /sys/fs/cgroup/freezer/sub1


# 创建一个长时间运行的进程
sleep 10000 &amp; 
[1] 20124
# 将上述进程加入新创建的子组
echo 20124 &gt; /sys/fs/cgroup/freezer/sub1/cgroup.procs


# 现在，创建一个新的子组
mkdir -p /sys/fs/cgroup/freezer/sub2
# 将当前Shell加入到新子组
echo $$
30655
echo 30655 &gt; /sys/fs/cgroup/freezer/sub2/cgroup.procs
# 查看当前进程所属freezer组
cat /proc/self/cgroup | grep freezer
# 输出     相对于当前组Cgroup根目录，也就是 /sys/fs/cgroup/freezer/sub2/
7:freezer:/sub2


# 最后，在新的Cgroups中执行Shell：
cat /proc/self/cgroup | grep freezer
7:freezer:/
cat /proc/20124/cgroup | grep freezer
7:freezer:/../sub1</pre>
<div class="blog_h2"><span class="graybg">IPC命名空间</span></div>
<p>该命名空间隔离进程间通信资源，也就是System V IPC对象、 POSIX消息队列。以下/proc接口在每个IPC命名空间都是独立的：</p>
<ol>
<li>/proc/sys/fs/mqueue POSIX消息队列</li>
<li>/proc/sys/kernel下的System V接口，包括msgmax, msgmnb, msgmni, sem, shmall, shmmax, shmmni,shm_rmid_forced</li>
<li>/proc/sysvipc下的System V接口</li>
</ol>
<p>要启用IPC命名空间，在构建内核时需要指定<pre class="crayon-plain-tag">CONFIG_IPC_NS</pre>选项。</p>
<p>创建新进程时，使用<pre class="crayon-plain-tag">CLONE_NEWIPC</pre>标记可以启用新的IPC命名空间。</p>
<div class="blog_h2"><span class="graybg">Network命名空间</span></div>
<p>该命名空间隔离：</p>
<ol>
<li>网络设备</li>
<li>IPv4/IPv6网络栈</li>
<li>IP路由表</li>
<li>IPtables</li>
<li>端口（套接字）</li>
<li>/proc/net（/proc/self/net）目录</li>
<li>/sys/class/net目录</li>
<li>/proc/sys/net下若干文件</li>
<li>Unix Domain Socket</li>
</ol>
<p>等网络相关资源。创建新进程时，使用<pre class="crayon-plain-tag">CLONE_NEWNET</pre>标记可以开启新的网络命名空间。要启用网络命名空间，在构建内核时需要指定<pre class="crayon-plain-tag">CONFIG_NET_NS</pre>选项。</p>
<p><span style="background-color: #c0c0c0;">每个物理网络设备，仅仅能存在于单个网络命名空间中</span>。当网络命名空间终结（命名空间中最后一个进程退出）后，其中的<span style="background-color: #c0c0c0;">网络设备归还到初始网络命名空间</span>（而不是最后一个进程的父进程所属的网络命名空间）。</p>
<p>一个虚拟网络设备（veth）对，可以用于创建两个网络命名空间之间的、行为类似于管道的隧道，也可以用于创建到其它网络命名空间物理设备的网桥。当网络命名空间终结时，其veth设备自动销毁。</p>
<p>任何网络设备，包括veth都可以在不同网络命名空间中移动。在Kubernetes中基于Calico构建CNI时，对于每个Pod都会创建一个veth对，在Pod网络命名空间为eth0，在宿主机网络命名空间为cali***：</p>
<pre class="crayon-plain-tag"># 在宿主机上执行
ip link list
# ...
34: cali84f62caf29f@if4: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1440 qdisc noqueue state UP mode DEFAULT group default 
     link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 16


# 在容器中执行
ip link list
# ...
4: eth0@if34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1440 qdisc noqueue state UP mode DEFAULT group default 
    link/ether c2:8a:a9:d4:b5:46 brd ff:ff:ff:ff:ff:ff


# 容器以宿主机为路由器，和外部通信
# 在宿主机执行
route -n
172.27.68.135   0.0.0.0         255.255.255.255 UH    0      0        0 cali84f62caf29f </pre>
<p>上述命令的<pre class="crayon-plain-tag">@ifxx</pre>，显示了veth对端在它自己的命名空间的网络设备索引号。查看网络设备索引号的其它方法包括：</p>
<pre class="crayon-plain-tag">cat /sys/class/net/eth1/iflink

sudo ethtool -S eth1 | grep peer_ifindex</pre>
<div class="blog_h3"><span class="graybg">ip netns</span></div>
<p>这组命令可以用于网络命名空间的管理。</p>
<p>使用下面的命令可以添加一个网络命名空间：</p>
<pre class="crayon-plain-tag">ip netns add netns1</pre>
<p>网络命名空间创建后，会在<pre class="crayon-plain-tag">/var/run/netns</pre>下生成一个挂载点。 </p>
<p>使用下面的命令，可以显示可用的网络命名空间列表：</p>
<pre class="crayon-plain-tag">ip netns list </pre>
<p>使用下面的命令，可以在指定的网络命名空间中执行ip子命令：</p>
<pre class="crayon-plain-tag">ip netns exec netns1 ip link list
# 1: lo: &lt;LOOPBACK&gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default 
#     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00</pre>
<p>默认情况下，一个新的命名空间中仅有一个looback设备，而且处于DOWN状态，因此无法访问：</p>
<pre class="crayon-plain-tag">ip netns exec netns1 ping localhost
# connect: Network is unreachable

# 启用lo设备后即可访问
ip netns exec netns1 ip link set dev lo up</pre>
<p>仅仅有一个Loopback设备，是无法和外部通信的。</p>
<p>要达成通信的目的，必须创建一对虚拟以太网卡veth pair：</p>
<pre class="crayon-plain-tag">ip link add veth0 type veth peer name veth1</pre>
<p>veth pair总是成对出现且相互连接，从一端发出的报文可以在另一端接收到。 最初创建的veth pair都位于初始网络命名空间中，我们需要将其中的一端移动到netns1：</p>
<pre class="crayon-plain-tag">ip link set veth1 netns netns1

# 物理设备无法放入到自定义的网络命名空间
ip link set wlan3 netns netns1
# RTNETLINK answers: Invalid argument

# 把veth放回到初始命名空间
#                                      放到PID为1的进程所在的网络命名空间
ip netns exec netns1 ip link set veth1 netns 1</pre>
<p>然后启用veth设备，配置IP地址：</p>
<pre class="crayon-plain-tag"># 配置netns1端
ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
# 配置初始命名空间端
ifconfig veth0 10.1.1.2/24 up


# 测试可以相互通信
ping 10.1.1.1
ip netns exec netns1 ping 10.1.1.2</pre>
<p>新网络命名空间的路由表、Iptables也是隔离的：</p>
<pre class="crayon-plain-tag">ip netns exec netns1 route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 veth1

ip netns exec netns1 iptables -L
# 空白</pre>
<p>要访问外部网络，必须通过初始网络命名空间进行中转，具体方法有多种，例如：</p>
<ol>
<li>在初始网络命名空间创建一个Linux Bridge，将veth1和eth0桥接</li>
<li>配置NAT规则，启用IP转发（net.ipv4.ip_forward=1）</li>
</ol>
<p>使用下面的命令，可以删除网络命名空间：</p>
<pre class="crayon-plain-tag">ip netns delete netns1 </pre>
<p> 如果网络命名空间中仍然有进程在运行，则不会真正删除命名空间，仅仅移除其挂载点。</p>
<div class="blog_h2"><span class="graybg">Mount命名空间</span></div>
<p>该命名空间隔离挂载点，也就是隔离文件系统目录树中可以看到的内容。</p>
<p>文件<pre class="crayon-plain-tag">/proc/[pid]/mounts</pre>、<pre class="crayon-plain-tag">/proc/[pid]/mountinfo</pre>、<pre class="crayon-plain-tag">/proc/[pid]/mountstats</pre>中的内容，取决于目标进程所属的Mount命名空间。</p>
<p>创建新进程时，使用标记<pre class="crayon-plain-tag">CLONE_NEWNS</pre>可以创建新的Mount命名空间。新Mount命名空间的初始化挂载点列表如下：</p>
<ol>
<li>如果命名空间通过<pre class="crayon-plain-tag">clone</pre>创建，则挂载点列表是父进程Mount命名空间挂载点列表的副本</li>
<li>如果命名空间通过<pre class="crayon-plain-tag">unshare</pre>创建，则挂载点列表取决于调用者的Mount命名空间</li>
</ol>
<p>默认情况下，使用mount/umount系统调用修改挂载点列表，不会影响其它命名空间。</p>
<div class="blog_h3"><span class="graybg">注意点</span></div>
<p>关于Mount命名空间，需要注意：</p>
<ol>
<li>每个Mount命名空间归属于一个User命名空间。上文提到新创建Mount命名空间会复制挂载点，如果两个Mount命名空间所属的User命名空间不同，则新命名空间是less<br /> privileged的</li>
<li>当前创建了less privileged的Mount命名空间时，Shared Mount退化为Slave Mount，以确保在less privileged空间进行的mapping不会传播到more priviledeged命名空间</li>
<li>来自more privileged命名空间的挂载点，是一个不可分割的整体，不能在less privileged命名空间中被分离</li>
<li>mount调用的选项MS_RDONLY、MS_NOSUID、MS_NOEXEC，以及MS_NOATIME、MS_NODIRATIME、MS_RELATIME被锁定，不能在less privileged中被修改</li>
<li>一个文件或目录，在命名空间A中可能是挂载点，在命名空间B则不是挂载点（没有挂载文件系统）。这些目录或文件可能被重命名、unlink或者删除，其结果是，那些将其作为挂载点的命名空间，对应的挂载点会被删除。在3.18-版本中，重命名、unlink、删除这种挂载点目录，会导致EBUSY错误</li>
</ol>
<div class="blog_h3"><span class="graybg">共享子树</span></div>
<p>在某些情况下，Mount命名空间提供的隔离太重了。举例来说，要让一个新载入的光盘能够在所有命名空间可见，必须在每个命名空间执行挂载操作。为了避免这种麻烦，从2.6.15开始，内核引入了共享子树特性，该特性允许跨越命名空间的、受控传播的自动mount/umount。</p>
<p>每个挂载点的传播类型，可以设置为：</p>
<ol>
<li>MS_SHARED：表示该挂载点在对等组（Peer Group，一组挂载点）成员之间共享mount/umount事件。也就是说组中任何命名空间进行了mount，其它命名空间自动的也进行mount</li>
<li>MS_PRIVATE：表示该挂载点是私有的，不具有对等组</li>
<li>MS_SLAVE：允许来自（Master）共享对等组的mount/umount事件传播到此挂载点。反之，该挂载点发起的mount/umount事件则不会传播。注意<span style="background-color: #c0c0c0;">一个对等组可以是另外一个的Slave</span></li>
<li>MS_UNBINDABLE：类似于MS_PRIVATE，增加一个额外限制，该挂载点不能作为绑定挂载的源（mount --bind，用于将某个目录挂载到另一个位置，两个地方内容一致）</li>
</ol>
<p>强调两点：</p>
<ol>
<li><span style="background-color: #c0c0c0;">传播类型是针对每个挂载点来设置</span>的，一个挂载命名空间的中的不同挂载点可以具有不同设置</li>
<li>传播类型决定了挂载点下的mount/umount事件的传播：
<ol>
<li>在共享挂载点P下面，创建了一个子挂载点C，那么对等组的其它挂载点也会自动看到子挂载点C</li>
<li>但是，C下的子挂载点如何传播，取决于C的传播类型，而非P（取决于直接父挂载点）</li>
</ol>
</li>
</ol>
<p>下面是个实例：</p>
<pre class="crayon-plain-tag"># 打开终端1
# 在初始命名空间，设置根挂载点为私有
mount --make-private /

# 现在在根挂载点下，创建两个共享挂载点
mount --make-shared /dev/sda3 /X
mount --make-shared /dev/sda5 /Y

# 打开终端2，创建新的挂载命名空间
unshare -m --propagation unchanged bash


# 返回终端1，创建一个Bind挂载
mkdir /Z
mount --bind /X /Z

# 回到终端2，你可以看到 X Y但是看不到Z
# 原因是：
# 1. X Y来自Unshare时复制的挂载点
# 2. 终端0、1的根挂载点是对等组
# 3. 由于根挂载点的传播类型为私有，因此它创建的新的绑定挂载Z不会传播 </pre>
<p>通过文件 /proc/PID/mountinfo可以查看传播类型、对等组信息：</p>
<pre class="crayon-plain-tag">cat /proc/self/mountinfo | sed 's/ - .*//'
                           # 根挂载点是私有的，没有share标记
61 0 8:2 / / rw,relatime
                           # 传播类型为共享，对等组编号为1
81 61 8:3 / /X rw,relatime shared:1
124 61 8:5 / /Y rw,relatime shared:2
                           # 通过绑定挂载mount的Z，因此和源X位于一个对等组
228   61   8:3 / /Z rw,relatime shared:1
# ID 父ID  注意在新mount命名空间复制的挂载点，具有自己的ID</pre>
<div class="blog_h3"><span class="graybg">对等组</span></div>
<p>对等组是一组挂载点，它们相互传播mount/umount事件。</p>
<p>新创建的挂载点加入旧挂载点所在对等组的条件是：</p>
<ol>
<li>旧挂载点被设置为MS_SHARED</li>
<li>并且，满足以下之一：
<ol>
<li>创建新命名空间时旧挂载点被复制</li>
<li>从旧挂载点创建了一个新的绑定挂载</li>
</ol>
</li>
</ol>
<div class="blog_h3"><span class="graybg">传播类型默认值</span></div>
<p>新挂载点的默认传播类型为：</p>
<ol>
<li>如果挂载点有父亲（即非根挂载点），并且父亲的传播类型是 MS_SHARED，则新挂载点的传播类型也是 MS_SHARED</li>
<li>否则，新挂载的传播类型为 MS_PRIVATE</li>
</ol>
<p>Systemd将所有挂载点的传播类型设置为MS_SHARED，因此大部分现代Linux中，不经特别设置，传播类型为MS_SHARED。</p>
<div class="blog_h2"><span class="graybg">PID命名空间</span></div>
<p>该命名空间隔离进程ID空间，允许不同命名空间中的进程拥有相同的ID。要启用此特性，编译内核时需要指定<pre class="crayon-plain-tag">CONFIG_PID_NS</pre>选项。</p>
<p>PID命名空间允许容器：</p>
<ol>
<li>暂停/恢复容器中的一组进程</li>
<li>将容器迁移到新的宿主机，而保持容器中进程的PID不变</li>
</ol>
<p>利用PID命名空间，你可以暂停容器中的一组进程，并且在另外一台机器上恢复它们，而保持PID不变。</p>
<p>在新的PID命名空间中，生成的PID从1开始，看起来就像是独立的系统。fork/vfork/clone等系统调用会生成在当前命名空间中唯一的PID。</p>
<div class="blog_h3"><span class="graybg">命名空间的Init进程</span></div>
<p>新命名空间中创建（执行clone/unshare系统调用时指定CLONE_NEWPID标记）的第一个进程，具有PID 1，作为命名空间的"init"进程。此进程将作为命名空间中任何孤儿进程（其父进程提前死亡）的养父进程。</p>
<p>如果PID 1死亡，则内核<span style="background-color: #c0c0c0;">以SIGKILL信号杀死命名空间中所有</span>其它进程，此行为遵循规则：Init进程是PID命名空间执行正确行为的基础。在此情况下，针对命名空间的后续fork调用会导致ENOMEM错误。</p>
<p>只有已经被Init进程设置了处理器的信号，才可以从命名空间其它进程发送给Init进程。即使是特权进程也不能违反此规则，这防止Init进程被意外的杀死。</p>
<p>类似的，祖先命名空间中的进程，也只有在Init进程设置了处理器的前提下，才能发送信号给Init。但是<span style="background-color: #c0c0c0;">SIGKILL、SIGSTOP不受限制，这些信号被强制的从祖先命名空间递送给Init进程</span>，并且这两个信号不能被Init进程捕获处理，结果就是导致Init进程终结。</p>
<p>从内核3.4开始，系统调用reboot会导致命名空间中的Init进程接收到信号。</p>
<div class="blog_h3"><span class="graybg">嵌套PID命名空间</span></div>
<p>PID命名空间可以形成树状层次，除了初始（root，initial）命名空间之外，所有PID命名空间都具有一个父命名空间。父命名空间就是调用clone/unshare生成新PID命名空间的那个进程，所属的PID命名空间。从3.7开始，PID命名空间树的深度被限制为最多32。</p>
<p>一个进程可以被以下进程看到：</p>
<ol>
<li>同一命名空间内的进程</li>
<li>直接或者间接的<span style="background-color: #c0c0c0;">祖先命名空间中的进程</span></li>
</ol>
<p>这意味着初始命名空间可以看到所有进程。反之，<span style="background-color: #c0c0c0;">子代命名空间则不能看到祖代命名空间中的进程</span>。</p>
<p>所谓“看到”，是指可以以PID为参数，针对目标进程执行系统调用，例如kill、 setpriority。很显然，在每个祖代命名空间看到的，同一个实际进程的PID是不一样的。执行系统调用时，必须使用调用者所在命名空间的PID“视图”。</p>
<p>通过系统调用setns，进程可以自由的加入子代PID命名空间，但是却<span style="background-color: #c0c0c0;">不能加入祖代命名空间</span>。</p>
<div class="blog_h2"><span class="graybg">UTS命名空间</span></div>
<p>该命名空间提供两种系统标识符 —— 主机名（hostname）、NIS域名——的隔离。</p>
<p>通过clone/unshare系统调用创建新的UTS命名空间时，来自调用者UTS命名空间的标识符会自动拷贝过来。</p>
<div class="blog_h2"><span class="graybg">User命名空间</span></div>
<p>该命名空间隔离安全相关的标识符、属性。包括User IDs、Group IDs、root目录、密钥（keyrings）、能力（ Capabilities）。</p>
<p>User命名空间是一个特殊的命名空间，它和其它命名空间具有交互性，它可以拥有（Own）其它命名空间。</p>
<p>一个进程的UID、GID在命名空间内外可以不同。一个进程可以<span style="background-color: #c0c0c0;">在命名空间内部具有UID 0，而在外部仅仅具有一个非特权UID</span>，这意味着进程<span style="background-color: #c0c0c0;">可以在一个命名空间内进行特权操作，而在此命名空间之外却不可以</span>。</p>
<div class="blog_h3"><span class="graybg">嵌套User命名空间</span></div>
<p>类似于PID命名空间，User命名空间也是可以嵌套的。除了初始命名空间之外，所有User命名空间都具有一个父命名空间。<span style="background-color: #c0c0c0;">父命名空间就是创建新命名空间（clone/unshares时指定标记CLONE_NEWUSER）的那个进程所属的User命名空间</span>。</p>
<p>从3.11版本开始，内核限制User命名空间树的最大深度为32。</p>
<p>每个进程都是单个User命名空间的成员，单线程的进程如果<span style="background-color: #c0c0c0;">具有CAP_SYS_ADMIN能力则可以调用setns来加入其它User命名空间</span>，从而获得目标命名空间的所有能力（capabilities）。</p>
<p>执行系统调用execve会导致能力的重新计算，结果就是，除非进程在当前命名空间具有UID 0或者进程的可执行文件提供了一个非空的可继承的能力掩码（Capabilities mask），进程会丢失所有能力。</p>
<div class="blog_h3"><span class="graybg">能力</span></div>
<p>基于CLONE_NEWUSER创建的进程，拥有新创建的命名空间的所有能力。类似的，通过setns加入现有User命名空间后，进程具有目标命名空间的所有能力。相反的，进程在该命名空间的祖代命名空间、或者进程先前所在的User的命名空间，则没有任何能力。</p>
<p>确定命名空间中一个进程是否具有某项能力的规则如下：</p>
<ol>
<li>如果进程的<span style="background-color: #c0c0c0;">有效能力集（Effective capability set）</span>中设置了能力A，则它具有所在命名空间的能力A。进程的有效能力集可以因多种原因设置：
<ol>
<li>执行了Set User ID的程序</li>
<li>执行了具有关联的能力的可执行文件</li>
<li>因为clone/unshare/setns调用而设置的能力</li>
</ol>
</li>
<li>如果进程在命名空间中具有能力，则在<span style="background-color: #c0c0c0;">该命名空间的所有子代命名空间中同样具有相同的能力</span></li>
<li>当User命名空间被创建时，内核将创建它的进程的有效UID记录为命名空间的Owner。此命名空间的父命名空间中 ，任何有效UID为Owner的进程，都具有该User命名空间的所有能力</li>
</ol>
<div class="blog_h3"><span class="graybg">能力的作用</span></div>
<p>如果进程在User命名空间中具有能力，那么它就可以对该<span style="background-color: #c0c0c0;">命名空间管理的资源执行相应的特权操作</span>。更精确的说，是对<span style="background-color: #c0c0c0;">该User命名空间所拥有（关联）</span>的<span style="background-color: #c0c0c0;">（非User）命名空间</span>所<span style="background-color: #c0c0c0;">管理的资源</span>具有特权操作。举例来说，进程P1属于UTS命名空间N1，N1属于User命名空间N2，那么，P1必须在N2中持有CAP_SYS_ADMIN能力，才能执行sethostname系统调用。</p>
<p>从另一角度来说，很多特权操作会影响到，<span style="background-color: #c0c0c0;">不被任何命名空间管理的资源</span>。例如：</p>
<ol>
<li>修改时间（对应能力CAP_SYS_TIME）</li>
<li>加载内核模块（对应能力CAP_SYS_MODULE）</li>
<li>创建设备（对应能力CAP_MKNOD）</li>
</ol>
<p>这些操作，仅仅能由位于<span style="background-color: #c0c0c0;">初始User命名空间中的特权进程</span>执行。</p>
<p>在拥有进程所属Mount命名空间的User命名空间中，持有CAP_SYS_ADMIN能力，允许进程：</p>
<ol>
<li>创建Bind挂载</li>
<li>挂载以下类型的文件系统
<ol>
<li>/proc，要求内核3.8+</li>
<li>/sys，要求内核3.8+</li>
<li>devpts，要求内核3.9+</li>
<li>tmpfs，要求内核3.9+</li>
<li>ramfs，要求内核3.9+</li>
<li>mqueue，要求内核3.9+</li>
<li>bpf，要求内核4.4+</li>
</ol>
</li>
</ol>
<p>在拥有进程所属Cgroup命名空间的User命名空间中，持有CAP_SYS_ADMIN能力，允许进程：</p>
<ol>
<li>从内核4.6开始，挂载Cgroup v2文件系统</li>
<li>挂载Cgroup v1命名结构（即通过选项none,name=挂载的Cgroups文件系统）</li>
</ol>
<p>但是，挂载块设备的文件系统，必须要求进程具有初始User命名空间的CAP_SYS_ADMIN能力。</p>
<p>在拥有进程所属PID命名空间的User命名空间中，持有CAP_SYS_ADMIN能力，允许进程：</p>
<ol>
<li>挂载/proc文件系统</li>
</ol>
<div class="blog_h3"><span class="graybg">和其它命名空间的交互</span></div>
<p>从3.8开始，非特权进程可以创建User命名空间，而其它类型的命名空间，只需要调用者进程具有所在User命名空间中的CAP_SYS_ADMIN能力。</p>
<p>当一个非User命名空间被创建时，它<span style="background-color: #c0c0c0;">被创建者进程，在创建它的那个时刻，所属于的User命名空间，所拥有</span>（Own）。</p>
<p>非User命名空间管理了某些系统资源，要对这些资源进行特权操作，则操作者进程必须在拥有这些命名空间的User命名空间中，持有必要的能力。</p>
<p>执行clone/unshare系统调用时，如果指定了CLONE_NEWUSER的同时，也指定了其它CLONE_NEW*标记，那么内核会确保<span style="background-color: #c0c0c0;">User命名空间首先被创建</span>。并且将<span style="background-color: #c0c0c0;">随后创建的其它类型的命名空间的特权赋予子进程（clone）、调用者进程（unshare）</span>。</p>
<p>执行clone/unshare系统调用时，如果指定了非CLONE_NEWUSER之外的CLONE_NEW*标记，则内核会将调用者进程的User命名空间作为新创建的命名空间的Owner。这种<span style="background-color: #c0c0c0;">Owner关系是可以被改变</span>的。</p>
<div class="blog_h3"><span class="graybg">UID/GID映射</span></div>
<p>对于<span style="background-color: #c0c0c0;">新创建的</span>User命名空间，它的UID/GID到父User命名空间对应的UID/GID的<span style="background-color: #c0c0c0;">映射关系，是空的</span>。</p>
<p>UID/GID映射关系，通过文件<pre class="crayon-plain-tag">/proc/[pid]/uid_map</pre> 和 <pre class="crayon-plain-tag">/proc/[pid]/gid_map</pre>暴露。这些文件可以在一个User命名空间中读取，可以被<span style="background-color: #c0c0c0;">写入单次</span>以定义映射关系。</p>
<p>注意，uid_map文件中定义的是，以读取者进程的视角来看，<span style="background-color: #c0c0c0;">PID进程所属的User命名空间的UID ⇨ 读取者进程所属的User命名空间的UID</span>的映射关系。对于gid_map类似。</p>
<div class="blog_h3"><span class="graybg">小结</span></div>
<ol>
<li>进程在命名空间中</li>
<li>其它命名空间属于User命名空间</li>
<li>User命名空间具有父User命名空间</li>
<li>父User命名空间就是创建它的进程，当时所在的User命名空间</li>
<li>进程具有所属的User命名空间的所有能力</li>
<li>User命名空间具有Owner，它是创建它的那个进程的Effective UID</li>
</ol>
<div class="blog_h2"><span class="graybg">相关命令</span></div>
<p>参考<a href="/linux-command-faq#nsenter">Linux命令知识集锦</a>。</p>
<div class="blog_h2"><span class="graybg">相关系统调用</span></div>
<div class="blog_h3"><span class="graybg">setns</span></div>
<p>该系统调用用于将一个线程关联到命名空间：</p>
<pre class="crayon-plain-tag">#define _GNU_SOURCE
#include &lt;sched.h&gt;

// fd：引用目标命名空间的文件描述符，对应/proc/[pid]/ns/目录中的一个条目
// nstype：执行此系统调用的线程可以关联那些类型的命名空间：
//   0 允许任何类型的命名空间
//   CLONE_NEWIPC   fd必须是IPC命名空间
//   CLONE_NEWNET   fd必须是网络命名空间
//   CLONE_NEWUTS   fd必须是UTS命名空间
//   CLONE_NEWNS    fd必须是Mount命名空间
//   CLONE_NEWPID   fd必须是PID命名空间
//   CLONE_NEWUSER  fd必须是User命名空间
//   CLONE_NEWCGROUP fd必须是Cgroup命名空间（4.6+）
// 返回值：0表示成功，-1表示错误，errno：
//   EBADF 无效文件描述符
//   EINVAL fd引用的命名空间类型和nstype不匹配，或者关联线程到命名空间时出现错误
//   ENOMEM 没有足够内存
//   EPERM 调用者线程缺少必要的权限（CAP_SYS_ADMIN）
int setns(int fd, int nstype);</pre>
<p>下面是一个例子：</p>
<pre class="crayon-plain-tag">#define _GNU_SOURCE
#include &lt;fcntl.h&gt;
#include &lt;sched.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;

int main(int argc, char *argv[])
{
    int fd = open("/proc/1/ns/mnt", O_RDONLY);   /* 读取文件描述符 */
    if (fd == -1) exit(EXIT_FAILURE);
    // 加入命名空间
    if (setns(fd, 0) == -1)   exit(EXIT_FAILURE);
    // 在命名空间中执行命令
    char * argv[] = {"ls", "-l", "/proc", 0};
    // execvp可以执行一个用户指定的命令
    execvp( "ls", argv);
}</pre>
<p>注意各种加入各种命名空间，都具有很多限制条件：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 100px; text-align: center;">命名空间</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>User</td>
<td>
<p>当前进程必须具有<span style="background-color: #c0c0c0;">目标User命名空间的CAP_SYS_ADMIN权限</span>，这意味着只能进入子代User命名空间</p>
<p>加入命名空间后，将获得目标命名空间的所有能力，不论进程的UID/PID</p>
<p><span style="background-color: #c0c0c0;">多线程上下文</span>下（也就是多线程进程）<span style="background-color: #c0c0c0;">不能通过setns修改</span>User命名空间</p>
<p>进程也<span style="background-color: #c0c0c0;">不能重新进入自己本来的User命名空间</span>，这个限制防止已经丢弃某种能力的进程通过setns重新获得该能力</p>
<p>出于安全的考虑，如果进程和其它进程共享了文件系统有关的属性，则不能加入到新的mount命名空间</p>
</td>
</tr>
<tr>
<td>Mount</td>
<td>
<p>当前进程必须具有在其<span style="background-color: #c0c0c0;">本身的User命名空间</span>中具有<span style="background-color: #c0c0c0;">CAP_SYS_CHROOT、CAP_SYS_ADMIN</span>能力。同时，在<span style="background-color: #c0c0c0;">拥有目标Mount命名空间的User命名空间中具有CAP_SYS_ADMIN能力</span></p>
<p>出于安全的考虑，如果进程和其它进程共享了文件系统有关的属性，则不能加入到新的mount命名空间</p>
</td>
</tr>
<tr>
<td>PID</td>
<td>
<p>当前进程在本身的User命名空间，拥有目标PID命名空间的User命名空间中<span style="background-color: #c0c0c0;">都需要CAP_SYS_ADMIN</span>能力</p>
<p>只有调用者创建的<span style="background-color: #c0c0c0;">子进程的PID命名空间才被修改</span>，调用者本身的不会修改，这是setns针对PID命名空间的特殊之处</p>
<p><span style="background-color: #c0c0c0;">只能切换到子代</span>PID命名空间中</p>
</td>
</tr>
<tr>
<td>Cgroup</td>
<td>
<p>当前进程在本身的User命名空间，拥有目标PID命名空间的User命名空间中都需要<span style="background-color: #c0c0c0;">CAP_SYS_ADMIN</span>能力</p>
<p>此操作<span style="background-color: #c0c0c0;">不会改变调用者的Cgroups成员关系</span> —— 也就是说不会将其移动到其它Cgourp</p>
</td>
</tr>
<tr>
<td>Network</td>
<td rowspan="3">当前进程在本身的User命名空间，拥有目标PID命名空间的User命名空间中都需要<span style="background-color: #c0c0c0;">CAP_SYS_ADMIN</span>能力  </td>
</tr>
<tr>
<td>IPC</td>
</tr>
<tr>
<td>UTS</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">unshare</span></div>
<p>这个系统调用功能和setns相反，用于逃离某种命名空间：</p>
<pre class="crayon-plain-tag">int unshare(int flags);</pre>
<p>该调用的工作方式时，先创建flags指定的那些类型的命名空间，然后将进程移动到这些新的命名空间 。</p>
<div class="blog_h1"><span class="graybg"><a id="cgroup"></a>Cgroups</span></div>
<div class="blog_h2"><span class="graybg">子系统</span></div>
<p>控制组（Control Group）是Linux内核的一个特性，用于控制进程对CPU、磁盘I/O、内存、网络等资源的使用。cgroup具有树状层次，子cgroup会继承父cgroup的设置。cgroup被映射到Linux的文件系统树中，对应目录/sys/fs/cgroup，你可以基于文件系统接口来管理它。/sys/fs/cgroup的子目录对应cgroup的各controllers（子系统）：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子目录</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>blkio</td>
<td>为块设备设定输入/输出限制</td>
</tr>
<tr>
<td>cpu</td>
<td>控制cgroup中任务的CPU使用</td>
</tr>
<tr>
<td>cpuacct</td>
<td>自动生成cgroup中任务所使用的CPU报告</td>
</tr>
<tr>
<td>cpuset</td>
<td>为cgroup中的任务分配独立CPU和内存节点</td>
</tr>
<tr>
<td>devices</td>
<td>允许或者拒绝cgroup中的任务访问设备</td>
</tr>
<tr>
<td>freezer</td>
<td>挂起或者恢复cgroup中的任务</td>
</tr>
<tr>
<td>memory</td>
<td>设定cgroup中任务使用的内存限制，并自动生成由那些任务使用的内存资源的报告</td>
</tr>
<tr>
<td>net_cls</td>
<td>使用classid标记网络数据包，可允许Linux流量控制程序tc识别从具体cgroup中生成的数据报</td>
</tr>
<tr>
<td>ns</td>
<td>名称空间子系统</td>
</tr>
</tbody>
</table>
<p>你可以执行mount命令看到多个类型为cgroup的文件系统，正好与上面这些子目录对应。</p>
<p>将任务（进程）的ID放入到一个cgroup中，该任务就受到cgroup的控制。</p>
<div class="blog_h2"><span class="graybg">使用cgroup</span></div>
<p>要新建一个cgroup，必须创建某个controller的子目录：</p>
<pre class="crayon-plain-tag"># 创建
mkdir /cgroup/cpu/test
# 删除
rmdir /cgroup/cpu/test</pre>
<p>注意子目录可以嵌套，对应了cgroup的层次。子目录中可以有一些文件，对应了当前cgroup的配置：</p>
<pre class="crayon-plain-tag"># 修改参数，CPU占用权重
echo 2048 &gt; /cgroup/cpu/test/cpu.shares
# 将任务加入到cgroup
echo $PID &gt; /cgroup/cpu/test/tasks</pre>
<p>你可以在指定的cgroup中执行命令：</p>
<pre class="crayon-plain-tag">sudo apt-get install cgroup-bin
cgexec -g cpu:test command args</pre>
<p>其它相关命令：</p>
<pre class="crayon-plain-tag"># 查看所有的cgroup
lscgroup
# 查看所有的子系统：
lssubsys -am
# 查看一个进程属于哪个cgroup
ps -O cgroup</pre>
<div class="blog_h1"><span class="graybg">可观察性</span></div>
<div class="blog_h2"><span class="graybg">追踪点</span></div>
<p>追踪点（tracepoint）是内核中静态编写的挂钩点，它允许调用一个动态挂钩的函数。</p>
<p>追踪点的状态可以是on/off，如果没有函数挂钩，则状态为off。off状态的追踪点有很小的时间成本（需要检查一个条件分支）和空间成本（在被instrument函数尾部增加若干字节的代码、在单独的section增加一个数据结构）。on状态的追踪点，会在每次到达时，在caller的执行上下文中，调用你提供的函数。</p>
<div class="blog_h2"><span class="graybg">Kprobes</span></div>
<p>Kprobe允许你动态的挂钩到几乎任何内核函数，从而无入侵的收集调试、性能信息。</p>
<p>Kprobe分为两类：</p>
<ol>
<li>kprobe：可以几乎在内核的任何指令处挂钩</li>
<li>return probe：当被挂钩的函数退出时，执行钩子</li>
</ol>
<div class="blog_h1"><span class="graybg"><a id="nss"></a>NSS</span></div>
<p>命名服务交换机（Name Service Switch）是类UNIX系统的一种名称解析机制。它以：</p>
<ol>
<li>本地系统配置文件，例如/etc/passwd、/etc/group、/etc/hosts</li>
<li>DNS服务器</li>
<li>网络信息服务（Network Information Service，NIS）</li>
<li>LDAP</li>
</ol>
<p>等多种文件/服务作为数据来源。来提供针对各种系统数据库（System database）的查询服务。</p>
<p>通过调用GNU C库中的函数，可以发起NSS查询。GNU C库的主机名查找，就是通过这种方式实现的。</p>
<div class="blog_h2"><span class="graybg">信息类型</span></div>
<p>即数据库类型：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">信息类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">aliases</td>
<td>邮件别名</td>
</tr>
<tr>
<td class="blog_h3">ethers</td>
<td>以太网编号</td>
</tr>
<tr>
<td class="blog_h3">group</td>
<td>用户组信息</td>
</tr>
<tr>
<td class="blog_h3">hosts</td>
<td>主机名信息</td>
</tr>
<tr>
<td class="blog_h3">netgroup</td>
<td>网络范围的主机/用户信息</td>
</tr>
<tr>
<td class="blog_h3">networks</td>
<td>网络名信息</td>
</tr>
<tr>
<td class="blog_h3">passwd</td>
<td>用户身份信息</td>
</tr>
<tr>
<td class="blog_h3">protocols</td>
<td>网络协议</td>
</tr>
<tr>
<td class="blog_h3">services</td>
<td>网络服务信息</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">信息源</span></div>
<p>即数据库的源：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">源类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">files</td>
<td>存放在客户端的/etc目录中的文件</td>
</tr>
<tr>
<td class="blog_h3">nisplus</td>
<td>一个NIS+表，例如hosts表</td>
</tr>
<tr>
<td class="blog_h3">nis</td>
<td>一个NIS映射，例如hosts映射</td>
</tr>
<tr>
<td class="blog_h3">compat</td>
<td>用于密码等敏感信息的/etc目录中的文件</td>
</tr>
<tr>
<td class="blog_h3">dns</td>
<td>从DNS获取主机信息</td>
</tr>
<tr>
<td class="blog_h3">ldap</td>
<td>从LDAP目录获取信息</td>
</tr>
<tr>
<td class="blog_h3">mdns4_minimal</td>
<td>Ubuntu中出现的源，它仅在名称以.local结尾时才尝试通过MDNS（multicast DNS）解析名称</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">nsswitch.conf</span></div>
<p>此文件列出各种数据库（passwd, shadow, group等）以及一个或多个获取数据的源：</p>
<pre class="crayon-plain-tag"># 数据库名字     可能的源的列表
passwd:         compat
group:          compat
shadow:         compat
gshadow:        files

#               发起针对hosts数据库的查询时，逐个尝试这些源
hosts:          files mdns4_minimal [NOTFOUND=return] dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis</pre>
<div class="blog_h2"><span class="graybg">规则</span></div>
<p>除了为数据库指定源外，还可以指定规则 —— 针对特定查找结果的操作。例如上面的 <pre class="crayon-plain-tag">[NOTFOUND=return]</pre>，表示如果通过mdns4_minimal源得到的结果是NOTFOUND，则立即返回，不去尝试下一个源，也就是dns</p>
<div class="blog_h3"><span class="graybg">状态：</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">状态</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class="blog_h3">success</td>
<td>没有错误，需要的条目已经找到</td>
</tr>
<tr>
<td class="blog_h3">notfound</td>
<td>查找过程正常，但是找不到条目。默认操作是continue</td>
</tr>
<tr>
<td class="blog_h3">unavail</td>
<td>服务永久的不可用（可能表示需要的文件不存在，或者DNS这样的服务器不可用/不允许查询）。默认操作是continue</td>
</tr>
<tr>
<td class="blog_h3">tryagain</td>
<td>服务临时的不可用（可能表示需要的文件被锁住，或者DNS这样的服务器暂时无法处理连接）。默热年操作是continue</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">操作：</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">操作</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td class=" blog_h3">return</td>
<td>
<p>停止查找过程：</p>
<ol>
<li>如果条目找到了，则返回给客户端</li>
<li>如果出现错误，报告给客户端</li>
<li>如果前面有merge操作，则将条目和先前的查找结果进行合并</li>
</ol>
</td>
</tr>
<tr>
<td class=" blog_h3">continue</td>
<td>继续使用后面的源进行查找</td>
</tr>
<tr>
<td class=" blog_h3">merge</td>
<td>保留当前查找结果，后后续的源的查找结果进行合并</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">环境变量</span></div>
<div class="blog_h3"><span class="graybg"><a id="envars-locale"></a>Locale相关</span></div>
<p>Locale直译为区域，是国际化/本地化过程中重要的概念。Localhe和某一个地域内的人们的语言习惯、文化传统和生活习惯有关。Linux下有多个与Locale相关的环境变量，可以影响OS的行为。</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">环境变量</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>LANGUAGE</td>
<td>用于设置消息语言（LC_MESSAGES）为多重值，例如<pre class="crayon-plain-tag">zh:en</pre> 表示消息语言尽可能使用中文，缺失时使用英文</td>
</tr>
<tr>
<td>LANG</td>
<td>通常情况下，<span style="background-color: #c0c0c0;">用户</span>使用该环境变量设置Locale，为所有没有显式设置的LC_XXX<span style="background-color: #c0c0c0;">提供默认值</span></td>
</tr>
<tr>
<td>LC_XXX</td>
<td>
<p>一系列环境变量，用于覆盖LANG并且影响Locale的某个方面</p>
<p>这些环境变量包括：<br />LC_CTYPE  用户所使用的语言符号及其分类<br />LC_NUMERIC 数字格式<br />LC_TIME 时间显示格式<br />LC_COLLATE 比较和排序习惯<br />LC_MONETARY 货币单位<br />LC_MEASUREMENT 度量衡表达方式<br />LC_ADDRESS  地址书写方式<br />LC_TELEPHONE 电话号码书写方式<br />LC_NAME 姓名书写方式<br />LC_PAPER 默认纸张尺寸大小<br />LC_MESSAGES</p>
</td>
</tr>
<tr>
<td>LC_ALL</td>
<td>
<p>覆盖上面所有Locale的方面以及LANG，通常用于<span style="background-color: #c0c0c0;">执行特定程序的脚本</span>中，<span style="background-color: #c0c0c0;">不应当在用户环境</span>下使用</p>
<p>这些环境变量<span style="background-color: #c0c0c0;">优先级从高到低</span>：LC_ALL ⇨ LC_XXX ⇨ LANG</p>
</td>
</tr>
<tr>
<td>C_INCLUDE_PATH</td>
<td>
<p>C头文件搜索位置</p>
</td>
</tr>
<tr>
<td>CPLUS_INCLUDE_PATH</td>
<td>
<p>C++头文件搜索位置</p>
</td>
</tr>
<tr>
<td>CPATH</td>
<td>
<p>C/C++头文件搜索位置</p>
</td>
</tr>
<tr>
<td>LIBRARY_PATH</td>
<td>
<p>编译期间，GCC基于此环境变量来定位库所在的目录</p>
</td>
</tr>
<tr>
<td><span style="color: #242729;">LD_LIBRARY_PATH</span></td>
<td>
<p>链接时，可以通过此环境变量指定目标共享库的位置</p>
<p>运行期间，基于此环境变量寻找共享库</p>
</td>
</tr>
</tbody>
</table>
<p>Locale相关环境变量的值使用一个字符串表示，格式为：<pre class="crayon-plain-tag">语言[_地域[.字符集]]</pre> ，例如：<pre class="crayon-plain-tag">zh_CN.GB2312</pre> 表示中文_中华人民共和国.国标2312字符集；<pre class="crayon-plain-tag">zh_TW.BIG5</pre> 表示中文_台湾.大五码字符集；<pre class="crayon-plain-tag">en_GB.ISO-8859-1</pre> 表示英文_英国.ISO-8859-1字符集；<pre class="crayon-plain-tag">en_US.UTF-8</pre> 表示英语_美国.UTF-8字符集，等等。</p>
<div class="blog_h3"><span class="graybg">代理相关</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">环境变量</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>HTTP_PROXY</td>
<td>指定一个代理服务器，应用程序可以基于此环境变量指定的代理连接到网络。示例：<br />
<pre class="crayon-plain-tag">http_proxy=http://USERNAME:PASSWORD@PROXYIP:PROXYPORT</pre>
</td>
</tr>
<tr>
<td>HTTPS_PROXY</td>
<td>指定一个代理服务器，应用程序可以基于此环境变量指定的代理连接到网络，来访问HTTPS</td>
</tr>
<tr>
<td>NO_PROXY</td>
<td>指定不通过代理连接的IP或者域名列表。示例：<br />
<pre class="crayon-plain-tag"># 注意不支持IP通配符
no_proxy="localhost,127.0.0.1,.gmem.cc,10.0.0.1" </pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">内核参数</span></div>
<div class="blog_h2"><span class="graybg"><a id="kernel-params"></a>参数列表</span></div>
<p>参考：<a href="https://www.cnblogs.com/tolimit/p/5065761.html">https://www.cnblogs.com/tolimit/p/5065761.html</a>。</p>
<div class="blog_h3"><span class="graybg">未归类</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 30%;">kernel.acct</td>
<td>acct功能用于系统记录进程信息，正常结束的进程都会在该文件尾添加对应的信息。异常结束是指重启或其它致命的系统问题，不能够记录永不停止的进程。该设置需要配置三个值，分别是：<br /> 1.如果文件系统可用空间低于这个百分比值，则停止记录进程信息。<br /> 2.如果文件系统可用空间高于这个百分比值，则开始记录进程信息。<br /> 3.检查上面两个值的频率(以秒为单位)。</td>
</tr>
<tr>
<td>kernel.auto_msgmni</td>
<td>系统自动设置同时运行的消息队列个数。<br /> 0：不自动<br /> 1：自动</td>
</tr>
<tr>
<td>kernel.blk_iopoll</td>
<td> </td>
</tr>
<tr>
<td>kernel.cad_pid</td>
<td>接收Ctrl-alt-del操作的INT信号的进程的PID</td>
</tr>
<tr>
<td>kernel.cap_last_cap</td>
<td>系统capabilities最高支持的权限等级。<br /> 详见：http://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html</td>
</tr>
<tr>
<td>kernel.compat-log</td>
<td> </td>
</tr>
<tr>
<td>kernel.core_pattern</td>
<td>设置core文件保存位置或文件名,只有文件名时，则保存在应用程序运行的目录下</td>
</tr>
<tr>
<td>kernel.core_pipe_limit</td>
<td>定义了可以有多少个并发的崩溃程序可以通过管道模式传递给指定的core信息收集程序。如果超过了指定数，则后续的程序将不会处理，只在内核日志中做记录。0是个特殊的值，当设置为0时，不限制并行捕捉崩溃的进程，但不会等待用户程序搜集完毕方才回收/proc/pid目录（就是说，崩溃程序的相关信息可能随时被回收，搜集的信息可能不全）。</td>
</tr>
<tr>
<td>kernel.core_uses_pid</td>
<td>Core文件的文件名是否添加应用程序pid做为扩展<br /> 0：不添加<br /> 1：添加</td>
</tr>
<tr>
<td>kernel.ctrl-alt-del</td>
<td>该值控制系统在接收到 ctrl+alt+delete<br /> 按键组合时如何反应：<br /> 1：不捕获ctrl-alt-del，将系统类似于直接关闭电源<br /> 0：捕获ctrl-alt-del，并将此信号传至cad_pid保存的PID号进程进行处理</td>
</tr>
<tr>
<td>kernel.dmesg_restrict</td>
<td>限制哪些用户可以查看syslog日志<br /> 0：不限制<br /> 1：只有特权用户能够查看</td>
</tr>
<tr>
<td>kernel.domainname</td>
<td>网络域名(重启失效)</td>
</tr>
<tr>
<td>kernel.ftrace_dump_on_oops</td>
<td>确定是否将ftrace的缓冲区的信息打印出来，是通过printk来打印的<br /> 0：不打印<br /> 1：在系统oops时，自动dump堆栈信息到输出终端</td>
</tr>
<tr>
<td>kernel.hostname</td>
<td>主机名(重启失效)</td>
</tr>
<tr>
<td>kernel.hotplug</td>
<td>该文件给出了当前系统支持热插拔(hotplug)时接收热插拔事件的程序的名字（包括路径）。</td>
</tr>
<tr>
<td>kernel.hung_task_check_count</td>
<td>hung_task检查的进程数量最大值</p>
<p>hung_task用于检测一个进程是否在TASK_UNINTERRUPTIBLE状态过长，只有在等待IO的时候进程才会处于TASK_UNINTERRUPTIBLE状态，这个状态的进程内核不能够通过信号将其唤醒并杀死。</p>
</td>
</tr>
<tr>
<td>kernel.hung_task_panic</td>
<td>设置hung_task发生后是否引发panic<br /> 1：触发<br /> 0：不触发</td>
</tr>
<tr>
<td>kernel.hung_task_timeout_secs</td>
<td>hung_task超时时间(以秒为单位)，当一个进程在TASK_UNINTERRUPTIBLE状态超过这个时间后，会发生一个hung_task<br /> linux会设置40%的可用内存用来做系统cache，当flush数据时这40%内存中的数据由于和IO同步问题导致超时。</td>
</tr>
<tr>
<td>kernel.hung_task_warnings</td>
<td>最大产生警告数量，当发生一次hung_task时会产生一次警告，但警告数量到达此值后之后的hung_task就不会发生警告</td>
</tr>
<tr>
<td>kernel.kexec_load_disabled</td>
<td>表示kexec_load系统调用是否被禁止，此系统调用用于kdump。当发生了一次kexec_load后，此值会自动设置为1。<br /> 0：开启kexec_load系统调用<br /> 1：禁止kexec_load系统调用</td>
</tr>
<tr>
<td>kernel.keys.gc_delay</td>
<td> </td>
</tr>
<tr>
<td>kernel.keys.maxbytes</td>
<td> </td>
</tr>
<tr>
<td>kernel.keys.maxkeys</td>
<td> </td>
</tr>
<tr>
<td>kernel.keys.persistent_keyring_expiry</td>
<td> </td>
</tr>
<tr>
<td>kernel.keys.root_maxbytes</td>
<td> </td>
</tr>
<tr>
<td>kernel.keys.root_maxkeys</td>
<td> </td>
</tr>
<tr>
<td>kernel.kptr_restrict</td>
<td><span class="font7">是否启用kptr_restrice，此功能为安全性功能，用于屏蔽内核指针。</span><span class="font6"><br /> </span><span class="font7"><span class="font7">0：该特性被完全禁止;</span></span></p>
<p>1：那些使用“%pk”打印出来的内核指针被隐藏(会以一长串0替换掉)，除非用户有CAP_SYSLOG权限，并且没有改变他们的UID/GID(防止在撤销权限之前打开的文件泄露指针信息);</p>
<p>2：所有内核指使用“%pk”打印的都被隐藏。</p>
</td>
</tr>
<tr>
<td>kernel.max_lock_depth</td>
<td>触发死锁检查的嵌套深度值</td>
</tr>
<tr>
<td>kernel.modprobe</td>
<td>该文件给出了当系统支持module时完成modprobe功能的程序的名字（包括路径）。</td>
</tr>
<tr>
<td>kernel.modules_disabled</td>
<td>表示是否禁止内核运行时可加载模块<br /> 0：不禁止<br /> 1：禁止</td>
</tr>
<tr>
<td>kernel.msgmax</td>
<td>消息队列中单个消息的最大字节数</td>
</tr>
<tr>
<td>kernel.msgmnb</td>
<td>单个消息队列中允许的最大字节长度(限制单个消息队列中所有消息包含的字节数之和)</td>
</tr>
<tr>
<td>kernel.msgmni</td>
<td>系统中同时运行的消息队列的个数</td>
</tr>
<tr>
<td>kernel.ngroups_max</td>
<td>每个用户最大的组数</td>
</tr>
<tr>
<td>kernel.nmi_watchdog</td>
<td>使能nmi_watchdog<br /> 0：禁止<br /> 1：开启</td>
</tr>
<tr>
<td>kernel.numa_balancing</td>
<td>是否开启numa_balancing？这块具体看代码</td>
</tr>
<tr>
<td>kernel.numa_balancing_scan_delay_ms</td>
<td>单个进程每次进行numa_balancing扫描的间隔时间</td>
</tr>
<tr>
<td>kernel.numa_balancing_scan_period_max_ms</td>
<td>每次扫描最多花费的时间？</td>
</tr>
<tr>
<td>kernel.numa_balancing_scan_period_min_ms</td>
<td>每次扫描最少花费的时间？</td>
</tr>
<tr>
<td>kernel.numa_balancing_scan_size_mb</td>
<td>一次扫描进程多少MB的虚拟地址空间内存</td>
</tr>
<tr>
<td>kernel.numa_balancing_settle_count</td>
<td> </td>
</tr>
<tr>
<td>kernel.osrelease</td>
<td>内核版本(例：3.10.0-229.7.2.rs1.2.ppc64)</td>
</tr>
<tr>
<td>kernel.ostype</td>
<td>操作系统的类型(例：Linux)</td>
</tr>
<tr>
<td>kernel.overflowgid</td>
<td>Linux的GID为32位，但有些文件系统只支持16位的GID，此时若进行写操作会出错；当GID超过65535时会自动被转换为一个固定值，这个固定值保存在这个文件中</td>
</tr>
<tr>
<td>kernel.overflowuid</td>
<td>Linux的UID为32位，但有些文件系统只支持16位的UID，此时若进行写操作会出错；当UID超过65535时会自动被转换为一个固定值，这个固定值保存在这个文件中</td>
</tr>
<tr>
<td>kernel.panic</td>
<td>系统发生panic时内核重新引导之前的等待时间<br /> 0：禁止重新引导<br /> &gt;0：重新引导前的等待时间(秒)</td>
</tr>
<tr>
<td>kernel.panic_on_oops</td>
<td>当系统发生oops或BUG时，所采取的措施<br /> 0：继续运行<br /> 1：让klog记录oops的输出，然后panic，若kernel.panic不为0，则等待后重新引导内核</td>
</tr>
<tr>
<td>kernel.panic_on_warn</td>
<td>0：只警告，不发生panic<br /> 1：发生panic</td>
</tr>
<tr>
<td>kernel.perf_cpu_time_max_percent</td>
<td>perf分析工具最大能够占用CPU性能的百分比<br /> 0：不限制<br /> 1~100：百分比值</td>
</tr>
<tr>
<td>kernel.perf_event_max_sample_rate</td>
<td>设置perf_event的最大取样速率，默认值为100000</td>
</tr>
<tr>
<td>kernel.perf_event_mlock_kb</td>
<td>设置非特权用户能够允许常驻内存的内存大小。默认为516(KB)</td>
</tr>
<tr>
<td>kernel.perf_event_paranoid</td>
<td>用于限制访问性能计数器的权限<br /> 0：仅允许访问用户空间的性能计数器<br /> 1：内核与用户空间的性能计数器都可以访问<br /> 2：仅允许访问特殊的CPU数据(不包括跟踪点)<br /> -1：不限制</td>
</tr>
<tr>
<td>kernel.pid_max</td>
<td>进程pid号的最大值</td>
</tr>
<tr>
<td>kernel.poweroff_cmd</td>
<td>执行关机命令的进程(包括路径)</td>
</tr>
<tr>
<td>kernel.powersave-nap</td>
<td>PPC专用，如果开启，则使用nap节能模式，关闭则使用doze节能模式<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>kernel.print-fatal-signals</td>
<td> </td>
</tr>
<tr>
<td>kernel.printk</td>
<td>该文件有四个数字值，它们根据日志记录消息的重要性，定义将其发送到何处。按顺序是：<br /> 1.控制台日志级别：优先级高于该值的消息将被打印至控制台<br /> 2.默认的消息日志级别：将用该优先级来打印没有优先级的消息<br /> 3.最低的控制台日志级别：控制台日志级别可被设置的最小值(最高优先级)<br /> 4.默认的控制台日志级别：控制台日志级别的缺省值<br /> 数值越小，优先级越高，级别有(0~7)</td>
</tr>
<tr>
<td>kernel.printk_delay</td>
<td>printk 消息之间的延迟毫秒数，此值不可设置</td>
</tr>
<tr>
<td>kernel.printk_ratelimit</td>
<td>等待允许再次printk的时间(以秒为单位)，与printk_ratelimit()函数有关<br /> 详见：http://m.blog.csdn.net/blog/chenglinhust/8599159</td>
</tr>
<tr>
<td>kernel.printk_ratelimit_burst</td>
<td>printk的缓存队列长度(每个printk为一个长度，比如此值为5，而有段代码是连续printk10次，系统的处理是先printk前5次，等待printk_ratelimit秒后，再打印后面5次)</td>
</tr>
<tr>
<td>kernel.pty.max</td>
<td>所能分配的PTY的最多个数(pty为虚拟终端，用于远程连接时)</td>
</tr>
<tr>
<td>kernel.pty.nr</td>
<td>当前分配的pty的个数</td>
</tr>
<tr>
<td>kernel.pty.reserve</td>
<td> </td>
</tr>
<tr>
<td>kernel.random.boot_id</td>
<td>此文件是个只读文件，包含了一个随机字符串，在系统启动的时候会自动生成这个uuid</td>
</tr>
<tr>
<td>kernel.random.entropy_avail</td>
<td>此文件是个只读文件，给出了一个有效的熵(4096位)</td>
</tr>
<tr>
<td>kernel.random.poolsize</td>
<td>熵池大小，一般是4096位，可以改成任何大小</td>
</tr>
<tr>
<td>kernel.random.read_wakeup_threshold</td>
<td>该文件保存熵的长度，该长度用于唤醒因读取/dev/random设备而待机的进程(random设备的读缓冲区长度？)</td>
</tr>
<tr>
<td>kernel.random.uuid</td>
<td>此文件是个只读文件，包含了一个随机字符串，在random设备每次被读的时候生成</td>
</tr>
<tr>
<td>kernel.random.write_wakeup_threshold</td>
<td>该文件保存熵的长度，该长度用于唤醒因写入/dev/random设备而待机的进程(random设备的写缓冲区长度？)</td>
</tr>
<tr>
<td>kernel.randomize_va_space</td>
<td>用于设置进程虚拟地址空间的随机化<br /> 0：关闭进程虚拟地址空间随机化<br /> 1：随机化进程虚拟地址空间中的mmap映射区的初始地址，栈空间的初始地址以及VDSO页的地址<br /> 2：在1的基础上加上堆区的随机化<br /> (VDSO是用于兼容不同内核与glibc的接口的机制)</td>
</tr>
<tr>
<td>kernel.real-root-dev</td>
<td>根文件系统所在的设备(写入格式是0x主设备号(16位)次设备号(16位)，例如0x801，主设备号是8，次设备号是1)，只有使用initrd.img此参数才有效</td>
</tr>
<tr>
<td>kernel.sched_autogroup_enabled</td>
<td>启用后，内核会创建任务组来优化桌面程序的调度。它将把占用大量资源的应用程序放在它们自己的任务组，这有助于性能提升<br /> 0：禁止<br /> 1：开启</td>
</tr>
<tr>
<td>kernel.sched_cfs_bandwidth_slice_us</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_child_runs_first</td>
<td>设置保证子进程初始化完成后在父进程之前先被调度<br /> 0：先调度父进程<br /> 1：先调度子进程</td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.busy_factor</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.busy_idx</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.cache_nice_tries</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.flags</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.forkexec_idx</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.idle_idx</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.imbalance_pct</td>
<td>判断该调度域是否已经均衡的一个基准值</td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.max_interval</td>
<td>设置此CPU进行负载均衡的最长间隔时间，上一次做了负载均衡经过了这个时间一定要再进行一次</td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.min_interval</td>
<td>设置此CPU进行负载均衡的最小间隔时间，在上一次负载均衡到这个时间内都不能再进行负载均衡</td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.name</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.newidle_idx</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_domain.{CPUID}.{域ID}.wake_idx</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_latency_ns</td>
<td>表示正在运行进程的所能运行的时间的最大值，即使只有一个处于running状态的进程，运行到这个时间也要重新调度一次(以纳秒为单位，在运行时会自动变化？)<br /> 同时这个值也是所有可运行进程都运行一次所需的时间，每个CPU的running进程数 = sched_latency_ns /<br /> sched_min_granularity_ns</td>
</tr>
<tr>
<td>kernel.sched_migration_cost_ns</td>
<td>该变量用来判断一个进程是否还是hot，如果进程的运行时间（now<br /> - p-&gt;se.exec_start）小于它，那么内核认为它的code还在cache里，所以该进程还是hot，那么在迁移的时候就不会考虑它</td>
</tr>
<tr>
<td>kernel.sched_min_granularity_ns</td>
<td>表示一个进程在CPU上运行的最小时间，在此时间内，内核是不会主动挑选其他进程进行调度(以纳秒为单位，在运行时会自动变化？)</td>
</tr>
<tr>
<td>kernel.sched_nr_migrate</td>
<td>在多CPU情况下进行负载均衡时，一次最多移动多少个进程到另一个CPU上</td>
</tr>
<tr>
<td>kernel.sched_rr_timeslice_ms</td>
<td>用来指示round<br /> robin调度进程的间隔，这个间隔默认是100ms。</td>
</tr>
<tr>
<td>kernel.sched_rt_period_us</td>
<td>该参数与sched_rt_runtime_us一起决定了实时进程在以sched_rt_period为周期的时间内，实时进程最多能够运行的总的时间不能超过sched_rt_runtime_us</td>
</tr>
<tr>
<td>kernel.sched_rt_runtime_us</td>
<td>该参数与sched_rt_period一起决定了实时进程在以sched_rt_period为周期的时间内，实时进程最多能够运行的总的时间不能超过sched_rt_runtime_us</td>
</tr>
<tr>
<td>kernel.sched_shares_window_ns</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_time_avg_ms</td>
<td> </td>
</tr>
<tr>
<td>kernel.sched_tunable_scaling</td>
<td>当内核试图调整sched_min_granularity，sched_latency和sched_wakeup_granularity这三个值的时候所使用的更新方法.<br /> 0：不调整<br /> 1：按照cpu个数以2为底的对数值进行调整<br /> 2：按照cpu的个数进行线性比例的调整</td>
</tr>
<tr>
<td>kernel.sched_wakeup_granularity_ns</td>
<td>该变量表示进程被唤醒后至少应该运行的时间的基数，它只是用来判断某个进程是否应该抢占当前进程，并不代表它能够执行的最小时间（sysctl_sched_min_granularity），如果这个数值越小，那么发生抢占的概率也就越高</td>
</tr>
<tr>
<td>kernel.sem</td>
<td>该文件包含4个值：<br /> 1.同一类信号的最多数量(semmsl)<br /> 2.系统中信号的最多数目，=semmni*semmsl (semmns)<br /> 3.每个semop系统调用所包含的最大的操作数(能调用的信号量的最多次数) (semopm)<br /> 4.系统中信号类型的数目的最大值，一个信号量标识符代表一个类型(semmni)<br /> 可见：http://www.cnblogs.com/jimeper/p/3141975.html</td>
</tr>
<tr>
<td>kernel.shmall</td>
<td>系统上可以使用的共享内存的总量（以字节为单位）。</td>
</tr>
<tr>
<td>kernel.shmmax</td>
<td>系统所允许的最大共享内存段的大小（以字节为单位）。</td>
</tr>
<tr>
<td>kernel.shmmni</td>
<td>整个系统共享内存段的最大数量。 </td>
</tr>
<tr>
<td>kernel.shm_rmid_forced</td>
<td>强制SHM空间和一个进程联系在一起，所以可以通过杀死进程来释放内存<br /> 0：不设置<br /> 1：设置</td>
</tr>
<tr>
<td>kernel.softlockup_panic</td>
<td>设置产生softlockup时是否抛出一个panic。Softlockup用于检测CPU可以响应中断，但是在长时间内不能调度（比如禁止抢占时间太长）的死锁情况。这个机制运行在一个hrtimer的中断上下文，每隔一段时间检测一下是否发生了调度，如果过长时间没发生调度，说明系统被死锁。<br /> 0：不产生panic<br /> 1：产生panic</td>
</tr>
<tr>
<td>kernel.sysrq</td>
<td>该文件指定的值为非零，则激活键盘上的sysrq按键。这个按键用于给内核传递信息，用于紧急情况下重启系统。当遇到死机或者没有响应的时候，甚至连<br /> tty 都进不去，可以尝试用 SysRq 重启计算机。</td>
</tr>
<tr>
<td>kernel.tainted</td>
<td>1：加载非GPL module<br /> 0：强制加载module </td>
</tr>
<tr>
<td>kernel.threads-max</td>
<td>系统中进程数量(包括线程)的最大值</td>
</tr>
<tr>
<td>kernel.timer_migration</td>
<td> </td>
</tr>
<tr>
<td>kernel.usermodehelper.bset</td>
<td> </td>
</tr>
<tr>
<td>kernel.usermodehelper.inheritable</td>
<td> </td>
</tr>
<tr>
<td>kernel.version</td>
<td>版本号(例：#1 SMP Mon Sep 7 18:12:36<br /> CST 2015)</td>
</tr>
<tr>
<td>kernel.watchdog</td>
<td>表示是否禁止softlockup模式和nmi_watchdog(softlockup用于唤醒watchdog)<br /> 0：禁止<br /> 1：开启</td>
</tr>
<tr>
<td>kernel.watchdog_thresh</td>
<td>用于设置高精度定时器(hrtimer)、nmi事件、softlockup、hardlockup的阀值(以秒为单位)<br /> 0：不设置阀值</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">内存参数</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 30%;">vm.admin_reserve_kbytes</td>
<td>给有cap_sys_admin权限的用户保留的内存数量(默认值是 min(free_page * 0.03, 8MB))</td>
</tr>
<tr>
<td>vm.block_dump</td>
<td>如果设置的是非零值，则会启用块I/O调试。</td>
</tr>
<tr>
<td>vm.compact_memory</td>
<td>进行内存压缩，只有在启用了CONFIG_COMPACTION选项才有效<br /> 1：开始进行内存压缩</td>
</tr>
<tr>
<td>vm.dirty_background_bytes</td>
<td>当脏页所占的内存数量超过dirty_background_bytes时，内核的flusher线程开始回写脏页。</td>
</tr>
<tr>
<td>vm.dirty_background_ratio</td>
<td>当脏页所占的百分比（相对于所有可用内存，即空闲内存页+可回收内存页）达到dirty_background_ratio时，write调用会唤醒内核的flusher线程开始回写脏页数据，直到脏页比例低于此值，与dirty_ratio不同，write调用此时并不会阻塞。</td>
</tr>
<tr>
<td>vm.dirty_bytes</td>
<td>当脏页所占的内存数量达到dirty_bytes时，执行磁盘写操作的进程自己开始回写脏数据。<br /> 注意： dirty_bytes参数和<br /> dirty_ratio参数是相对的，只能指定其中一个。当其中一个参数文件被写入时，会立即开始计算脏页限制，并且会将另一个参数的值清零</td>
</tr>
<tr>
<td>vm.dirty_expire_centisecs</td>
<td>脏数据的过期时间，超过该时间后内核的flusher线程被唤醒时会将脏数据回写到磁盘上，单位是百分之一秒。</td>
</tr>
<tr>
<td>vm.dirty_ratio</td>
<td>脏页所占的百分比（相对于所有可用内存，即空闲内存页+可回收内存页）达到dirty_ratio时，write调用会唤醒内核的flusher线程开始回写脏页数据，直到脏页比例低于此值，注意write调用此时会阻塞。</td>
</tr>
<tr>
<td>vm.dirty_writeback_centisecs</td>
<td>设置flusher内核线程唤醒的间隔，此线程用于将脏页回写到磁盘，单位是百分之一秒</td>
</tr>
<tr>
<td>vm.drop_caches</td>
<td>写入数值可以使内核释放page_cache，dentries和inodes缓存所占的内存。<br /> 1：只释放page_cache<br /> 2：只释放dentries和inodes缓存<br /> 3：释放page_cache、dentries和inodes缓存</td>
</tr>
<tr>
<td>vm.extfrag_threshold</td>
<td> </td>
</tr>
<tr>
<td>vm.hugepages_treat_as_movable</td>
<td>用来控制是否可以从ZONE_MOVABLE内存域中分配大页面。如果设置为非零，大页面可以从ZONE_MOVABLE内存域分配。ZONE_MOVABLE内存域只有在指定了kernelcore启动参数的情况下才会创建，如果没有指定kernelcore启动参数，<br /> hugepages_treat_as_movable参数则没有效果。</td>
</tr>
<tr>
<td>vm.hugetlb_shm_group</td>
<td>指定组ID，拥有该gid的用户可以使用大页创建SysV共享内存段</td>
</tr>
<tr>
<td>vm.laptop_mode</td>
<td>设置开启laptop<br /> mode，此模式主要是通过降低硬盘的转速来延长电池的续航时间。<br /> 0：关闭<br /> 1：启动</td>
</tr>
<tr>
<td>vm.legacy_va_layout</td>
<td>进程地址空间内存布局模式<br /> 0：经典布局<br /> 1：新布局<br /> 对于64位系统，默认采用经典布局</td>
</tr>
<tr>
<td>vm.lowmem_reserve_ratio</td>
<td>决定了内核保护这些低端内存域的强度。预留的内存值和lowmem_reserve_ratio数组中的值是倒数关系，如果值是256，则代表1/256，即为0.39%的zone内存大小。如果想要预留更多页，应该设更小一点的值。</td>
</tr>
<tr>
<td>vm.max_map_count</td>
<td>定义了一个进程能拥有的最多的内存区域</td>
</tr>
<tr>
<td>vm.memory_failure_early_kill</td>
<td>控制发生某个内核无法处理的内存错误发生的时候，如何去杀掉这个进程。当这些错误页有swap镜像的时候，内核会很好的处理这个错误，不会影响任何应用程序，但是如果没有的话，内核会把进程杀掉，避免内存错误的扩大<br /> 1：在发现内存错误的时候，就会把所有拥有此内存页的进程都杀掉<br /> 0：只是对这部分页进行unmap，然后把第一个试图进入这个页的进程杀掉</td>
</tr>
<tr>
<td>vm.memory_failure_recovery</td>
<td>是否开启内存错误恢复机制<br /> 1：开启<br /> 0：一旦出现内存错误，就panic</td>
</tr>
<tr>
<td>vm.min_free_kbytes</td>
<td>每个内存区保留的内存大小(以KB计算)</td>
</tr>
<tr>
<td>vm.min_slab_ratio</td>
<td>只在numa架构上使用，如果一个内存域中可以回收的slab页面所占的百分比（应该是相对于当前内存域的所有页面）超过min_slab_ratio，在回收区的slabs会被回收。这样可以确保即使在很少执行全局回收的NUMA系统中，slab的增长也是可控的。</td>
</tr>
<tr>
<td>vm.min_unmapped_ratio</td>
<td>只有在当前内存域中处于zone_reclaim_mode允许回收状态的内存页所占的百分比超过min_unmapped_ratio时，内存域才会执行回收操作。</td>
</tr>
<tr>
<td>vm.mmap_min_addr</td>
<td>指定用户进程通过mmap可使用的最小虚拟内存地址，以避免其在低地址空间产生映射导致安全问题；如果非0，则不允许mmap到NULL页，而此功能可在出现NULL指针时调试Kernel；mmap用于将文件映射至内存；<br /> 该设置意味着禁止用户进程访问low 4k地址空间</td>
</tr>
<tr>
<td>vm.nr_hugepages</td>
<td>大页的最小数目，需要连续的物理内存；oracle使用大页可以降低TLB的开销，节约内存和CPU资源，但要同时设置memlock且保证其大于大页；其与11gAMM不兼容</td>
</tr>
<tr>
<td>vm.nr_hugepages_mempolicy</td>
<td>与nr_hugepages类似，但只用于numa架构，配合numactl调整每个node的大页数量</td>
</tr>
<tr>
<td>vm.nr_overcommit_hugepages</td>
<td>保留于紧急使用的大页数，系统可分配最大大页数=<br /> nr_hugepages + nr_overcommit_hugepages</td>
</tr>
<tr>
<td>vm.nr_pdflush_threads</td>
<td>只读文件，保存了当前正在运行的pdflush线程的数量</td>
</tr>
<tr>
<td>vm.numa_zonelist_order</td>
<td>设置内核选择zonelist的模式：<br /> 0：让内核智能选择使用Node或Zone方式的zonelist<br /> 1：选择Node方式的zonelist，Node(0) ZONE_NORMAL -&gt; Node(0) ZONE_DMA -&gt;<br /> Node(1) ZONE_NORMAL<br /> 2：选择Zone方式的，Node(0) ZONE_NORMAL -&gt; Node(1) ZONE_NORMAL -&gt; Node(0)<br /> ZONE_DMA</td>
</tr>
<tr>
<td>vm.oom_dump_tasks</td>
<td>如果启用，在内核执行OOM-killing时会打印系统内进程的信息（不包括内核线程），信息包括pid、uid、tgid、vm<br /> size、rss、nr_ptes，swapents，oom_score_adj和进程名称。这些信息可以帮助找出为什么OOM<br /> killer被执行，找到导致OOM的进程，以及了解为什么进程会被选中。<br /> 0：不打印系统内进程信息<br /> 1：打印系统内进程信息</td>
</tr>
<tr>
<td>vm.oom_kill_allocating_task</td>
<td>决定在oom的时候，oom<br /> killer杀哪些进程<br /> 非0：它会扫描进程队列，然后将可能导致内存溢出的进程杀掉，也就是占用内存最大的进程<br /> 0：它只杀掉导致oom的那个进程，避免了进程队列的扫描，但是释放的内存大小有限</td>
</tr>
<tr>
<td>vm.overcommit_kbytes</td>
<td>内存可过量分配的数量(单位为KB)</td>
</tr>
<tr>
<td>vm.overcommit_memory</td>
<td>是否允许内存的过量分配，允许进程分配比它实际使用的更多的内存。<br /> 0：当用户申请内存的时候，内核会去检查是否有这么大的内存空间，当超过地址空间会被拒绝<br /> 1：内核始终认为，有足够大的内存空间，直到它用完了位置<br /> 2：内核禁止任何形式的过量分配内存<br /> Memory allocation limit = swapspace + physmem * (overcommit_ratio / 100)</td>
</tr>
<tr>
<td>vm.overcommit_ratio</td>
<td>内存可过量分配的百分比。</td>
</tr>
<tr>
<td>vm.page-cluster</td>
<td>参数控制一次写入或读出swap分区的页面数量。它是一个对数值，如果设置为0，表示1页；如果设置为1，表示2页；如果设置为2，则表示4页。</td>
</tr>
<tr>
<td>vm.panic_on_oom</td>
<td>用于控制如何处理out-of-memory，可选值包括0/1/2<br /> 0：当内存不足时内核调用OOM killer杀死一些rogue进程，每个进程描述符都有一个oom_score标示，oom<br /> killer会选择oom_score较大的进程<br /> 1：发生了OOM以后，如果有mempolicy/cpusets的进程限制，而这些nodes导致了内存问题的时候，OOM<br /> Killer会干掉这些中的一个，系统也会恢复<br /> 2：OOM后必然panic</td>
</tr>
<tr>
<td>vm.percpu_pagelist_fraction</td>
<td>每个CPU能从每个zone所能分配到的pages的最大值(单位每个zone的1/X)，0为不限制</td>
</tr>
<tr>
<td>vm.stat_interval</td>
<td>VM信息更新频率(以秒为单位)</td>
</tr>
<tr>
<td>vm.swappiness</td>
<td>该值越高则<span class="font6">linux越倾向于将部分长期没有用到的页swap，即便有足够空余物理内存(1~100)</span></td>
</tr>
<tr>
<td>vm.user_reserve_kbytes</td>
<td> </td>
</tr>
<tr>
<td>vm.vfs_cache_pressure</td>
<td>表示内核回收用于directory和inode<br /> cache内存的倾向；缺省值100表示内核将根据pagecache和swapcache，把directory和inode<br /> cache保持在一个合理的百分比；降低该值低于100，将导致内核倾向于保留directory和inode<br /> cache；增加该值超过100，将导致内核倾向于回收directory和inode cache</td>
</tr>
<tr>
<td>vm.zone_reclaim_mode</td>
<td>参数只有在启用CONFIG_NUMA选项时才有效,zone_reclaim_mode用来控制在内存域OOM时，如何来回收内存。<br /> 0：禁止内存域回收，从其他zone分配内存<br /> 1：启用内存域回收<br /> 2：通过回写脏页回收内存<br /> 4：通过swap回收内存</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">网络参数</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 30%;">net.bridge.bridge-nf-call-arptables</td>
<td rowspan="4">
<p>netfilter既可以在L2层过滤，也可以在L3层过滤</p>
<p>这些参数设置为0，则iptables不对bridge的数据进行处理</p>
<p>如果设置为1则意味着<span style="background-color: #c0c0c0;">二层的网桥在转发包时也会被iptables的FORWARD规则所过滤</span>，这样就会出现<span style="background-color: #c0c0c0;">L3层的iptables rules去过滤L2的帧的问题</span>，从而导致某些NAT不生效</p>
</td>
</tr>
<tr>
<td>net.bridge.bridge-nf-call-ip6tables</td>
</tr>
<tr>
<td>net.bridge.bridge-nf-call-iptables</td>
</tr>
<tr>
<td>net.bridge.bridge-nf-filter-pppoe-tagged</td>
</tr>
<tr>
<td>net.bridge.bridge-nf-filter-vlan-tagged</td>
<td> </td>
</tr>
<tr>
<td>net.bridge.bridge-nf-pass-vlan-input-dev</td>
<td> </td>
</tr>
<tr>
<td>net.core.bpf_jit_enable</td>
<td>基于时间规则的编译器，用于基于PCAP（packet capture library）并使用伯克利包过滤器（Berkeley Packet Filter，如tcpdump）的用户工具，可以大幅提升复杂规则的处理性能。<br /> 0：禁止<br /> 1：开启<br /> 2：开启并请求编译器将跟踪数据时间写入内核日志</td>
</tr>
<tr>
<td>net.core.busy_poll</td>
<td>默认对网络设备进行poll和select操作的超时时间(us)，具体数值最好以sockets数量而定</td>
</tr>
<tr>
<td>net.core.busy_read</td>
<td>默认读取在设备帧队列上数据帧的超时时间(us)，推荐值：50</td>
</tr>
<tr>
<td>net.core.default_qdisc</td>
<td> </td>
</tr>
<tr>
<td>net.core.dev_weight</td>
<td>每个CPU一次NAPI中断能够处理网络包数量的最大值</td>
</tr>
<tr>
<td>net.core.message_burst</td>
<td>设置每十秒写入多少次请求警告；此设置可以用来防止DOS攻击</td>
</tr>
<tr>
<td>net.core.message_cost</td>
<td>设置每一个警告的度量值，缺省为5，当用来防止DOS攻击时设置为0</td>
</tr>
<tr>
<td>net.core.netdev_budget</td>
<td>每次软中断处理的网络包个数</td>
</tr>
<tr>
<td>net.core.netdev_max_backlog</td>
<td>设置当个别接口接收包的速度快于内核处理速度时允许的最大的包序列</td>
</tr>
<tr>
<td>net.core.netdev_tstamp_prequeue</td>
<td>0：关闭，接收的数据包的时间戳在RPS程序处理之后进行标记，这样有可能时间戳会不够准确<br /> 1：打开，时间戳会尽可能早的标记</td>
</tr>
<tr>
<td>net.core.optmem_max</td>
<td>表示每个socket所允许的最大缓冲区的大小(字节)</td>
</tr>
<tr>
<td>net.core.rmem_default</td>
<td>设置接收socket的缺省缓存大小(字节)</td>
</tr>
<tr>
<td>net.core.rmem_max</td>
<td>设置接收socket的最大缓存大小(字节)</td>
</tr>
<tr>
<td>net.core.rps_sock_flow_entries</td>
<td> </td>
</tr>
<tr>
<td>net.core.somaxconn</td>
<td>定义了系统中每一个端口最大的监听队列的长度，这是个全局的参数。</td>
</tr>
<tr>
<td>net.core.warnings</td>
<td>已经不使用此参数</td>
</tr>
<tr>
<td>net.core.wmem_default</td>
<td>设置发送的socket缺省缓存大小(字节)</td>
</tr>
<tr>
<td>net.core.wmem_max</td>
<td>设置发送的socket最大缓存大小(字节)</td>
</tr>
<tr>
<td>net.core.xfrm_acq_expires</td>
<td> </td>
</tr>
<tr>
<td>net.core.xfrm_aevent_etime</td>
<td> </td>
</tr>
<tr>
<td>net.core.xfrm_aevent_rseqth</td>
<td> </td>
</tr>
<tr>
<td>net.core.xfrm_larval_drop</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.cipso_cache_bucket_size</td>
<td>限制cipso缓存项的数量，如果在缓存中新添加一行超出了这个限制，那么最旧的缓存项会被丢弃以释放出空间。</td>
</tr>
<tr>
<td>net.ipv4.cipso_cache_enable</td>
<td>是否启用cipso缓存。<br /> 0：不启用<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.cipso_rbm_optfmt</td>
<td>是否开启cipso标志优化选项，如果开启，数据包中标志将会在32bit对齐<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.cipso_rbm_strictvalid</td>
<td>是否开启cipso选项的严格检测<br /> 0：不开启<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.conf.all.accept_local</td>
<td>
<p>设置是否允许接收从本机IP地址上发送给本机的数据包<br /> 0：不允许<br /> 1：允许</p>
<p>配合适当的路由，可以用于<span style="background-color: #c0c0c0;">在两个本地网络接口之间（透过网线）传递封包</span>。rp_filter必须设置为<span style="background-color: #c0c0c0;">非0</span>以配合</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.all.accept_redirects</td>
<td>收发接收ICMP重定向消息。对于主机来说默认为True，对于用作路由器时默认值为False<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv4.conf.all.arp_accept</td>
<td>默认对不在ARP表中的IP地址发出的APR包的处理方式<br /> 0：不在ARP表中创建对应IP地址的表项<br /> 1：在ARP表中创建对应IP地址的表项</td>
</tr>
<tr>
<td>net.ipv4.conf.all.arp_announce</td>
<td>对网络接口上，本地IP地址的发出的，ARP回应，作出相应级别的限制:<br /> 确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口<br /> 0： 在任意网络接口（eth0,eth1，lo）上的任何本地地址<br /> 1：尽量避免不在该网络接口子网段的本地地址做出arp回应.<br /> 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一.如果改来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.</p>
<p>2：对查询目标使用最适当的本地地址.在此模式下将忽略这个IP数据包的源地址并尝试选择与能与该地址通信的本地地址.首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址.<br /> 如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.all.arp_filter</td>
<td>0：内核设置每个网络接口各自应答其地址上的arp询问。这项看似会错误的设置却经常能非常有效，因为它增加了成功通讯的机会。在Linux主机上，每个IP地址是网络接口独立的，而非一个复合的接口。只有在一些特殊的设置的时候，比如负载均衡的时候会带来麻烦。</p>
<p>1：允许多个网络介质位于同一子网段内，每个网络界面依据是否内核指派路由该数据包经过此接口来确认是否回答ARP查询(这个实现是由来源地址确定路由的时候决定的),换句话说，允许控制使用某一块网卡（通常是第一块）回应arp询问。</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.all.arp_ignore</td>
<td>定义对目标地址为本地IP的ARP询问不同的应答模式<br /> 0：回应任何网络接口上对任何本地IP地址的arp查询请求<br /> 1：只回答目标IP地址是来访网络接口本地地址的ARP查询请求<br /> 2：只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内 <br /> 3：不回应该网络界面的arp请求，而只对设置的唯一和连接地址做出回应<br /> 8：不回应所有（本地地址）的arp查询</td>
</tr>
<tr>
<td>net.ipv4.conf.all.arp_notify</td>
<td>arp通知链操作<br /> 0：不做任何操作<br /> 1：当设备或硬件地址改变时自动产生一个arp请求</td>
</tr>
<tr>
<td>net.ipv4.conf.all.bootp_relay</td>
<td>接收源地址为0.a.b.c，目的地址不是本机的数据包，是为了支持bootp服务<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.conf.all.disable_policy</td>
<td>禁止internet协议安全性验证<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.all.disable_xfrm</td>
<td>禁止internet协议安全性加密<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.all.force_igmp_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.all.forwarding</td>
<td>在该接口打开转发功能<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.log_martians</td>
<td>记录带有不允许的地址的数据报到内核日志中。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.mc_forwarding</td>
<td>是否进行多播路由。只有内核编译有CONFIG_MROUTE并且有路由服务程序在运行该参数才有效。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.medium_id</td>
<td>通常,这个参数用来区分不同媒介.两个网络设备可以使用不同的值,使他们只有其中之一接收到广播包.通常,这个参数被用来配合proxy_arp实现roxy_arp的特性即是允许arp报文在两个不同的网络介质中转发.<br /> 0：表示各个网络介质接受他们自己介质上的媒介<br /> -1：表示该媒介未知。</td>
</tr>
<tr>
<td>net.ipv4.conf.all.promote_secondaries</td>
<td>0：当接口的主IP地址被移除时，删除所有次IP地址<br /> 1：当接口的主IP地址被移除时，将次IP地址提升为主IP地址</td>
</tr>
<tr>
<td>net.ipv4.conf.all.proxy_arp</td>
<td>打开arp代理功能。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.proxy_arp_pvlan</td>
<td>回应代理ARP的数据包从接收到此代理ARP请求的网络接口出去。</td>
</tr>
<tr>
<td>net.ipv4.conf.all.route_localnet</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.all.rp_filter</td>
<td>
<p>0：不进行源地址验证<br />1：使用定义在RFC3704中的Strict <strong><span style="background-color: #c0c0c0;">Reverse</span></strong> Path进行源地址验证。每个封包的源地址都在FIB中进行测试，如果接收网络接口不是<span style="background-color: #c0c0c0;">最佳的Reverse Path则验证失败</span><br />2：使用定义在RFC3704中的Loose Reverse Path进行源地址验证。每个封包的源地址都在FIB中进行测试，如果<span style="background-color: #c0c0c0;">源地址不能通过任何接口到达则验证失败</span></p>
<p>当前推荐使用严格模式（1），如果使用非对称路由（来/回路径不一致）则推荐Loose模式</p>
<p>开启rp_filter的意义：</p>
<ol>
<li>
<p>减少DDoS攻击：校验数据包的反向路径，如果反向路径不合适，则直接丢弃数据包，避免过多的无效连接消耗系统资源。<span style="background-color: #c0c0c0;">DDos通过构造大量的无用数据包</span>向目标服务发起请求，占用目标服务主机大量的资源，还可能造成网络拥塞，进而影响到正常用户的访问</p>
</li>
<li>
<p>防止IP欺骗（Spoofing，伪造源IP）：校验数据包的反向路径，如果客户端伪造的源IP地址对应的反向路径不在路由表中，或者反向路径不是最佳路径，则直接丢弃数据包，不会向伪造IP的客户端回复响应</p>
</li>
</ol>
<p>FIB：转发信息库，包含了最近使用的路由条目的哈希表</p>
<p>假设你有两网络接口 eth0 192.168.0.1  eth1 10.0.0.1，并且路由表如下：</p>
<pre class="crayon-plain-tag">default     192.168.0.254   0.0.0.0 　　   UG    0   0  0 eth0
172.21.0.1  0.0.0.0         255.255.255.0   U    0   0  0 eth0</pre>
<p>如果rp_filter=1， 现在有一个IP封包172.21.0.1 -&gt; 10.0.0.1，从eth1接收到，则校验会失败。因为从路由表上看，<span style="background-color: #c0c0c0;"><strong>反向</strong>封包从eth0发出</span>，而正向封包却来自eth0，不匹配，封包会被丢弃</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.all.secure_redirects</td>
<td>仅仅接收发给默认网关列表中网关的ICMP重定向消息<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.send_redirects</td>
<td>允许发送重定向消息。(路由使用)<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.shared_media</td>
<td>发送或接收RFC1620<br /> 共享媒体重定向。会覆盖ip_secure_redirects的值。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.all.src_valid_mark</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.all.tag</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.accept_local</td>
<td>设置是否允许接收从本机IP地址上发送给本机的数据包<br /> 0：不允许<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.accept_redirects</td>
<td>收发接收ICMP重定向消息。对于主机来说默认为True，对于用作路由器时默认值为False<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.arp_accept</td>
<td>默认对不在ARP表中的IP地址发出的APR包的处理方式<br /> 0：不在ARP表中创建对应IP地址的表项<br /> 1：在ARP表中创建对应IP地址的表项</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.arp_announce</td>
<td>对网络接口上，本地IP地址的发出的，ARP回应，作出相应级别的限制:<br /> 确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口<br /> 0： 在任意网络接口（eth0,eth1，lo）上的任何本地地址<br /> 1：尽量避免不在该网络接口子网段的本地地址做出arp回应.<br /> 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一.如果改来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.</p>
<p>2：对查询目标使用最适当的本地地址.在此模式下将忽略这个IP数据包的源地址并尝试选择与能与该地址通信的本地地址.首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址.<br /> 如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.arp_filter</td>
<td>0：内核设置每个网络接口各自应答其地址上的arp询问。这项看似会错误的设置却经常能非常有效，因为它增加了成功通讯的机会。在Linux主机上，每个IP地址是网络接口独立的，而非一个复合的接口。只有在一些特殊的设置的时候，比如负载均衡的时候会带来麻烦。</p>
<p>1：允许多个网络介质位于同一子网段内，每个网络界面依据是否内核指派路由该数据包经过此接口来确认是否回答ARP查询(这个实现是由来源地址确定路由的时候决定的),换句话说，允许控制使用某一块网卡（通常是第一块）回应arp询问。</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.arp_ignore</td>
<td>定义对目标地址为本地IP的ARP询问不同的应答模式<br /> 0：回应任何网络接口上对任何本地IP地址的arp查询请求<br /> 1：只回答目标IP地址是来访网络接口本地地址的ARP查询请求<br /> 2：只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内 <br /> 3：不回应该网络界面的arp请求，而只对设置的唯一和连接地址做出回应<br /> 8：不回应所有（本地地址）的arp查询</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.arp_notify</td>
<td>arp通知链操作<br /> 0：不做任何操作<br /> 1：当设备或硬件地址改变时自动产生一个arp请求</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.bootp_relay</td>
<td>接收源地址为0.a.b.c，目的地址不是本机的数据包，是为了支持bootp服务<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.disable_policy</td>
<td>禁止internet协议安全性验证<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.disable_xfrm</td>
<td>禁止internet协议安全性加密<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.force_igmp_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.forwarding</td>
<td>在该接口打开转发功能<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.log_martians</td>
<td>记录带有不允许的地址的数据报到内核日志中。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.mc_forwarding</td>
<td>是否进行多播路由。只有内核编译有CONFIG_MROUTE并且有路由服务程序在运行该参数才有效。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.medium_id</td>
<td>通常,这个参数用来区分不同媒介.两个网络设备可以使用不同的值,使他们只有其中之一接收到广播包.通常,这个参数被用来配合proxy_arp实现roxy_arp的特性即是允许arp报文在两个不同的网络介质中转发.<br /> 0：表示各个网络介质接受他们自己介质上的媒介<br /> -1：表示该媒介未知。</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.promote_secondaries</td>
<td>0：当接口的主IP地址被移除时，删除所有次IP地址<br /> 1：当接口的主IP地址被移除时，将次IP地址提升为主IP地址</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.proxy_arp</td>
<td>打开arp代理功能。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.proxy_arp_pvlan</td>
<td>回应代理ARP的数据包从接收到此代理ARP请求的网络接口出去。</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.route_localnet</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.rp_filter</td>
<td>1：通过反向路径回溯进行源地址验证(在RFC1812中定义)。对于单穴主机和stub网络路由器推荐使用该选项。<br /> 0：不通过反向路径回溯进行源地址验证。</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.secure_redirects</td>
<td>仅仅接收发给默认网关列表中网关的ICMP重定向消息<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.send_redirects</td>
<td>允许发送重定向消息。(路由使用)<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.shared_media</td>
<td>发送或接收RFC1620<br /> 共享媒体重定向。会覆盖ip_secure_redirects的值。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.src_valid_mark</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.{IF}.tag</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.default.accept_local</td>
<td>设置是否允许接收从本机IP地址上发送给本机的数据包<br /> 0：不允许<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.accept_redirects</td>
<td>收发接收ICMP重定向消息。对于主机来说默认为True，对于用作路由器时默认值为False<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv4.conf.default.arp_accept</td>
<td>默认对不在ARP表中的IP地址发出的APR包的处理方式<br /> 0：不在ARP表中创建对应IP地址的表项<br /> 1：在ARP表中创建对应IP地址的表项</td>
</tr>
<tr>
<td>net.ipv4.conf.default.arp_announce</td>
<td>对网络接口上，本地IP地址的发出的，ARP回应，作出相应级别的限制:<br /> 确定不同程度的限制,宣布对来自本地源IP地址发出Arp请求的接口<br /> 0： 在任意网络接口（eth0,eth1，lo）上的任何本地地址<br /> 1：尽量避免不在该网络接口子网段的本地地址做出arp回应.<br /> 当发起ARP请求的源IP地址是被设置应该经由路由达到此网络接口的时候很有用.此时会检查来访IP是否为所有接口上的子网段内ip之一.如果改来访IP不属于各个网络接口上的子网段内,那么将采用级别2的方式来进行处理.</p>
<p>2：对查询目标使用最适当的本地地址.在此模式下将忽略这个IP数据包的源地址并尝试选择与能与该地址通信的本地地址.首要是选择所有的网络接口的子网中外出访问子网中包含该目标IP地址的本地地址.<br /> 如果没有合适的地址被发现,将选择当前的发送网络接口或其他的有可能接受到该ARP回应的网络接口来进行发送.</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.default.arp_filter</td>
<td>0：内核设置每个网络接口各自应答其地址上的arp询问。这项看似会错误的设置却经常能非常有效，因为它增加了成功通讯的机会。在Linux主机上，每个IP地址是网络接口独立的，而非一个复合的接口。只有在一些特殊的设置的时候，比如负载均衡的时候会带来麻烦。</p>
<p>1：允许多个网络介质位于同一子网段内，每个网络界面依据是否内核指派路由该数据包经过此接口来确认是否回答ARP查询(这个实现是由来源地址确定路由的时候决定的),换句话说，允许控制使用某一块网卡（通常是第一块）回应arp询问。</p>
</td>
</tr>
<tr>
<td>net.ipv4.conf.default.arp_ignore</td>
<td>定义对目标地址为本地IP的ARP询问不同的应答模式<br /> 0：回应任何网络接口上对任何本地IP地址的arp查询请求<br /> 1：只回答目标IP地址是来访网络接口本地地址的ARP查询请求<br /> 2：只回答目标IP地址是来访网络接口本地地址的ARP查询请求,且来访IP必须在该网络接口的子网段内 <br /> 3：不回应该网络界面的arp请求，而只对设置的唯一和连接地址做出回应<br /> 8：不回应所有（本地地址）的arp查询</td>
</tr>
<tr>
<td>net.ipv4.conf.default.arp_notify</td>
<td>arp通知链操作<br /> 0：不做任何操作<br /> 1：当设备或硬件地址改变时自动产生一个arp请求</td>
</tr>
<tr>
<td>net.ipv4.conf.default.bootp_relay</td>
<td>接收源地址为0.a.b.c，目的地址不是本机的数据包，是为了支持bootp服务<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.conf.default.disable_policy</td>
<td>禁止internet协议安全性验证<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.default.disable_xfrm</td>
<td>禁止internet协议安全性加密<br /> 0：禁止禁止<br /> 1：开启禁止</td>
</tr>
<tr>
<td>net.ipv4.conf.default.force_igmp_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.default.forwarding</td>
<td>在该接口打开转发功能<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.log_martians</td>
<td>记录带有不允许的地址的数据报到内核日志中。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.mc_forwarding</td>
<td>是否进行多播路由。只有内核编译有CONFIG_MROUTE并且有路由服务程序在运行该参数才有效。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.medium_id</td>
<td>通常,这个参数用来区分不同媒介.两个网络设备可以使用不同的值,使他们只有其中之一接收到广播包.通常,这个参数被用来配合proxy_arp实现roxy_arp的特性即是允许arp报文在两个不同的网络介质中转发.<br /> 0：表示各个网络介质接受他们自己介质上的媒介<br /> -1：表示该媒介未知。</td>
</tr>
<tr>
<td>net.ipv4.conf.default.promote_secondaries</td>
<td>0：当接口的主IP地址被移除时，删除所有次IP地址<br /> 1：当接口的主IP地址被移除时，将次IP地址提升为主IP地址</td>
</tr>
<tr>
<td>net.ipv4.conf.default.proxy_arp</td>
<td>打开arp代理功能。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.proxy_arp_pvlan</td>
<td>回应代理ARP的数据包从接收到此代理ARP请求的网络接口出去。</td>
</tr>
<tr>
<td>net.ipv4.conf.default.route_localnet</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.default.rp_filter</td>
<td>1：通过反向路径回溯进行源地址验证(在RFC1812中定义)。对于单穴主机和stub网络路由器推荐使用该选项。<br /> 0：不通过反向路径回溯进行源地址验证。</td>
</tr>
<tr>
<td>net.ipv4.conf.default.secure_redirects</td>
<td>仅仅接收发给默认网关列表中网关的ICMP重定向消息<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.send_redirects</td>
<td>允许发送重定向消息。(路由使用)<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.shared_media</td>
<td>发送或接收RFC1620<br /> 共享媒体重定向。会覆盖ip_secure_redirects的值。<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.conf.default.src_valid_mark</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.conf.default.tag</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.icmp_echo_ignore_all</td>
<td>忽略所有接收到的icmp<br /> echo请求的包(会导致机器无法ping通)<br /> 0：不忽略<br /> 1：忽略</td>
</tr>
<tr>
<td>net.ipv4.icmp_echo_ignore_broadcasts</td>
<td>忽略所有接收到的icmp echo请求的广播<br /> 0：不忽略<br /> 1：忽略</td>
</tr>
<tr>
<td>net.ipv4.icmp_errors_use_inbound_ifaddr</td>
<td>当前为 ICMP<br /> 错误消息选择源地址的方式，是使用存在的接口地址<br /> 1：表示内核通过这个选项允许使用接收到造成这一错误的报文的接口的地址</td>
</tr>
<tr>
<td>net.ipv4.icmp_ignore_bogus_error_responses</td>
<td>某些路由器违背RFC1122标准，其对广播帧发送伪造的响应来应答。这种违背行为通常会被以警告的方式记录在系统日志中。<br /> 0：记录到系统日志中<br /> 1：忽略</td>
</tr>
<tr>
<td>net.ipv4.icmp_ratelimit</td>
<td>限制发向特定目标的匹配icmp_ratemask的ICMP数据报的最大速率。配合icmp_ratemask使用。<br /> 0：没有任何限制<br /> &gt;0：表示指定时间内中允许发送的个数。(以jiffies为单位)</td>
</tr>
<tr>
<td>net.ipv4.icmp_ratemask</td>
<td>在这里匹配的ICMP被icmp_ratelimit参数限制速率.<br /> 匹配的标志位: ＩＨＧＦＥＤＣＢＡ９８７６５４３２１０<br /> 默认的掩码值: ００００００１１００００００１１０００ (6168)<br /> 0 Echo Reply<br /> 3 Destination Unreachable *<br /> 4 Source Quench *<br /> 5 Redirect<br /> 8 Echo Request<br /> B Time Exceeded *<br /> C Parameter Problem *<br /> D Timestamp Request<br /> E Timestamp Reply<br /> F Info Request<br /> G Info Reply<br /> H Address Mask Request<br /> I Address Mask Reply<br /> * 号的被默认限速</td>
</tr>
<tr>
<td>net.ipv4.igmp_max_memberships</td>
<td>限制加入一个多播组的最大成员数.</td>
</tr>
<tr>
<td>net.ipv4.igmp_max_msf</td>
<td>限制多播源地址过滤数量.</td>
</tr>
<tr>
<td>net.ipv4.igmp_qrv</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.inet_peer_maxttl</td>
<td>条目的最大存活期。在此期限到达之后，如果缓冲池没有耗尽压力的话(例如：缓冲池中的条目数目非常少)，不使用的条目将会超时。该值以<br /> jiffies为单位测量。</td>
</tr>
<tr>
<td>net.ipv4.inet_peer_minttl</td>
<td>条目的最低存活期。在重组端必须要有足够的碎片(fragment)存活期。这个最低存活期必须保证缓冲池容积是否少于<br /> inet_peer_threshold。该值以 jiffies为单位测量。</td>
</tr>
<tr>
<td>net.ipv4.inet_peer_threshold</td>
<td>INET对端存储器某个合适值，当超过该阀值条目将被丢弃。该阀值同样决定生存时间以及废物收集通过的时间间隔。条目越多，存活期越低，GC<br /> 间隔越短。</td>
</tr>
<tr>
<td>net.ipv4.ip_default_ttl</td>
<td>该文件表示一个数据报的生存周期（Time To<br /> Live），即最多经过多少路由器。</td>
</tr>
<tr>
<td>net.ipv4.ip_dynaddr</td>
<td>拨号上网大部分都是使用动态IP地址，我们不知道远程拨号服务器的IP地址是多少，也不可能知道它会给电脑分配什么IP地址<br /> 0：使用静态IP<br /> 1：使用动态IP地址</td>
</tr>
<tr>
<td>net.ipv4.ip_early_demux</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.ip_forward</td>
<td>是否打开ipv4的IP转发。<br /> 0：禁止<br /> 1：打开</td>
</tr>
<tr>
<td>net.ipv4.ip_forward_use_pmtu</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.ipfrag_high_thresh</td>
<td>表示用于重组IP分段的内存分配最高值，一旦达到最高内存分配值，其它分段将被丢弃，直到达到最低内存分配值。</td>
</tr>
<tr>
<td>net.ipv4.ipfrag_low_thresh</td>
<td>表示用于重组IP分段的内存分配最低值</td>
</tr>
<tr>
<td>net.ipv4.ipfrag_max_dist</td>
<td>相同的源地址ip碎片数据报的最大数量.<br /> 这个变量表示在ip碎片被添加到队列前要作额外的检查.如果超过定义的数量的ip碎片从一个相同源地址到达，那么假定这个队列的ip碎片有丢失，已经存在的ip碎片队列会被丢弃，如果为0关闭检查</td>
</tr>
<tr>
<td>net.ipv4.ipfrag_secret_interval</td>
<td>hash表中ip碎片队列的重建延迟.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.ipfrag_time</td>
<td>表示一个IP分段在内存中保留多少秒</td>
</tr>
<tr>
<td>net.ipv4.ip_local_port_range</td>
<td>本地发起连接时使用的端口范围，tcp初始化时会修改此值</td>
</tr>
<tr>
<td>net.ipv4.ip_local_reserved_ports</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.ip_nonlocal_bind</td>
<td>允许进程绑定到非本地地址<br /> 0：禁止<br /> 1：允许</td>
</tr>
<tr>
<td>net.ipv4.ip_no_pmtu_disc</td>
<td>该文件表示在全局范围内关闭路径MTU探测功能。</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.anycast_delay</td>
<td>对相邻请求信息的回复的最大延迟时间(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.app_solicit</td>
<td>在使用多播探测前，通过netlink发送到用户空间arp守护程序的最大探测数</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.base_reachable_time</td>
<td>一旦发现相邻记录，至少在一段介于<br /> base_reachable_time/2和3*base_reachable_time/2之间的随机时间内，该记录是有效的.<br /> 如果收到上层协议的肯定反馈， 那么记录的有效期将延长.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.base_reachable_time_ms</td>
<td>一旦发现相邻记录，至少在一段介于<br /> base_reachable_time/2和3*base_reachable_time/2之间的随机时间内，该记录是有效的.<br /> 如果收到上层协议的肯定反馈， 那么记录的有效期将延长.(单位 毫秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.delay_first_probe_time</td>
<td>发现某个相邻层记录无效后，发出第一个探测要等待的时间。(单位<br /> 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.gc_stale_time</td>
<td>决定检查一次相邻层记录的有效性的周期.<br /> 当相邻层记录失效时，将在给它发送数据前，再解析一次.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.locktime</td>
<td>防止相邻记录被过度频繁刷新，引起抖动，只有距邻居上次刷新时间超过这时才允许被再次刷新.(单位<br /> 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.mcast_solicit</td>
<td>在把记录标记为不可达之前，<br /> 用多播/广播方式解析地址的最大次数.</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.proxy_delay</td>
<td>当接收到有一个arp请求时，在回应前可以延迟的时间，这个请求是要得到一个已知代理arp项的地址.(单位<br /> 百毫秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.proxy_qlen</td>
<td>能放入代理 ARP 地址队列的数据包最大数目.</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.retrans_time</td>
<td>重发一个arp请求前的等待的秒数</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.retrans_time_ms</td>
<td>重发一个arp请求前的等待的毫秒数</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.ucast_solicit</td>
<td>arp请求最多发送次数</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.unres_qlen</td>
<td>最大挂起arp请求的数量，这些请求都正在被解析中.</td>
</tr>
<tr>
<td>net.ipv4.neigh.{IF}.unres_qlen_bytes</td>
<td>最大处理arp包的字节数</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.anycast_delay</td>
<td>对相邻请求信息的回复的最大延迟时间(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.app_solicit</td>
<td>在使用多播探测前，通过netlink发送到用户空间arp守护程序的最大探测数</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.base_reachable_time</td>
<td>一旦发现相邻记录，至少在一段介于<br /> base_reachable_time/2和3*base_reachable_time/2之间的随机时间内，该记录是有效的.<br /> 如果收到上层协议的肯定反馈， 那么记录的有效期将延长.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.base_reachable_time_ms</td>
<td>一旦发现相邻记录，至少在一段介于<br /> base_reachable_time/2和3*base_reachable_time/2之间的随机时间内，该记录是有效的.<br /> 如果收到上层协议的肯定反馈， 那么记录的有效期将延长.(单位 毫秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.delay_first_probe_time</td>
<td>发现某个相邻层记录无效后，发出第一个探测要等待的时间。(单位<br /> 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.gc_interval</td>
<td>垃圾收集器收集相邻层记录和无用记录的运行周期(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.gc_stale_time</td>
<td>决定检查一次相邻层记录的有效性的周期.<br /> 当相邻层记录失效时，将在给它发送数据前，再解析一次.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.gc_thresh1</td>
<td>存在于ARP高速缓存中的最少个数，如果少于这个数，垃圾收集器将不会运行</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.gc_thresh2</td>
<td>保存在 ARP 高速缓存中的最多的记录软限制.<br /> 垃圾收集器在开始收集前，允许记录数超过这个数字，在创建新表项时如果发现5秒没有刷新过，那么进行强制回收</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.gc_thresh3</td>
<td>保存在 ARP 高速缓存中的最多记录的硬限制，<br /> 一旦高速缓存中的数目高于此， 垃圾收集器将马上运行</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.locktime</td>
<td>防止相邻记录被过度频繁刷新，引起抖动，只有距邻居上次刷新时间超过这时才允许被再次刷新.(单位<br /> 秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.mcast_solicit</td>
<td>在把记录标记为不可达之前，<br /> 用多播/广播方式解析地址的最大次数.</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.proxy_delay</td>
<td>当接收到有一个arp请求时，在回应前可以延迟的时间，这个请求是要得到一个已知代理arp项的地址.(单位<br /> 百毫秒)</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.proxy_qlen</td>
<td>能放入代理 ARP 地址队列的数据包最大数目.</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.retrans_time</td>
<td>重发一个arp请求前的等待的秒数</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.retrans_time_ms</td>
<td>重发一个arp请求前的等待的毫秒数</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.ucast_solicit</td>
<td>arp请求最多发送次数</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.unres_qlen</td>
<td>最大挂起arp请求的数量，这些请求都正在被解析中.</td>
</tr>
<tr>
<td>net.ipv4.neigh.default.unres_qlen_bytes</td>
<td>最大处理arp包的字节数</td>
</tr>
<tr>
<td>net.ipv4.ping_group_range</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.route.error_burst</td>
<td>这个参数和error_cast一起用于限制有多少个icmp不可达消息被发送.当数据包不能到达下一跳时会发送icmp不可达数据包.<br /> 当一些主机忽略我们的icmp重定向消息时也会打印一些错误信息到dmesg.这个选项也控制打印的次数.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.route.error_cost</td>
<td>这个参数和error_burst一起用于限制有多少个icmp不可达消息被发送.当数据包不能到达下一跳时会发送icmp不可达数据包.<br /> 当一些主机忽略我们的icmp重定向消息时也会打印一些错误信息到dmesg.这个选项也控制打印的次数.<br /> error_cost值越大，那么icmp不可达和写错误信息的频率就越低.(单位 秒)</td>
</tr>
<tr>
<td>net.ipv4.route.flush</td>
<td>写这个文件就会刷新路由高速缓冲.</td>
</tr>
<tr>
<td>net.ipv4.route.gc_elasticity</td>
<td>用来控制路由缓存垃圾回收机制的频率和行为.当路由表一个hash项的长度超过此值时，会进行缓存缩减,当路由缓存项长度超过<br /> ip_rt_gc_elasticity &lt;&lt; rt_hash_log(表示路由高速缓存hash table的容量以2为对数所得的值)<br /> 时会进行强烈的回收.</td>
</tr>
<tr>
<td>net.ipv4.route.gc_interval</td>
<td>此参数定义了路由表垃圾回收的间隔(秒)</td>
</tr>
<tr>
<td>net.ipv4.route.gc_min_interval</td>
<td>已不再使用，并被gc_min_interval_ms取代</td>
</tr>
<tr>
<td>net.ipv4.route.gc_min_interval_ms</td>
<td>此参数定义了路由表垃圾回收的最小间隔(ms)</td>
</tr>
<tr>
<td>net.ipv4.route.gc_thresh</td>
<td>路由hash<br /> table的大小，当cache中的路由条数超过此值时，开始垃圾回收.</td>
</tr>
<tr>
<td>net.ipv4.route.gc_timeout</td>
<td>设置一个路由表项的过期时长(秒).</td>
</tr>
<tr>
<td>net.ipv4.route.max_size</td>
<td>路由高速缓存的最大项数，超过会进行清除旧项操作.</td>
</tr>
<tr>
<td>net.ipv4.route.min_adv_mss</td>
<td>该文件表示最小的MSS（Maximum Segment<br /> Size）大小，取决于第一跳的路由器MTU。(以字节为单位)</td>
</tr>
<tr>
<td>net.ipv4.route.min_pmtu</td>
<td>该文件表示最小路径MTU的大小。</td>
</tr>
<tr>
<td>net.ipv4.route.mtu_expires</td>
<td>该文件表示PMTU信息缓存多长时间（秒）。</td>
</tr>
<tr>
<td>net.ipv4.route.redirect_load</td>
<td>决定是否要向特定主机发送更多的ICMP重定向的时间因子.一旦达到load时间或number个数就不再发送.</td>
</tr>
<tr>
<td>net.ipv4.route.redirect_number</td>
<td>决定是否要向特定主机发送更多的ICMP重定向的数量因子.一旦达到load时间或number个数就不再发送.</td>
</tr>
<tr>
<td>net.ipv4.route.redirect_silence</td>
<td>重定向的超时.经过这么长时间后,重定向会重发,而不管是否已经因为超过load或者number限制而停止.</td>
</tr>
<tr>
<td>net.ipv4.tcp_abort_on_overflow</td>
<td>守护进程太忙而不能接受新的连接，就向对方发送reset消息<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_adv_win_scale</td>
<td>计算缓冲开销bytes/2^tcp_adv_win_scale(如果tcp_adv_win_scale<br /> &gt; 0)或者bytes-bytes/2^(-tcp_adv_win_scale)(如果tcp_adv_win_scale &lt;= 0）。</td>
</tr>
<tr>
<td>net.ipv4.tcp_allowed_congestion_control</td>
<td>列出了tcp目前允许使用的拥塞控制算法，只能在下面可用的算法中选择.</td>
</tr>
<tr>
<td>net.ipv4.tcp_app_win</td>
<td>保留max(window/2^tcp_app_win,<br /> mss)数量的窗口用于应用缓冲。当为0时表示不需要缓冲。</td>
</tr>
<tr>
<td>net.ipv4.tcp_available_congestion_control</td>
<td>列出了tcp目前可以使用的拥塞控制算法.</td>
</tr>
<tr>
<td>net.ipv4.tcp_base_mss</td>
<td>tcp探察路径上mtu的最低边界限制,<br /> mss+TCP头部+TCP选项+IP头＋IP选项.</td>
</tr>
<tr>
<td>net.ipv4.tcp_challenge_ack_limit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_congestion_control</td>
<td>当前正在使用的拥塞控制算法.</td>
</tr>
<tr>
<td>net.ipv4.tcp_dsack</td>
<td>表示是否允许TCP发送“两个完全相同”的SACK。<br /> 0：禁止<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.tcp_early_retrans</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_ecn</td>
<td>表示是否打开TCP的直接拥塞通告功能。<br /> 0：禁止<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.tcp_fack</td>
<td>表示是否打开FACK拥塞避免和快速重传功能。<br /> 0：禁止<br /> 1：打开</td>
</tr>
<tr>
<td>net.ipv4.tcp_fastopen</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_fastopen_key</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_fin_timeout</td>
<td>本端断开的socket连接，TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。默认值为<br /> 60 秒。过去在2.2版本的内核中是 180<br /> 秒。您可以设置该值，但需要注意，如果您的机器为负载很重的web服务器，您可能要冒内存被大量无效数据报填满的风险，FIN-WAIT-2 sockets<br /> 的危险性低于 FIN-WAIT-1，因为它们最多只吃 1.5K 的内存，但是它们存在时间更长。</td>
</tr>
<tr>
<td>net.ipv4.tcp_frto</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_keepalive_intvl</td>
<td>表示发送TCP探测的频率，乘以tcp_keepalive_probes表示断开没有相应的TCP连接的时间。</td>
</tr>
<tr>
<td>net.ipv4.tcp_keepalive_probes</td>
<td>该文件表示丢弃TCP连接前，进行最大TCP保持连接侦测的次数。保持连接仅在SO_KEEPALIVE套接字选项被打开时才被发送。</td>
</tr>
<tr>
<td>net.ipv4.tcp_keepalive_time</td>
<td>表示从最后一个包结束后多少秒内没有活动，才发送keepalive包保持连接，默认7200s，理想可设为1800s，即如果非正常断开，1800s后可通过keepalive知道。</td>
</tr>
<tr>
<td>net.ipv4.tcp_limit_output_bytes</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_low_latency</td>
<td>允许 TCP/IP<br /> 栈适应在高吞吐量情况下低延时的情况；这个选项一般情形是的禁用。(但在构建Beowulf 集群的时候,打开它很有帮助)<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_max_orphans</td>
<td>系统所能处理不属于任何进程的TCP<br /> sockets最大数量。假如超过这个数量，那么不属于任何进程的连接会被立即reset，并同时显示警告信息。之所以要设定这个限制，纯粹为了抵御那些简单的<br /> DoS 攻击，千万不要依赖这个或是人为的降低这个限制。</td>
</tr>
<tr>
<td>net.ipv4.tcp_max_ssthresh</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_max_syn_backlog</td>
<td>对于那些依然还未获得客户端确认的连接请求，需要保存在队列中最大数目。默认值是1024，可提高到2048。</td>
</tr>
<tr>
<td>net.ipv4.tcp_max_tw_buckets</td>
<td>系统在同时所处理的最大timewait sockets<br /> 数目。如果超过此数的话，time-wait socket 会被立即砍除并且显示警告信息。</td>
</tr>
<tr>
<td>net.ipv4.tcp_mem</td>
<td>该文件保存了三个值，分别是<br /> low：当TCP使用了低于该值的内存页面数时，TCP不会考虑释放内存。</p>
<p>presure：当TCP使用了超过该值的内存页面数量时，TCP试图稳定其内存使用，进入pressure模式，当内存消耗低于low值时则退出pressure状态。<br /> high：允许所有tcp sockets用于排队缓冲数据报的页面量。</p>
</td>
</tr>
<tr>
<td>net.ipv4.tcp_min_tso_segs</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_moderate_rcvbuf</td>
<td>接收数据时是否调整接收缓存<br /> 0：不调整<br /> 1：调整</td>
</tr>
<tr>
<td>net.ipv4.tcp_mtu_probing</td>
<td>是否开启tcp层路径mtu发现，自动调整tcp窗口等信<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_no_metrics_save</td>
<td>如果开启，tcp会在连接关闭时也就是LAST_ACK状态保存各种连接信息到路由缓存中，新建立的连接可以使用这些条件来初始化.。通常这会增加总体的系统性能，但是有些时候也会引起性能下降.<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_orphan_retries</td>
<td>针对孤立的socket(也就是已经从进程上下文中删除了，可是还有一些清理工作没有完成).在丢弃TCP连接之前重试的最大的次数</td>
</tr>
<tr>
<td>net.ipv4.tcp_reordering</td>
<td>TCP流中重排序的数据报最大数量。</td>
</tr>
<tr>
<td>net.ipv4.tcp_retrans_collapse</td>
<td>对于某些有bug的打印机提供针对其bug的兼容性。<br /> 0：不启用<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.tcp_retries1</td>
<td>该文件表示放弃回应一个TCP连接请求前进行重传的次数。</td>
</tr>
<tr>
<td>net.ipv4.tcp_retries2</td>
<td>该文件表示放弃在已经建立通讯状态下的一个TCP数据包前进行重传的次数。</td>
</tr>
<tr>
<td>net.ipv4.tcp_rfc1337</td>
<td>这个开关可以启动对于在RFC1337中描述的"tcp<br /> 的time-wait暗杀危机"问题的修复。启用后，内核将丢弃那些发往time-wait状态TCP套接字的RST 包.<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_rmem</td>
<td>此文件中保存有三个值，分别是<br /> Min：为TCP socket预留用于接收缓冲的内存最小值。每个tcp socket都可以在建立后使用它。即使在内存出现紧张情况下tcp<br /> socket都至少会有这么多数量的内存用于接收缓冲<br /> Default：为TCP socket预留用于接收缓冲的内存数量，默认情况下该值会影响其它协议使用的net.core.rmem_default<br /> 值，一般要低于net.core.rmem_default的值。该值决定了在tcp_adv_win_scale、tcp_app_win和tcp_app_win=0默认值情况下，TCP窗口大小为65535。<br /> Max：用于TCP<br /> socket接收缓冲的内存最大值。该值不会影响net.core.rmem_max，"静态"选择参数SO_SNDBUF则不受该值影响。</td>
</tr>
<tr>
<td>net.ipv4.tcp_sack</td>
<td>表示是否启用有选择的应答（Selective<br /> Acknowledgment），这可以通过有选择地应答乱序接收到的报文来提高性能（这样可以让发送者只发送丢失的报文段）；（对于广域网通信来说）这个选项应该启用，但是这会增加对<br /> CPU 的占用。<br /> 0：不启用<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.tcp_slow_start_after_idle</td>
<td>如果设置满足RFC2861定义的行为，在从新开始计算拥塞窗口前延迟一些时间，这延迟的时间长度由当前rto决定.<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv4.tcp_stdurg</td>
<td>使用 TCP urg pointer<br /> 字段中的主机请求解释功能。大部份的主机都使用老旧的BSD解释，因此如果您在 Linux 打开它，或会导致不能和它们正确沟通。<br /> 0：关闭<br /> 1：打开</td>
</tr>
<tr>
<td>net.ipv4.tcp_synack_retries</td>
<td>对于远端的连接请求SYN，内核会发送SYN ＋<br /> ACK数据报，以确认收到上一个 SYN连接请求包。<br /> 这是所谓的三次握手.这里决定内核在放弃连接之前所送出的 SYN+ACK 数目.</td>
</tr>
<tr>
<td>net.ipv4.tcp_syncookies</td>
<td>表示是否打开TCP同步标签(syncookie)，内核必须打开了<br /> CONFIG_SYN_COOKIES项进行编译。同步标签(syncookie)可以防止一个套接字在有过多试图连接到达时引起过载。<br /> 0：关闭<br /> 1：打开</td>
</tr>
<tr>
<td>net.ipv4.tcp_syn_retries</td>
<td>表示本机向外发起TCP<br /> SYN连接超时重传的次数，不应该高于255；该值仅仅针对外出的连接，对于进来的连接由tcp_retries1控制。</td>
</tr>
<tr>
<td>net.ipv4.tcp_thin_dupack</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_thin_linear_timeouts</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.tcp_timestamps</td>
<td>表示是否启用以一种比超时重发更精确的方法（请参阅 RFC<br /> 1323）来启用对 RTT 的计算；为了实现更好的性能应该启用这个选项。<br /> 0：不启用<br /> 1：启用</td>
</tr>
<tr>
<td>net.ipv4.tcp_tso_win_divisor</td>
<td>控制根据拥塞窗口的百分比，是否来发送相应的延迟tso<br /> frame<br /> 0：关闭<br /> &gt;0：值越大表示tso frame延迟发送可能越小.</td>
</tr>
<tr>
<td>net.ipv4.tcp_tw_recycle</td>
<td>打开快速 TIME-WAIT sockets<br /> 回收。除非得到技术专家的建议或要求，请不要随意修改这个值。</td>
</tr>
<tr>
<td>net.ipv4.tcp_tw_reuse</td>
<td>表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接。<br /> 0：关闭<br /> 1：打开</td>
</tr>
<tr>
<td>net.ipv4.tcp_window_scaling</td>
<td>表示设置tcp/ip会话的滑动窗口大小是否可变。<br /> 0：不可变<br /> 1：可变</td>
</tr>
<tr>
<td>net.ipv4.tcp_wmem</td>
<td>此文件中保存有三个值，分别是<br /> Min：为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建立后使用它。<br /> Default：为TCP socket预留用于发送缓冲的内存数量，默认情况下该值会影响其它协议使用的net.core.wmem_default<br /> 值，一般要低于net.core.wmem_default的值。<br /> Max：用于TCP socket发送缓冲的内存最大值。该值不会影响net.core.wmem_max，"静态"选择参数SO_SNDBUF则不受该值影响。</td>
</tr>
<tr>
<td>net.ipv4.tcp_workaround_signed_windows</td>
<td>0：假定远程连接端正常发送了窗口收缩选项，即使对端没有发送.<br /> 1：假定远程连接端有错误,没有发送相关的窗口缩放选项</td>
</tr>
<tr>
<td>net.ipv4.udp_mem</td>
<td>该文件保存了三个值，分别是<br /> low：当UDP使用了低于该值的内存页面数时，UDP不会考虑释放内存。</p>
<p>presure：当UDP使用了超过该值的内存页面数量时，UDP试图稳定其内存使用，进入pressure模式，当内存消耗低于low值时则退出pressure状态。<br /> high：允许所有UDP sockets用于排队缓冲数据报的页面量。</p>
</td>
</tr>
<tr>
<td>net.ipv4.udp_rmem_min</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.udp_wmem_min</td>
<td> </td>
</tr>
<tr>
<td>net.ipv4.xfrm4_gc_thresh</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.bindv6only</td>
<td>默认监听ipv6端口(不管监听与否，都与是否关闭ipv4监听无关)<br /> 0：不监听<br /> 1：监听</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_dad</td>
<td>0：取消DAD功能<br /> 1：启用DAD功能，但link-local地址冲突时，不关闭ipv6功能<br /> 2：启用DAD功能，但link-local地址冲突时，关闭ipv6功能</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_ra</td>
<td>接受IPv6路由通告.并且根据得到的信息自动设定.<br /> 0：不接受路由通告<br /> 1：当forwarding禁止时接受路由通告<br /> 2：任何情况下都接受路由通告</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_ra_defrtr</td>
<td>是否接受ipv6路由器发出的默认路由设置<br /> 0：不接受<br /> 1：接受</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_ra_pinfo</td>
<td>当accept_ra开启时此选项会自动开启，关闭时则会关闭</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_ra_rt_info_max_plen</td>
<td>在路由通告中路由信息前缀的最大长度。当</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_ra_rtr_pref</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_redirects</td>
<td>是否接受ICMPv6重定向包<br /> 0：拒绝接受ICMPv6，当forwarding=1时，此值会自动设置为0<br /> 1：启动接受ICMPv6，当forwarding=0时，此值会自动设置为1</td>
</tr>
<tr>
<td>net.ipv6.conf.all.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv6.conf.all.autoconf</td>
<td>设定本地连结地址使用L2硬件地址. 它依据界面的L2-MAC<br /> address自动产生一个地址如:"fe80::201:23ff:fe45:6789"</td>
</tr>
<tr>
<td>net.ipv6.conf.all.dad_transmits</td>
<td>接口增加ipv6地址时，发送几次DAD包</td>
</tr>
<tr>
<td>net.ipv6.conf.all.disable_ipv6</td>
<td>是否禁用ipv6<br /> 0：不禁用<br /> 1：禁用</td>
</tr>
<tr>
<td>net.ipv6.conf.all.force_mld_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.all.force_tllao</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.all.forwarding</td>
<td>所有网络接口开启ipv6转发<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv6.conf.all.hop_limit</td>
<td>缺省hop限制</td>
</tr>
<tr>
<td>net.ipv6.conf.all.max_addresses</td>
<td>所有网络接口自动配置IP地址的数量最大值<br /> 0：不限制<br /> &gt;0：最大值</td>
</tr>
<tr>
<td>net.ipv6.conf.all.max_desync_factor</td>
<td>DESYNC_FACTOR的最大值，DESYNC_FACTOR是一个随机数，用于防止客户机在同一时间生成新的地址</td>
</tr>
<tr>
<td>net.ipv6.conf.all.mc_forwarding</td>
<td>是否使用多路广播进行路由选择，需要内核编译时开启了CONFIG_MROUTE选项并且开启了多路广播路由选择的后台daemon<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv6.conf.all.mldv1_unsolicited_report_interval</td>
<td>每次发送MLDv1的主动报告的时间间隔(ms)</td>
</tr>
<tr>
<td>net.ipv6.conf.all.mldv2_unsolicited_report_interval</td>
<td>每次发送MLDv2的主动报告的时间间隔(ms)</td>
</tr>
<tr>
<td>net.ipv6.conf.all.mtu</td>
<td>ipv6的最大传输单元</td>
</tr>
<tr>
<td>net.ipv6.conf.all.ndisc_notify</td>
<td>如何向邻居设备通知地址和设备的改变<br /> 0：不通知<br /> 1：主动向邻居发送广播报告硬件地址或者设备发生了改变</td>
</tr>
<tr>
<td>net.ipv6.conf.all.optimistic_dad</td>
<td>是否启用optimistic<br /> DAD(乐观地进行重复地址检查)<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv6.conf.all.proxy_ndp</td>
<td>此功能类似于ipv4的nat，可将内网的包转发到外网，外网不能主动发给内网。<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.ipv6.conf.all.regen_max_retry</td>
<td>尝试生成临时地址的次数</td>
</tr>
<tr>
<td>net.ipv6.conf.all.router_probe_interval</td>
<td>路由器探测间隔(秒)</td>
</tr>
<tr>
<td>net.ipv6.conf.all.router_solicitation_delay</td>
<td>在发送路由请求之前的等待时间(秒).</td>
</tr>
<tr>
<td>net.ipv6.conf.all.router_solicitation_interval</td>
<td>在每个路由请求之间的等待时间(秒).</td>
</tr>
<tr>
<td>net.ipv6.conf.all.router_solicitations</td>
<td>假定没有路由的情况下发送的请求个数</td>
</tr>
<tr>
<td>net.ipv6.conf.all.temp_prefered_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.all.temp_valid_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.all.use_tempaddr</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_dad</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_ra</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_ra_defrtr</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_ra_pinfo</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_ra_rt_info_max_plen</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_ra_rtr_pref</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_redirects</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.autoconf</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.dad_transmits</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.disable_ipv6</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.force_mld_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.force_tllao</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.forwarding</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.hop_limit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.max_addresses</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.max_desync_factor</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.mc_forwarding</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.mldv1_unsolicited_report_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.mldv2_unsolicited_report_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.mtu</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.ndisc_notify</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.optimistic_dad</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.proxy_ndp</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.regen_max_retry</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.router_probe_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.router_solicitation_delay</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.router_solicitation_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.router_solicitations</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.temp_prefered_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.temp_valid_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.{IF}.use_tempaddr</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_dad</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_ra</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_ra_defrtr</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_ra_pinfo</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_ra_rt_info_max_plen</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_ra_rtr_pref</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_redirects</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.accept_source_route</td>
<td>接收带有SRR选项的数据报。主机设为0，路由设为1</td>
</tr>
<tr>
<td>net.ipv6.conf.default.autoconf</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.dad_transmits</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.disable_ipv6</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.force_mld_version</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.force_tllao</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.forwarding</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.hop_limit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.max_addresses</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.max_desync_factor</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.mc_forwarding</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.mldv1_unsolicited_report_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.mldv2_unsolicited_report_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.mtu</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.ndisc_notify</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.optimistic_dad</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.proxy_ndp</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.regen_max_retry</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.router_probe_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.router_solicitation_delay</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.router_solicitation_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.router_solicitations</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.temp_prefered_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.temp_valid_lft</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.conf.default.use_tempaddr</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.icmp.ratelimit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.ip6frag_high_thresh</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.ip6frag_low_thresh</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.ip6frag_secret_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.ip6frag_time</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.mld_max_msf</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.mld_qrv</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.anycast_delay</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.app_solicit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.base_reachable_time</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.base_reachable_time_ms</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.delay_first_probe_time</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.gc_stale_time</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.locktime</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.mcast_solicit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.proxy_delay</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.proxy_qlen</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.retrans_time</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.retrans_time_ms</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.ucast_solicit</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.unres_qlen</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.neigh.{IF}.unres_qlen_bytes</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.flush</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_elasticity</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_min_interval</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_min_interval_ms</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_thresh</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.gc_timeout</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.max_size</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.min_adv_mss</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.route.mtu_expires</td>
<td> </td>
</tr>
<tr>
<td>net.ipv6.xfrm6_gc_thresh</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_acct</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_buckets</td>
<td>只读，描述当前系统的ip_conntrack的hash<br /> table大小.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_checksum</td>
<td>验证协议是否错误是，是否对协议进行校验和验证<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_count</td>
<td>内存中ip_conntrack结构的数量.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_events</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_events_retry_timeout</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_expect_max</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_generic_timeout</td>
<td>通用或未知协议的conntrack被设置的超时时间(每次看到包都会用这值重新更新定时器),一旦时间到conntrack将被回收.(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_helper</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_icmp_timeout</td>
<td>icmp协议的conntrack被设置的超时时间，一旦到时conntrack将被回收.(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_log_invalid</td>
<td>调试时使用，可以指定一个数字，这个数字是内核定义的协议号比如IPPROTO_TCP是6，当指定协议解析时发现一些错误包会打印相关的错误信息到dmesg中.<br /> 最小值0,最大值255，默认不打印.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_max</td>
<td>内存中最多ip_conntrack结构的数量.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_be_liberal</td>
<td>当开启只有不在tcp窗口内的rst包被标志为无效，当关闭（默认）所有不在tcp窗口中的包都被标志为无效.<br /> 0：关闭<br /> 1：开启</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_loose</td>
<td>当想追踪一条已经连接的tcp会话，<br /> 在系统可以假设sync和window追逐已经开始后要求每个方向必须通过的包的数量.<br /> 如果为0，从不追踪一条已经连接的tcp会话.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_max_retrans</td>
<td>没有从目的端接收到一个ack而进行包重传的次数，一旦达到这限制nf_conntrack_tcp_timeout_max_retrans将作为ip_conntrack的超时限制.</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_close</td>
<td>TCP处于close状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_close_wait</td>
<td>TCP处于close wait状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_established</td>
<td>TCP处于established状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_fin_wait</td>
<td>TCP处于fin wait状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_last_ack</td>
<td>TCP处于last ack状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_max_retrans</td>
<td>TCP处于max retrans状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_syn_recv</td>
<td>TCP处于syn recv状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_syn_sent</td>
<td>TCP处于syn sent状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_time_wait</td>
<td>TCP处于time wait状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_tcp_timeout_unacknowledged</td>
<td>TCP处于unacknowledged状态超时时间(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_timestamp</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_udp_timeout</td>
<td>udp协议的conntrack被设置的超时时间(每次看到包都会用这值重新更新定时器)，一旦到时conntrack将被回收.(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_conntrack_udp_timeout_stream</td>
<td>当看到一些特殊的udp传输时(传输在双向)设置的ip_conntrack超时时间(每次看到包都会用这值重新更新定时器).(秒)</td>
</tr>
<tr>
<td>net.netfilter.nf_log.0</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.1</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.10</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.11</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.12</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.2</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.3</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.4</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.5</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.6</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.7</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.8</td>
<td> </td>
</tr>
<tr>
<td>net.netfilter.nf_log.9</td>
<td> </td>
</tr>
<tr>
<td>net.nf_conntrack_max</td>
<td>内存中最多ip_conntrack结构的数量.</td>
</tr>
<tr>
<td>net.unix.max_dgram_qlen</td>
<td>允许域套接字中数据包的最大个数，在初始化unix域套接字时的默认值.<br /> 在调用listen函数时第二个参数会复盖这个值.</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">文件系统参数</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 30%;">fs.aio-max-nr</td>
<td>最大允许aio请求数量(会涉及到数据库的aio请求)</td>
</tr>
<tr>
<td>fs.aio-nr</td>
<td>当前aio请求数量</td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-alpha</td>
<td>binfmt_misc用于支持当前芯片架构的系统是否支持通过qemu进行chroot到其他架构的根文件系统中进行操作。</td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-arm</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-armeb</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-cris</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-i386</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-i486</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-m68k</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-microblaze</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-microblazeel</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-mips</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-mips64</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-mips64el</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-mipsel</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-s390x</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-sh4</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-sh4eb</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-sparc</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-sparc32plus</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.qemu-sparc64</td>
<td> </td>
</tr>
<tr>
<td>fs.binfmt_misc.register</td>
<td>
<p>用于注册或修改以上的binfmt_misc，输入格式是 :name:type:offset:magic:mask:interpreter:flags<br /> 例：echo<br /> ':i386:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03:</p>
<p>\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:'<br /> &gt; register</p>
</td>
</tr>
<tr>
<td>fs.binfmt_misc.status</td>
<td>设置binfmt_misc开启<br /> 0：禁止<br /> 1：开启</td>
</tr>
<tr>
<td>fs.dentry-state</td>
<td>保存目录缓存的状态，保存有六个值，只有前三个有效<br /> nr_dentry：当前已经分配的目录项数量<br /> nr_unused：还没有使用的目录项数量<br /> age_limit：当内存紧缺时，延迟多少秒后会回收目录项所占内存</td>
</tr>
<tr>
<td>fs.dir-notify-enable</td>
<td>设置是否启用dnotify，已被inotify取代，因为dnotify<br /> 需要您为每个打算监控是否发生改变的目录打开一个文件描述符。当同时监控多个目录时，这会消耗大量的资源，因为有可能达到每个进程的文件描述符限制。并且不允许卸载（unmount）支持的设备<br /> 0：不使用<br /> 1：使用</td>
</tr>
<tr>
<td>fs.epoll.max_user_watches</td>
<td>IO复用epoll监听文件句柄的数量最大值</td>
</tr>
<tr>
<td>fs.file-max</td>
<td>系统中所有进程能够同时打开的文件句柄数量</td>
</tr>
<tr>
<td>fs.file-nr</td>
<td>此文件中保存了三个值，分别是：系统中已分配的文件句柄数量    已分配但没有使用的文件句柄数量    最大的文件句柄号</td>
</tr>
<tr>
<td>fs.inode-nr</td>
<td>此文件保存了两个值，是：已分配inode数    空闲inode数</td>
</tr>
<tr>
<td>fs.inode-state</td>
<td>此文件保存了三个值，前两个分别表示<br /> 已分配inode数和空闲inode数。第三个是已超出系统最大inode值的数量，此时系统需要清除排查inode列表</td>
</tr>
<tr>
<td>fs.inotify.max_queued_events</td>
<td>inotify用于监控文件系统事件</p>
<p>该文件中的值为调用inotify_init函数时分配给inotify队列的事件数目的最大值，超出这个值得事件被丢弃，但会触发IN_Q_OVERFLOW事件<br /> 文件系统变化越频繁，这个值就应该越大 </p>
</td>
</tr>
<tr>
<td>fs.inotify.max_user_instances</td>
<td>设置每个用户可以运行的inotifywait或inotifywatch命令的进程数。</td>
</tr>
<tr>
<td>fs.inotify.max_user_watches</td>
<td>设置inotifywait或inotifywatch命令可以监视的文件数量(单进程)。</td>
</tr>
<tr>
<td>fs.lease-break-time</td>
<td>当进程尝试打开一个被租借锁保护的文件时，该进程会被阻塞，同时，在一定时间内拥有该文件租借锁的进程会收到一个信号。收到信号之后，拥有该文件租借锁的进程会首先更新文件，从而保证了文件内容的一致性，接着，该进程释放这个租借锁。如果拥有租借锁的进程在一定的时间间隔内没有完成工作，内核就会自动删除这个租借锁或者将该锁进行降级，从而允许被阻塞的进程继续工作。<br /> 此保存租借锁的超时时间(以秒为单位)</td>
</tr>
<tr>
<td>fs.leases-enable</td>
<td>是否启用文件的租借锁<br /> 1：启用<br /> 0：不启用</td>
</tr>
<tr>
<td>fs.mqueue.msg_default</td>
<td>POSIX的消息队列<br /> 此文件保存一个消息队列中消息数量的默认值，如果此值超过msg_max，则会被设置为msg_max</td>
</tr>
<tr>
<td>fs.mqueue.msg_max</td>
<td>一个消息队列的最大消息数</td>
</tr>
<tr>
<td>fs.mqueue.msgsize_default</td>
<td>消息队列中一个消息的默认大小(以字节为单位)</td>
</tr>
<tr>
<td>fs.mqueue.msgsize_max</td>
<td>消息队列中一个消息的最大大小(以字节为单位)</td>
</tr>
<tr>
<td>fs.mqueue.queues_max</td>
<td>系统中允许的消息队列的最大数量</td>
</tr>
<tr>
<td>fs.nfs.idmap_cache_timeout</td>
<td>设置idmapper缓存项的最大寿命，单位是秒</td>
</tr>
<tr>
<td>fs.nfs.nfs_callback_tcpport</td>
<td>设置NFSv4回复通道(callback<br /> channel)监听的TCP端口</td>
</tr>
<tr>
<td>fs.nfs.nfs_congestion_kb</td>
<td> </td>
</tr>
<tr>
<td>fs.nfs.nfs_mountpoint_timeout</td>
<td> </td>
</tr>
<tr>
<td>fs.nfs.nlm_grace_period</td>
<td>参数设置服务器重新引导后客户机回收NFSv3锁和NFSv4锁所需的秒数。因此，grace_period的值可控制NFSv3和NFSv4的锁定恢复的宽延期长度。</td>
</tr>
<tr>
<td>fs.nfs.nlm_tcpport</td>
<td>为NFS锁管理器指定TCP端口</td>
</tr>
<tr>
<td>fs.nfs.nlm_timeout</td>
<td>为NFS锁管理器指定默认超时时间，单位是秒。默认值是10秒。取值范围在[3-20]</td>
</tr>
<tr>
<td>fs.nfs.nlm_udpport</td>
<td>为NFS锁管理器指定UDP端口</td>
</tr>
<tr>
<td>fs.nfs.nsm_local_state</td>
<td> </td>
</tr>
<tr>
<td>fs.nfs.nsm_use_hostnames</td>
<td> </td>
</tr>
<tr>
<td>fs.nr_open</td>
<td>一个进程最多同时打开的文件句柄数量</td>
</tr>
<tr>
<td>fs.overflowgid</td>
<td>Linux的GID为32位，但有些文件系统只支持16位的GID，此时若进行写操作会出错；当GID超过65535时会自动被转换为一个固定值，这个固定值保存在这个文件中</td>
</tr>
<tr>
<td>fs.overflowuid</td>
<td>Linux的UID为32位，但有些文件系统只支持16位的UID，此时若进行写操作会出错；当UID超过65535时会自动被转换为一个固定值，这个固定值保存在这个文件中</td>
</tr>
<tr>
<td>fs.pipe-max-size</td>
<td>此文件限制非特权程序使用pipe时的缓存最大大小(以字节为单位，最小设置为4096)</td>
</tr>
<tr>
<td>fs.protected_hardlinks</td>
<td>用于限制普通用户建立硬链接<br /> 0：不限制用户建立硬链接<br /> 1：限制，如果文件不属于用户，或者用户对此用户没有读写权限，则不能建立硬链接</td>
</tr>
<tr>
<td>fs.protected_symlinks</td>
<td>用于限制普通用户建立软链接<br /> 0：不限制用户建立软链接<br /> 1：限制，允许用户建立软连接的情况是 软连接所在目录是全局可读写目录或者软连接的uid与跟从者的uid匹配，又或者目录所有者与软连接所有者匹配</td>
</tr>
<tr>
<td>fs.quota.allocated_dquots</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.cache_hits</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.drops</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.free_dquots</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.lookups</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.reads</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.syncs</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.warnings</td>
<td> </td>
</tr>
<tr>
<td>fs.quota.writes</td>
<td> </td>
</tr>
<tr>
<td>fs.suid_dumpable</td>
<td> </td>
</tr>
<tr>
<td>fscache.object_max_active</td>
<td> </td>
</tr>
<tr>
<td>fscache.operation_max_active</td>
<td> </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg"><a id="kernel-params-opt-ref"></a>调优参考</span></div>
<pre class="crayon-plain-tag">### 改善系统内存管理 ###

# 取消文件句柄和inode缓存限制
fs.file-max = 2097152

# 减少Swap操作
vm.swappiness = 10
vm.dirty_ratio = 60
vm.dirty_background_ratio = 2

### 一般性网络安全选项 ###

# 被动TCP链接的SYNACKs重试次数
net.ipv4.tcp_synack_retries = 2

# 允许的本地端口访问
net.ipv4.ip_local_port_range = 2000 65535

# 防止 TCP Time-Wait问题
net.ipv4.tcp_rfc1337 = 1

# 减少tcp_fin_timeout超时
net.ipv4.tcp_fin_timeout = 15

# 减少TCP保活探测周期
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15

### 调整网络性能 ###

# 默认套接字接收缓冲
net.core.rmem_default = 31457280
# 最大套接字接收缓冲
net.core.rmem_max = 12582912

# 默认套接字发送缓冲
net.core.wmem_default = 31457280
# 最大套接字发送缓冲
net.core.wmem_max = 12582912

# 最大入站连接数
net.core.somaxconn = 4096

# 最大入站连接排队数量
net.core.netdev_max_backlog = 65536

# 增加的option memory buffers最大量
net.core.optmem_max = 25165824

# 增大总计的允许分配的缓冲的数量（单位页，也就是4KB
net.ipv4.tcp_mem = 65536 131072 262144
net.ipv4.udp_mem = 65536 131072 262144

# 总计可分配读缓冲
net.ipv4.tcp_rmem = 8192 87380 16777216
net.ipv4.udp_rmem_min = 16384

# 总计可分配写缓冲
net.ipv4.tcp_wmem = 8192 65536 16777216
net.ipv4.udp_wmem_min = 16384

# 增加tcp-time-wait桶（bucket）池的尺寸，应对简单的DOS攻击
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1</pre>
<div class="blog_h2"><span class="graybg">自动调优</span></div>
<p>tuned是一个服务，它能够自适应的进行系统调优。它将一系列系统设置集合在一起，称为profile。</p>
<p>优化后的profile中包含CPU管理、IO调度、内核参数调整等指令。</p>
<p>执行下面的命令安装：</p>
<pre class="crayon-plain-tag">yum -y install tuned
service tuned start
chkconfig tuned on</pre>
<p>执行下面的命令查看可用的profile列表：</p>
<pre class="crayon-plain-tag">tuned-adm list</pre>
<p>切换到最大吞吐量模式：</p>
<pre class="crayon-plain-tag">tuned-adm profile throughput-performance  </pre>
<div class="blog_h2"><span class="graybg">常见问题</span></div>
<div class="blog_h3"><span class="graybg">tcp_timestamps导致RST</span></div>
<p>修改net.ipv4.tcp_timestamps = 0后，出现和某个服务通信时，有1%几率Connection reset by peer的情况。抓包仅仅发现对方发送RST报文。恢复此参数为默认值后问题消失。</p>
<div class="blog_h3"><span class="graybg">sack导致的LVC+NAT相关的RST</span></div>
<p>在Docker环境下出现，详见<a href="/docker-study-note#lvs-related-rst">Docker学习笔记</a>。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/linux-faq">Linux知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/linux-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
