<?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>Soul Of Free Loop &#187; TCP</title>
	<atom:link href="https://zohead.com/archives/tag/tcp/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>Linux下使用tcpkill工具重置NFS连接</title>
		<link>https://zohead.com/archives/tcpkill-nfs/</link>
		<comments>https://zohead.com/archives/tcpkill-nfs/#comments</comments>
		<pubDate>Wed, 14 Sep 2016 18:00:14 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[网络技术]]></category>
		<category><![CDATA[Dsniff]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[RST]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[tcpkill]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1272</guid>
		<description><![CDATA[有些时候在 Linux 系统中使用 NFS 挂载远程共享（使用默认的 TCP 方式）之后，偶尔会因为网络异常出现 NFS 连接出错的问题，这种情况下使用任何 ls 或者 df 等等常用的命令对 NFS 挂载目录进行简单的查看操作都可能卡顿几十秒乃至几分钟的时间。 此时如果在别的 Linux 客户机系统上又是可以正常访问 NFS 共享的，而 NFS 服务器端考虑到有多个客户机正在使用不方便直接重启服务，一般只能等待 NFS 挂载连接恢复正常或者重启客户端系统，这样还是很麻烦的，为此我专门找了个使用 tcpkill 命令重置 NFS 连接的方法分享给大家。 tcpkill 命令属于 Dsniff  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>有些时候在 Linux 系统中使用 NFS 挂载远程共享（使用默认的 TCP 方式）之后，偶尔会因为网络异常出现 NFS 连接出错的问题，这种情况下使用任何 ls 或者 df 等等常用的命令对 NFS 挂载目录进行简单的查看操作都可能卡顿几十秒乃至几分钟的时间。</p>
<p>此时如果在别的 Linux 客户机系统上又是可以正常访问 NFS 共享的，而 NFS 服务器端考虑到有多个客户机正在使用不方便直接重启服务，一般只能等待 NFS 挂载连接恢复正常或者重启客户端系统，这样还是很麻烦的，为此我专门找了个使用 tcpkill 命令重置 NFS 连接的方法分享给大家。</p>
<p>tcpkill 命令属于 Dsniff 嗅探工具包，Dsniff 本身是一个高级的网络嗅探器，Dsniff 可以将制造的数据包注入到网络，一般 Linux 系统中可以找到对应的 dsniff 软件包进行安装，我测试使用的 CentOS 6.1 64 位系统使用的下面的 RPM 安装包：</p>
<p><a href="http://rpm.repo.onapp.com/ramdisk-hv/centos6/dsniff/">http://rpm.repo.onapp.com/ramdisk-hv/centos6/dsniff/</a></p>
<p>tcpkill 的工作原理是利用 libpcap 库监控符合过滤条件的 TCP 连接并等待，当该 TCP 连接上有数据传输时就会被 tcpkill 感知到，不过作为应用程序的 tcpkill 当然不能直接关闭 TCP 连接，此时就会构造一条 RST 报文发回去直接导致 TCP 连接异常关闭（其实 TCP RST 就是 GFW 对敏感网页进行屏蔽的老手段之一哈）。</p>
<p>对于 NFS 共享使用的 TCP 连接而言，通过 tcpkill 发送 RST 报文导致连接异常关闭之后，NFS 客户端就能知道原有的连接不可用，会自动重新开启新的 TCP 连接，这样就能达到我们需要的重置 NFS 连接的效果。</p>
<p>下面具体实验看看，首先在 NFS 服务器上使用 <code>netstat</code> 命令确认所有 TCP 方式挂载的 NFS 连接，这里使用 NFS 服务的 <code>2049</code> 端口进行过滤：</p>
<pre class="brush: bash; title: ; notranslate">
~ # netstat -anp 2&gt;/dev/null | grep 2049
tcp        0      0 0.0.0.0:2049            0.0.0.0:*               LISTEN      -
tcp        0      0 192.168.1.56:2049       192.168.1.52:953        ESTABLISHED -
tcp        0      0 192.168.1.56:2049       192.168.1.129:824       ESTABLISHED -
udp        0      0 0.0.0.0:2049            0.0.0.0:*                           -
</pre>
<p>可以看到 NFS 服务器上有 192.168.1.52 和 192.168.1.129 这两个客户端正在连接，看看 tcpkill 命令的用法：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill [-i interface] [-1...9] expression
</pre>
<p>tcpkill 的 <code>-i</code> 参数指定绑定哪个网卡，<code>expression</code> 就是 tcpdump 的过滤表达式参数，具体如何使用可以参考 tcpdump 的帮助信息：</p>
<p><a href="http://www.tcpdump.org/tcpdump_man.html">http://www.tcpdump.org/tcpdump_man.html</a></p>
<p>我们就以关闭上面的 192.168.1.52 客户端使用的 NFS 连接为例：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]
</pre>
<p>tcpkill 命令启动之后会一直等待新的数据传输，我们就可以在 NFS 客户端上随便运行 ls 或者 df 等命令查看 NFS 共享文件夹就可以让系统发送新的 NFS 数据（仍然是有问题的 NFS 连接所以可能会卡住），此时服务器端的 tcpkill 命令就有反应了：</p>
<pre class="brush: bash; title: ; notranslate">
~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581713:1715581713(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581896:1715581896(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715582262:1715582262(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624540755:624540755(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624540919:624540919(0) win 0
192.168.1.56:2049 &gt; 192.168.1.52:953: R 624541247:624541247(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581801:1715581801(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715581984:1715581984(0) win 0
192.168.1.52:953 &gt; 192.168.1.56:2049: R 1715582350:1715582350(0) win 0
</pre>
<p>可以看到 tcpkill 已经发送了 RST 包导致 NFS 客户端重新连接，如果客户端的 NFS 共享操作已经恢复正常了，就可以退出 tcpkill 命令了。</p>
<p>从上面的例子我们也会发现 tcpkill 默认是只能“关闭”活跃有数据传输的 TCP 连接的；对于非活跃的连接我们可能需要主动发送 SYN 包，并根据返回的 TCP 序列号构造新的 RST 包再次发送以达到关闭连接的效果，看起来已经有网友实现了一个支持关闭非活跃连接的 tcpkill 程序，有兴趣的朋友也可以关注看看哦。最后祝大家中秋佳节玩的开心 ^_^。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/tcpkill-nfs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NFS读写块大小问题分析</title>
		<link>https://zohead.com/archives/nfs-rwsize/</link>
		<comments>https://zohead.com/archives/nfs-rwsize/#comments</comments>
		<pubDate>Sat, 05 Jul 2014 14:44:32 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[存储]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[块大小]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=723</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/nfs-rwsize/ Linux NFS 客户端在挂载服务器的 NFS 共享时可以使用 rsize 和 wsize 参数指定 NFS 读写的块大小，但实际使用时发现并不完全凑效，下面简单分析一下。 我先在一台 RHEL6 客户端上挂载另一台 RHEL6 服务器上的 NFS 共享： 从上面可以看到不指定 rsize 和 wsize 参数时，默认的读写块大小都是 256KB（rsize=262144），而且使用的是 TCP 协议（proto=tcp）。 下面使用 UDP 协议挂载 NFS 共享： 从结果可以 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/nfs-rwsize/" target="_blank">https://zohead.com/archives/nfs-rwsize/</a></p>
<p>Linux NFS 客户端在挂载服务器的 NFS 共享时可以使用 rsize 和 wsize 参数指定 NFS 读写的块大小，但实际使用时发现并不完全凑效，下面简单分析一下。</p>
<p>我先在一台 RHEL6 客户端上挂载另一台 RHEL6 服务器上的 NFS 共享：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>从上面可以看到不指定 rsize 和 wsize 参数时，默认的读写块大小都是 256KB（rsize=262144），而且使用的是 TCP 协议（proto=tcp）。</p>
<p>下面使用 UDP 协议挂载 NFS 共享：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o udp 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,proto=udp,timeo=11,retrans=3,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>从结果可以看出，使用 UDP 协议时块大小就只有 32KB 了。</p>
<p>准备在客户端这边修改 mount 参数将 NFS TCP 方式的读写块大小增加到 1MB：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o rsize=1048576,wsize=1048576 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>但从上面的结果来看，实际使用的块大小还是 256KB。</p>
<p>在客户端这边修改 mount 参数将 NFS UDP 方式的读写块大小增加到 256KB：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o udp,rsize=262144,wsize=262144 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,proto=udp,timeo=11,retrans=3,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>UDP 模式下读写的块大小也无法修改，客户端似乎已经使用了最大的读写块大小。</p>
<p>没办法，下面来看看 Linux kernel 源代码，找出真正的原因，先在 include/linux/nfs_xdr.h 文件中找到了 NFS I/O 块大小的定义：</p>
<p><pre class="brush: cpp; title: include/linux/nfs_xdr.h; notranslate">
/*
 * To change the maximum rsize and wsize supported by the NFS client, adjust
 * NFS_MAX_FILE_IO_SIZE.  64KB is a typical maximum, but some servers can
 * support a megabyte or more.  The default is left at 4096 bytes, which is
 * reasonable for NFS over UDP.
 */
#define NFS_MAX_FILE_IO_SIZE    (1048576U)
#define NFS_DEF_FILE_IO_SIZE    (4096U)
#define NFS_MIN_FILE_IO_SIZE    (1024U)
</pre>
</p>
<p>这里可以看到 NFS 默认使用 4KB 块大小，客户端实际挂载时会做调整，最小 1KB，最大 1MB。</p>
<p>NFS 客户端在挂载时会与 NFS 服务器协商适合的读写块大小值，我们来看看 fs/nfs/client.c 文件中协商设置 NFS 文件系统信息的代码：</p>
<p><pre class="brush: cpp; highlight: [0,11,12,13,14]; title: fs/nfs/client.c; notranslate">
static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo)
{
	unsigned long max_rpc_payload;

	/* Work out a lot of parameters */
	if (server-&gt;rsize == 0)
		server-&gt;rsize = nfs_block_size(fsinfo-&gt;rtpref, NULL);
	if (server-&gt;wsize == 0)
		server-&gt;wsize = nfs_block_size(fsinfo-&gt;wtpref, NULL);

	if (fsinfo-&gt;rtmax &gt;= 512 &amp;&amp; server-&gt;rsize &gt; fsinfo-&gt;rtmax)
		server-&gt;rsize = nfs_block_size(fsinfo-&gt;rtmax, NULL);
	if (fsinfo-&gt;wtmax &gt;= 512 &amp;&amp; server-&gt;wsize &gt; fsinfo-&gt;wtmax)
		server-&gt;wsize = nfs_block_size(fsinfo-&gt;wtmax, NULL);

	max_rpc_payload = nfs_block_size(rpc_max_payload(server-&gt;client), NULL);
	if (server-&gt;rsize &gt; max_rpc_payload)
		server-&gt;rsize = max_rpc_payload;
	if (server-&gt;rsize &gt; NFS_MAX_FILE_IO_SIZE)
		server-&gt;rsize = NFS_MAX_FILE_IO_SIZE;
	server-&gt;rpages = (server-&gt;rsize + PAGE_CACHE_SIZE - 1) &gt;&gt; PAGE_CACHE_SHIFT;

	server-&gt;backing_dev_info.name = &quot;nfs&quot;;
	server-&gt;backing_dev_info.ra_pages = server-&gt;rpages * NFS_MAX_READAHEAD;

	if (server-&gt;wsize &gt; max_rpc_payload)
		server-&gt;wsize = max_rpc_payload;
	if (server-&gt;wsize &gt; NFS_MAX_FILE_IO_SIZE)
		server-&gt;wsize = NFS_MAX_FILE_IO_SIZE;
	/*......*/
}
</pre>
</p>
<p>从上面 nfs_server_set_fsinfo 函数的代码可以看到 NFS 客户端实际参考了服务器返回的 rtmax 和 wtmax 值，而这个值可以在挂载 NFS 文件系统时用抓包工具看到（NFS 使用的 RPC 协议）。</p>
<p>下面的图片中显示的就是 NFS 客户端中指定 rsize 和 wsize 参数为 1MB 时 Wireshark 上抓到的 NFS FSINFO 请求的实际数据；</p>
<div style="width: 513px" class="wp-caption alignnone"><a href="http://zohead.com/wp-content/uploads/nfs-mount-cap.jpg" target="_blank"><img alt="挂载NFS的网络抓包" src="http://zohead.com/wp-content/uploads/nfs-mount-cap.jpg" width="503" height="231" /></a><p class="wp-caption-text">挂载NFS的网络抓包</p></div>
<p>上面图片里小椭圆圈表示的是 NFS FSINFO 请求，大椭圆圈里就是服务器传过来的 rtmax 和 wtmax 值了，我们可以看到值就是 256KB。这样也就能解释了为什么客户端增大 NFS 读写块大小也不起作用了。</p>
<p>我们后台登陆到 NFS 服务器上，可以从 /proc/fs/nfsd/max_block_size 文件中看到当前 NFS 服务器的最大块大小，然后尝试修改它：</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # cat /proc/fs/nfsd/max_block_size
262144
~ # echo 524288 &gt; /proc/fs/nfsd/max_block_size
~ # cat /proc/fs/nfsd/max_block_size
262144
</pre>
</p>
<p>可以看到当前 NFS 服务器的最大读写块大小确实是 256KB，但是我们想修改它的值的时候，却似乎又修改不了。这样只能再看看修改 max_block_size 的 kernel 源代码了，对应的代码在 nfsd/nfsctl.c 文件中：</p>
<p><pre class="brush: cpp; highlight: [14,15,18]; title: nfsd/nfsctl.c; notranslate">
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
{
	char *mesg = buf;
	if (size &gt; 0) {
		int bsize;
		int rv = get_int(&amp;mesg, &amp;bsize);
		if (rv)
			return rv;
		/* force bsize into allowed range and
		 * required alignment.
		 */
		if (bsize &lt; 1024)
			bsize = 1024;
		if (bsize &gt; NFSSVC_MAXBLKSIZE)
			bsize = NFSSVC_MAXBLKSIZE;
		bsize &amp;= ~(1024-1);
		mutex_lock(&amp;nfsd_mutex);
		if (nfsd_serv &amp;&amp; nfsd_serv-&gt;sv_nrthreads) {
			mutex_unlock(&amp;nfsd_mutex);
			return -EBUSY;
		}
		nfsd_max_blksize = bsize;
		mutex_unlock(&amp;nfsd_mutex);
	}

	return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, &quot;%d\n&quot;,
							nfsd_max_blksize);
}
</pre>
</p>
<p>write_maxblksize 函数中判断了传入的参数，如果写入的值超过 NFSSVC_MAXBLKSIZE 值则固定为 NFSSVC_MAXBLKSIZE 值，那我们来看看 NFSSVC_MAXBLKSIZE 的定义：</p>
<p><pre class="brush: cpp; title: linux/nfsd/const.h; notranslate">
/*
 * Maximum blocksizes supported by daemon under various circumstances.
 */
#define NFSSVC_MAXBLKSIZE   RPCSVC_MAXPAYLOAD
/* NFSv2 is limited by the protocol specification, see RFC 1094 */
#define NFSSVC_MAXBLKSIZE_V2    (8*1024)
</pre>
</p>
<p>linux/nfsd/const.h 中 NFSSVC_MAXBLKSIZE 定义为了 RPCSVC_MAXPAYLOAD 的值，那看看 linux/sunrpc/svc.h 中 RPCSVC_MAXPAYLOAD 的实际值：</p>
<p><pre class="brush: cpp; title: linux/sunrpc/svc.h; notranslate">
/*
 * Maximum payload size supported by a kernel RPC server.
 * This is use to determine the max number of pages nfsd is
 * willing to return in a single READ operation.
 *
 * These happen to all be powers of 2, which is not strictly
 * necessary but helps enforce the real limitation, which is
 * that they should be multiples of PAGE_CACHE_SIZE.
 *
 * For UDP transports, a block plus NFS,RPC, and UDP headers
 * has to fit into the IP datagram limit of 64K.  The largest
 * feasible number for all known page sizes is probably 48K,
 * but we choose 32K here.  This is the same as the historical
 * Linux limit; someone who cares more about NFS/UDP performance
 * can test a larger number.
 *
 * For TCP transports we have more freedom.  A size of 1MB is
 * chosen to match the client limit.  Other OSes are known to
 * have larger limits, but those numbers are probably beyond
 * the point of diminishing returns.
 */
#define RPCSVC_MAXPAYLOAD   (1*1024*1024u)
#define RPCSVC_MAXPAYLOAD_TCP   RPCSVC_MAXPAYLOAD
#define RPCSVC_MAXPAYLOAD_UDP   (32*1024u)
</pre>
</p>
<p>从 linux/sunrpc/svc.h 中可以看到 NFS 读写块大小必须为 2 的幂，这样也大概知道读写块大小限制的原因了：</p>
<p>对于 UDP 来说，由于一个 UDP 包最大才 64KB，因此使用 UDP 协议的 NFS 读写块大小最大不超过 48KB，而 kernel 中则直接限制为 32KB 了；而使用 TCP 协议的 NFS 由于没有这个限制允许更大的读写块大小，但 Linux kernel 还是将其限制为 1MB 了。</p>
<p>至于 max_block_size 值不能直接修改的现象也找到原因了，在 nfsd/nfsctl.c 文件中高亮显示的第 18 行代码里判断了 NFS 服务器是否在启动运行，如果在运行则不允许修改。</p>
<p>下面就好办了，先卸载 NFS 共享的挂载，停止服务器的 NFS 服务，修改 max_block_size 值，然后重新启动 NFS 服务，</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # service nfs stop
Shutting down NFS mountd:                                  [  OK  ]
Shutting down NFS daemon:                                  [  OK  ]
Shutting down NFS services:                                [  OK  ]
~ # echo 1048576 &gt; /proc/fs/nfsd/max_block_size
~ # cat /proc/fs/nfsd/max_block_size
1048576
~ # service nfs start
</pre>
</p>
<p>可以看到现在 NFS 的最大块大小可以修改了，接着在客户端中指定读写块大小并重新挂载 NFS 共享，这个时候客户端也能正确使用更大的块大小了：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost fs]# mount -t nfs -o rsize=1048576,wsize=1048576 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost fs]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>如果要深究一下 NFS 服务器初始的最大块大小只有 256KB 的原因，可以看看 kernel 中 nfsd/nfssvc.c 文件中的代码：</p>
<p><pre class="brush: cpp; highlight: [13,20,22,23,24]; title: nfsd/nfssvc.c; notranslate">
int nfsd_create_serv(void)
{
	int err = 0;

	WARN_ON(!mutex_is_locked(&amp;nfsd_mutex));
	if (nfsd_serv) {
		svc_get(nfsd_serv);
		return 0;
	}
	if (nfsd_max_blksize == 0) {
		/* choose a suitable default */
		struct sysinfo i;
		si_meminfo(&amp;i);
		/* Aim for 1/4096 of memory per thread
		 * This gives 1MB on 4Gig machines
		 * But only uses 32K on 128M machines.
		 * Bottom out at 8K on 32M and smaller.
		 * Of course, this is only a default.
		 */
		nfsd_max_blksize = NFSSVC_MAXBLKSIZE;
		i.totalram &lt;&lt;= PAGE_SHIFT - 12;
		while (nfsd_max_blksize &gt; i.totalram &amp;&amp;
		       nfsd_max_blksize &gt;= 8*1024*2)
			nfsd_max_blksize /= 2;
	}

	nfsd_serv = svc_create_pooled(&amp;nfsd_program, nfsd_max_blksize,
				      nfsd_last_thread, nfsd, THIS_MODULE);
	if (nfsd_serv == NULL)
		err = -ENOMEM;
	else
		set_max_drc();

	do_gettimeofday(&amp;nfssvc_boot);		/* record boot time */
	return err;
}
</pre>
</p>
<p>从上面的可以看到，NFS 服务器在决定默认的最大读写块大小时考虑到内存占用情况，每个 NFS 内核线程最多只使用 1/4096 的物理内存大小，对于物理内存超过 4GB 的机器才使用最大的 1MB 读写块大小。</p>
<p>来看看我们使用的 NFS 服务器的内存情况，可以看到服务器只使用了 2GB 的内存：</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # free
             total       used       free     shared    buffers     cached
Mem:       2040272     335476    1704796          0       2360      70076
-/+ buffers/cache:     263040    1777232
Swap:            0          0          0
</pre>
</p>
<p>按照 nfsd/nfssvc.c 文件中的代码，i.totalram 实际值为所有可用的物理内存的页数量，我们这里就是 2040272 / 4KB（默认的 PAGE_SIZE 页大小），按照高亮的第 22 - 24 行代码计算出来的默认最大块大小值就是 262144 了。</p>
<p>本文为个人经过测试及分析 Linux kernel 源代码得出的分析结果，如果其中存在错误还请提出指正哦~~~ ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/nfs-rwsize/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
