<?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; mlock</title>
	<atom:link href="https://zohead.com/archives/tag/mlock/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>page cache诊断控制工具 vmtouch 源代码分析</title>
		<link>https://zohead.com/archives/vmtouch-code-analysis/</link>
		<comments>https://zohead.com/archives/vmtouch-code-analysis/#comments</comments>
		<pubDate>Thu, 17 May 2012 16:29:51 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[代码分析]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[fadvise]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[mincore]]></category>
		<category><![CDATA[mlock]]></category>
		<category><![CDATA[mmap]]></category>
		<category><![CDATA[page cache]]></category>
		<category><![CDATA[posix_fadvise]]></category>
		<category><![CDATA[vmtouch]]></category>
		<category><![CDATA[内存]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=166</guid>
		<description><![CDATA[本文同步自（如浏览不正常请点击跳转）：https://zohead.com/archives/vmtouch-code-analysis/ vmtouch 是一个 portable 的 page cache 诊断和控制工具，可以查看文件或者设备中有多少在 page cache 中，知道之后对这些在 page cache 中的内存引用可以避免 page fault，支持将文件的内容从 page cache 逐出，同时还可以将文件手工 touch 到 page cache 中，支持 lock 文件部分区域到 memory 中防止被交换出去从而提高。 vmtouch 可以在 Linux、BSD 等系 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（如浏览不正常请点击跳转）：<a href="https://zohead.com/archives/vmtouch-code-analysis/" target="_blank">https://zohead.com/archives/vmtouch-code-analysis/</a></p>
<p>vmtouch 是一个 portable 的 page cache 诊断和控制工具，可以查看文件或者设备中有多少在 page cache 中，知道之后对这些在 page cache 中的内存引用可以避免 page fault，支持将文件的内容从 page cache 逐出，同时还可以将文件手工 touch 到 page cache 中，支持 lock 文件部分区域到 memory 中防止被交换出去从而提高。</p>
<p>vmtouch 可以在 Linux、BSD 等系统上使用，在这下载编译：</p>
<p><a href="http://hoytech.com/vmtouch/" target="_blank">http://hoytech.com/vmtouch/</a></p>
<p>今天简单看了下 vmtouch 的代码，发现还比较简单，自己写个类似的程序验证之后，将代码分析结果写下。vmtouch 的代码比较少，我只贴出最关键的一个函数 vmtouch_file（关键部分已经高亮显示），这个函数做 分析 page cache 使用、touch、lock 的操作，其它部分只是加了读了目录的遍历处理之类的。</p>
<pre class="brush: cpp; highlight: [2,10,57,75,78,89,91,105,126]; title: vmtouch.c; notranslate">
int64_t bytes2pages(int64_t bytes) {
	return (bytes+pagesize-1) / pagesize;
}

int aligned_p(void *p) {
	return 0 == ((long)p &amp; (pagesize-1));
}

int is_mincore_page_resident(char p) {
	return p &amp; 0x1;
}

void vmtouch_file(char *path) {
	int fd;
	void *mem;
	struct stat sb;
	int64_t len_of_file;
	int64_t pages_in_file;
	int i;
	int res;

	res = o_followsymlinks ? stat(path, &amp;sb) : lstat(path, &amp;sb);

	if (res) {
		warning(&quot;unable to stat %s (%s), skipping&quot;, path, strerror(errno));
		return;
	}

	if (S_ISLNK(sb.st_mode)) {
		warning(&quot;not following symbolic link %s&quot;, path);
		return;
	}

	if (sb.st_size == 0) return;

	if (sb.st_size &gt; o_max_file_size) {
		warning(&quot;file %s too large, skipping&quot;, path);
		return;
	}

	len_of_file = sb.st_size;

	retry_open:

	fd = open(path, O_RDONLY, 0);

	if (fd == -1) {
		if (errno == ENFILE || errno == EMFILE) {
			increment_nofile_rlimit();
			goto retry_open;
		}

		warning(&quot;unable to open %s (%s), skipping&quot;, path, strerror(errno));
		return;
	}

	mem = mmap(NULL, len_of_file, PROT_READ, MAP_SHARED, fd, 0);

	if (mem == MAP_FAILED) {
		warning(&quot;unable to mmap file %s (%s), skipping&quot;, path, strerror(errno));
		close(fd);
		return;
	}

	if (!aligned_p(mem)) fatal(&quot;mmap(%s) wasn't page aligned&quot;, path);

	pages_in_file = bytes2pages(len_of_file);

	total_pages += pages_in_file;

	if (o_evict) {
		if (o_verbose) printf(&quot;Evicting %s\n&quot;, path);

#if defined(__linux__)
		if (posix_fadvise(fd, 0, len_of_file, POSIX_FADV_DONTNEED))
			warning(&quot;unable to posix_fadvise file %s (%s)&quot;, path, strerror(errno));
#elif defined(__FreeBSD__) || defined(__sun__)
		if (msync(mem, len_of_file, MS_INVALIDATE))
			warning(&quot;unable to msync invalidate file %s (%s)&quot;, path, strerror(errno));
#else
		fatal(&quot;cache eviction not (yet?) supported on this platform&quot;);
#endif
	} else {
		char mincore_array[pages_in_file];
		int64_t pages_in_core=0;
		double last_chart_print_time=0.0, temp_time;

		// 3rd arg to mincore is char* on BSD and unsigned char* on linux
		if (mincore(mem, len_of_file, (void*)mincore_array)) fatal(&quot;mincore %s (%s)&quot;, path, strerror(errno));
		for (i=0; i&lt;pages_in_file; i++) {
			if (is_mincore_page_resident(mincore_array[i])) {
				pages_in_core++;
				total_pages_in_core++;
			}
		}

		if (o_verbose) {
			printf(&quot;%s\n&quot;, path);
			last_chart_print_time = gettimeofday_as_double();
			print_page_residency_chart(stdout, mincore_array, pages_in_file);
		}

		if (o_touch) {
			for (i=0; i&lt;pages_in_file; i++) {
				junk_counter += ((char*)mem)[i*pagesize];
				mincore_array[i] = 1;

				if (o_verbose) {
					temp_time = gettimeofday_as_double();

					if (temp_time &gt; (last_chart_print_time+CHART_UPDATE_INTERVAL)) {
						last_chart_print_time = temp_time;
						print_page_residency_chart(stdout, mincore_array, pages_in_file);
					}
				}
			}
		}

		if (o_verbose) {
			print_page_residency_chart(stdout, mincore_array, pages_in_file);
			printf(&quot;\n&quot;);
		}
	}

	if (o_lock) {
		if (mlock(mem, len_of_file))
			fatal(&quot;mlock: %s (%s)&quot;, path, strerror(errno));
	}

	if (!o_lock &amp;&amp; !o_lockall) {
		if (munmap(mem, len_of_file)) warning(&quot;unable to munmap file %s (%s)&quot;, path, strerror(errno));
		close(fd);
	}
}
</pre>
<p>稍微有点基础就可以看明白了，先 mmap 映射文件到当前进程，按 page size 对齐之后，调用 mincore 函数就可以得到文件中每一个 page 是否在 page cache 中，结果保存在 mincore_array 数组中，该数据中每个字节的第一位即表示是否在 page cache 中。</p>
<p>将文件内容逐出（指定 o_evict）出 page cache 是通过 posix_fadvise 函数调用 fadvise 系统调用来实现的（BSD通过 msync 实现，这个在 Linux 上没有效果）。fadvise 系统调用可以告诉 kernel 要操作的文件在接下来要干什么，kernel 可以提前做一些操作而提高性能，Linux kernel 里实现了以下几种控制方式：</p>
<p>POSIX_FADV_NORMAL - 正常操作，对文件使用底层设备的默认 readahead 值；<br />
POSIX_FADV_SEQUENTIAL - 顺序I/O，对文件使用两倍的 readahead 值；<br />
POSIX_FADV_RANDOM - 随机I/O，禁用文件上的 readahead；<br />
POSIX_FADV_NOREUSE - 只使用一次<br />
POSIX_FADV_WILLNEED - 很快需要使用，对文件使用非阻塞读到 page cache<br />
POSIX_FADV_DONTNEED - 不再需要使用文件，从 page cache 中逐出</p>
<p>posix_fadvise 加 POSIX_FADV_DONTNEED 参数就可以将文件从 page cache 中逐出，需要注意的是如果需要确保文件从 page cache 中逐出，还需要在调用 fadvise 之前用 fsync/fdatasync/sync_file_range 之类的函数将 dirty page 清理。</p>
<p>下面是我在 Linux 下用 posix_fadvise 的一个测试程序测试的结果：</p>
<pre class="brush: bash; highlight: [4,7,13,16,20]; title: fadvise测试; notranslate">
[root@localhost ~]# echo 3 &gt; /proc/sys/vm/drop_caches
[root@localhost ~]# free
total       used       free     shared    buffers     cached
Mem:        374092      61832     312260          0        136       5060
-/+ buffers/cache:      56636     317456
Swap:       707576        436     707140
[root@localhost ~]# dd if=/dev/zero of=test bs=1024k count=100
记录了100+0 的读入
记录了100+0 的写出
104857600字节(105 MB)已复制，22.5514 秒，4.6 MB/秒
[root@localhost ~]# free
total       used       free     shared    buffers     cached
Mem:        374092     168960     205132          0        564     109816
-/+ buffers/cache:      58580     315512
Swap:       707576        436     707140
[root@localhost ~]# ./fadvise test POSIX_FADV_DONTNEED
OK
[root@localhost ~]# free
total       used       free     shared    buffers     cached
Mem:        374092      63932     310160          0        580       7424
-/+ buffers/cache:      55928     318164
Swap:       707576        436     707140
</pre>
<p>从 free 命令的结果可以很明显的看到，dd 之后基本文件都在 page cache 中，fadvise 之后从 page cache 中正确逐出。</p>
<p>接着是 vmtouch 中的 touch 操作（指定 o_touch）就更简单了，对 mmap 到的地址直接遍历引用，不在 page cache 的内容会自动产生 page fault 到 page cache 中。</p>
<p>lock 内存（指定 o_lock）也则直接使用 mlock 函数来实现，mlock 对于对安全性和实时性有很高要求的程序非常有用，可以保证指定的文件区域在内存中，不被 swap 出去。</p>
<p>以上为个人分析结果，有任何问题欢迎指正咯 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/vmtouch-code-analysis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
