<?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>Beyond the Void &#187; C++</title>
	<atom:link href="http://www.byvoid.com/blog/tag/c/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.byvoid.com/blog</link>
	<description></description>
	<lastBuildDate>Tue, 29 Jun 2010 08:44:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Vakuum开发笔记02 核心与安全问题</title>
		<link>http://www.byvoid.com/blog/vakuum-dev-note-02/</link>
		<comments>http://www.byvoid.com/blog/vakuum-dev-note-02/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 09:29:42 +0000</pubDate>
		<dc:creator>BYVoid</dc:creator>
				<category><![CDATA[設計開發]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Cena]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[vakuum]]></category>
		<category><![CDATA[信号]]></category>
		<category><![CDATA[安全]]></category>
		<category><![CDATA[底层]]></category>
		<category><![CDATA[权限]]></category>
		<category><![CDATA[汇编]]></category>
		<category><![CDATA[系统调用]]></category>
		<category><![CDATA[调用]]></category>
		<category><![CDATA[预处理器]]></category>

		<guid isPermaLink="false">http://www.byvoid.com/blog/?p=1905</guid>
		<description><![CDATA[3.judger核心设计 评测系统最重要部分就是评测核心了(judger)。核心judger负责了编译、执行、检查三大部分，也就是评测系统的灵魂所在，因此judger设计的好坏，直接影响到整个评测系统的整体水... ]]></description>
			<content:encoded><![CDATA[<h3>3.judger核心设计</h3>
<p>评测系统最重要部分就是评测核心了(judger)。核心judger负责了编译、执行、检查三大部分，也就是评测系统的灵魂所在，因此judger设计的好坏，直接影响到整个评测系统的整体水准。judger的设计要考虑到几个方面，首先是对安全性要求很高。别忘了，这是一个在线评测系统，任何人都可以提交任何代码，并在服务器上执行，这意味着给骇客们提供了方便之门。骇客们（注意，不是黑客）总是希望得到一个Shell，并在上面执行想要的命令。而评测系统直接允许了源码的提交，如果我是骇客的话简直乐坏了。当然，只有傻瓜才会对用户提交的代码直接编译、运行，就像编译自己的程序一样，限制是必不可少的。</p>
<h4>不当的方法</h4>
<p>一个愚蠢的方案是直接对代码进行扫描，然后过滤掉某些字符串，如&#8221;system&#8221;。当然这个过滤列表可能长得匪夷所思，就像某墙，然而绕过它却是很容易的，例如以下代码：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1905code3'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p19053"><td class="code" id="p1905code3"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;stdlib.h&gt;</span>
<span style="color: #339900;">#define dosomething sys##tem</span>
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	dosomething<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;shutdown&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>怎么办呢？有人会继续想到，我可以用g++的-E命令，将预处理以后的代码输入到一个文件中，然后再检查关键字。这样的话上面的方法就不行了，不过更厉害的骇客还有办法。什么办法呢？直接按地址调用函数，代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1905code4'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p19054"><td class="code" id="p1905code4"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;stdlib.h&gt;</span>
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>func<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	func <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">char</span><span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #0000dd;">134513676</span><span style="color: #008080;">;</span>
	func<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;ls&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>上述代码中的数值就是 system函数的绝对地址，在不同的环境中不一样，不过获取这个数值并不困难的。由此可见，依靠过滤源代码的方式几乎是行不通的安全性解决方案。如果不依赖检查源代码，就要监控程序的“行为”，这就需要比较复杂的调试器技术了。在Linux下，有一个ptrace函数，可以在程序运行时监测程序的行为，捕获接收到的信号。</p>
<h4>解决方案</h4>
<p>vakuum的执行器executor，就是用的ptrace的原理。主程序为两个进程，子进程用来执行用户程序，父进程用来监视子进程。子进程每进行系统调用的时候，运行就被中断，由父进程检查子进程的行为是否合法，如果不合法就杀掉子进程。对于open调用，还要检查用户打开的文件是否被允许。有了这个程序，监控程序的用户时间和内存也就不难了，用户时间的监测需要在每个系统调用后检查，对于内存，在任何一个与内存分配相关的系统调用后检查一下/proc/{pid}/statm即可。</p>
<p>如此还有一个问题容易被忽略，就是如果用户程序中长时间不进行系统调用，只是在做一些运算的时候，如何让程序暂停下来判断是否超时呢？设置CPU_Limit为时间限制显然是不行的，因为在监控下的用户程序可能会运行时间更久。不知道大家在用Cena的时候是否发现过一个现象，例如某个程序本来运行0.8秒，设为1秒时限以后显示超时，设为2秒时间反而显示成了0.8秒。这是怎么回事呢？很可能就是设定了不合适的CPU_Limit。稳妥的方法是设置为时间限制的若干倍，但是到底是几倍不好估计，而且很多时候还会浪费时间。思考问题的根源还在于长时间没有被暂停，我的解决方法是给父进程设置一个alarm，每秒向子进程发送一个信号，设置子进程接收到信号以后也会暂停，并检查时间是否超时。这样的话，超时的程序最多会在不超过时限1秒的情况下被终止。</p>
<p>在这种严密的监控下，很难找到突破的方法，至少我至今还没有想到如何攻破executor的监控。其代价就是降低执行效率了，不过由于我监视的是用户时间，用户程序不会被误判超时，尽管感觉实际执行时间多了不少。如果你有什么攻破监控想法，欢迎与我联系做安全性测试。</p>
<h4>潜在的缺陷</h4>
<p>虽然我并不了解，但是我还是担心，如果用户程序中有汇编代码嵌入，是否可以获得底层的权限。如果真的可以，我还没有想到应对的方法。</p>
<h3  class="related_post_title">Maybe you like</h3><ul class="related_post"><li><a href="http://www.byvoid.com/blog/fast-readfile/" title="探寻C++最快的读取文件的方案">探寻C++最快的读取文件的方案</a></li><li><a href="http://www.byvoid.com/blog/linux-cprogram-syscall/" title="Linux下C程序启动时的系统调用">Linux下C程序启动时的系统调用</a></li><li><a href="http://www.byvoid.com/blog/vakuum-dev-note-01/" title="Vakuum开发笔记01 开天辟地">Vakuum开发笔记01 开天辟地</a></li><li><a href="http://www.byvoid.com/blog/vakuum-oj-dev/" title="开源的在线评测系统——Vakuum">开源的在线评测系统——Vakuum</a></li><li><a href="http://www.byvoid.com/blog/linux-c-1/" title="Linux C语言编程学习笔记 (1)进程控制入门">Linux C语言编程学习笔记 (1)进程控制入门</a></li><li><a href="http://www.byvoid.com/blog/dvcs-mercurial/" title="分布式版本控制系统——Mercurial">分布式版本控制系统——Mercurial</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.byvoid.com/blog/vakuum-dev-note-02/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>探寻C++最快的读取文件的方案</title>
		<link>http://www.byvoid.com/blog/fast-readfile/</link>
		<comments>http://www.byvoid.com/blog/fast-readfile/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 06:00:01 +0000</pubDate>
		<dc:creator>BYVoid</dc:creator>
				<category><![CDATA[計算機技術]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[cin]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[iostream]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[mingw]]></category>
		<category><![CDATA[Pascal]]></category>
		<category><![CDATA[scanf]]></category>
		<category><![CDATA[VC]]></category>
		<category><![CDATA[效率]]></category>
		<category><![CDATA[流]]></category>
		<category><![CDATA[读入]]></category>
		<category><![CDATA[输入]]></category>
		<category><![CDATA[速度]]></category>

		<guid isPermaLink="false">http://www.byvoid.com/blog/?p=1830</guid>
		<description><![CDATA[在竞赛中，遇到大数据时，往往读文件成了程序运行速度的瓶颈，需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头，于是从此以后发誓不用cin读数据。还有人... ]]></description>
			<content:encoded><![CDATA[<p>在竞赛中，遇到大数据时，往往读文件成了程序运行速度的瓶颈，需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头，于是从此以后发誓不用cin读数据。还有人说Pascal的read语句的速度是C/C++中scanf比不上的，C++选手只能干着急。难道C++真的低Pascal一等吗？答案是不言而喻的。一个进阶的方法是把数据一下子读进来，然后再转化字符串，这种方法传说中很不错，但具体如何从没试过，因此今天就索性把能想到的所有的读数据的方式都测试了一边，结果是惊人的。<br />
<span id="more-1830"></span><br />
竞赛中读数据的情况最多的莫过于读一大堆整数了，于是我写了一个程序，生成一千万个随机数到data.txt中，一共55MB。然后我写了个程序主干计算运行时间，代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code14'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183014"><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code" id="p1830code14"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;ctime&gt;</span>
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> start <span style="color: #000080;">=</span> <span style="color: #0000dd;">clock</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #666666;">//DO SOMETHING</span>
	<span style="color: #0000dd;">printf</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;%.3lf<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>,<span style="color: #0000ff;">double</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">clock</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span>start<span style="color: #008000;">&#41;</span><span style="color: #000040;">/</span><span style="color: #0000ff;">CLOCKS_PER_SEC</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>最简单的方法就算写一个循环scanf了，代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code15'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183015"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code" id="p1830code15"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> scanf_read<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000dd;">freopen</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,<span style="color: #FF0000;">&quot;r&quot;</span>,<span style="color: #0000ff;">stdin</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>i<span style="color: #000080;">&lt;</span>MAXN<span style="color: #008080;">;</span>i<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span>
		<span style="color: #0000dd;">scanf</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;%d&quot;</span>,<span style="color: #000040;">&amp;</span>numbers<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>可是效率如何呢？在我的电脑Linux平台上测试结果为2.01秒。接下来是cin，代码如下</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code16'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183016"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code" id="p1830code16"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> cin_read<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000dd;">freopen</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,<span style="color: #FF0000;">&quot;r&quot;</span>,<span style="color: #0000ff;">stdin</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>i<span style="color: #000080;">&lt;</span>MAXN<span style="color: #008080;">;</span>i<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span>
		std<span style="color: #008080;">::</span><span style="color: #0000dd;">cin</span> <span style="color: #000080;">&gt;&gt;</span> numbers<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>出乎我的意料，cin仅仅用了6.38秒，比我想象的要快。cin慢是有原因的，其实默认的时候，cin与stdin总是保持同步的，也就是说这两种方法可以混用，而不必担心文件指针混乱，同时cout和stdout也一样，两者混用不会输出顺序错乱。正因为这个兼容性的特性，导致cin有许多额外的开销，如何禁用这个特性呢？只需一个语句std::ios::sync_with_stdio(false);，这样就可以取消cin于stdin的同步了。程序如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code17'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183017"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code" id="p1830code17"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> cin_read_nosync<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000dd;">freopen</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,<span style="color: #FF0000;">&quot;r&quot;</span>,<span style="color: #0000ff;">stdin</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	std<span style="color: #008080;">::</span><span style="color: #007788;">ios</span><span style="color: #008080;">::</span><span style="color: #007788;">sync_with_stdio</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">false</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>i<span style="color: #000080;">&lt;</span>MAXN<span style="color: #008080;">;</span>i<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span>
		std<span style="color: #008080;">::</span><span style="color: #0000dd;">cin</span> <span style="color: #000080;">&gt;&gt;</span> numbers<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>取消同步后效率究竟如何？经测试运行时间锐减到了2.05秒，<strong>与scanf效率相差无几了</strong>！有了这个以后可以放心使用cin和cout了。</p>
<p>接下来让我们测试一下读入整个文件再处理的方法，首先要写一个字符串转化为数组的函数，代码如下</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code18'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183018"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code" id="p1830code18"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXS <span style="color: #000080;">=</span> <span style="color: #0000dd;">60</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">char</span> buf<span style="color: #008000;">&#91;</span>MAXS<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> analyse<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span>buf,<span style="color: #0000ff;">int</span> len <span style="color: #000080;">=</span> MAXS<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> i<span style="color: #008080;">;</span>
	numbers<span style="color: #008000;">&#91;</span>i<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008000;">&#93;</span><span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span>p<span style="color: #000080;">=</span>buf<span style="color: #008080;">;</span><span style="color: #000040;">*</span>p <span style="color: #000040;">&amp;&amp;</span> p<span style="color: #000040;">-</span>buf<span style="color: #000080;">&lt;</span>len<span style="color: #008080;">;</span>p<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span>
		<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>p <span style="color: #000080;">==</span> <span style="color: #FF0000;">' '</span><span style="color: #008000;">&#41;</span>
			numbers<span style="color: #008000;">&#91;</span><span style="color: #000040;">++</span>i<span style="color: #008000;">&#93;</span><span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">else</span>
			numbers<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> numbers<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000040;">*</span> <span style="color: #0000dd;">10</span> <span style="color: #000040;">+</span> <span style="color: #000040;">*</span>p <span style="color: #000040;">-</span> <span style="color: #FF0000;">'0'</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>把整个文件读入一个字符串最常用的方法是用fread，代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code19'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183019"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code" id="p1830code19"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXS <span style="color: #000080;">=</span> <span style="color: #0000dd;">60</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">char</span> buf<span style="color: #008000;">&#91;</span>MAXS<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> fread_analyse<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000dd;">freopen</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,<span style="color: #FF0000;">&quot;rb&quot;</span>,<span style="color: #0000ff;">stdin</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">int</span> len <span style="color: #000080;">=</span> <span style="color: #0000dd;">fread</span><span style="color: #008000;">&#40;</span>buf,<span style="color: #0000dd;">1</span>,MAXS,<span style="color: #0000ff;">stdin</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	buf<span style="color: #008000;">&#91;</span>len<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> <span style="color: #FF0000;">'<span style="color: #006699; font-weight: bold;">\0</span>'</span><span style="color: #008080;">;</span>
	analyse<span style="color: #008000;">&#40;</span>buf,len<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>上述代码有着惊人的效率，经测试读取这10000000个数只用了0.29秒，效率提高了几乎10倍！掌握着种方法简直无敌了，不过，我记得fread是封装过的read，如果直接使用read，是不是更快呢？代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code20'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183020"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code" id="p1830code20"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXS <span style="color: #000080;">=</span> <span style="color: #0000dd;">60</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">char</span> buf<span style="color: #008000;">&#91;</span>MAXS<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">void</span> read_analyse<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> fd <span style="color: #000080;">=</span> open<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,O_RDONLY<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">int</span> len <span style="color: #000080;">=</span> read<span style="color: #008000;">&#40;</span>fd,buf,MAXS<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	buf<span style="color: #008000;">&#91;</span>len<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> <span style="color: #FF0000;">'<span style="color: #006699; font-weight: bold;">\0</span>'</span><span style="color: #008080;">;</span>
	analyse<span style="color: #008000;">&#40;</span>buf,len<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>测试发现运行时间仍然是0.29秒，可见read不具备特殊的优势。到此已经结束了吗？不，我可以调用Linux的底层函数mmap，这个函数的功能是将文件映射到内存，是所有读文件方法都要封装的基础方法，直接使用mmap会怎样呢？代码如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code21'); return false;">View Code</a> CPP</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183021"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code" id="p1830code21"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXN <span style="color: #000080;">=</span> <span style="color: #0000dd;">10000000</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAXS <span style="color: #000080;">=</span> <span style="color: #0000dd;">60</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #000040;">*</span><span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> numbers<span style="color: #008000;">&#91;</span>MAXN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">char</span> buf<span style="color: #008000;">&#91;</span>MAXS<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">void</span> mmap_analyse<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> fd <span style="color: #000080;">=</span> open<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;data.txt&quot;</span>,O_RDONLY<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">int</span> len <span style="color: #000080;">=</span> lseek<span style="color: #008000;">&#40;</span>fd,<span style="color: #0000dd;">0</span>,<span style="color: #0000ff;">SEEK_END</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span>mbuf <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span> mmap<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span>,len,PROT_READ,MAP_PRIVATE,fd,<span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>	
	analyse<span style="color: #008000;">&#40;</span>mbuf,len<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>经测试，运行时间缩短到了0.25秒，效率继续提高了14%。到此为止我已经没有更好的方法继续提高读文件的速度了。回头测一下Pascal的速度如何？结果<strong>令人大跌眼镜</strong>，居然运行了2.16秒之多。程序如下：</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1830code22'); return false;">View Code</a> PASCAL</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p183022"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code" id="p1830code22"><pre class="pascal" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">const</span>
	MAXN <span style="color: #339933;">=</span> <span style="color: #cc66cc;">10000000</span>;
<span style="color: #000000; font-weight: bold;">var</span>
	numbers <span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">array</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span>..<span style="color: #0066ee;">MAXN</span><span style="color: #009900;">&#93;</span> <span style="color: #000000; font-weight: bold;">of</span> <span style="color: #000066; font-weight: bold;">longint</span>;
	i <span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">longint</span>;
<span style="color: #000000; font-weight: bold;">begin</span>
	assign<span style="color: #009900;">&#40;</span>input<span style="color: #339933;">,</span><span style="color: #ff0000;">'data.txt'</span><span style="color: #009900;">&#41;</span>;
	reset<span style="color: #009900;">&#40;</span>input<span style="color: #009900;">&#41;</span>;
	<span style="color: #000000; font-weight: bold;">for</span> i<span style="color: #339933;">:=</span><span style="color: #cc66cc;">0</span> <span style="color: #000000; font-weight: bold;">to</span> MAXN <span style="color: #000000; font-weight: bold;">do</span>
		<span style="color: #000066;">read</span><span style="color: #009900;">&#40;</span>numbers<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #000000; font-weight: bold;">end</span>.</pre></td></tr></table></div>

<p>为确保准确性，我又换到Windows平台上测试了一下。结果如下表：</p>
<table border="1">
<tbody>
<tr>
<td>方法/平台/时间(秒)</td>
<td>Linux gcc</td>
<td>Windows mingw</td>
<td>Windows VC2008</td>
</tr>
<tr>
<td>scanf</td>
<td>2.010</td>
<td>3.704</td>
<td>3.425</td>
</tr>
<tr>
<td>cin</td>
<td>6.380</td>
<td>64.003</td>
<td>19.208</td>
</tr>
<tr>
<td>cin取消同步</td>
<td>2.050</td>
<td>6.004</td>
<td>19.616</td>
</tr>
<tr>
<td>fread</td>
<td>0.290</td>
<td>0.241</td>
<td>0.304</td>
</tr>
<tr>
<td>read</td>
<td>0.290</td>
<td>0.398</td>
<td>不支持</td>
</tr>
<tr>
<td>mmap</td>
<td>0.250</td>
<td>不支持</td>
<td>不支持</td>
</tr>
<tr>
<td>Pascal read</td>
<td>2.160</td>
<td>4.668</td>
<td></td>
</tr>
</tbody>
</table>
<p>从上面可以看出几个问题</p>
<ol>
<li>Linux平台上运行程序普遍比Windows上快。</li>
<li>Windows下VC编译的程序一般运行比MINGW（MINimal Gcc for Windows）快。</li>
<li>VC对cin取消同步与否<strong>不敏感</strong>，前后效率相同。反过来MINGW则<strong>非常敏感</strong>，前后效率相差8倍。</li>
<li>read本是linux系统函数，MINGW可能采用了某种模拟方式，read比fread更慢。</li>
<li>Pascal程序运行速度实在令人不敢恭维。</li>
</ol>
<p>希望此文能对大家有所启发，欢迎与我继续讨论。</p>
<p><a href="http://www.byvoid.com/">BYVoid原创 转载请注明</a></p>
<h3  class="related_post_title">Maybe you like</h3><ul class="related_post"><li><a href="http://www.byvoid.com/blog/vakuum-dev-note-02/" title="Vakuum开发笔记02 核心与安全问题">Vakuum开发笔记02 核心与安全问题</a></li><li><a href="http://www.byvoid.com/blog/linux-cprogram-syscall/" title="Linux下C程序启动时的系统调用">Linux下C程序启动时的系统调用</a></li><li><a href="http://www.byvoid.com/blog/linux-c-1/" title="Linux C语言编程学习笔记 (1)进程控制入门">Linux C语言编程学习笔记 (1)进程控制入门</a></li><li><a href="http://www.byvoid.com/blog/pascaler-ignorance/" title="Pascaler:别拿自己的无知当个性">Pascaler:别拿自己的无知当个性</a></li><li><a href="http://www.byvoid.com/blog/cpp-fstream/" title="C++中fstream的用法">C++中fstream的用法</a></li><li><a href="http://www.byvoid.com/blog/thinking-in-delphi/" title="Thinking in Delphi：语言的变革">Thinking in Delphi：语言的变革</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.byvoid.com/blog/fast-readfile/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Linux下C程序启动时的系统调用</title>
		<link>http://www.byvoid.com/blog/linux-cprogram-syscall/</link>
		<comments>http://www.byvoid.com/blog/linux-cprogram-syscall/#comments</comments>
		<pubDate>Wed, 09 Dec 2009 17:37:35 +0000</pubDate>
		<dc:creator>BYVoid</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[启动]]></category>
		<category><![CDATA[系统调用]]></category>

		<guid isPermaLink="false">http://www.byvoid.com/blog/?p=1656</guid>
		<description><![CDATA[写程序跟踪发现，在Linux i386中，一个程序体完全为空的C语言程序启动时要进行近100个系统调用，如下所示。 [ 1]syscall: 11 //execve [ 2]syscall: 45 //brk 改变进程的数据段边界 [ 3]syscall: 45 //brk [ 4]syscal... ]]></description>
			<content:encoded><![CDATA[<p>写程序跟踪发现，在Linux i386中，一个程序体完全为空的C语言程序启动时要进行近100个系统调用，如下所示。</p>
<blockquote><p>[ 1]syscall: 11   //execve<br />
[ 2]syscall: 45   //brk 改变进程的数据段边界<br />
[ 3]syscall: 45   //brk<br />
[ 4]syscall: 33   //access 确定文件的可存取性<br />
[ 5]syscall: 33   //access<br />
[ 6]syscall: 192  //mmap2 映射虚拟内存页<br />
[ 7]syscall: 192  //mmap2<br />
[ 8]syscall: 33   //access   //access<br />
[ 9]syscall: 33   //access   //access<br />
[10]syscall: 5    //open 打开文件<br />
[11]syscall: 5    //open<br />
[12]syscall: 197  //fstat64 取文件系统信息<br />
[13]syscall: 197  //fstat64<br />
[14]syscall: 192  //mmap2<br />
[15]syscall: 192  //mmap2<br />
[16]syscall: 6    //close 关闭文件描述字<br />
[17]syscall: 6    //close<br />
[18]syscall: 33   //access<br />
[19]syscall: 33   //access<br />
[20]syscall: 5    //open<br />
[21]syscall: 5    //open<br />
[22]syscall: 3    //read 读文件<br />
[23]syscall: 3    //read<br />
[24]syscall: 197  //fstat64<br />
[25]syscall: 197  //fstat64<br />
[26]syscall: 192  //mmap2<br />
[27]syscall: 192  //mmap2<br />
[28]syscall: 192  //mmap2<br />
[29]syscall: 192  //mmap2<br />
[30]syscall: 192  //mmap2<br />
[31]syscall: 192  //mmap2<br />
[32]syscall: 6    //close<br />
[33]syscall: 6    //close<br />
[34]syscall: 33   //access<br />
[35]syscall: 33   //access<br />
[36]syscall: 5    //open<br />
[37]syscall: 5    //open<br />
[38]syscall: 3    //read<br />
[39]syscall: 3    //read<br />
[40]syscall: 197  //fstat64<br />
[41]syscall: 197  //fstat64<br />
[42]syscall: 192  //mmap2<br />
[43]syscall: 192  //mmap2<br />
[44]syscall: 192  //mmap2<br />
[45]syscall: 192  //mmap2<br />
[46]syscall: 6    //close<br />
[47]syscall: 6    //close<br />
[48]syscall: 33   //access<br />
[49]syscall: 33   //access<br />
[50]syscall: 5    //open<br />
[51]syscall: 5    //open<br />
[52]syscall: 3    //read<br />
[53]syscall: 3    //read<br />
[54]syscall: 197  //fstat64<br />
[55]syscall: 197  //fstat64<br />
[56]syscall: 192  //mmap2<br />
[57]syscall: 192  //mmap2<br />
[58]syscall: 192  //mmap2<br />
[59]syscall: 192  //mmap2<br />
[60]syscall: 192  //mmap2<br />
[61]syscall: 192  //mmap2<br />
[62]syscall: 6    //close<br />
[63]syscall: 6    //close<br />
[64]syscall: 33   //access<br />
[65]syscall: 33   //access<br />
[66]syscall: 5    //open<br />
[67]syscall: 5    //open<br />
[68]syscall: 3    //read<br />
[69]syscall: 3    //read<br />
[70]syscall: 197  //fstat64<br />
[71]syscall: 197  //fstat64<br />
[72]syscall: 192  //mmap2<br />
[73]syscall: 192  //mmap2<br />
[74]syscall: 192  //mmap2<br />
[75]syscall: 192  //mmap2<br />
[76]syscall: 192  //mmap2<br />
[77]syscall: 192  //mmap2<br />
[78]syscall: 6    //close<br />
[79]syscall: 6    //close<br />
[80]syscall: 192  //mmap2<br />
[81]syscall: 192  //mmap2<br />
[82]syscall: 243  //set_thread_area 设置线程入口<br />
[83]syscall: 243  //set_thread_area<br />
[84]syscall: 125  //mprotect 设置内存映像保护<br />
[85]syscall: 125  //mprotect<br />
[86]syscall: 125  //mprotect<br />
[87]syscall: 125  //mprotect<br />
[88]syscall: 125  //mprotect<br />
[89]syscall: 125  //mprotect<br />
[90]syscall: 125  //mprotect<br />
[91]syscall: 125  //mprotect<br />
[92]syscall: 125  //mprotect<br />
[93]syscall: 125  //mprotect<br />
[94]syscall: 125  //mprotect<br />
[95]syscall: 125  //mprotect<br />
[96]syscall: 91   //munmap 去除内存页映射<br />
[97]syscall: 91   //munmap<br />
[98]syscall: 252  //exit_group 结束
</p></blockquote>
<p>程序很简单</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1656code24'); return false;">View Code</a> C</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p165624"><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code" id="p1656code24"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<h3  class="related_post_title">Maybe you like</h3><ul class="related_post"><li><a href="http://www.byvoid.com/blog/vakuum-dev-note-02/" title="Vakuum开发笔记02 核心与安全问题">Vakuum开发笔记02 核心与安全问题</a></li><li><a href="http://www.byvoid.com/blog/fast-readfile/" title="探寻C++最快的读取文件的方案">探寻C++最快的读取文件的方案</a></li><li><a href="http://www.byvoid.com/blog/linux-c-1/" title="Linux C语言编程学习笔记 (1)进程控制入门">Linux C语言编程学习笔记 (1)进程控制入门</a></li><li><a href="http://www.byvoid.com/blog/dvcs-mercurial/" title="分布式版本控制系统——Mercurial">分布式版本控制系统——Mercurial</a></li><li><a href="http://www.byvoid.com/blog/ibus-bopomofo-1-3-9-features/" title="ibus-pinyin注音模式新特性">ibus-pinyin注音模式新特性</a></li><li><a href="http://www.byvoid.com/blog/ubuntu-wine-wow/" title="Ubuntu下用wine玩魔兽世界">Ubuntu下用wine玩魔兽世界</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.byvoid.com/blog/linux-cprogram-syscall/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Linux C语言编程学习笔记 (1)进程控制入门</title>
		<link>http://www.byvoid.com/blog/linux-c-1/</link>
		<comments>http://www.byvoid.com/blog/linux-c-1/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 13:48:32 +0000</pubDate>
		<dc:creator>BYVoid</dc:creator>
				<category><![CDATA[計算機技術]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[exec]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[僵尸进程]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[子进程]]></category>
		<category><![CDATA[孤儿进程]]></category>
		<category><![CDATA[守护进程]]></category>
		<category><![CDATA[实际用户]]></category>
		<category><![CDATA[有效用户]]></category>
		<category><![CDATA[父进程]]></category>
		<category><![CDATA[笔记]]></category>
		<category><![CDATA[系统]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[阻塞]]></category>

		<guid isPermaLink="false">http://www.byvoid.com/blog/?p=1616</guid>
		<description><![CDATA[想进行Linux系统开发已经很久了，一直没有付诸实践。今日终于开始学习Linux下的C语言编程，研究一天，终于大概弄明白了Linux系统进程管理的一些基本概念和编程方法，总结下来以方便大家学... ]]></description>
			<content:encoded><![CDATA[<p>想进行Linux系统开发已经很久了，一直没有付诸实践。今日终于开始学习Linux下的C语言编程，研究一天，终于大概弄明白了Linux系统进程管理的一些基本概念和编程方法，总结下来以方便大家学习和自己实践。</p>
<h2>进程系统</h2>
<p>Linux是个多任务多用户的操作系统，系统直接管理的每个任务的最小单位，就是<strong>进程(process)</strong>。每个进程都有一个惟一的标识符pid，不同的进程pid不相同，在Shell下输入ps -A，可以显示当前的所有进程。一个进程不代表一个<strong>应用程序(application)</strong>，因为一个应用程序可能对应多个进程，也不代表一个<strong>可执行文件(executable file)</strong>，因为一些可执行文件可以被同时运行多个，它们是互不相干的。</p>
<p>在Linux中，进程不是相互独立的，每个进程（除了init进程）都有一个<strong>父进程(parent process)</strong>，同时每个进程可以有0个1个或多个<strong>子进程(child process)</strong>。换句话说，Linux的进程是一个树形结构，在Shell下输入pstree可以查看这个树的形状。下图为pstree返回结果的一部分。</p>
<blockquote><p>init─┬─NetworkManager─┬─dhclient<br />
│                └─{NetworkManager}<br />
├─SystemToolsBack<br />
├─avahi-daemon───avahi-daemon<br />
├─bonobo-activati───{bonobo-activati}<br />
├─console-kit-dae───63*[{console-kit-dae}]<br />
├─hald───hald-runner─┬─hald-addon-acpi<br />
│                    ├─hald-addon-cpuf<br />
├─pulseaudio─┬─gconf-helper<br />
│            └─2*[{pulseaudio}]<br />
├─rsyslogd───2*[{rsyslogd}]<br />
├─seahorse-daemon<br />
├─telepathy-gabbl<br />
├─telepathy-haze─┬─telepathy-haze<br />
│                └─{telepathy-haze}<br />
├─trashapplet<br />
└─wpa_supplicant</p></blockquote>
<p>在C语言中，获得当前进程的pid的函数是pid_t getpid(void);，获得当前进程的父进程的pid的函数是pid_t getppid(void);，两者都在unistd.h中声明。</p>
<h2>用户和权限</h2>
<p>因为Linux是多用户的系统，所以内核中有着强大的用户控制，因此每个进程还有一个所有者，即<strong>实际用户ID(uid)</strong>。系统uid是一个整数，不同于用户名。默认情况下进程的uid继承于父进程。例如我用所有者为byvoid(uid为1000)的bash终端启动了一个进程，那么这个进程的uid也是1000。用户uid可以通过uid_t getuid(void);函数获得。如果权限满足，程序在运行时可以修改uid，C语言函数为int setuid(uid_t uid);，如果成功执行返回0，否则返回-1。只有具有root用户权限的进程可以设置uid。</p>
<p>除此以外，进程还有一个<strong>有效用户ID(euid)</strong>。euid是决定进程文件系统权限的身份，一般情况下进程euid和uid是相同的。在C语言中可以通过uid_t geteuid(void);函数获得进程euid。同样euid也可以修改，函数为int seteuid(uid_t uid);仅当当前uid和euid中至少有一个为0(root)时，才可以设置euid。有一种特殊情况，就是一个二进制可执行文件所有者为root，并且被chmod +s后，在一般用户身份下执行，这时产生的进程uid为一般用户，而euid为0(root)，这种情况下该进程具有和root一样高的权限。</p>
<h2>进程生成</h2>
<h3>fork函数</h3>
<p>Linux允许用户创建用户进程的子进程，在C语言中通过pid_t fork(void);函数实现。fork函数的基本功能是生成一个子进程，并复制当前进程的数据段和堆栈段，子进程和父进程共用代码段。因为复制了堆栈段，所以父进程和子进程都停留在fork函数的栈帧中，fork函数要返回两次，一次在父进程中返回，一次在子进程中返回，但是两次的返回值是不一样的。在父进程中，fork函数返回值为子进程的pid（如果成功调用的话），在子进程中，fork函数的返回值为0。因此可以根据返回值的不同确定程序的运行流程。父进程和子进程默认情况下是同步执行的，由系统内核调度，哪个先执行是未知的。因为父子进程的数据段和堆栈段都是独立的，所以两者互不干涉，各行其是，内存不能直接共享。</p>
<h3>执行程序</h3>
<p>Linux中要执行一个外部程序，必须生成一个子进程，因为内核执行程序的命令exec会替换掉当前进程的地址空间的所有内容并继续执行，执行另一个程序意味着当前程序不再执行。在C语言中，并没有exec这样的一个函数，而是有下列一组函数。</p>
<blockquote><p>int execl (const char * file,const char * arg,&#8230;);<br />
int execlp(const char * file,const char * arg,&#8230;);<br />
int execle(const char * file,const char * arg,&#8230;,NULL,char * const envp[ ]);<br />
int execv (const char * file, char * const argv[ ]);<br />
int execvp(const char *file ,char * const argv []);<br />
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);</p></blockquote>
<p>其中以execl开头的函数，第一个参数file为可执行文件名，接下来有若干个参数，分别为传入的argv[0],argv[1],argv[2],&#8230;，最有以NULL结束。如果file参数为路径名(其中包含&#8217;/')，execl函数会直接定位到文件并执行，否则仅在当前目录下寻找文件，而execlp函数遇到文件名则会按照环境变量PATH的顺序寻找。execle最后一个参数为二维字符数组，表示传递给程序新的环境变量列表。execv，execvp，execve和前三者用法相似，只不过不以可变参数列表的方式传递参数，换以二维字符数组。上述函数执行失败后会返回-1，如果执行成功的话将会不返回，因为代码段已经被新的可执行程序替换。</p>
<h2>进程阻塞</h2>
<h3>wait函数</h3>
<p>在实际的应用中，有时候需要让父进程停下来等待子进程的执行完毕，这时候就需要进行<strong>进程阻塞(process blocking)</strong>。C语言中使用pid_t wait(int *statloc)函数可以得到子进程的结束信息。调用wait函数的进程会阻塞，直到该进程的任意一个子进程结束，wait函数会返回结束的子进程的pid，结束信息保存在statloc指针指向的内存区域。如果该进程没有活动的子进程，那么立即出错并返回-1。如果statloc指针为NULL，那么表示不关心进程结束的状态。如果有多个子进程，wait函数返回哪个数不确定的，需要通过pid来判断。</p>
<p>如果我们需要等待特定的一个进程，可以使用pid_t waitpid(pid_t pid,int *statloc,int options)函数。waitpid函数的第一个参数指定了要等待的进程pid，并且有更多的选项。</p>
<h3>僵尸进程</h3>
<p>当一个子进程退出时，如果没有被父进程通过wait取得状态信息，这些信息会一直保留在内核内存中，子进程的pid也不会被消除，直到父进程退出，这时候这些子进程被称为<strong>僵尸进程（zombie process）</strong>。虽然僵尸进程只占用很少的一点内存，但如果是长期运行的服务器，积累大量的僵尸进程会导致系统进程表被塞满，以至于无法创建新的进程。产生一个僵尸进程很容易，只需要让子进程先于父进程退出即可，在父进程退出之前，子进程将会成为僵尸进程。</p>
<h3>孤儿进程</h3>
<p>与僵尸进程相反，如果父进程没有阻塞并先于子进程退出，那么子进程将会成为<strong>孤儿进程(orphan process)</strong>。Linux系统中init进程负责领养所有孤儿进程，也就是说，孤儿进程的父进程会被设为init进程。init进程作为系统<strong>守护进程(daemon process)</strong>，会不断调用wait函数等待领养的孤儿进程退出，不会产生僵尸进程。</p>
<h3>利用孤儿进程避免僵尸进程</h3>
<p>许多时候我们不能让父进程阻塞下来等待子进程处理完以后再继续，例如在多用户的服务器程序上。这时如果让子进程处理事务，就会产生大量僵尸进程。避免僵尸进程出现的一个经典方法就是利用孤儿进程，具体方法为首先用父进程产生一个子进程，然后让子进程立刻产生一个孙子进程，用孙子进程来处理事务。同时父进程阻塞等待，并让子进程则立刻退出。这时候由于子进程已经退出，孙子进程就变成了孤儿进程，被init领养。而子进程立刻退出后，父进程收到信号并正确销毁了子进程，相比之下父进程只阻塞了可以忽略不计的一瞬间。下面程序是一个例子避免僵尸进程。</p>

<div class="wp_codebox_msgheader"><span class="right"><sup><a href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target="_blank" title="WP-CodeBox HowTo?"><span style="color: #99cc00">?</span></a></sup></span><span class="left"><a href="javascript:;" onclick="javascript:showCodeTxt('p1616code26'); return false;">View Code</a> C</span><div class="codebox_clear"></div></div><div class="wp_codebox"><table><tr id="p161626"><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code" id="p1616code26"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#include &lt;stdio.h&gt;</span>
<span style="color: #339933;">#include &lt;stdlib.h&gt;</span>
<span style="color: #339933;">#include &lt;unistd.h&gt;</span>
&nbsp;
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>fork<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//启动一个子进程</span>
	<span style="color: #009900;">&#123;</span>
		<a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;the child<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>fork<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">//启动一个孙子进程</span>
		<span style="color: #009900;">&#123;</span>
			<a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;do something you want<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			sleep<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">5</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;done<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">else</span> <span style="color: #666666; font-style: italic;">//子进程立刻退出</span>
			exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #b1b100;">else</span>
	<span style="color: #009900;">&#123;</span> <span style="color: #666666; font-style: italic;">//父进程立即阻塞</span>
		wait<span style="color: #009900;">&#40;</span>NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<a href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #000066;">printf</span></a><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;the parent<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		sleep<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p><a href="http://www.byvoid.com/" target="_blank">BYVoid 原创作品</a>。</p>
<h3  class="related_post_title">Maybe you like</h3><ul class="related_post"><li><a href="http://www.byvoid.com/blog/ibus-bopomofo-1-3-9-features/" title="ibus-pinyin注音模式新特性">ibus-pinyin注音模式新特性</a></li><li><a href="http://www.byvoid.com/blog/ubuntu-wine-wow/" title="Ubuntu下用wine玩魔兽世界">Ubuntu下用wine玩魔兽世界</a></li><li><a href="http://www.byvoid.com/blog/linux-ssh-wall/" title="Linux下实现自动设置SSH代理">Linux下实现自动设置SSH代理</a></li><li><a href="http://www.byvoid.com/blog/vakuum-dev-note-02/" title="Vakuum开发笔记02 核心与安全问题">Vakuum开发笔记02 核心与安全问题</a></li><li><a href="http://www.byvoid.com/blog/fast-readfile/" title="探寻C++最快的读取文件的方案">探寻C++最快的读取文件的方案</a></li><li><a href="http://www.byvoid.com/blog/linux-cprogram-syscall/" title="Linux下C程序启动时的系统调用">Linux下C程序启动时的系统调用</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.byvoid.com/blog/linux-c-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
