﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-嵌入式-文章分类-嵌入式Linux技术资料</title><link>http://www.cnitblog.com/zouzheng/category/2078.html</link><description>要像阿甘一直向前奔跑！</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 06:35:37 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 06:35:37 GMT</pubDate><ttl>60</ttl><item><title>build software for arm(zhuanzai)</title><link>http://www.cnitblog.com/zouzheng/articles/63227.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Dec 2009 05:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/63227.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/63227.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/63227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/63227.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/63227.html</trackback:ping><description><![CDATA[转自http://descent.myftp.org/~descent/arm/index.htm<br>how to build software for arm<br><br><a href="http://descent.myftp.org/%7Edescent/arm/cross-2.95.3.tar.bz2"><u><font color=#0000ff>cross-2.95.3.tar.bz2</font></u></a> (這是 corss compiler for arm 2.95.3)<br><a href="http://descent.myftp.org/%7Edescent/arm/arm-linux-gcc-3.3.2.tar.bz2"><u><font color=#0000ff>arm-linux-gcc-3.3.2.tar.bz2</font></u></a> (這是 corss compiler for arm 3.3.2, 我目前是使用這一版)<br>需解開到 /usr/local/arm/<br><br>
<table border=1>
    <tbody>
        <tr>
            <td style="WHITE-SPACE: nowrap">software package name</td>
            <td>configure 方法</td>
            <td>補充</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/e2fsprogs-1.35-arm.tar.bz2"><u><font color=#0000ff>e2fsprogs-1.35</font></u></a></td>
            <td>CC=arm-linux-gcc <br>./configure --build=i386-linux <br>--host=arm-linux --target=arm-linux <br>--prefix=/build_package/ipaq/e2fsprogs-1.35-arm <br>--disable-nls<br>--enable-elf-shlibs</td>
            <td>--enable-elf-shlibs 可以產生 so library<br>make<br>make install<br>// 安裝 library<br>make install-libs<br>這些均由 INSTALL 得知<br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/jpeg-6b-arm.tar.bz2"><u><font color=#0000ff>jpeg-6b</font></u></a></td>
            <td>./configure --target=arm-linux --host=arm-linux <br>--build=i386-linux --enable-shared --enable-static<br>--prefix=/build_package/ipaq/jpeg-6b-arm </td>
            <td>make CC=arm-linux-gcc<br>make install<br>when make install, 必須要自己建立目錄,<br>因為 make install 的 script 不會自己建立目錄。 </td>
        </tr>
        <tr>
            <td>pcmcia-cs</td>
            <td>./Configure --kernel=/build_package/glibc-2.2.4/linux-2.4.16-rmk1-hh12<br>--noprompt --arch=arm --nocardbus --ucc=arm-linux-gcc --ld=arm-linux-ld <br>--target=/build_package/myroot </td>
            <td>需要 linux kernel source</td>
        </tr>
        <tr>
            <td>lrzsz</td>
            <td>CFLAGS=-Wstrict-prototypes CC=arm-linux-gcc ./configure --disable-nls<br>--disable-timesync --prefix="dir" arm-linux<br>perl -i -p -e "s/-lnsl//;" src/Makefile<br>perl -i -p -e "s~(#define ENABLE_SYSLOG.*)~/*<br>\1 */~;" config.h<br></td>
            <td>make<br>make install // will install lrz lsz l* file<br>this build method form tuxscreen<br></td>
        </tr>
        <tr>
            <td>lrzsz-0.12.21</td>
            <td>CFLAGS=-Wstrict-prototypes CC=arm-linux-gcc ./configure --disable-nls<br>--disable-timesync --prefix="dir" arm-linux<br>perl -i -p -e "s/-lnsl//;" src/Makefile<br>perl -i -p -e "s~(#define ENABLE_SYSLOG.*)~/* \1 */~;" config.h<br></td>
            <td>cd lib and type make<br>cd src and type make<br>perl -i -p -e "s/-lnsl//;" src/Makefile, mean don't link nsl library<br>make install // will install lrz lsz l* file<br>this build method form tuxscreen<br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/freetype-2.1.5-arm.tar.bz2"><u><font color=#0000ff>freetype-2.1.5</font></u></a></td>
            <td>CC=/arm_tools/bin/arm-linux-gcc ./configure --host=arm-linux<br>--target=arm-linux --prefix=/arm_tools/freetype-2.1.1-arm<br>new configure:<br>emacs include/freetype/config/ftoption.h<br>./configure --host=strongarm-linux<br>OBJ_DIR=. LIBTOOL=libtool make<br></td>
            <td>new configure ref : <a href="http://emmie.koka-in.org/%7Ekensyu/zaurus/diary/20030419.html"><u><font color=#0000ff>http://emmie.koka-in.org/~kensyu/zaurus/diary/20030419.html</font></u></a> </td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/konqueror-embedded-snapshot-20030705-arm.tar.bz2"><u><font color=#0000ff>konqueror</font></u></a></td>
            <td>CXX=arm-linux-g++ /usr/bin/xconfigure --disable-debug --enable-static --disable-shared --enable-qt-embedded --enable-qpe --with-extra-libs=$PWD/../lib/ --with-extra-includes=$PWD/../include -without-ssl --prefix=$PWD/../konqueror-embedded-snapshot-20030705-arm<br><br>./configure --host=arm-linux --target=arm-linux --disable-mt --disable-threading --without-ssl --with-extra-includes=/arm-develop/zlib-1.1.3-arm/include/ --with-extra-libs=/arm-develop/zlib-1.1.3-arm/lib --enable-qt-embedded --enable-embedded --enable-qtopia --with-qt-dir=$QTDIR --enable-qpe --with-qtopia-dir=$QPEDIR --with-gui=qpe --prefix=$PWD/../konqueror-embedded-snapshot-20030705-arm<br><br>CC=arm-linux-gcc CXX=arm-linux-g++<br>./configure --host=arm-linux --target=arm-linux --disable-mt<br>--disable-threading --without-ssl<br>--with-extra-includes=/arm-develop/zlib-1.1.3-arm/include/<br>--with-extra-libs=/arm-develop/zlib-1.1.3-arm/lib<br>--enable-qt-embedded --enable-embedded --enable-qtopia<br>--with-qt-dir=/arm-develop/qtopia-1.5/ipaq/qt-2.3.3<br>--enable-qpe<br>--with-qtopia-dir=/arm-develop/qtopia-1.5/ipaq/qtopia-free-1.5.0<br>--with-gui=qpe<br><br>CC=arm-linux-gcc CXX=arm-linux-g++ ./configure --host=arm-linux \<br>--target=arm-linux --disable-mt --disable-threading --without-ssl \<br>--with-extra-includes=/arm_dep/zlib-1.1.4-arm/include/ \<br>--with-extra-libs=/arm_dep/zlib-1.1.4-arm/lib/ --enable-qt-embedded \<br>--enable-embedded --enable-qtopia \<br>--with-qt-dir=/home/descent/arm_dep/qtopia-1.6/arm/qt-2.3.4 \<br>--enable-qpe \<br>--with-qtopia-dir=/home/descent/arm_dep/qtopia-1.6/arm/qtopia-free-1.6.0 \<br>--with-gui=qpe --prefix=/arm_dep/qtopia-1.6/arm<br>CC=arm-linux-gcc CXX=arm-linux-g++ ./configure --host=arm-linux \<br>--target=arm-linux --disable-mt --disable-threading --without-ssl \<br>--enable-qt-embedded --enable-embedded --enable-qtopia \<br>--with-qt-dir=$QTDIR --enable-qpe --with-qtopia-dir=$QPEDIR \<br>--with-gui=qpe --prefix=$PWD/../konqueror-embedded-snapshot_20020127-arm<br><br><br>// 這是作者的 configure method<br><br>There are tons of special configure options and the like. I'm getting confused. How does your configuration look like?<br><br>Here's what I use for cross-compiling for the iPAQ:<br>Qt:<br><br>./configure -gif -qt-libpng -no-jpeg -no-mng -no-thread -no-opengl -release<br>-shared -no-g++-exceptions -I/usr/local/arm-linux/include -depths 16<br>-qconfig local -no-qvfb -xplatform linux-ipaq-g++<br><br>Konq/E:<br><br>../xconfigure --disable-debug --enable-static --disable-shared<br>--enable-qt-embedded --enable-qpe --with-extra-libs=$PWD/../lib/<br>--with-extra-includes=$PWD/../include -without-ssl<br>--prefix=$PWD/../install<br><br>xconfigure is a configure wrapper script I found on the net. It's handy<br>when cross-compiling for ARM-Linux, as it sets up all necessary<br>special configure switches to get things right. I put up a copy of it here .<br></td>
            <td>需要 libz 及 jpeg library<br>depend package: zlib<br>KDEDIR=/arm_tools/arm-linux/lib/ 此變數看情形而使用<br>xconfigure 請到 http://developer.kde.org/~hausmann/xconfigure 下載<br>第一組 configure 可以 build konqueror-embedded-snapshot-20030705 for arm<br>/win/opie_sdk/arm/opie/include/qpe/version.h<br>#define QPE_VENDOR "Project Opie"<br>#define QPE_VERSION "1.1.6"<br>#define SUB_VERSION ""<br>if configure check qtopia version is 1.1.6 and don't continue, I modify 1.1.6 to 1.5.6 let configure can work. Then change to 1.1.6 and make for opie 1.1.6 .<br>need set QPEDIR to opie dir or qtopia dir<br>suggest symbolic link $QPEDIR/lib/* to $QTDIR/lib maybe avoid some configure error<br>若是遇到以 opie 來 configure 時發生 check qtopia 版本不合, 改 opie/include/qpe/version.h 其中的<br>#define QPE_VERSION "1.1.6"<br>1.1.6 -&gt; 1.5.6 即可通過 configure check。通過檢察在改回來即可。<br>在做 configure 時, 最好把 $OPIEDIR/lib 或是 $QPEDIR/lib 所有<br>library link 到 $QTDIR/lib 下, 在檢查 libqpe 時才會過。<br></td>
        </tr>
        <tr>
            <td>libpcap-0.8.1</td>
            <td>./configure --build=i386-linux --target=arm-linux --prefix=$PWD/../libpcap-0.8.1-arm</td>
            <td>need flex to configure<br>need byacc to compile<br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/libpng-1.2.5-arm.tar.bz2"><u><font color=#0000ff>libpng-1.2.5</font></u></a></td>
            <td>copy scripts/makefile.linux to ./Makefile or symbolic link,<br>modify Makefile zlib include, lib variable,<br>and prefix variable to install,<br>type make install CC=arm-linux-gcc<br></td>
            <td>depend: zlib</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/zlib-1.1.4-arm.tar.bz2"><u><font color=#0000ff>zlib-1.1.4</font></u></a></td>
            <td><u><br><font color=#0000ff></font></u></td>
            <td>ref: http://www.ailis.de/~k/docs/crosscompiling/zlib.php</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/sqlite-2.8.15-arm.tar.bz2"><u><font color=#0000ff>sqlite 2.8.15</font></u></a></td>
            <td>I modify makefile to build it. ./configure --target=arm-linux --host=arm-linux --prefix=$PWD/../sqlite-2.8.15-arm This configure I get a Makefile and I modify it to build sqlite. TCC (in Makefile) I change gcc to arm-linux-gcc, and I remove libtool to build sqlite and use arm-linux-ar to build libsqlite.a. </td>
            <td>dep ncurses library if you want sqlite command. If you only need library and include file, you don't need ncurses library.</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/ncurses-5.2-arm.tar.bz2"><u><font color=#0000ff>ncruses 5.2</font></u></a></td>
            <td>需要 tic 來產生 terinfo file, make install 時要用到 tic, 必須要是 host version, in my pc, it is x86 format and static link which I build from source code. Tic (x86) is in this tar ball. arm version tic, I delete it.</td>
            <td><br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/fam-2.7.0-arm.tar.bz2"><u><font color=#0000ff>fam-2.7.0</font></u></a></td>
            <td>./configure -target=arm-linux --host=arm-linux --prefix=$PWD/../fam-2.7.0-arm</td>
            <td>modify some source code (fam.h, src/NetConnection.h) ＃i nclude &lt;limits.h&gt; to ＃i nclude &lt;linux/limits.h&gt; in my toolchain.</td>
        </tr>
        <tr>
            <td>esound-0.2.29</td>
            <td>CC=arm-linux-gcc ./configure --host=arm-linux --prefix=$PWD/../esound-0.2.29-arm</td>
            <td>modify Makefile: audiofile-config 在 libaudiofile/bin AUDIOFILE_CONFIG = ./audiofile-0.2.3-arm/bin/audiofile-config AUDIOFILE_LIBS = `./audiofile-0.2.3-arm/bin/audiofile-config --libs` make make install </td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/libid3tag-0.15.1b-arm.tar.bz2"><u><font color=#0000ff>libid3tag-0.15.1b</font></u></a></td>
            <td>CC=arm-linux-gcc ./configure --host=arm-linux --prefix=$PWD/../libid3tag-0.15.1b-arm </td>
            <td>read mp3 id3 tag</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/libmad-0.15.1b-arm.tar.bz2"><u><font color=#0000ff>libmad-0.15.1b-arm</font></u></a></td>
            <td>CC=arm-linux-gcc ./configure --host=arm-linux --prefix=$PWD/../libmad-0.15.1b-arm </td>
            <td>mp3 library</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/libmad-0.15.1b-arm.tar.bz2"><u><font color=#0000ff>bluez-libs-2.15</font></u></a></td>
            <td>./configure --build=i386-linux --host=arm-linux --target=arm-linux --prefix=$PWD/../bluez-libs-2.15-arm </td>
            <td>bluetooth library</td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/rsync-2.6.3-arm.tar.bz2"><u><font color=#0000ff>rsync-2.6.3</font></u></a></td>
            <td>CC=arm-linux-gcc ./configure --build=i386-linux --host=arm-linux --prefix=$PWD/../rsync-2.6.3-arm </td>
            <td>modify rsync.h add #define uint64 unsigned long and remove all uint64 macro </td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/module-init-tools-3.2-pre1-arm.tar.bz2"><u><font color=#0000ff>module-init-tools-3.2-pre1</font></u></a> </td>
            <td>./configure --build=i386-linux --host=arm-linux --target=arm-linux --prefix=/home/descent/module-init-tools-3.2-pre1/upstream/tarballs/module-init-tools-3.2-pre1/../module-init-tools-3.2-pre1-arm </td>
            <td><br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/index.htm"><u><font color=#0000ff>bash-3.0</font></u></a> </td>
            <td>./configure --build=i386-linux --host=arm-linux --enable-readline --prefix=$PWD/../bash-3.0-arm </td>
            <td><br></td>
        </tr>
        <tr>
            <td><a href="http://descent.myftp.org/%7Edescent/arm/index.htm"><u><font color=#0000ff>/util-linux-2.12r</font></u></a> </td>
            <td>CC=arm-linux-gcc ./configure --build=i386-linux --host=arm-linux --target=arm-linux --prefix=$PWD/../util-linux-2.12r-arm </td>
            <td>只有編譯其中的 fdisk 程式。<br>modify util-linux-2.12r/MCONFIG ARCH 改成 ARCH=arm,<br>cd fdisk<br>make CC=arm-linux-gcc<br></td>
        </tr>
    </tbody>
</table>
<br><!--
ppp-2.4.3
./configure
make CC=arm-linux-gcc
rp-pppoe-3.5
./configure
modify /src/Makefile, /src/libevent/Makefile 的
gcc => arm-linux-gcc
ar => arm-linux-ar
ranlib => arm-linux-ranlib
將 Makefile install -s 的 -s 去掉 (會呼叫 strip, 但是要呼叫 arm-linux-strip 才對)
-->
<img src ="http://www.cnitblog.com/zouzheng/aggbug/63227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2009-12-18 13:49 <a href="http://www.cnitblog.com/zouzheng/articles/63227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下patch的制作和应用</title><link>http://www.cnitblog.com/zouzheng/articles/42126.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 08 Apr 2008 06:40:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/42126.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/42126.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/42126.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/42126.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/42126.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 因为在u-boot移植过程中，有几处通用文件要修改，如果每次都要手动修改就太麻烦了。制作补丁可以解决这个问题。学习资料的收集比较简单，方法一类似于这种初级问题网上资料非常丰富，google或者baidu搜索一下，然后选择有价值的资料，方法二是阅读man在线文档。完成收集工作，当然最终要在自己的Linux上作实验，比较总结，消化吸收为自己的东西。要除去这么一种错误思想：一定要学全。要知道，一次学...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/42126.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/42126.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-04-08 14:40 <a href="http://www.cnitblog.com/zouzheng/articles/42126.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>交叉编译Linux内核(2.6.22.6)</title><link>http://www.cnitblog.com/zouzheng/articles/40301.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 29 Feb 2008 08:46:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40301.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40301.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40301.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40301.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40301.html</trackback:ping><description><![CDATA[动手编译内核之前，至少应该看看源码根目录下的README文件，并参考一些其他的资料，了解编译一个内核所需要的基本条件和环境，以及基本的步骤。<br>&nbsp;&nbsp;&nbsp; 我使用的交叉编译器是<a href="http://blog.chinaunix.net/u/26710/showart_394113.html" target=_blank>arm-iwmmxt-linux-gnueabi-gcc(4.2.1)</a>，选用的内核版本是Linux-2.6.22.6。以下操作均以普通用户身份进行：<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; (1)make mrproper<br>&nbsp;&nbsp;&nbsp; 编译之前检查内核源码树是否&#8220;纯净&#8221;(clean)，必作。<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; (2)make ARCH=arm CROSS_COMPILE=arm-iwmmxt-linux-gnueabi- menuconfig<br>&nbsp;&nbsp;&nbsp; 配置内核。2.6内核早已支持图形界面的xconfig和gconfig，不过还是推荐使用menuconfig，更容易配置。选择使用PXA270处理器的mainstone开发板作为目标系统进行试验(Intel HCDDBBVA0 Development Platform)，并精简不必要的选项和模块。<br>&nbsp;&nbsp;&nbsp; 内核配置选项中目标系统名称与具体开发板的对应关系可以查看arch/arm/mach-pxa/Kconfig文件。<br>&nbsp;&nbsp;&nbsp; 配置完毕选择保存，默认会将当前配置保存到.config文件，也可以指定一个文件名如test.config，下次配置时可以load这个文件加载相应的配置。<br><br>&nbsp;&nbsp;&nbsp; (3)make ARCH=arm CROSS_COMPILE=arm-iwmmxt-linux-gnueabi-<br>&nbsp;&nbsp;&nbsp; 编译内核。<br>&nbsp;&nbsp;&nbsp; 如果前面配置了PXA27x的Keyboard支持，则编译过程中会出现关于pxa27x_keyboard.c的错误："CKEN19_KEYPAD"未声明，这是一个<a href="http://ftp.frugalware.org/pub/frugalware/frugalware-current/source/base/kernel/pxa27x.diff" target=_blank>BUG</a>，修正如下：<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>
            <pre>--- a/drivers/input/keyboard/pxa27x_keyboard.c<br>+++ b/drivers/input/keyboard/pxa27x_keyboard.c<br>@@ -140,7 +140,7 @@ static int pxakbd_resume(struct platform_device *pdev)<br> 		KPREC = pdata-&gt;reg_kprec;<br> <br> 		/* Enable unit clock */<br>-		pxa_set_cken(CKEN19_KEYPAD, 1);<br>+		pxa_set_cken(CKEN_KEYPAD, 1);<br> 	}<br> <br> 	mutex_unlock(&amp;input_dev-&gt;mutex);</pre>
            </td>
        </tr>
    </tbody>
</table>
&nbsp;&nbsp;&nbsp; 另外，还可能遇到nvram.c中&#8220;undefined reference to `rtc_lock'&#8221;的错误。修正办法是：在nvram.c中&#8220;#include &lt;linux/mc146818rtc.h&gt;&#8221;，修改include/linux/mc146818rtc.h文件，将其中对&#8220;#include &lt;linux/spinlock.h&gt;&#8221;一段的条件编译选项&#8220;#ifdef __KERNEL__ ... #endif&#8221;去掉。<br><br>&nbsp;&nbsp;&nbsp; 编译完毕可以使用命令"echo $?"检查编译是否正常完成，正确结果为0。<br>&nbsp;&nbsp;&nbsp; 这时在内核源码树顶层目录生成了vmlinux(ELF格式的非压缩内核)以及相应的符号表文件System.map，可以直接下载到内存进行调试。另外还生成了arch/arm/boot/compressed/vmlinux(ELF格式的压缩内核)，还有arch/arm/boot/zImage(压缩内核映像文件)。<br>&nbsp;&nbsp;&nbsp; 查看vmlinux文件信息如下：<br>
<table style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td><span style="COLOR: rgb(0,1,102)">[aaronwong@localhost linux-2.6.22.6]$ file vmlinux</span> <br>vmlinux: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped<br></td>
        </tr>
    </tbody>
</table>
<br>&nbsp;&nbsp;&nbsp; (4)make ARCH=arm CROSS_COMPILE=arm-iwmmxt-linux-gnueabi- INSTALL_MOD_PATH=${TESTROOTFS} modules_install<br>&nbsp;&nbsp;&nbsp; 安装内核模块。先定义一个${TESTROOTFS}，将模块安装到这里。<br><br>&nbsp;&nbsp;&nbsp; (5)安装内核。<br>&nbsp;&nbsp;&nbsp; a)cp vmlinux ${TESTROOTFS}/boot/vmlinux-2.6.22.6<br>&nbsp;&nbsp;&nbsp; b)cp System.map ${TESTROOTFS}/boot/System.map-2.6.22.6<br>&nbsp;&nbsp;&nbsp; c)cp .config ${TESTROOTFS}/boot/config-2.6.22.6<br>
<hr style="WIDTH: 100%; HEIGHT: 2px">
[推荐阅读]<br>1. <a href="http://www.arm.linux.org.uk/docs/kerncomp.php" target=_blank>Kernel Compilation</a><br>2. <a href="http://cross-lfs.org/view/clfs-sysroot/arm/bootable/kernel.html" target=_blank>Making the CLFS System Bootable-Linux-2.6.22.6</a><br>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/40301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-29 16:46 <a href="http://www.cnitblog.com/zouzheng/articles/40301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>H.264标准三个框架  H.264学习笔记</title><link>http://www.cnitblog.com/zouzheng/articles/38450.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 04 Jan 2008 06:41:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/38450.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/38450.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/38450.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/38450.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/38450.html</trackback:ping><description><![CDATA[H.264的编解码框架与以前提出的标准如H.261、H.263及MPEG-1/2/4并无显著变化，也是基于混合编码的方案：以运动矢量代表图象序列各帧的运动内容，使用前面已解码帧对其进行运动估计和补偿或使用帧内预测技术，所得的图象参差值要经过变换、量化、熵编码等部分的处理。所以，新标准的性能提升在于各个部分的技术方案的改进及新算法的应用。新标准在提高图象传输的容错性方面做了大量工作，重新定义了适于图像的结构划分。在编码时，图象帧各部分被划分到多个slice结构中去，每个slice都可以被独立解码不受其它部分的影响。Slice由图象最基本的结构——宏块组成，每个宏块包含一个16&#215;16的亮度块和两个8&#215;8的色度块。为进一步提高鲁棒性，整个系统被划分为视频编码层和网络抽象层。视频编码层主要描述要传输的视频数据所承载的视频内容。而网络抽象层则是考虑不同的应用，如视频会议通信、H.32X连续包的视频传输或RTP/UDP/IP的通信。 H.264标准分成三个框架(profile)：Baseline、Main及X，代表了针对不同应用的算法集及技术限定。其中，Baseline主要包含了低复杂度、低延时的技术特征；主要是针对交互式的应用；考虑到了恶劣环境下的容错性，Baseline的内容基本都被其它更高级别的profile所包含。而Main profile是针对更高编码效率的应用，如视频广播。X profile 的设计主要针对流媒体的应用，在这一框架中所有容错技术和对比特流的灵活访问及切换技术都将包括其中。 Baseline profile的主要技术特征 ⑴ Baseline的解码器只对I slice及P slice进行操作。对于帧间预测，相比以前的标准，为了更精确的对图象的运动内容进行预测补偿，新标准允许宏块更进一步划分为16&#215;16、16&#215;8、8&#215;16、8&#215;8、8&#215;4、4&#215;8、4&#215;4的子块；运动估计精确到经由6-tap滤波器得到的1/4象素位置；运动矢量由相邻块预测得到，其预测的差值被编码传输。H.264支持多参考帧的预测，规定运动估计使用的参考帧数最多可达15帧，多参考帧的使用大大提高了对图像传输的容错性，抑制了错误在空间和时间上的蔓延。对于所有的slice编码类型，H.264支持两类帧内编码：4x4与16x16编码模式。对于4x4模式，每一个亮度4x4块有8种不同方向上的预测模式及DC预测模式。对于16x16模式，每个16x16亮度块有4种帧内预测模式。而对于宏块的8x8色度采样，采用与亮度16x16几乎相同的预测模式。为了保证slice的编码独立性，帧内预测是不允许跨越slice边界的。对于变换、量化部分。不同于以前标准对预测参差值的变换编码使用DCT变换，H.264使用了简单的整数变换。这种变换与DCT相比压缩性能几乎相同且有许多优势，其核心变换的计算只使用加减、移位运算，避免了精度的损失。对变换参差系数的量化使用了52级步长的量化器，而H.263标准只有31级。量化步长以12.5％递增，量化步长范围的扩大似的编码器能够更灵活和精确的进行控制，在比特率和图象质量之间达到折中。对熵编码部分。对于要传输的量化变换系数，若使用基于上下文的变长编码（CAVLC），它是根据前面已编码传输的量化变换系数值的大小来选择接下来系数编码要使用的变长编码表。由于变长编码表的设计是基于相应的统计条件，所以其性能要优于使用单一变长编码表。而对其它数据如头信息等，使用一种单一的变长编码表(Exp-Golomb code)。新标准仍然使用基于块的预测及重构方式，为了去除由此产生的影响图象主观质量的方块效应，H.264使用了去块效应滤波器。其主要思想是当块边界上两边差较小则使用滤波器使差别&#8220;平滑&#8221;掉,若边界上图象特征明显就不使用滤波。这样既为减弱&#8220;块效应&#8221;的影响又避免滤掉图象的客观特征。同时在相同主观质量下使得比特率减少5－10％。另外，对图象数据的组织及传输。在H.264标准中的图象宏块可以灵活的宏块组织顺序(FMO)划分为多个slice group；slice之间是相互独立的可以任意的顺序传输到解码端(ASO)。而且在比特流中slice可以使用重复的方式(RS)传输，这在slice数据出错的情况下可用来进行恢复，增强了图象传输的鲁棒性。同时slice间的相互独立性抑制了错误的空间传播，因此提高了比特流的容错性。 ⑵ Main profile的技术特征 Main profile包含Baseline profile的所有算法并具有额外的技术特征，但它并不支持FMO、ASO及RS等技术。只支持对I、P、B slice的处理操作。在此框架内提出了适配块划分尺寸的变换(ABT)的概念。此概念是针对帧间编码的，其主要思想是将对预测参差进行变换编码的块尺寸与用来进行运动补偿的块尺寸联系起来。这样就尽可能的利用最大的信号长度进行变换编码。但是由于复杂度的原因，进行变换的最大块尺寸被限制在8&#215;8以下。对熵编码部分，为更高效的进行编码，这里使用了基于上下文的算术编码（CABAC）使熵编码的性能进一步提高。相比较CAVLC，在相同图象质量下编码电视信号使用CABAC将会使比特率减少10-15％。另，Main profile不支持多个slice group的划分。 ⑶ 相关的编码问题如何对已提出的预测模式进行选择(mode decision)和使用运动估计策略(ME)历来都视频编码实现的重点研究课题。在H.263标准的实现软件中对模式的选择是简单的基于对阀值的比较。在新标准的测试软件中使用了拉格朗日率失真优化策略，它是基于使用每种图象块尺寸和每种预测模式而产生的参差及其传输的码率。这样，模式选择可以取得优化的率失真性能但这是以提高运算复杂度为代价的。此优化操作是对下面拉各朗日函数的最小化： J ＝ SATD + &#955;&#8226;R 其中，R为对应传输的各部分的比特率；&#955;为优化参数，其与量化参数有很强的相关性。SATD为经过哈德曼变换的4&#215;4块的预测参差绝对值总和。对于所有帧内、帧间宏块编码模式及多参考帧的选择都通过对拉各朗日函数的最小化来实现。通常，视频标准只是包括解码规范，而模式选择的技术研究是属于编码端的范畴，所以不列在标准之内.<br><br><br><a id=viewpost1_TitleUrl href="http://blog.yesky.com/blog/langboy/archive/2006/10/27/1583139.html"><strong>H.264学习笔记一</strong></a><br>
<p>2.1介绍<br>视频编码是对一个数字视频信号的编码和解码的过程.这一章讨论了数字图象和视频信号的结构和特征以及对于视频编码来说很重要的一些基本概念,比如采样格式等.数字视频是对于一人自然的视觉场景的从时间和空间上进行采样的表示方式.一个场景是由通过在时间上对于点进行采样来得到帧从而产生的(一种对于在时间上点在整个视频场景中的表示方法)或是一个场(由奇数或偶数行的空间采样组成).采样在一定的时间间隔上(通常是1/25或1/30秒时间间隔)进行重复，从而产生一个可动的视频信号。一般来说，需要三种采样集来表示一个有色的场景。表示数字视频的流行的方法是使用ITU-R 601标准并使用"中间集"。对于一个视觉场景的重建的准确性必须被计算来决定一个视频通信系统的性能，这是一个出了名的困难和极为不准确的过程。主观的测量方法是极耗时间而且它与观察者对于变换的反应程序不同而不同。客观的测量方法实现起来就更简单一些，但是目前还不能与人类实际视觉感完完全全匹配。</p>
<p>2.2 自然视频场景</p>
<p>一个经典的&#8220;现实世界&#8221;或&#8220;自然世界&#8220;的视频场景是由多个有各自特征形状，深度，纹理和亮度的物体构成的。视频场景的颜色和明亮度在不同的场景中根据不同程序的光滑度而定。一个与视频处理和压缩相关的经典的自然视频场景包括空间特征（纹理变换，物体的数目和形状，颜色等）和时间特征（物体运动，亮度的变化，视点的移动等）</p>
<p>2.3 捕捉<br>一个自然视频场景在空间和时间上是连续的。用数字的形式表示一个视频场景包括在空间对实际场景进行采样(通常是通过在视频图形面上用长方形格处理)和时间采样(以一系列以某时间间隔采样得到的静态帧组成)。数字视频就是在数字形式下的一个视频场景的采样的表示方式。每一个时-空采样（像素）用一个数或一组数来表示，用来描述采样点的亮度和色度。</p>
<p>为了得到一个二维的采样图像，摄像头把一个视频场景的二维投影聚焦到传感器上，比如一组电荷耦合装置(CCD)。在带色的图像捕捉过程中，每一个颜色成员都分别被过滤并投影到一组CCD中。</p>
<p>2.3.1 空间采样<br>一组CCD的输出就是一个模拟的视频信号，一组可以表示一个视频图像的电信号。在时间上对一点进行采样就形成了一个有定值的采样点图像或帧。最常用的采样方法是把采样点置于一个正方形或长方形格中进行操作。然后对于每个格交点处的点进行采样，重建过程就以采样值对应到像素上进行显示。重建图的视觉效果取决于采样点的数量。选择一个粗糙的采样格会得到一个低分辨率的采样图像，而增加采样点的数量就会增加采样图像的分辨率。</p>
<p>2.3.2 时间采样<br>一个可动的视频图像是通过对信号在周期性的时间间隔上进行快照得到的。重放这一系列的帧会得到一个运动的效果。一个高的时间采样率(帧率)会产生更加平滑的运动但是它就要求有更多的采样要被捕捉并被保存。在10帧每秒之下的帧率有些被用于一些很低码率的视频通信中(因为被传输的数据量非常的小)但是运动却看起来很笨拙而且不自然。在10-20帧每秒是比较经典的低码率视频。在25-30帧每秒进行采样是标准电视信号图象的采样帧率(配合隔行扫描采样来达到更好的运动效果）。50-60帧每秒就可以形成平滑的运动(代价就是帧率太过高，传输和存储的压力大).</p>
<p>2.3.3 帧和场<br>一个视频信号可以被通过对于一系列帧(渐进采样)或一个序列的隔行扫描的场(隔行扫描采样)来进行采样。在一个隔行扫描采样的视频序列里，一帧的一半的数据是在每个时间采样间隔进行采样的。一个场由奇数个或偶数个扫描线组成，而一个隔行扫描的视频序列包括一系列的视频帧。这种采样方式的优点在于与有相同帧数的同样码率的渐进序列相比，可以在一秒中传输两倍多的场，这样就可以形成更加平滑的运动。比如，一个PAL视频序列由50场/秒的码率组成，在回放过程中，运动可以比与之相同的25帧每秒的用渐进视频序列形成的运动显得更加的平滑。</p>
<p>2.4 颜色空间<br>大多数数字视频程序都依赖于彩色视频的显示，这样的话，就需要一个来捕捉并表示颜色空间的表示方法。一个单色的图像只需要一个在空间内表示一个像素点的亮度或流明度的值就可以了。但对于颜色图像来说，对于一个像素点至少需要三个数来把颜色信息准确地表示出来。用来表示亮度和颜色的方法叫做颜色空间。</p>
<p>2.4.1 RGB<br>在RGB颜色空间中，一个带颜色的图象采样是用三个值来表示一个象素点的相对的红，绿和蓝色比(三种光线的主样构成颜色)。任何颜色都可以通过把红，绿和蓝来通过不同的比例相混得到。RGB颜色空间更加适合于捕捉并显示颜色图像。捕捉RGB图像包括过滤出红，绿和蓝色的构成比率，并用一个单独的传感器数组来捕捉。CRT和LCD通过分别对每个像素点的红绿蓝值进行显示来得到各种颜色。从一个通常的观察距离来看，不同的构成部分可以达到颜色上的真实感。</p>
<p>2.4.2 YCbCr<br>人类视觉系统(HVS)相比亮度来说对于颜色不是那么敏感的。在RGB颜色空间中，三种颜色被平等地看待，并用相同的分辨率存放起来。但是通过把亮度与颜色信息分离，并对亮度值取更高的分辨率可以更有效地表示一个颜色图像。</p>
<p>YCbCr颜色空间和它的变换(通常写为YUV)是一种流行而高效的表示一个颜色图像的方法。Y是亮度值，由R,G,B的加权平均可以得到： Y=krR + kgG + kbB<br>这里k是加权因子。</p>
<p>颜色信号可以由不同的颜色差别来表示：<br>Cb = B-Y<br>Cr = R-Y<br>Cg = G-Y<br>对于一个颜色图像的完整的描述由给定Y和三个色差:Cb,Cr,Cg来表示。</p>
<p>目前为止，我们的表示方法好像并不那么好，因为相比RGB表示来说，我们这次用了四个参数。然后Cb+Cr+Cg是一个常数，那么我们只需要两个色度参数就可以了，第三个可以通过其他两个计算出来。在YCbCr空间中，只有Y和Cb,Cr值被传输和存储，而且Cb和Cr的分辨率可以比Y低，因为人类视觉系统对于亮度更加敏感。这就减少了表示图像的数据量。通常的观察情况下，RGB和YCbCr表示的图像看上去没有什么不同。对于色度采用比亮度低的分辨率进行采样是一种简单而有效的压缩办法。</p>
<p>一个RGB图像可以在捕捉之后转换为YCbCr格式用来减少存储和传输负担。在显示图象之前，再转回为RGB.注意没有必要去指明分别的加权值kg（因为kb+kr+kg=1)，而且G可以从YCbCr中解压出来，这说明不需要存储和传输Cg参数。</p>
<p>Y = kr R + (1-kb-kr)G + kb B<br>Cb = 0.5/(1-kb) * (B-Y)<br>Cr = 0.5/(1-kr) * (R-Y)</p>
<p>R = Y + (1-kr)/0.5 * Cr<br>G = Y - 2kb(1-kb)/(1-kb-kr) * Cb - 2kr(1-kr)/(1-kb-kr) * Cr<br>B = Y + (1-kb)/0.5 * Cb</p>
<p>ITU-R的BT.601决议定义了kb=0.114,kr=0.299，那么代换参数就有了如下等式：</p>
<p>Y = 0.299R + 0.587G + 0.114B<br>Cb = 0.564(B - Y )<br>Cr = 0.713(R - Y )</p>
<p>R = Y + 1.402Cr<br>G = Y - 0.344Cb - 0.714Cr<br>B = Y + 1.772Cb</p>
<p>2.4.3 YCbCr采样格式<br>4:4:4采样就是说三种元素Y,Cb,Cr有同样的分辨率,这样的话,在每一个像素点上都对这三种元素进行采样.数字4是指在水平方向上对于各种元素的采样率,比如说,每四个亮度采样点就有四个Cb的Cr采样值.4:4:4采样完整地保留了所有的信息值.4:2:2采样中(有时记为YUY2),色度元素在纵向与亮度值有同样的分辨率,而在横向则是亮度分辨率的一半(4:2:2表示每四个亮度值就有两个Cb和Cr采样.)4:2:2视频用来构造高品质的视频彩色信号.</p>
<p>在流行的4:2:0采样格式中(常记为YV12)Cb和Cr在水平和垂直方向上有Y分辨率的一半.4:2:0有些不同，因为它并不是指在实际采样中使用4:2:0，而是在编码史中定义这种编码方法是用来区别于4:4:4和4:2:2方法的).4:2:0采样被广泛地应用于消费应用中，比如视频会议，数字电视和DVD存储中。因为每个颜色差别元素中包含了四分之一的Y采样元素量，那么4:2:0YCbCr视频需要刚好4:4:4或RGB视频中采样量的一半。</p>
<p>4:2:0采样有时被描述是一个"每像素12位"的方法。这么说的原因可以从对四个像素的采样中看出.使用4:4:4采样，一共要进行12次采样，对每一个Y,Cb和Cr，就需要12*8=96位，平均下来要96/4=24位。使用4:2:0就需要6*8=48位，平均每个像素48/4=12位。</p>
<p>在一个4:2:0隔行扫描的视频序列中，对应于一个完整的视频帧的Y,Cb,Cr采样分配到两个场中。可以得到，隔行扫描的总采样数跟渐进式扫描中使用的采样数目是相同的。</p>
<p>2.5 视频格式<br>这本书中描述的视频压缩标准可以压缩很多种视频帧格式。实际中，捕捉或转化一个中间格式或一系列中间格式是很平常的事情。CIF就是一种常见的流行的格式，并由它衍生出了4CIF和Sub-QCif。帧分辨率的选择取决于应用程序，可使用的存储量以及传输带宽。比如说4CIF对于标准定义的电视和DVD视频来说是合适的,CIF和QCIF在视频会议中是常被使用的格式。QCIF和SQCIF对于移动设备的多媒体程序来说是合适的，在这样的情况下，显示分辨率和码率都是有限的。以下是各种格式的具体使用位数的需求（使用4:2:0采样，对于每个元素用8个位大小表示)：</p>
<p>格式：&nbsp;Sub-QCIF&nbsp;亮度分辨率：&nbsp;128*96&nbsp;&nbsp;每帧使用的位:&nbsp;147456<br>格式：&nbsp;QCIF&nbsp;&nbsp;亮度分辨率：&nbsp;176*144&nbsp;&nbsp;每帧使用的位:&nbsp;304128<br>格式：&nbsp;CIF&nbsp;&nbsp;亮度分辨率：&nbsp;352*288&nbsp;&nbsp;每帧使用的位:&nbsp;1216512<br>格式：&nbsp; 4CIF&nbsp;&nbsp;亮度分辨率：&nbsp;704*576&nbsp;&nbsp;每帧使用的位:&nbsp;4866048</p>
<p><br>一种在电视信号中被应用的很广的数字视频信号编码格式就是ITU-R的BT.601-5 提案。亮度元素被在13.5MHz下采样，而亮度值则在6.75MHz下采样，这样就形成了一个4:2;2的Y:Cb:Cr采样结果。采样数字信号的参数取决于视频码率(对于NTSC来说是30Hz,对于PAL/SECAM来说是25Hz)。NTSC的30Hz是对低空间分辨率的补偿，这样总的码率就是216Mbps.实际显示的激活部分的区域要比总量小，因为它去掉了在一帧边缘处的水平和垂直空白间隔。<br>每一个采样都有0-255的采样范围。0和255两个等级被留作同步，而且激活的亮度信号被限制到26(黑色）到235(白色)之间.</p>
<p>2.6 质量<br>为了指定，评价和比较视频通信系统，我们需要决定向观察者显示的视频图像的质量。衡量视频信号的质量是一件困难的事情，通常也是不准确的，因为有太多的因素会影响到衡量的结果了。视觉质量与生俱来就是主观的因素，它被很多因素影响着，这就使对于这个衡量结果的准确性变得更难了。比如说，一个视频信号的质量对于一个观察者来说主要取决于任务本身，比如说，被动地观看一部DVD影片，主动地参与一个视频会议，用符号评议进行通信交流，或是试图从一个视频场景中认出一个人。衡量视频信号的客观分类给定了一个准确的可重复的结果，但是没有哪种客观的测量方法可以完全地模拟人类视觉主观的感受。</p>
<p>2.6.1&nbsp; 主观质量测量<br>2.6.1.1 影响主观质量的因素<br>对于一个视频场景的感觉是由人类视觉系统对于不同元素复杂交互性决定的----眼睛和大脑.对于视频信号的感知是受空间保真度的影响的(不管有没有明显的失真，问题在于我们是否可以清楚地看到一个场景的各个部分)和时间保真度(运动是否自然平滑)。然而，一个观察者对于质量的看法经常会被观察环境，观察者的心情和观察者与场景的交互程序相关。一个执行特定任务的用户需要关注于视频场景的一部分。观察一个场景常与看一个电影时的对于&#8220;好&#8221;的概念是不同的。例如，一个观察者的对视频质量的看法在观察环境好的情况下会更好一些（而这一点不取决于视频信号本身的好坏）</p>
<p>其他的重要的影响因素包括视觉焦点（一个观察者通过一系列的观察点而不是同时观察所有的内容）和所谓的"最新效应"（我们对于一个视频序列的看法总是更多地受更新看到的内容的影响而不是老的内容)。所有的这些因素都让衡量一个视频的质量的好坏的任务变得极为困难。</p>
<p>2.6.1.2 ITU-R 500<br>很多的关于主观质量认下的测试过程都在ITU-R BT.500-11中被定义。一个常用的过程就是Double Stimulus Continuous Quality Scale(DSCQS)方法，评价者被展示了一系列的图片或两个视频序列A和B（一个接一个地），然后被要求给出A和B的质量评价值，方法是在五个分隔着的评价值（从"Excellent"到"Bad")画连续线来定。在一个典型的测试会话中，评价者被展示了一系列的序列，并被要求对它们进行评价。对于每对序列来说，一个是未受损的"参考&#8220;序列，另一个是同样的序列，它被在测试的系统或过程中修改了。</p>
<p>这两个序列的顺序，原始的和有损的，在测试地过程中被随机的给出，这样评价者就不知道哪个是原始的，哪个是改变过的序列。这样就防止了评价者带偏见地比交这两个测试序列。在结束的时候，评分被转化到一个规范化的范围内，最终的结果是用平均评价值来说的，用它来指明相应的帧的质量。</p>
<p>像DSCQS这样的测试被广泛地接受，并被用来评价主观的视频效果。然而，这样的测试受实际问题的影响。这样结果对于评价者来说差别会非常大。这个不同会被在重复测试的过程中被弥补过来。一个有经验的评价者(对视频压缩失真了解的比较多的）会比那些非有经验性的用户会给出一个更带偏见的评分。这就意味着一个很大的评价用户群是需要的，因为没有经验的用户很快会发现被改变的视频的一些特征。这些因素使得使用DSCQS方式的代价更大。</p>
<p>2.6.2 客观的质量测量<br>主观测量质量的方法的复杂性和消耗性让用算法自动测量质量要更加的吸引人。视频压缩的开发者和视频处理系统很大程序上依赖于所谓的客观质量测量方法。最广泛应用的方法是PSNR方法，但是这种方法的局限性使人们不得不找更加复杂的方法来逼近人类视觉性。</p>
<p>2.6.2.1 PSNR<br>PSNR是用来在对数级上描述质量，并且依赖于原始信号和改变后信号的均方差（MSE）:<br>PSNR(db) = 10log(10)(2^n-1)^2/MSE</p>
<p>PSNR可以很方便而快速地被计算出来，这样它就成为了一种很流行使用的方法，并用来测量压缩和解码视频图像的质量。</p>
<p>PSNR方法有几个局限性，PSNR需要一个原始的图像做为对比，但是这也许是无法在所有情况下都可以实现的,也难保所谓的原始图象没受过影响。PSNR不能准确地给出主观的视频质量值。对于给定的一个图象或一个图象序列来说，高的PSNR通常说明质量高，低PSNR说明质量低。然而，一个特定的PSNR值并不等于绝对的主观的质量。主观上感觉好的图象不一定PSNR值高。这种情况下，人类的观察敏感区中心让人感觉清晰度很好，但信嗓比不一定高。</p>
<p>2.6.2.2 其他的客观质量衡量方法<br>因为PSNR方法的局限性，最近有很多工作用来开发更加复杂的客观的测试过程，而且表示更准确的主观信息。很多不同的方法都被提出了。但是没有一个可以完全代替主观测试。所以还没有一个比较标准的，准确的，可用的方法。意识到这一点之后，ITU-T视频质量专家组（VQEG）就致力于提出一种客观的视频质量评价机制。每一步就是测试并比较隐藏的模型与测试模型。在2000年三月，VQEG宣布有10个这样的测试系统备选。不幸的是，没有一种被认为是适合的。VQED在2003年进行了第二次的评估。除非非常在自动质量评价中有一个非常大的突破，否则这个问题是很难被解决了。</p>
<p>2.7 结论<br>采样模拟信号会形成数字视频信号，它有准确，高质量和对于数字媒体的存储传递等各种优势，但是会占用比较在的空间。与生俱来的问题包括空间和时间分辨率，颜色表示和视频质量的测量问题。下一章会介绍视频压缩的一些其他的基本理论.</p>
<br><a id=viewpost1_TitleUrl href="http://blog.yesky.com/blog/langboy/archive/2006/10/27/1583142.html"><strong>H.264学习笔记二</strong></a><br>
<p>3.1 介绍<br>压缩(compress) 动词： 挤压到更小的空间中;即condense<br>压缩(conpress) 名词： 压缩的行为或是压缩的状态</p>
<p>压缩是把数据用更小的空间来存放的技术.视频压缩(视频编码)是把数字视频流序列用更少的数据位进行存放的方法."Raw"或叫没压缩过的视频需要大量的码(大约每秒信息216M),而且压缩对于数字视频的存储和传输来说都是需要的.</p>
<p>压缩包括一对互补的系统,一个编码器(encoder)和一个解压器(decoder).编码器把原数据在传输或存放之前转变为压缩格式(占用更少的数据位),而解压器把压缩的格式转会到原来的视频数据格式上.编码器/解码器对经常被叫做CODEC(enCOder/DECoder)</p>
<p>数据压缩是通过移除数据冗余来实现的，比如说，对于数据重构过程中无用的数据。很多种数据都有统计上的冗余性，它们就可以通过无损压缩进行有效的压缩，这样的标准比如说JPEG-LS，它可以达到3-4倍的压缩。有损压缩可以达到更高的压缩比。在有损压缩系统中，解压数据与源码流数据是不同的，高压缩率是通过视频质量的下降来达到的。有损视频压缩系统是建立在删除主观冗余的原理之上的，从图象或视频中删掉的部分不会很大程度上影响观察者对于视频质量的认识的。</p>
<p>大多数视频编码方法寻找空间和时间上的冗余来达到压缩的效果。在时间域中，连续几帧的视频通常有很强的相关性，特别是当时域采样率是非常高的时候尤其如止。在空间域中，通常像素采样点之间是相互关联的，比如说，相邻象素之间很相近。</p>
<p>H.264和MPEG4视频标准共用了一部分特征。这两种标准都假设了一种以块(block）为基础的运动补偿，变换，量化和熵编码。我们主要关注到这些主要的方法中，并从时间模型开始，接下来是图象变换，量化，预测式编码和熵编码。并以对于一个图象采样块进行编码和解码的过程进行描述。</p>
<p>3.2 视频编解码器<br>一个视频编码器器把一个源图像或视频序列转化为一种压缩模式，并在解码器把它构造为源序列的一个拷贝的或是一个近似.如果解压了的视频序列与原序列是相同的，那么编码过程是无损的，如果解压序列与源序列是不同的，那么这个过程是有损的。</p>
<p>CODEC用一种模型来表示原始视频流(一个被有效编码的表示方式，并可以用它来重建视频数据的近似结果或尽量取得高的码率。这两个目标(压缩效率和高质量)通常是相矛盾的，因为一个低的压缩码率通常在解压部分会降低图象的质量。码率和质量的平衡我们会在之后进行介绍</p>
<p>视频编码器是由三个主要的功能部件实现的：时域模型，空域模型和熵编码。时域模型的输入为一个未压缩的视频流序列。时域模型试图用邻近帧的相似性来消除了时域冗余,通常是构造通当前帧的预测。在MPEG4视频部分和H.264中，预测通常从一个或多个之前或之后的帧来进行的，并通过对于帧之间的差别进行补偿。时域模型的输出是一个剩余帧(通过从实际当前帧中减去预测值得到)，而一系列的模型参数，通常是一系列用来描述运动是如何补偿的运动向量。</p>
<p>剩余帧构成了时域模型的输入，它会利用邻近采样点的相似性来降低空域的冗余。在MPEG4视频部分和H.264中，这通常通过一些变换来对样点进行处理来实现。变换把采样点转到其他的域中，在这些域中用变换系数来表示。这些系数被量化来删除不明显的值，只留下很少的大系数来对剩余帧进行表示。空域模型的输出是一系列的量化变换的系数。</p>
<p>时域模型的参数(通常是运动向量)和空域模型的参数(系数)通常用熵编码来进行压缩。这就删除掉了统计上的冗余度(比如，用短二进制码表示当前的向量和系数)并制造出一个压缩流或文件来进行传输或存储。一个压缩的序列由编码的运动向量参数，编码的剩余系数和头信息表示。</p>
<p>视频解吗器从一个压缩流中构造一个视频帧。系数和运动向量由熵解码器进行解码，然后空域模型构建出一个版本的剩余帧。解码器利用运动向量参数与一个或多个解压帧来构成一个对当前帧的预测，然后帧就可以通过加入这个剩余帧来得到。</p>
<p>3.3 时域模型<br>时域模型的目标是要减少减少传输帧的冗余性。这个过程的输出是一个剩余帧，而且预测过程越准确，剩余帧中包含的能量就越小。剩余帧被编码并送到解压器用来构造预测帧，加上解码的剩余帧来构成当前帧。预测帧是通过一个或多个过去或未来的帧(都叫做参考帧)来创建的。预测的准确性通常可以由参考帧和当前帧之间的运动补偿来改进。</p>
<p>3.3.1 从前一视频帧进行预测<br>时间域的预测的最简单的方法是从当前帧的前一帧进行预测。用两帧之间的差来做为剩余帧。这个方法的明显的问题在于剩余帧中的能量太多了，这也就是说在时域压缩之后还有大量的数据可以被压缩。大部分的剩余能量是由于在二帧中的物体运动可能造成两帧的运动补偿形成的。</p>
<p>3.3.2 由于运动造成的改变<br>在视频帧之间的改变可能是由物体的运动造成的(刚性的物体运动，比如说一辆运动着的汽车，或可变形物体运动，比如说移动的手臂），摄象头的移动，被去覆盖的区域(比如说一个运动场景中由于物体的运动致成背景的去覆盖性)和光线的变化。除去去覆盖和光线变化，差别都是由于帧间的像素点的运动造成的。这样构造的像素点的邻接被称为光流(optical flow)。完整的域包括对于每个像素位置的光流向量，但是这些域都是被子采样过的，这样的话，只有每隔两个像素点的向量才被显示出来。</p>
<p>如果光流域是准确描述的，那么它应该可以构成一个对大多数像素点准确的预测，方法即为沿着光流向量移动参考帧中的每个像素点。然而，种种原因之下，这并不是一个实用的运动补偿的方法.对于光流的准确的计算对于计算来说是非常敏感的而且把每个像素点的光流传给解码器也是必要的，以使它能重构造预测帧(这样就得到了大量的传输数据，与我们小剩余值的目标是相矛盾的）。</p>
<p>3.3.3 基于块的运动估计和补偿<br>一个实用并被广泛使用的运动补偿方法是为了裣一个矩形区域的运动或叫当前帧的&#8220;块&#8221;。下列的步骤对当前帧的一个M &#215; N采样的块的处理过程：</p>
<p>1. 在参考帧中查找一帧(过去的或以后的，已被编码的或传输的)，来找到一个相匹配的M * N的区域。这是由在查找区域中比较M * N的块,并找到一个最接近这一块的区域。一个流行的匹配方式是用把两个块的能量作差，这样能量相差最小的两个区域被认为是最佳的匹配结果。这个查找匹配的过程被叫做运动估计。</p>
<p>2. 选定的区域成为对于当前M * N块的预测块，它被减掉当前的块来构成一个M*N的剩余块(运动补偿).</p>
<p>3. 剩余块被编码并传输，当前块和选定块的差值(运动向量)也被传输.</p>
<p>解码器使用接收到的运动向量来重建预测区域并解压剩余块，加上之前的预测块来重建一个原始块。</p>
<p>基于块的运动补偿这么流行是有很多原因的。它很直接，计算性上也很易处理，它与矩形视频帧很相适应，并且使用基于块的图形变换(比如DCT等)，而且它对于视频序列提供了一个有很效的时域模型。然而它也有一些缺点，比如说，实际物体中几乎没有平滑的边缘来进行矩形边界的匹配，物体通常在帧之间用很小的像素点位置移动（比如说可变形的物体，旋转和扭曲），而且很多种运动是难以用基于块的方法进行补偿的。尽管它有这些缺点，基于块的运动补偿是当前所有的视频标准使用的时域模型的基础.</p>
<p>3.3.4 对于一个宏块的运动补偿<br>宏块，对应于一个16*16的帧区域，是大多数编码标准(包括MPEG1,MPEG2,MPEG4,H.261,H.263和H.264)运动补偿预测方法的基本组成部分。对于源4:2:0的视频信号来说，一个对于源帧的16*16像素区域的是由256个亮度采样(用4个8*8的采样块排列)，64个Cb色度采样(用一个8*8的采样块排列),64个Cr色度采样(用一个8*8采样块排列)，这样就一共六个8*8的块。MPEG4或H.264的CODEC通过宏块来处理每一个视频帧.</p>
<p>运动估计<br>对于一个宏块的运动估计包括在参考帧中找一个与当前宏块相近的16*16的采样区域。参考帧是一个在之前被编码过的帧。参考帧的区域以当前宏块的位置为中心，宏块区域的查找16*16的相匹配区域做为最佳匹配结果。</p>
<p>运动补偿<br>在参考帧中选定的最佳匹配区域是从当前宏块中减掉的部分，它用来构成一个剩余帧，接着通过运动向量被编码和传输，它也描述了最佳对应区域。在编码器之内，剩余帧被编码和解码并被加到相应区域之中，来重构一个宏块，这个宏块被存为以后的运动补偿预测的参考帧。用一个解码的剩余帧来重构宏块来保证编码器和解码器在运动补偿的时候使用相同的参考帧。</p>
<p>在运动估计和补偿的过程中有很多不同方法。参考帧可以是之前的或之后的一帧，也可以是结合或是更多帧的搭配。。如果一个之后的帧用来进行参考的话，那么就要先对这一帧进行编码。在参考帧和当前帧之间有一个很大的改变的地方（比如说，一个场景的变化），也许不使用运动补偿进行编码宏块会更有效。这样编码器就选择帧内编码(在没有运动补偿的情况下进行编码.在移动场景中移动的物体很少符合16*16像素边缘而且这样也许使用可变的块大小进行运动估计和补偿会更高效。物体可能有很少的像素部分运动，这样使用在参考帧中插值法会是一种更加好的预测方法。</p>
<p>3.3.5 运动补偿块大小<br>视频序列中的连续两帧中图1是从图2中不通过运动补偿而进行减操作得到的一个剩余帧.剩余帧中的能量由于通过16*16的宏块做运动补偿而降低.每8*8块(而不是16*16)的块会更近一步降低剩余帧中的能量,4*4会更少.这个例子说明了小的运动补偿块可以得到更佳的运动补偿效果.然而,一个更小的运动补偿块会导致复杂度的升高(更多的搜索操作被执行了)，这样更多的运动向量就要被传输。发送每个运动向量需要一些位来做到，这个多余的负担会抵销剩余帧中能量的减少的优势。一个更有效的妥协的方法是采用调节图的块大小，比如说在相似的区域上选择一个大的块,而在高度细节化运动的部分选用更小的块。H.264使用了一种自适应的运动补偿块大小，在以后我们会提到。</p>
<p>3.3.6 子像素运动补偿<br>一些情况下，通过在参考帧中从插值采样位置来预测会得到更好的运动补偿预测。&#8220;子像素&#8221;运动估计和补偿包括寻找子采样的插值位置与整值采样位置，来寻找最佳匹配(比如说，最小化剩余帧的能量)并在这个位置使用整或子采样值来直行运动补偿预测。对于四分之一像素运动裣来说，第一步，运动估计会寻找对于整采样格的最佳匹配,接下来编码器在最佳匹配附近寻找半采样位置来看匹配是否可以被改进，如果需要的话，对半采样最佳匹配像素附近的四分之一采样位置进行寻找。最终的匹配（整，子或四分之一采样位置）由当前块或宏块相差得到。</p>
<p>用4*4的块大小得到的剩余帧可以使用半采样插值并有更低的剩余能量。这种方法可以由从四分之一采样格来得到更小的剩余帧能量得到扩展。总体上说，&#8220;更好的&#8221;插值可以达到更好的运动补偿性能（更小的剩余帧能量），代价则是复杂度会更高一些。这样得到的性能提升会因为插值的步骤的增多而抵销。半采样插值相比整采样有更好的性能，四分之一采样插值会给出更好的效果，八分之一更胜之，以此类推。</p>
<p>一个运动补偿过的参考帧被从当前帧中减去，剩余帧的能量如下（用总绝对误差来近似SAE）： <br>Sequence&nbsp;No motion compensation &nbsp;Integer-pel &nbsp;Half-pel &nbsp;Quarter-pel<br>'Violin',QCIF&nbsp;171945&nbsp;&nbsp;&nbsp;153475 &nbsp;&nbsp;128320 &nbsp;&nbsp;113744<br>'Grasses',QCIF&nbsp;248316&nbsp;&nbsp;&nbsp;245784 &nbsp;&nbsp;228952 &nbsp;&nbsp;215585<br>'Carphone',QCIF&nbsp;102418&nbsp;&nbsp;&nbsp;73952&nbsp;&nbsp;56492&nbsp;&nbsp;47780</p>
<p>一个低的SAE表示更好的运动补偿性能。在每种情况下，子像素运动补偿相对整像素补偿都有更好的性能表现，而四分之一补偿会更好。"Grasses"序列有更复杂的运动，这样就更难以实施运动补偿，悲痛"Violen"和"Carphone"就更容易进行补偿了。</p>
<p>在四分之一采样插值中寻找一个匹配的4*4的块是比在没有插值的16*16的块中更加复杂的。除了更大的复杂度之外，因为每个块的运动向量都要被编码并传输到接收端以更好的重建图象，这样就有了编码上的性能损失了。因为块大小减小了，传输的向量数增加了。需要更多的位来表示半采样或四分之一采样向量，因为向量的更细节部分也必须被像整采样那样进行编码。这样有另复杂运动的补偿模式下就有编码效率上的平衡度了，因为运动补偿越准确，就有更多的数据来表示运动域，但是在不那么准确的情况下，就不需要那么多位了。<br>3.3.7 基于区域的运动补偿<br>在一个自然的视频场景中移动的物体很少被对很整齐地对齐到块的边缘，它们很可能是不规则形状的，放置在任意位置上，而且在帧间改变他们的形状。这样就很难在参考帧中找到一个理想的匹配，因为它覆盖了一部分运动着的物体，一部分静态的物体。</p>
<p>在图像的任意区域应用运动补偿是可以达到更好的性能的。比如说，如果我们试图在椭圆形的物体中进行运动补偿的话，我们可以在参考帧中找到一个理想的匹配。然而使用基于区域的运动补偿会遇到很多的实际困难，包括识别区域边界，指出边界的轮廓，在运动补偿之后编码剩余帧，MPEG-4视频包括了一系列的工具来支持基于区域的补偿和编码。</p>
<br><br>
<div class=rightmenu>
<div><a id=viewpost1_TitleUrl href="http://blog.yesky.com/blog/langboy/archive/2006/10/27/1583144.html"><strong>H.264学习笔记三</strong></a></div>
<p>3.4 图像模型<br>一个自然的视频图像包括一格采样值.自然图像在它们原始的状态下通常很难于压缩,这是因为相邻图像采样值之间的很强的联系造成的.我们可以从一幅图像的自相关函数图中得到不同图像之间的相似程度.在中点处的最顶点表示图像未经移动时的图像.当空域移动拷贝被从原始图像的任一个方向移除的时候,这个函数值就会急骤下降,就这说明了一个图像采样值的邻域内是高度相关的.</p>
<p>一个运动补偿的剩余图象的自相关函数在当空域移动增加的时候快速衰减,表明了相邻的采样点是弱相关的.有效的运动补偿在剩余帧中减少了本地相关性,这样就让图象比原始状态下的图象更易于压缩.图像模型的作用就是把图像或剩余图像的相关性去除掉,并把它转变为一种可以被熵编码器有效编码的数据形式.实际图像模型一般有三个主要的组成部分：变换（去相关和简化数据），量化（减小转换数据的相关性）和重组（组合数据来把大值分组到一起）。</p>
<p>3.4.1 预测图像编码<br>运动补偿是预测编码的一个实例，在这种补偿下，编码器基于上一帧创造了对于当前帧的一个区域的预测并从当前域中减去这个预测来形成一个剩余帧。如果预测是成功的，那和在剩余帧中的能量是比原始帧中的能量少得多的，而且剩余帧可以用更少的位来表示。</p>
<p>与之相似的是,同一个图像采样或区域的预测可以由前面传输的同样图象或帧中组成.预测性编码被用作是早期的压缩算法的基础,也是H.264的一个很重要的组成部分.帧内编码(应用在变换域中,见后).空域预测有时被描述为"差分脉冲编码调制"(DPCM)--从通信系统中引入的一个差分的PCM编码方法.</p>
<p><br>&nbsp;B&nbsp;C<br>A&nbsp;X</p>
<p>图中假设一个像素X是被编码的像素点,如果在光栅顺序下处理这帧的话,那么点A,B和C(在当前和之前行的邻近像素)在编码和解码器中都是可得的(因为他们已经在X之前被解码).编码器在根据一些在之前编码的像素的组合来得到对于X的预测,从X中减掉这个预测,然后编码剩余帧频(做差之后的结果).解压器形成相同的预测,然后把解码的剩余帧加进去来重建像素值.如果编码过程是有损的(比如说剩余帧被量化了)那么解码的像素值A,B,C也许与原始的A,B,C是不同的(因为编码过程的损失),这样的话上述的过程可能在编码器和解码器上引起累积的不匹配.这种情况下,编码器应该解码剩余帧中的R(X),并重建像素.</p>
<p>比如说:<br>编码器预测: P(X) = (2A + B + C) / 4<br>剩余帧&nbsp; R(X) = X - P(X) 被编码并传输<br>解码器解码R(X)并组成同样的预测: P(X) = (2A + B + C) / 4<br>重建像素 X = R(X) + P(X)</p>
<p>编码器使用解码的像素值A,B,C来构成重建,比如说,P(X)=(2A+B+C)/4.这样的话,编码器和解码器使用同样的P(X),这样就可以避免错位的发生.</p>
<p>这种方法的压缩的效率依赖于对于P(X)预测的准确性.如果预测是准确的(P(X)与X的值是相近的),那么剩余帧的能量就非常小了.然而,并不是经常都能选择一个对复杂图像来说那么理想的预测器的.编码器就有必要指出对解码端选择预测器,这样就有一个预测和需要的多余位来表示信号和预测器的一个折衷考虑了.</p>
<p>3.4.2 变换编码<br>3.4.2.1 总述<br>图像中或视频CODEC中的变换阶段是把图像或运动补偿剩余值转到另一个域中(变换域中)。变换的选择依赖于以下几个分类<br>1. 在转换域中的数据是去相关性的(转换域中的数据的大多数能量都会聚集到很小的值中)<br>2. 转换应该是可逆的.<br>3. 转换的计算过程应该是易于处理的，</p>
<p>对图像和视频压缩以及很多的变换都建议使用以下两类方式：基于块的变换和基于图像的变换。基于块的变换的例子比如Karhunen-Loeve变换(KLT)，单值分解(SVD)和余弦变换（DCT）。每一种变换都是对于N*N的图像块或剩余样本来说的，这样图像就被用块的单元来处理。块变换有很低的内存需求而且很适于基于块的运动补偿剩余帧的压缩，但是受边界区域的影响比较严重。基于图像的变换对于整个图像或帧来进行操作（或一个图像的一大段进行操作）。最常用的图像变换是离散小波变换(DWT或直接说小波变换)。像DWT这样的变换对于表态图像的压缩来说已经被证明是很有效的了，但是它们对内存的消耗都比较大（因为整个图像或段都以单个单元来进行处理）而且不适用于基于块的运动补偿方法。DCT和DWT在MPEG4视频部分中都得到了应用（而且一个DCT的变种在H.264中被使用）。它们将在下面几节被讨论。</p>
<p>3.4.2.2<br>余弦变换在X中进行运算（X是一个N*N的采样块，通常是指在预测之后的图像采样或剩余帧值)来构造Y，一个N*M的系数块。DCT的操作(及它的反变换IDCT),可以用变换矩阵A来描述。一个N*N采样块的DCT变换由如下式子得到：Y=AXA(T),反DCT变换:Y=AXA(T),A(T)表示A的对称矩阵</p>
<p>X是一个采样的矩阵，Y是一个系统的矩阵，A是一个N*N的变换矩阵。A的元素是：</p>
<p>A(i,j) = C(i) * cos [(2j+1)*i*PI]/2N<br>C(i)=&nbsp;&nbsp; N^(-1/2)&nbsp;i=0<br>&nbsp;(2/N)^(-1/2)&nbsp;i&gt;0</p>
<p>一个二维DCT的输出是一组N*N的系数，它表示了图像在DCT域中的块数据，这些系数可以被想成是一组标准基的&#8221;权&#8220;值。任何图像块都可以由编合所有的N*N标准基来重组，通过这些基乘以相应的权因素值(系数）。</p>
<p>例2 图像块的DCT系数<br>一个选定的4*4的块，以及DCT系数。这种用DCT域表示块的方法的优点不是很明显的，因为并没有减少存储的空间，原来我们存16个像素值，我们现在成了需要存16个DCT系数。DCT的实用性当块被从系数的一个子集中构建出来的时候表现了出来：</p>
<p>把除去一些很大的值之外的所有的系数设为0，执行IDCT之后的结果如下图：在IDCT之前加入更多的系数可以形成渐近的更加准确的对原图的重建。这样，就可能从从一个子集的系数集中近似于原始的图像。删除系数中的小系数值（比如说通过量化）可以让图像数据在更少的系数表示位中表示出来，尽管这样做会影响图像的质量。</p>
<p><br>3.4.2.3 小波<br>流行的"小波变换"要(基于一系列系数与离散小波函数相等的滤波器来进行图像压缩的一种广泛使用的方法).一个应用于有N个采样结果的离散信号的离散小波变换的基本运行过程如下.一对滤波器被用来把信号分解为一个低频部分(L)和一个高频部分(H).每个波段由因子2被子采样化，这样这两个频率波都含有N/2个采样点。如果正确选择了滤波器的话，这个操作过程是可逆的。</p>
<p>这种方法可以被扩展应用到一个二维的信号上，比如说灰度图中。一个2D图的每一行都被经过低通和高通的滤波(Lx和Hx)，每个滤波器的输出被下采样用来制造中间图像L和H。L是原始图象的低通滤波，并通过在x方向进行下采样的结果.H是原图像的高通滤波并在x方向的下采样结果。接下来，这些新图的每一列都使用低和高通的滤波器(Ly和Hy)，并经过下采样来制造出四个子图象(LL,LH,HL,HH)。这四个子图象可以被组合为与原图的采样数相同的图象。LL是原图通过在水平和垂直方向经过低通，并用因素2进行子采样的结果。HL是通过在垂直方向经过高通滤波并包含了剩余垂直频率的结果。LH是通过在水平方向高通滤波并包含了剩余的水平频的结果，而HH是通过在水平和垂直两个方向进行高通滤波的结果。它们之间，四个图像包含了所有的原始图像的信息，但是LH,HL,HH的稀疏性使它们更加容易压缩。</p>
<p>在图像压缩程序中，二维的小波分解被继续应用到LL图中，来组成四个新的子图。得到的新的低通图象继续被得到子图象。很多高频的采样结果都是接近0的，它就可以通过把小值来移除来达到更优化的传输。在解码端，原图像通过上采样，滤波和加值被重建。</p>
<p>3.4.3 量化<br>量化器把一个在值域X的信号量化减为到值域Y中。使用更小的位来表示量化后的信号是可行的，因为转换后的值域比原来更小了。一个标量的量化器把输入的信号映射到一个量化的输出值域中，而一个向量的量化器把一组输入采样值映射到一组量化值中。</p>
<p>3.4.3.1 标量量化<br>标量量化的一个简单的例子就是把小数近似到最近的整数上，比如，从R到Z的映射。这个过程是有损的(不可逆的)因为它是无法从被近似后的结果来恢复出原来的小数的。</p>
<p>一个量化的更加通用的例子就是:<br>FQ = round (X/QP)<br>Y=FQ * QP</p>
<p>这里QP是量化的步长。量化输出级间隔单一的QP间断值。</p>
<p>在图像或视频的CODEC中，量化操作通常由两部分构成：编码端的前向量化FQ，和解码端的反量化(IQ).（事实上量化是不可逆的，这样用一个更准确的叫法的话：标量器和重标量器。在两个标量值之间的QP步长是一个重要的参数。如果步长很长的话，那么量化值的范围就很小，这样的话就可以在传输过程中被更有效地被表示(高压缩比地压缩).但是重量化值是原信号值的近似。如果步长很小的话，重量化值与原始信号更加匹配，但是量化值就落到一个更大的范围之内，就降低了压缩的效率。</p>
<p><br>量化可以被用来在像DCT或小波变换之后，除去了小系数之后降低图像数据的精确度。一个图像或视频的前向量化器设计为映射小系数值到0而保留一部分大系数值。前向量化器的输出通常是一个稀疏的量化系数的数组，大都为0。</p>
<p>3.4.3.2 向量量化<br>一个向量量化器把一系列的输入数据(比如一块图像采样)映射到一个单值(codeword)，而在解码端，每个单值（codeword)都映射到一个对于原始输入数据的近似。这组向量保存在编码和解码端，被用来当作一个编码表。在图像压缩中一个典型的向量量化程序如下：</p>
<p>1. 把原始图像分区(比如M &#215; N像素块)<br>2. 从编码表中选择一个与当前区域最相近的向量<br>3. 把选定的向量的序号传给解码器<br>4. 在解码端，用选定的向量来重建一个原始图像的拷贝。</p>
<p>量化是被用在空间域中(比如说用向量量化过的图像采样组)，但是它也可以被用在运动补偿或变换数据中.向量量化设计中的关键问题包含编码表的设计和在编码表中有效都查找最优化的向量的问题。</p>
<p>3.4.4 重排列和零编码<br>量化过的变换系数需要被仔细编码来进行存储和传输。在基于变换的图像或视频编码器上，量化器的输出是一个稀疏的数组，它包含了一些非0的系数和很多的0值系数。重排（把非0系数进行重组）和有效地表示0系数要在熵编码前进行操作。这些过程在DCT和小波变换中被提到了。</p>
<p>3.4.4.1 DCT<br>系数分布<br>一块图像或剩余采样的大的DCT系数通常都是在DC(0,0）系数&#8220;低频&#8221;部分。DCT系数的非0系数都聚在上顶部系数，而分布在水平和垂直方向是大约对称的。对于剩余区域来说，聚集在DC位置的系数是歪斜的，比如说，更多的非0系数在左手边出现。这是因为区域图片在垂直方向有很强的高频成分(因为在垂直方向的子采样)，这样因为垂直频率的原因得到了更大的DCT系数。</p>
<p>扫描<br>在量化之后，块的DCT系数被重排以把非0系数进行组合，以使对于剩余0值的表示方法更加有效。优化的重组方法(扫描方法)依赖于非0DCT系数的分布情况。对于经典的一帧来说，合适的扫描方式应该是gzag法，它是从左上角的DC系数开始的.从DC系数开始，每一个量化的系数被拷贝到一维数组里面。非0系数在重排数列的最前端被重组，而在之后是很长序列的0值。</p>
<p>gzag扫描扫描方式对于一个域块可能并不是理想的，因为系数是歪斜分布的，那么一个修改后的扫描顺序可会是有效的，比如说左边的系数在右边系数扫描之前被扫描。</p>
<p><br>运行级编码<br>重排过程的输出是一个通常在开始端包含一个或多个簇的非0系数，后面的是一串0值系数。这些0值被编码而实现更优化的表示方法，比如说，通过表示一串0里面的数量来缩减表示位数等等。</p>
<p>例如：<br>输入数组：16,0,0,-3,5,6,0,0,0,0,-7, . . .<br>输出值：&nbsp; (0,16),(2,-3),(0,5),(0,6),(4,-7). . .</p>
<p>DCT系数高频部分常被量化为0，这样一个重排的块通常以一串0结尾。一个特殊情况是需要指出一个块中的最后一个非0系数。在所谓的二维运行级编码被使用。每一个运行级对被用上面的方式进行编码，而一个单独的编码符号"last"，用来指出最后一个0值的位置。如果三维的运行级编码被使用的话，每个符号编码要编三个量化值：运行度，级数以及最后非0值。如上例就可以写为：<br>(0, 16, 0), (2,-3, 0), (0, 5, 0), (0, 6, 0), (4,-7, 1)</p>
<p>最后一个码中的1表示这是这个块中最后一个非0值。</p>
<p>3.4.4.2 小波<br>系数分布<br>很多在高子波中的系数（右下方的系数值）都是接近于0值的，可以被量化到0而不损失图像的质量。非0系数对应于图像的结构，比如说，在violin图中，弓就有很清晰的垂直结构。当低频中的系数是非0时，有很大的可能性在高频相应的系数也是0。这们设想一个非0的量化系数树，以在低频中根部开始。在第一层的一个单个LL的系数在其他的第一层有相应的系数值。第一层系数位置映射到四个相应的子段的同一位置的系数位置.</p>
<p>零树编码<br>在熵编码之前越好地编码非0小波系数就越好.达到这一点的一个有效的方法就是从最低层开始编码每个非0系数树.最低层的系数需要被编码,接着是它在高层的子系数,如此反复.编码过程在树达到0值系数的时候一直运行.之后的的全0值的层的子层值都为0.在解码端,重建过程从树根开始,非0系数被解码并重建,当遇到零树的时候,所有的剩余的子结点都被设为0.这嵌入零树(EZW)的小波系数编码方法.编码过程包含一种其他的可能性,0值系数可能跟着(a)一个零树(就像原来那样),或(b)一个非零的子系数阵.(b)不常发生,但是如果考虑到这些不常发生的情况的时候,重建图像的质量会被轻微地提高.</p>
</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/38450.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-04 14:41 <a href="http://www.cnitblog.com/zouzheng/articles/38450.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>S3C2410平台上支持NAND boot的u-boot移植和共享</title><link>http://www.cnitblog.com/zouzheng/articles/38385.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 02 Jan 2008 07:26:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/38385.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/38385.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/38385.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/38385.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/38385.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt">S3C2410平台上支持NAND boot的u-boot移植和共享<br>--luofuchong大侠</p>
小弟现有一个可用的nand flash启动的u-boot源代码愿意分享，具体下载地址如下： <br>　　<a href="http://www.hhcn.org/maindoc/nandboot-2410-luofuchong.rar">http://www.hhcn.org/maindoc/nandboot-2410-luofuchong.rar</a> 在此感谢华恒斑竹hn的帮助和提供空间^_^。<br>　　小弟不敢贪功，先注明一下这个源码的出处： 补丁来源于以下网站： <br>　　<a href="http://linux.insigma.com.cn/devbbs/dispbbs.asp?boardID=15&amp;ID=382&amp;page=1" target=_blank>http://linux.insigma.com.cn/devbbs/dispbbs.asp?boardID=15&amp;ID=382&amp;page=1</a><br>　　其实这个补丁早就下载了，但小弟不懂cvs的用法，怕麻烦一直没下载其对应版本的源码（在这里的显示版本虽然是1.1.4，但可是经过多人修改的，所以和官方的版本相差甚远）。 <br>　　后来我哥把它下载下来，于是就抽空把它编译了一下，串口居然有输出，兴致来了，花了几天搞了一下，现在基本可以用了。<br><br>这是cvs下载源码的方法： <br>cvs -d:pserver:anonymous@u-boot.cvs.sourceforge.net:/cvsroot/u-boot login <br>cvs -z3 -d:pserver:anonymous@u-boot.cvs.sourceforge.net:/cvsroot/u-boot co -D 20060523 -P u-boot <br>我经已经试过可以下载的了，不过最好晚上再下载成功率会高一点^_^ <br>登录的时候提示密码的时候直接按回车就行。<br><br>在些说一下这几天编译的经验： <br>1）打上补丁的过程中提示2、3个文件有些地方未能正常打补丁，但不影响编译。 <br>2）要用2.95.3的交叉编译器来编译，我试过用3.3.2的编译器来编译通过，但nand flash检测的那段代码没有运行，导致nand flash检测不出来，这可花了我不少的时间~_~ <br>3）附上的补丁是我修改过的（主要是一些提示信息和env地址的修改），打补丁的方法如下（假如你把补丁放到u-boot目录下）： <br><br>代码：<br>---------------------------------------------------------------------------------------------<br>patch -p1 &lt;u-boot-lfc.patch <br>---------------------------------------------------------------------------------------------<br>好了，下面介绍一下这个u-boot的用法： <br>U-Boot 1.1.4 (Oct 23 2006 - 22:04:37)<br><br>U-Boot code: 33F80000 -&gt; 33F9DC98 BSS: -&gt; 33FA2308 <br>RAM Configuration: <br>Bank #0: 30000000 64 MB <br>Nor Flash: 512 kB <br>Nand Flash: 64 MB <br><br>In: serial <br>Out: serial <br>Err: serial <br>Hit any key to stop autoboot: 0<br><br>sbc2410=&gt; printenv <br>bootargs=noinitrd root=/dev/mtdblock/3 console=ttySAC0 <br>bootdelay=3 <br>baudrate=115200 <br>ethaddr=08:00:3e:26:0a:5b <br>bootfile="zImage" <br>filesize=13469c <br>fileaddr=30008000 <br>netmask=255.255.255.0 <br>ipaddr=192.168.1.128 <br>serverip=192.168.1.15 <br>bootcmd=nand read 30008000 30000 1d0000;go 30008000 <br>stdin=serial <br>stdout=serial <br>stderr=serial <br>Environment size: 319/65532 bytes<br><br>sbc2410=&gt; nand <br>nand - NAND sub-system <br>info - show available NAND devices <br>nand device [dev] - show or set current device <br>nand read[.jffs2[s] addr off size <br>nand write[.jffs2] addr off size - read/write `size' bytes starting at offset `off' to/from memory address `addr' <br>nand erase [clean] [off size] - erase `size' bytes from offset `off' (entire device if not specified) <br>nand bad - show bad blocks <br>nand read.oob addr off size - read out-of-band data <br>nand write.oob addr off size - read out-of-band data<br><br>注：这是对nand flash操作的命令，我修改过源码好让它提示出来（默认好像没有提示）<br>按照提示应该都会它的用法了吧？不过在此还是举一个烧写内核的例子吧：<br>sbc2410=&gt; tftp <br>TFTP from server 192.168.1.15; our IP address is 192.168.1.128 <br>Filename 'zImage'. <br>Load address: 0x30008000 <br>Loading: ################################################################# <br>################################################################# <br>################################################################# <br>#################################################### <br>done <br>Bytes transferred = 1263260 (13469c hex) <br>注：可以看出，网络也是好的^_^ <br><br>sbc2410=&gt; nand erase 30000 1d0000 <br>NAND erase: device 0 offset 196608, size 1900544 ... OK<br><br>sbc2410=&gt; nand write 30008000 30000 1d0000 <br>NAND write: device 0 offset 196608, size 1900544 ... 1900544 bytes written: OK <br><br>OK，现在内核已经写入nand flash了，想开机引导它？<br><br>sbc2410=&gt; setenv bootcmd nand read 30008000 30000 1d0000\;go 30008000 <br>sbc2410=&gt; saveenv <br>Saving Environment to NAND... <br>Erasing Nand...Writing to Nand... done<br><br>这样就可以了。好了，现在按一下你的复位键试试： <br>U-Boot 1.1.4 (Oct 23 2006 - 21:28:24) <br><br>U-Boot code: 33F80000 -&gt; 33F9DC88 BSS: -&gt; 33FA22F8 <br>RAM Configuration: <br>Bank #0: 30000000 64 MB <br>Nor Flash: 512 kB <br>Nand Flash: 64 MB<br><br>In: serial <br>Out: serial <br>Err: serial <br>Hit any key to stop autoboot: 0<br><br>NAND read: device 0 offset 196608, size 1900544 ... 1900544 bytes read: OK <br>## Starting application at 0x30008000 ... <br>Uncompressing Linux................................................................................. done, booting the kernel. <br>Linux version 2.6.14 (root@luofuchong) (gcc version 3.4.1) #21 Fri Oct 20 17:20:39 CST 2006 <br>CPU: ARM920Tid(wb) [41129200] revision 0 (ARMv4T) <br>Machine: SMDK2410 <br>ATAG_INITRD is deprecated; please update your bootloader. <br>Memory policy: ECC disabled, Data cache writeback <br>CPU S3C2410A (id 0x32410002) <br>S3C2410: core 202.800 MHz, memory 101.400 MHz, peripheral 50.700 MHz <br>S3C2410 Clocks, (c) 2004 Simtec Electronics <br>CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on <br>USB Control, (c) 2006 sbc2410 <br>CPU0: D VIVT write-back cache <br>CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets <br>CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets <br>Built 1 zonelists <br>Kernel command line: noinitrd root=/dev/mtdblock/3 console=ttySAC0 <br>irq: clearing subpending status 00000002 <br>PID hash table entries: 512 (order: 9, 8192 bytes) <br>timer tcon=00500000, tcnt a509, tcfg 00000200,00000000, usec 00001e4c <br>Console: colour dummy device 80x30 <br>Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) <br>Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) <br>Memory: 64MB = 64MB total <br>Memory: 62208KB available (1924K code, 529K data, 108K init) <br>Mount-cache hash table entries: 512 <br>CPU: Testing write buffer coherency: ok <br>softlockup thread 0 started up. <br>NET: Registered protocol family 16 <br>S3C2410: Initialising architecture <br>SCSI subsystem initialized <br>usbcore: registered new driver usbfs <br>usbcore: registered new driver hub <br>S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics <br>DMA channel 0 at c4800000, irq 33 <br>DMA channel 1 at c4800040, irq 34 <br>DMA channel 2 at c4800080, irq 35 <br>DMA channel 3 at c48000c0, irq 36 <br>NetWinder Floating Point Emulator V0.97 (double precision) <br>devfs: 2004-01-31 Richard Gooch (rgooch@atnf.csiro.au) <br>devfs: devfs_debug: 0x0 <br>devfs: boot_options: 0x1 <br>yaffs Oct 18 2006 12:39:51 Installing. <br>Console: switching to colour frame buffer device 30x40 <br>fb0: s3c2410fb frame buffer device <br>fb1: Virtual frame buffer device, using 1024K of video memory <br>led driver initialized <br>s3c2410 buttons successfully loaded <br>s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2410 <br>s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2410 <br>s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2410 <br>io scheduler noop registered <br>io scheduler anticipatory registered <br>io scheduler deadline registered <br>io scheduler cfq registered <br>RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize <br>usbcore: registered new driver ub <br>Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410) <br>eth0: CS8900A rev E at 0xe0000300 irq=53, no eeprom , addr: 08: 0:3E:26:0A:5B <br>S3C24XX NAND Driver, (c) 2004 Simtec Electronics <br>s3c2410-nand: mapped registers at c4980000 <br>s3c2410-nand: timing: Tacls 10ns, Twrph0 30ns, Twrph1 10ns <br>NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit) <br>Scanning device for bad blocks <br>Bad eraseblock 1884 at 0x01d70000 <br>Creating 4 MTD partitions on "NAND 64MiB 3,3V 8-bit": <br>0x00000000-0x00020000 : "vivi" <br>0x00020000-0x00030000 : "param" <br>0x00030000-0x00200000 : "kernel" <br>0x00200000-0x04000000 : "root" <br>usbmon: debugfs is not available <br>s3c2410-ohci s3c2410-ohci: S3C24XX OHCI <br>s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1 <br>s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000 <br>hub 1-0:1.0: USB hub found <br>hub 1-0:1.0: 2 ports detected <br>Initializing USB Mass Storage driver... <br>usbcore: registered new driver usb-storage <br>USB Mass Storage support registered. <br>usbcore: registered new driver usbmouse <br>drivers/usb/input/usbmouse.c: v1.6:USB HID Boot Protocol mouse driver <br>mice: PS/2 mouse device common for all mice <br>s3c2410 TouchScreen successfully loaded <br>UDA1341 audio driver initialized <br>NET: Registered protocol family 2 <br>IP route cache hash table entries: 1024 (order: 0, 4096 bytes) <br>TCP established hash table entries: 4096 (order: 2, 16384 bytes) <br>TCP bind hash table entries: 4096 (order: 2, 16384 bytes) <br>TCP: Hash tables configured (established 4096 bind 4096) <br>TCP reno registered <br>TCP bic registered <br>NET: Registered protocol family 1 <br>Root-NFS: No NFS server available, giving up. <br>VFS: Unable to mount root fs via NFS, trying floppy. <br>yaffs: dev is 32505859 name is "mtdblock3" <br>yaffs: Attempting MTD mount on 31.3, "mtdblock3" <br>block 1757 is bad <br>VFS: Mounted root (yaffs filesystem). <br>Mounted devfs on /dev <br>Freeing init memory: 108K<br><br>init started: BusyBox v1.1.3 (2006.09.20-14:52+0000) multi-call binary <br>Starting pid 695, console /dev/tts/0: '/etc/init.d/rcS' <br>Please press Enter to activate this console. <br>注：这就是在我的板上的启动信息了^_^ <br><br>遗留的问题： <br>1：这个u-boot只提供jffs2文件系统的烧写功能（具体未用过，因为我只搞过yaffs文件系统~_~），没有对yaffs文件系统的烧写支持，我想这部分就留给各位去做吧^_^ <br>2：好像用同样的方法来启动2.4内核解压后内核不能启动，但2.6内核就可以，具体不知道原因，知道的朋友请告知并把修改好的补丁共享，发挥一下共享精神，谢谢！<br><br>原文地址 <a href="http://www.hhcn.org/s3c2410_u-boot.htm" target=_blank>http://www.hhcn.org/s3c2410_u-boot.htm</a> 
<img src ="http://www.cnitblog.com/zouzheng/aggbug/38385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-02 15:26 <a href="http://www.cnitblog.com/zouzheng/articles/38385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QTE与触摸屏(转http://onebyte.21ic.org/）</title><link>http://www.cnitblog.com/zouzheng/articles/25337.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 11:05:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25337.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25337.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25337.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25337.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25337.html</trackback:ping><description><![CDATA[QTE触摸屏加载有两种方法：
<p>（<span style="font-family: AR PL ZenKai Uni,serif;">1</span>）<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>本身触摸屏加载；</p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">	</span>主要注意三个内容：</p>
<ol type="i">
    <li>
    <p>编译<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>时，在其自定义配置文件（位于<span style="font-family: AR PL ZenKai Uni,serif;">QTDIR/src/tools/qconfig-XXX.h</span>，其<span style="font-family: AR PL ZenKai Uni,serif;">XXX</span>为自定义的名字，该特征字可以在<span style="font-family: AR PL ZenKai Uni,serif;">configure</span>时用<span style="font-family: AR PL ZenKai Uni,serif;">-qconfig
    XXX</span>指定）所应加的内容：</p>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">#define QT_QWS_IPAQ</span></p>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">#define QT_QWS_IPAQ_RAW</span></p>
    <p>去掉鼠标自动识别功能 ：<span style="font-family: AR PL ZenKai Uni,serif;">#define
    QT_NO_QWS_MOUSE_AUTO</span></p>
    <p>去掉不用的鼠标驱动，（在实际中，这一步可以不要，但对于缩减<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>的大小很有帮助），方法主要是在<span style="font-family: AR PL ZenKai Uni,serif;">configure</span>时设置配置参数为<span style="font-family: AR PL ZenKai Uni,serif;">-no-mouse-pc
    -no-mouse-bus -no-mouse-yopy -no-mouse-vr41xx </span>。</p>
    <p>以该编译好的<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>库为基础，编译应用程序；</p>
    </li>
    <li>
    <p>
    在目标机上，要将触摸屏驱动链接好。在<span style="font-family: AR PL ZenKai Uni,serif;">QTE3.0</span>的版本中，如果定义了上述的两个<span style="font-family: AR PL ZenKai Uni,serif;">IPAQ</span>相关的宏，其默认打开设备文件<span style="font-family: AR PL ZenKai Uni,serif;">/dev/h3600_tsraw</span>。如果只定义<span style="font-family: AR PL ZenKai Uni,serif;">QT_QWS_IPAQ</span>，则打开设备文件为<span style="font-family: AR PL ZenKai Uni,serif;">/dev/h3600_ts</span>。</p>
    </li>
    <li>
    <p> 在目标机上，设定环境变量：<span style="font-family: AR PL ZenKai Uni,serif;">QWS_MOUSE_PROTO=linuxtp:/dev/h3600_tsraw</span>。</p>
    </li>
</ol>
<p>（<span style="font-family: AR PL ZenKai Uni,serif;">2</span>）使用<span style="font-family: AR PL ZenKai Uni,serif;">tslib</span>方法；</p>
<ul>
    <li>
    <p>编译<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB1.3</span>：</p>
    <ul>
        <li>
        <p>进行如下操作</p>
        </li>
    </ul>
    </li>
</ul>
<ul>
    <ul>
        <ul>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">#./autogen.sh
            #</span>生成<span style="font-family: AR PL ZenKai Uni,serif;">configure</span>文件</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">CC=
            $ARM_TOOL_PATH/arm-linux-gcc</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">CXX=
            $ARM_TOOL_PATH/arm-linux-g++ </span>
            </p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">./configure	--host=arm-linux
            --target=arm-linux --disable-inputapi --prefix=$PWD/build </span>
            </p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">make</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">make install</span></p>
            </li>
        </ul>
        <li>
        <p>将<span style="font-family: AR PL ZenKai Uni,serif;">build</span>的整个目录拷贝到目标机，然后在目标机做如下设定，修改<span style="font-family: AR PL ZenKai Uni,serif;">/etc/profile</span>文件，在其中加入以下环境变量的设置</p>
        <ul>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            V_ROOT=/xxx/xxx/build  #</span>目标机上<span style="font-family: AR PL ZenKai Uni,serif;">build</span>所在的路径</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_TSEVENTTYPE=H3600
            #</span>设定类型为<span style="font-family: AR PL ZenKai Uni,serif;">H3600</span>，其主要是对着<span style="font-family: AR PL ZenKai Uni,serif;">tslib</span>中的设备结构体定义，该定义要与触摸屏驱动中的一致</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CONSOLEDEVICE=none
            #</span>设定控制台设备为<span style="font-family: AR PL ZenKai Uni,serif;">none</span>，否则默认为<span style="font-family: AR PL ZenKai Uni,serif;">/dev/tty</span>。在<span style="font-family: AR PL ZenKai Uni,serif;">tslib1.3</span>源码中，只要不是<span style="font-family: AR PL ZenKai Uni,serif;">none</span>，它就会打开<span style="font-family: AR PL ZenKai Uni,serif;">$(TSLIB_CONSOLEDEVICE)1</span>，默认的为<span style="font-family: AR PL ZenKai Uni,serif;">/dev/tty1</span>的设备，然后从中读取<span style="font-family: AR PL ZenKai Uni,serif;">console</span>的可用设置。这样可以避免出现&#8220;<span style="font-family: AR PL ZenKai Uni,serif;">open
            consoledevice: No such file or directory KDSETMODE: Bad file
            descriptor&#8221;</span>的错误</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_FBDEVICE=/dev/fb0  #</span>指定帧缓冲设备</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_TSDEVICE=/dev/touchscreen/0raw  #</span>指定触摸屏设备节点文件</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CALIBFILE=$V_ROOT/etc/pointercal  #</span>指定触摸屏校准文件<span style="font-family: AR PL ZenKai Uni,serif;">pintercal</span>的存放位置</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CONFFILE=$V_ROOT/etc/ts.conf  #</span>指定<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB</span>配置文件的位置</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_PLUGINDIR=$V_ROOT/share/ts/plugins  #</span>指定触摸屏插件所在路径</p>
            </li>
        </ul>
        </li>
    </ul>
    <li>
    <p>编译<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB1.4</span>（<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB07072006</span>）</p>
    <ul>
        <li>
        <p>进行如下操作：</p>
        <ul>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">echo
            "ac_cv_func_malloc_0_nonnull=yes" &gt;
            tslib/arm-linux.autogen   #</span>为了防止出现<span style="font-family: AR PL ZenKai Uni,serif;">undefined
            reference to `rpl_malloc' </span>错误</p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">CONFIG_SITE=arm-linux.autogen
            ./configure CC=arm-linux-gcc CXX=arm-linux-g++
            --host=arm-s3c2410-linux-gnu --target=arm-s3c2410-linux-gnu
            --disable-input --disable-arctic2 --disable-mk712 --disable-collie
            --disable-corgi --disable-ucb1x00 --disable-linear-h2200
            --with-gnu-ld --prefix=$PWD/build</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">make</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">make install</span></p>
            </li>
        </ul>
        </li>
        <li>
        <p>将<span style="font-family: AR PL ZenKai Uni,serif;">build</span>的整个目录拷贝到目标机，然后在目标机做如下设定，修改<span style="font-family: AR PL ZenKai Uni,serif;">/etc/profile</span>文件，在其中加入以下环境变量的设置。其解释类上，不同于<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB1.3</span>的是，<span style="font-family: AR PL ZenKai Uni,serif;">TLIB1.4</span>通过配置文件<span style="font-family: AR PL ZenKai Uni,serif;">ts.conf</span>中<span style="font-family: AR PL ZenKai Uni,serif;">module_raw
        h3600</span>语句来加载对应的设备结构体定义。已废除了<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB_TSEVENTTYPE</span>宏。</p>
        <ul>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            V_ROOT=/xxx/xxx/build  </span>
            </p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_TSDEVICE=/dev/touchscreen/0raw</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CALIBFILE=/etc/pointercal</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CONFFILE=$V_ROOT/etc/ts.conf</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_PLUGINDIR=$V_ROOT/lib/ts</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_CONSOLEDEVICE=none</span></p>
            </li>
            <li>
            <p><span style="font-family: AR PL ZenKai Uni,serif;">export
            TSLIB_FBDEVICE=/dev/fb0</span></p>
            </li>
        </ul>
        </li>
        <li>
        <p>一个值得注意的问题是：<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB1.4</span>在<span style="font-family: AR PL ZenKai Uni,serif;">ts_config</span>函数中通过<span style="font-family: AR PL ZenKai Uni,serif;">while((p=fgets(buf,BUFF_SIZE,f))!=NULL)</span>来加载<span style="font-family: AR PL ZenKai Uni,serif;">ts.conf</span>中的语句，而<span style="font-family: AR PL ZenKai Uni,serif;">buf</span>只有<span style="font-family: AR PL ZenKai Uni,serif;">512byte</span>。所以对于<span style="font-family: AR PL ZenKai Uni,serif;">ts.conf</span>没有用的定义语句，请将其删除，以防止误发生段错误。</p>
        </li>
    </ul>
    </li>
    <li>
    <p>以<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB</span>库为基础，编译<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>。</p>
    <ul>
        <li>
        <p><span style="font-family: AR PL ZenKai Uni,serif;">QTE2.3.10</span>直接支持<span style="font-family: AR PL ZenKai Uni,serif;">-tslib</span>选项开关的应用。不用修改任何文件。可以通过网上所说的拷贝<span style="font-family: AR PL ZenKai Uni,serif;">tslib.h</span>头文件及库文件到<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>相关目录来进行编译（对于<span style="font-family: AR PL ZenKai Uni,serif;">tslib1.4</span>，还要拷贝<span style="font-family: AR PL ZenKai Uni,serif;">tslib_private.h</span>）。也可以在<span style="font-family: AR PL ZenKai Uni,serif;">./configure</span>进指定<span style="font-family: AR PL ZenKai Uni,serif;">-L&lt;path
        to tslib library&gt; -I&lt;path to tslib header&gt;</span>来防止出现找不到相应库文件
        的错误。（对于<span style="font-family: AR PL ZenKai Uni,serif;">tslib1.4</span>，应该还加上<span style="font-family: AR PL ZenKai Uni,serif;">-lts</span>选项）。</p>
        </li>
        <li>
        <p>对于其它版本的<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>，可以通过在<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>文件夹路径下执行<span style="font-family: AR PL ZenKai Uni,serif;">grep
        -lir 'tslib'
        *</span>来查看是否有对<span style="font-family: AR PL ZenKai Uni,serif;">tslib</span>的支持，如果没有则需要对源码进行相应的修改。（<span style="font-family: AR PL ZenKai Uni,serif;">onebyte</span>注：虽然网上说有补丁可下，但俺一直没找到相关的<span style="font-family: AR PL ZenKai Uni,serif;">patch</span>，但看看<span style="font-family: AR PL ZenKai Uni,serif;">QTE2.3.10</span>的源码后再做相应的修改还算比较方便：）</p>
        </li>
    </ul>
    </li>
</ul>
<p><br><br>
</p>
<p><span style="font-weight: bold;">［</span><span style="font-weight: bold; font-family: AR PL ZenKai Uni,serif;">onebyte</span><span style="font-weight: bold;">后记］</span>：</p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">	</span>由于是一个人在做基于<span style="font-family: AR PL ZenKai Uni,serif;">embedded
linux</span>的软件开发，没有他人直接的交流，所以摸索<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>触摸屏支持的过程是相当辛苦的！网上关于这一块的说法不一，使得自己对<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>触摸屏支持的方法很长时间辨不清方向。但在这里还是要非常感谢网上的许多朋友，正是通过大家的讨论才将这块内容逐渐理通。</p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">	</span>在这里特将这些方法记载下来，送给同我一样经历过困惑的朋友们：<span style="font-family: AR PL ZenKai Uni,serif;">P</span></p>
<p>［参考网站］</p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">	</span>网上各论坛中关于<span style="font-family: AR PL ZenKai Uni,serif;">TSLIB</span>内容的帖子不少，这里只记一些主要的论坛网站，有兴趣的可以到这上面去看看：</p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">[1]  </span><a  href="http://kiss.bitunion.org/forumdisplay.php?fid=113&amp;sid=WcnNGjWX">北京理工大学嵌入式<span style="font-family: AR PL ZenKai Uni,serif;">linux
BBS</span></a></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">[2] </span><a  href="http://www.hhcn.com/cgi-bin/leoboard.cgi">华恒嵌入论坛</a></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">[3] <a  href="http://www.qtcn.org/bbs/">Qt</a></span><a  href="http://www.qtcn.org/bbs/">中文论坛</a></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">[4] <a  href="http://www.qtforum.org/index.html">QT
Forum</a></span></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;">[5] <a  href="http://www.linuxforum.net/">China
Linux Forum</a></span></p>
<p><br><span style="font-family: AR PL ZenKai Uni,serif;"></span></p>
<p><a  href="http://www.linuxforum.net/">onebyte注：前一阵子简略地分析了一下QT中触摸屏相关文件，现在把它共享出来。希望能对志同道合的朋友有所帮助：P<br><br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>
</a></p>
<p style="margin-bottom: 0cm;">文件位于<span style="font-family: AR PL ZenKai Uni,serif;">QTDIR/src/embedded/</span>文件夹中。</p>
<p>（<span style="font-family: AR PL ZenKai Uni,serif;">1</span>）类结构示意图，如下图所示。</p>
<p><img  src="http://blog.21ic.com/uploadfile-/2006-12/1228521281.jpg" alt="类结构分析图" align="left" border="0"><br><span style="font-family: AR PL ZenKai Uni,serif;">	</span></p>
<p>（<span style="font-family: AR PL ZenKai Uni,serif;">2</span>）与触摸屏相关主要函数进行解释如下：</p>
<ul>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QWSLinuxTPMouseHandlerPrivate</span>其构造函数</p>
    <p>在其中打开设备文件<span style="font-family: AR PL ZenKai Uni,serif;">/dev/h3600_tsraw</span>或<span style="font-family: AR PL ZenKai Uni,serif;">h3600_ts</span>或<span style="font-family: AR PL ZenKai Uni,serif;">ts</span>（视宏定义），并将其<span style="font-family: AR PL ZenKai Uni,serif;">readMouseData()</span>槽同<span style="font-family: AR PL ZenKai Uni,serif;">QSocketNotifier</span>的<span style="font-family: AR PL ZenKai Uni,serif;">activated</span>信号连接起来。</p>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QsocketNotifier</span>由所打开的设备文件号初始化。这样就可以将驱动中的事件同上层应用程序中的鼠标动作联系起来。</p>
    </li>
</ul>
<ul>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QWSLinuxTPMouseHandlerPrivate::readMouseData()</span></p>
    <p>该函数读取设备文件<span style="font-family: AR PL ZenKai Uni,serif;">/dev/h3600_tsraw</span>或<span style="font-family: AR PL ZenKai Uni,serif;">h3600_ts</span>，视宏定义。并在其内部将触摸屏设备坐标（也就是电压值）通过其父类：<span style="font-family: AR PL ZenKai Uni,serif;">QWSCalibrateMouseHandler::transform()</span>函数转换为屏幕坐标。并取一定点的平均值。</p>
    </li>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QWSCalibrateMouseHandler::transform()</span></p>
    <p>通过<span style="font-family: AR PL ZenKai Uni,serif;">a, b, c, d, e,
    f</span>转换参数将设备坐标转换为屏幕坐标。</p>
    </li>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QWSCalibrateMouseHandler::Calibration()</span></p>
    <p>求取坐标转换系数及偏差（<span style="font-family: AR PL ZenKai Uni,serif;">a,
    b, c, d, e,
    f</span>，实际用的是二点校正法：左上及右下点。）。并调用<span style="font-family: AR PL ZenKai Uni,serif;">writeCalibration</span>将参数值输出到<span style="font-family: AR PL ZenKai Uni,serif;">/etc/pointcal</span>文件中。</p>
    </li>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">QWSCalibrateMouseHandler::readCalibration()</span></p>
    <p>从<span style="font-family: AR PL ZenKai Uni,serif;">/etc/pointcal</span>文件读取坐标转换系统及偏差。</p>
    </li>
</ul>
<p>（<span style="font-family: AR PL ZenKai Uni,serif;">3</span>）上述类都是通过<span style="font-family: AR PL ZenKai Uni,serif;">qmousedriverfatory</span>进行初始创建的。</p>
<br>
<p><br></p>
<p><span style="color: #ff0000;">［编译环境注：］</span><br>
</p>
<p style="font-weight: normal; color: #ff0000;">宿主机Linux操作系统为Fedro Core 5<br></p>
<p style="font-weight: normal; color: #ff0000;">交叉编译工具链版本：<span style="font-family: AR PL ZenKai Uni,serif;">arm-linux-g++
2.95.3</span>，</p>
<p style="font-weight: normal; color: #ff0000;">宿主机<span style="font-family: AR PL ZenKai Uni,serif;">编译工具链GCC
</span>版本为<span style="font-family: AR PL ZenKai Uni,serif;">4.1.0</span></p>
<p style="font-weight: normal; color: #ff0000;"><span style="font-family: AR PL ZenKai Uni,serif;">QT-X11版本为2.3.2</span></p>
<p style="font-weight: normal; color: #ff0000;"><span style="font-family: AR PL ZenKai Uni,serif;">QT/E版本为2.3.7</span></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;"><span style="font-weight: normal; color: #ff0000;">QTOPIA版本为1.7.0</span></span></p>
<p><span style="font-family: AR PL ZenKai Uni,serif;"><span style="font-weight: normal; color: #ff0000;"></span><br></span></p>
<h3 class="cjk">（<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">1</span>）编译<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">QT-X11</span></h3>
<ul>
    <li>
    <p>解压<span style="font-family: AR PL ZenKai Uni,serif;">qt-x11-2.3.2.tar.gz</span>，并更改解压文件夹名为<span style="font-family: AR PL ZenKai Uni,serif;">qt-x11</span></p>
    </li>
    <li>
    <p><span style="font-family: AR PL ZenKai Uni,serif;">cd qt-x11</span></p>
    </li>
    <li>
    <p>更改<span style="font-family: AR PL ZenKai Uni,serif;">qt-x11</span>文件夹中<span style="font-family: AR PL ZenKai Uni,serif;">src/tools/qvaluestack.h</span>第<span style="font-family: AR PL ZenKai Uni,serif;">57</span>行源代码<span style="font-family: AR PL ZenKai Uni,serif;">remove(
    this-&gt;fromLast() );</span>为<span style="font-family: AR PL ZenKai Uni,serif;">this-&gt;remove(
    this-&gt;fromLast() );</span>否则，会出现<span style="font-family: AR PL ZenKai Uni,serif;">[<span style="color: #000000;">xml/qxml.o]</span></span><span style="font-size: 10pt; color: #000000;">错误</span></p>
    </li>
    <li>
    <p>建立<span style="font-family: AR PL ZenKai Uni,serif;">set-env</span>文件</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export QTDIR=$PWD</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export PATH=$QTDIR/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    MANPATH=$QTDIR/doc/man:$MANPATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">echo yes | ./configure
    -no-xft -no-opengl -no-sm</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">make</span></p>
    </li>
    <li>
    <p>在该文件夹下，执<span style="font-family: AR PL ZenKai Uni,serif;">"<span style="color: #800000;">source
    set-env</span>"</span>命令（<span style="font-family: AR PL ZenKai Uni,serif;">.</span>与<span style="font-family: AR PL ZenKai Uni,serif;">set-env</span>之间有空格）</p>
    </li>
</ul>
<br>
<h3 class="cjk">（<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">2</span>）编译目标机版本的<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">qte-2.3.7</span></h3>
<ul>
    <li>
    <p>为了编译<span style="font-family: AR PL ZenKai Uni,serif;">QTOPIA</span>，将<span style="font-family: AR PL ZenKai Uni,serif;">QTOPIA</span>文件夹下<span style="font-family: AR PL ZenKai Uni,serif;">./src/qt/qconfig-qpe.h</span>拷贝到<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>文件夹下
    <span style="font-family: AR PL ZenKai Uni,serif;">src/tools/</span></p>
    </li>
    <li>
    <p>然后建立<span style="font-family: AR PL ZenKai Uni,serif;">set-env</span>文件</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#!/bin/bash</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#</span>编译<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>脚本</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#</span>此处<span style="font-family: AR PL ZenKai Uni,serif;">$PWD</span>指的是<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>所在的文件夹</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export QTDIR=$PWD</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export QTEDIR=$QTDIR</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export PATH=$QTDIR/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    PATH=/usr/local/arm/2.95.3/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">echo yes | ./configure
    -platform linux-x86-g++ -xplatform linux-arm-g++ -qconfig qpe
    -depths 16,24,32</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">make</span></p>
    </li>
    <li>
    <p>执行<span style="font-family: AR PL ZenKai Uni,serif;">"<span style="color: #800000;">source
    set-env</span>"</span></p>
    </li>
    <li>
    <p>注意，在此处<span style="font-family: AR PL ZenKai Uni,serif;">configure</span>时，一定要写明<span style="font-family: AR PL ZenKai Uni,serif;">platform</span>及<span style="font-family: AR PL ZenKai Uni,serif;">xplatform</span>，且<span style="font-family: AR PL ZenKai Uni,serif;">platform</span>要设为<span style="font-family: AR PL ZenKai Uni,serif;">linux-x86-g++</span>，否则当使用<span style="font-family: AR PL ZenKai Uni,serif;">linux-generic-g++</span>时，会找当前系统默认的<span style="font-family: AR PL ZenKai Uni,serif;">g++</span>，由于前面在<span style="font-family: AR PL ZenKai Uni,serif;">PATH</span>处设定新的<span style="font-family: AR PL ZenKai Uni,serif;">g++</span>路径，所以会出现错误。</p>
    </li>
</ul>
<ul>
    <li>
    <p>如果<span style="font-weight: bold;">需要移植到目标机上</span>，则将<span style="font-family: AR PL ZenKai Uni,serif;">$QTEDIR/lib/libqte.so.x.y.z</span>拷贝至目标机上<span style="font-family: AR PL ZenKai Uni,serif;">lib</span>文件夹下，并命名为<span style="font-family: AR PL ZenKai Uni,serif;">/lib/libqte.so.x</span>（根据前面的<span style="font-family: AR PL ZenKai Uni,serif;">libqte</span>编号而定）<span style="font-family: AR PL ZenKai Uni,serif;">;</span></p>
    <p>将<span style="font-family: AR PL ZenKai Uni,serif;">$QTEDIR/lib/fonts/fontdir</span>拷贝到目标机上<span style="font-family: AR PL ZenKai Uni,serif;">qt/e</span>的安装路径上（通过在目标机上设置<span style="font-family: AR PL ZenKai Uni,serif;">$QTDIR</span>来实现），如<span style="font-family: AR PL ZenKai Uni,serif;">/usr/local/qt-embedded/lib/fonts/fontdir</span></p>
    </li>
</ul>
<br>
<h3 class="cjk">（<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">3</span>）编译目标机版本的<span style="font-family: AR PL ShanHeiSun Uni,sans-serif;">qtopia-1.7.0</span></h3>
<ul>
    <li>
    <p>由于在编译<span style="font-family: AR PL ZenKai Uni,serif;">qtopia</span>时，需要用到<span style="font-family: AR PL ZenKai Uni,serif;">QTE</span>的<span style="font-family: AR PL ZenKai Uni,serif;">uic</span>命令来解释有关的资源，所以此处，需要将前面编译的<span style="font-family: AR PL ZenKai Uni,serif;">qt-x11</span>中<span style="font-family: AR PL ZenKai Uni,serif;">bin</span>目录下的<span style="font-family: AR PL ZenKai Uni,serif;">uic</span>命令拷贝至<span style="font-family: AR PL ZenKai Uni,serif;">QTEDIR/bin</span>下。</p>
    </li>
    <li>
    <p>建立<span style="font-family: AR PL ZenKai Uni,serif;">set-env</span>文件：</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#!/bin/bash</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#</span>安装<span style="font-family: AR PL ZenKai Uni,serif;">QTOPIA</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export QPEDIR=$PWD</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#</span>此处的<span style="font-family: AR PL ZenKai Uni,serif;">QTDIR</span>是前面编译的<span style="font-family: AR PL ZenKai Uni,serif;">QT/E</span>的路径</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    QTDIR=/friendly-arm/arm-qtopia/qt</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export QTEDIR=$QTDIR</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#</span>本机工具链的位置，根据个人情况设置</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    PATH=/usr/local/arm/2.95.3/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export PATH=$QTDIR/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    PATH=$QPEDIR/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export
    LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">cd ./src</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">echo yes | ./configure
    -platform linux-arm-g++</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">make</span></p>
    </li>
    <li>
    <p>执行<span style="font-family: AR PL ZenKai Uni,serif;">"<span style="color: #800000;">source
    set-env</span>"</span></p>
    </li>
    <li>
    <p>如果<span style="font-weight: bold;">需要移植到目标机</span>上，将<span style="font-family: AR PL ZenKai Uni,serif;">QPEDIR</span>目录下的<span style="font-family: AR PL ZenKai Uni,serif;">bin,
    apps, etc, plugins, services, sounds, i18n, lib, pics,
    help</span>文件夹拷贝到目标机上的<span style="font-family: AR PL ZenKai Uni,serif;">QPEDIR</span>处（通过设置<span style="font-family: AR PL ZenKai Uni,serif;">QPEDIR</span>来实现）</p>
    </li>
    <li>
    <p>在目标机的<span style="font-family: AR PL ZenKai Uni,serif;">bin</span>目录下，建立<span style="font-family: AR PL ZenKai Uni,serif;">qtopia</span>脚本文件（如下）</p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">#!/bin/sh</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set HOME=/root</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set QTDIR=/opt/qt</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set
    QPEDIR=/opt/qtopia</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set
    QWS_KEYBOARD="USB:/dev/input/event1"</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set
    QWS_MOUSE_PROTO="USB:/dev/input/mouse0"</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set
    PATH=$QPEDIR/bin:$PATH</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">export set
    LD_LIBRARY_PATH=$QTDIR/lib:$QPEDIR/lib</span></p>
    <p style="color: #800000;"><span style="font-family: AR PL ZenKai Uni,serif;">$QPEDIR/bin/qpe &gt;
    /dev/null 2&gt;/dev/null</span></p>
    </li>
    <li>
    <p>在目标机启动后，就可以使用<span style="font-family: AR PL ZenKai Uni,serif;">qtopia</span>直接启动</p>
    </li>
</ul><img src ="http://www.cnitblog.com/zouzheng/aggbug/25337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 19:05 <a href="http://www.cnitblog.com/zouzheng/articles/25337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gcc与gdb使用学习笔记</title><link>http://www.cnitblog.com/zouzheng/articles/25336.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 11:00:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25336.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25336.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25336.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25336.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25336.html</trackback:ping><description><![CDATA[今天学习了一下gcc与gdb的具体使用方法。并亲自用gdb调试了一下程序。gdb还是用不熟，只能以后慢慢加深体会了。（gdb刚开始用还真是不方便，也不知道如果做嵌入开发的话，有没有其它比较好的工具？如果有朋友知道，推荐一二，在此多谢啦！！）
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">1</span><span style="font-family: simsun;" lang="ZH-CN">、</span><span style="font-family: verdana;" lang="EN-US">GNU C</span><span style="font-family: simsun;" lang="ZH-CN">编译器（即</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">）</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">gcc -v </span><span style="font-family: simsun;" lang="ZH-CN">：</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">版本</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-c</span><span style="font-family: simsun;" lang="ZH-CN">：仅把源代友编译为目标代码；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-s</span><span style="font-family: simsun;" lang="ZH-CN">：</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">在为</span><span style="font-family: verdana;" lang="EN-US">C</span><span style="font-family: simsun;" lang="ZH-CN">代码产生了汇编语言文件后，就停止编译。</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">产生的汇编语言文件的缺省扩展名为</span><span style="font-family: verdana;" lang="EN-US">.s</span><span style="font-family: simsun;" lang="ZH-CN">。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-E</span><span style="font-family: simsun;" lang="ZH-CN">：指示编译器仅对输入文件进行预处理。此时，预处理器的输出被送到标准输出（如显示器）而不是储存在文件里。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-O</span><span style="font-family: simsun;" lang="ZH-CN">：</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">对源码进行基本优化。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-O2</span><span style="font-family: simsun;" lang="ZH-CN">：</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">产生尽可能小和尽可能快的代码</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-g</span><span style="font-family: simsun;" lang="ZH-CN">选项告诉</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">产生能被</span><span style="font-family: verdana;" lang="EN-US">GNU</span><span style="font-family: simsun;" lang="ZH-CN">调试器（如</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">）使用的调试信息，以便调试用户的程序。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">-pg</span><span style="font-family: simsun;" lang="ZH-CN">选项告诉</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">在用户的程序里加入额外的代码，执行时，产生</span><span style="font-family: verdana;" lang="EN-US">gporf</span><span style="font-family: simsun;" lang="ZH-CN">用的剖析信息以显示程序的耗时情况。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">gcc info page</span><span style="font-family: simsun;" lang="ZH-CN">：详细</span><span style="font-family: verdana;" lang="EN-US">GCC</span><span style="font-family: simsun;" lang="ZH-CN">编译器参数的说明，在</span><span style="font-family: verdana;" lang="EN-US">Emacs</span><span style="font-family: simsun;" lang="ZH-CN">内，按下</span><span style="font-family: verdana;" lang="EN-US">C</span><span style="font-family: simsun;" lang="ZH-CN">－</span><span style="font-family: verdana;" lang="EN-US">hi</span><span style="font-family: simsun;" lang="ZH-CN">，然后选&#8220;</span><span style="font-family: verdana;" lang="EN-US">gcc</span><span style="font-family: simsun;" lang="ZH-CN">&#8221;的选项。</span></p>
<p style="margin: 0in; font-size: 10pt; font-family: SimSun;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">2</span><span style="font-family: simsun;" lang="ZH-CN">、使用</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">：</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">gdb filename</span><span style="font-family: simsun;" lang="ZH-CN">：能直接指定想要调试的程序。也可用</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">去检查一个因程序异常终止而产生的</span><span style="font-family: verdana;" lang="EN-US">core</span><span style="font-family: simsun;" lang="ZH-CN">文件，或者与一个正在运行的程序相连。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: simsun;" lang="ZH-CN">（</span><span style="font-family: verdana;" lang="EN-US">1</span><span style="font-family: simsun;" lang="ZH-CN">）为使</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">正常工作，必须使程序在编译时包含调试信息。调试信息包含程序里的每个变量的类型、在可执行文件里的地址映射以及源代码的行号。</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: simsun;" lang="ZH-CN">（</span><span style="font-family: verdana;" lang="EN-US">2</span><span style="font-family: simsun;" lang="ZH-CN">）</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">命令一览：</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">file</span><span style="font-family: simsun;" lang="ZH-CN">：装入想要调试的可执行文件</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">kill</span><span style="font-family: simsun;" lang="ZH-CN">：终止正在调试的程序；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">list</span><span style="font-family: simsun;" lang="ZH-CN">：列出产生执行文件的源代码的一部分</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">next</span><span style="font-family: simsun;" lang="ZH-CN">：执行一行源代码但不进入函数内部；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">step</span><span style="font-family: simsun;" lang="ZH-CN">：执行一行源代码而且进入函数内部；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">run</span><span style="font-family: simsun;" lang="ZH-CN">：执行当前被调试的程序；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">quit</span><span style="font-family: simsun;" lang="ZH-CN">：终止</span><span style="font-family: verdana;" lang="EN-US">gdb</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">watch</span><span style="font-family: simsun;" lang="ZH-CN">：能监视一个变量的值</span><span style="font-family: verdana;" lang="EN-US"> </span><span style="font-family: simsun;" lang="ZH-CN">而不管它何时被改变；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">print</span><span style="font-family: simsun;" lang="ZH-CN">：显示表达示的值；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">break</span><span style="font-family: simsun;" lang="ZH-CN">：在代码里设置断点，这将使程序执行到这里时被挂起；</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">make</span><span style="font-family: simsun;" lang="ZH-CN">：不退出</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">就可以重新产生可执行文件</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: verdana;" lang="EN-US">shell</span><span style="font-family: simsun;" lang="ZH-CN">：不离开</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">就执行</span><span style="font-family: verdana;" lang="EN-US">UNIX shell</span><span style="font-family: simsun;" lang="ZH-CN">命令</span></p>
<p style="margin: 0in; font-size: 10pt;"><span style="font-family: simsun;" lang="ZH-CN">（</span><span style="font-family: verdana;" lang="EN-US">3</span><span style="font-family: simsun;" lang="ZH-CN">）可在</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">中按</span><span style="font-family: verdana;" lang="EN-US">Tab</span><span style="font-family: simsun;" lang="ZH-CN">键让</span><span style="font-family: verdana;" lang="EN-US">gdb</span><span style="font-family: simsun;" lang="ZH-CN">补齐一个命令。</span></p><img src ="http://www.cnitblog.com/zouzheng/aggbug/25336.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 19:00 <a href="http://www.cnitblog.com/zouzheng/articles/25336.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GDB调试精粹及使用实例 （转）</title><link>http://www.cnitblog.com/zouzheng/articles/25308.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 05 Apr 2007 04:16:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25308.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25308.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25308.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25308.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25308.html</trackback:ping><description><![CDATA[一：列文件清单 <br>1． List <br>(gdb) list line1,line2 <br><br>二：执行程序 <br>要想运行准备调试的程序，可使用run命令，在它后面可以跟随发给该程序的任何参数，包括标准输入和标准输出说明符(&lt;和&gt;)和外壳通配符（*、？、[、]）在内。 <br>如果你使用不带参数的run命令，gdb就再次使用你给予前一条run命令的参数，这是很有用的。 <br>利用set args 命令就可以修改发送给程序的参数，而使用show args 命令就可以查看其缺省参数的列表。 <br>（gdb）set args &#8211;b &#8211;x <br>(gdb) show args <br>backtrace命令为堆栈提供向后跟踪功能。 <br>Backtrace 命令产生一张列表，包含着从最近的过程开始的所以有效过程和调用这些过程的参数。 <br><br>三：显示数据 <br>利用print 命令可以检查各个变量的值。 <br>(gdb) print p (p为变量名) <br>whatis 命令可以显示某个变量的类型 <br>(gdb) whatis p <br>type = int * <br><br>print 是gdb的一个功能很强的命令，利用它可以显示被调试的语言中任何有效的表达式。表达式除了包含你程序中的变量外，还可以包含以下内容： <br>l 对程序中函数的调用 <br>(gdb) print find_entry(1,0) <br>l 数据结构和其他复杂对象 <br>(gdb) print *table_start <br>$8={e=reference=&#8217;\000&#8217;,location=0x0,next=0x0} <br>l 值的历史成分 <br>(gdb)print $1 ($1为历史记录变量,在以后可以直接引用 $1 的值) <br>l 人为数组 <br>人为数组提供了一种去显示存储器块（数组节或动态分配的存储区）内容的方法。早期的调试程序没有很好的方法将任意的指针换成一个数组。就像对待参数一样，让我们查看内存中在变量h后面的10个整数，一个动态数组的语法如下所示： <br>base@length <br>因此，要想显示在h后面的10个元素，可以使用h@10： <br>(gdb)print h@10 <br>$13=(-1,345,23,-234,0,0,0,98,345,10) <br><br>四：<a style="TEXT-DECORATION: underline" href="http://www.sogou.com/sogoupedia?query=断点" target=_blank><font color=#22229c>断点</font></a>(breakpoint) <br>break命令（可以简写为b）可以用来在调试的程序中设置断点，该命令有如下四种形式： <br>l break line-number 使程序恰好在执行给定行之前停止。 <br>l break function-name 使程序恰好在进入指定的函数之前停止。 <br>l break line-or-function if condition 如果condition（条件）是真，程序到达指定行或函数时停止。 <br>l break routine-name 在指定例程的入口处设置断点 <br><br>如果该程序是由很多原文件构成的，你可以在各个原文件中设置断点，而不是在当前的原文件中设置断点，其方法如下： <br>(gdb) break filename:line-number <br>(gdb) break filename:function-name <br><br>要想设置一个条件断点，可以利用break if命令，如下所示： <br>(gdb) break line-or-function if expr <br>例： <br>(gdb) break 46 if testsize==100 <br><br>从断点继续运行：countinue 命令 <br>五．断点的管理 <br><br>1． 显示当前gdb的断点信息： <br>(gdb) info break <br>他会以如下的形式显示所有的断点信息： <br>Num Type Disp Enb Address What <br>1 breakpoint keep y 0x000028bc in init_random at qsort2.c:155 <br>2 breakpoint keep y 0x0000291c in init_organ at qsort2.c:168 <br>(gdb) <br>2.删除指定的某个断点： <br>(gdb) delete breakpoint 1 <br>该命令将会删除编号为1的断点，如果不带编号参数，将删除所有的断点 <br>(gdb) delete breakpoint <br>3.禁止使用某个断点 <br>(gdb) disable breakpoint 1 <br>该命令将禁止断点 1,同时断点信息的 (Enb)域将变为 n <br>4．允许使用某个断点 <br>(gdb) enable breakpoint 1 <br>该命令将允许断点 1,同时断点信息的 (Enb)域将变为 y <br>5．清除原文件中某一代码行上的所有断点 <br>(gdb)clean number <br>注：number 为原文件的某个代码行的行号 <br>六．变量的检查和赋值 <br>l whatis:识别数组或变量的类型 <br>l ptype:比whatis的功能更强，他可以提供一个结构的定义 <br>l set variable:将值赋予变量 <br>l print 除了显示一个变量的值外，还可以用来赋值 <br><br>七．单步执行 <br>l next <br>不进入的单步执行 <br>l step <br>进入的单步执行 <br>如果已经进入了某函数，而想退出该函数返回到它的调用函数中，可使用命令finish <br>八．函数的调用 <br>l call name 调用和执行一个函数 <br>(gdb) call gen_and_sork( 1234,1,0 ) <br>(gdb) call printf(&#8220;abcd&#8221;) <br>$1=4 <br>l finish 结束执行当前函数，显示其返回值（如果有的话） <br><br>九．机器语言工具 <br>有一组专用的gdb变量可以用来检查和修改计算机的通用寄存器，gdb提供了目前每一台计算机中实际使用的4个寄存器的标准名字： <br>l $pc ： 程序计数器 <br>l $fp ： 帧指针（当前堆栈帧） <br>l $sp ： 栈指针 <br>l $ps ： 处理器状态 <br><br>十．信号 <br>gdb通常可以捕捉到发送给它的大多数信号，通过捕捉信号，它就可决定对于正在运行的进程要做些什么工作。例如，按CTRL-C将中断信号发送给gdb，通常就会终止gdb。但是你或许不想中断gdb，真正的目的是要中断gdb正在运行的程序，因此，gdb要抓住该信号并停止它正在运行的程序，这样就可以执行某些调试操作。 <br><br>Handle命令可控制信号的处理，他有两个参数，一个是信号名，另一个是接受到信号时该作什么。几种可能的参数是： <br>l nostop 接收到信号时，不要将它发送给程序，也不要停止程序。 <br>l stop 接受到信号时停止程序的执行，从而允许程序调试；显示一条表示已接受到信号的消息（禁止使用消息除外） <br>l print 接受到信号时显示一条消息 <br>l noprint 接受到信号时不要显示消息（而且隐含着不停止程序运行） <br>l pass 将信号发送给程序，从而允许你的程序去处理它、停止运行或采取别的动作。 <br>l nopass 停止程序运行，但不要将信号发送给程序。 <br>例如，假定你截获SIGPIPE信号，以防止正在调试的程序接受到该信号，而且只要该信号一到达，就要求该程序停止，并通知你。要完成这一任务，可利用如下命令： <br>(gdb) handle SIGPIPE stop print <br>请注意，UNIX的信号名总是采用大写字母！你可以用信号编号替代信号名 <br>如果你的程序要执行任何信号处理操作，就需要能够测试其信号处理程序，为此，就需要一种能将信号发送给程序的简便方法，这就是signal命令的任务。该 命令的参数是一个数字或者一个名字，如SIGINT。假定你的程序已将一个专用的SIGINT（键盘输入，或CTRL-C；信号2）信号处理程序设置成采 取某个清理动作，要想测试该信号处理程序，你可以设置一个断点并使用如下命令： <br>（gdb） signal 2 <br>continuing with signal SIGINT(2) <br>该程序继续执行，但是立即传输该信号，而且处理程序开始运行. <br><br>十一. 原文件的搜索 <br>search text:该命令可显示在当前文件中包含text串的下一行。 <br>Reverse-search text:该命令可以显示包含text 的前一行。 <br><br>十二.UNIX接口 <br>shell 命令可启动UNIX外壳，CTRL-D退出外壳，返回到 gdb. <br><br>十三.命令的历史 <br>为了允许使用历史命令，可使用 set history expansion on 命令 <br>(gdb) set history expansion on <br><br>小结：常用的gdb命令 <br>backtrace 显示程序中的当前位置和表示如何到达当前位置的栈跟踪（同义词：where） <br>breakpoint 在程序中设置一个断点 <br>cd 改变当前工作目录 <br>clear 删除刚才停止处的断点 <br>commands 命中断点时，列出将要执行的命令 <br>continue 从断点开始继续执行 <br>delete 删除一个断点或监测点；也可与其他命令一起使用 <br>display 程序停止时显示变量和表达时 <br>down 下移栈帧，使得另一个函数成为当前函数 <br>frame 选择下一条continue命令的帧 <br>info 显示与该程序有关的各种信息 <br>jump 在源程序中的另一点开始运行 <br>kill 异常终止在gdb 控制下运行的程序 <br>list 列出相应于正在执行的程序的原文件内容 <br>next 执行下一个源程序行，从而执行其整体中的一个函数 <br>print 显示变量或表达式的值 <br>pwd 显示当前工作目录 <br>pype 显示一个数据结构（如一个结构或C++类）的内容 <br>quit 退出gdb <br>reverse-search 在源文件中反向搜索正规表达式 <br>run 执行该程序 <br>search 在源文件中搜索正规表达式 <br>set variable 给变量赋值 <br>signal 将一个信号发送到正在运行的进程 <br>step 执行下一个源程序行，必要时进入下一个函数 <br>undisplay display命令的反命令，不要显示表达式 <br>until 结束当前循环 <br>up 上移栈帧，使另一函数成为当前函数 <br>watch 在程序中设置一个监测点（即数据断点） <br>whatis 显示变量或函数类型 <br>**************************************************** <br>　GNU的调试器称为gdb，该程序是一个交互式工具，工作在字符模式。在 X Window 系统中，有一个gdb的前端图形工具，称为xxgdb。gdb 是功能强大的调试程序，可完成如下的调试任务： <br>　　* 设置断点； <br>　　* 监视程序变量的值； <br>　　* 程序的单步执行； <br>　　* 修改变量的值。 <br>　　在可以使用 gdb 调试程序之前，必须使用 -g 选项编译源文件。可在 makefile 中如下定义 CFLAGS 变量： <br>　　 CFLAGS = -g <br>　　 运行 gdb 调试程序时通常使用如下的命令： <br>　　 gdb progname <br><br>　　在 gdb 提示符处键入help，将列出命令的分类，主要的分类有： <br>　　* aliases：命令别名 <br>　　* breakpoints：断点定义； <br>　　* data：数据查看； <br>　　* files：指定并查看文件； <br>　　* internals：维护命令； <br>　　* running：程序执行； <br>　　* stack：调用栈查看； <br>　　* statu：状态查看； <br>　　* tracepoints：跟踪程序执行。 <br>　　键入 help 后跟命令的分类名，可获得该类命令的详细清单。 <br><br><br>gdb 的常用命令 <br>命令 解释 <br>　　break NUM 在指定的行上设置断点。 <br>　　bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序。 <br>　　clear 删除设置在特定源文件、特定行上的断点。其用法为clear FILENAME:NUM <br>　　continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而 导致停止运行时。 <br>　　display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成。 <br>　　file FILE 装载指定的可执行文件进行调试。 <br>　　help NAME 显示指定命令的帮助信息。 <br>　　info break 显示当前断点清单，包括到达断点处的次数等。 <br>　　info files 显示被调试文件的详细信息。 <br>　　info func 显示所有的函数名称。 <br>　　info local 显示当函数中的局部变量信息。 <br>　　info prog 显示被调试程序的执行状态。 <br>　　info var 显示所有的全局和静态变量名称。 <br>　　kill 终止正被调试的程序。 <br>　　list 显示源代码段。 <br>　　make 在不退出 gdb 的情况下运行 make 工具。 <br>　　next 在不单步执行进入其他函数的情况下，向前执行一行源代码。 <br>　　print EXPR 显示表达式 EXPR 的值。 <br><br>******gdb 使用范例************************ <br>----------------- <br>清单 一个有错误的 C 源程序 bugging.c <br>代码: <br><br>----------------- <br>1　＃i nclude <br>2 <br>3　static char buff [256]; <br>4　static char* string; <br>5　int main () <br>6　{ <br>7　　　printf ("Please input a string: "); <br>8　　　gets (string);　　 <br>9　　 printf ("\nYour string is: %s\n", string); <br>10 } <br><br><br>----------------- <br>　 上面这个程序非常简单，其目的是接受用户的输入，然后将用户的输入打印出来。该程序使用了一个未经过初始化的字符串地址 string，因此，编译并运行之后，将出现 Segment Fault 错误： <br>$ gcc -o bugging -g bugging.c <br>$ ./bugging <br>Please input a string: asfd <br>Segmentation fault (core dumped) <br>为了查找该程序中出现的问题，我们利用 gdb，并按如下的步骤进行： <br>1．运行 gdb bugging 命令，装入 bugging 可执行文件； <br>2．执行装入的 bugging 命令 run； <br>3．使用 where 命令查看程序出错的地方； <br>4．利用 list 命令查看调用 gets 函数附近的代码； <br>5．唯一能够导致 gets 函数出错的因素就是变量 string。用print命令查看 string 的值； <br>6．在 gdb 中，我们可以直接修改变量的值，只要将 string 取一个合法的指针值就可以了，为此，我们在第8行处设置断点 break 8； <br>7．程序重新运行到第 8行处停止，这时，我们可以用 set variable 命令修改 string 的取值； <br>8．然后继续运行，将看到正确的程序运行结果。
<img src ="http://www.cnitblog.com/zouzheng/aggbug/25308.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-05 12:16 <a href="http://www.cnitblog.com/zouzheng/articles/25308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下Socket编程</title><link>http://www.cnitblog.com/zouzheng/articles/25019.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 01 Apr 2007 16:13:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25019.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/25019.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/25019.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/25019.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/25019.html</trackback:ping><description><![CDATA[<div>
<p><strong>什么是Socket</strong><br>　　 Socket接口是TCP/IP网络的API，Socket接口定义了许多函数或例程，程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程，必须理解Socket接口。<br>
Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话，就很容易了解Socket了。网络的
Socket数据传输是一种特殊的I/O，Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket()，该函数返
回一个整型的Socket描述符，随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种：流式Socket
（SOCK_STREAM）和数据报式Socket（SOCK_DGRAM）。流式是一种面向连接的Socket，针对于面向连接的TCP服务应用；数据
报式Socket是一种无连接的Socket，对应于无连接的UDP服务应用。</p>
<p><strong>Socket建立</strong><br>　　为了建立Socket，程序可以调用Socket函数，该函数返回一个类似于文件描述符的句柄。socket函数原型为：<br>　　 int socket(int domain, int type, int protocol);<br>
domain指明所使用的协议族，通常为PF_INET，表示互联网协议族（TCP/IP协议族）；type参数指定socket的类型：
SOCK_STREAM
或SOCK_DGRAM，Socket接口还定义了原始Socket（SOCK_RAW），允许程序使用低层协议；protocol通常赋值"0"。
Socket()调用返回一个整型socket描述符，你可以在后面的调用使用它。<br>　　 Socket描述符是一个指向内部数据结构的指针，它指向描述符表入口。调用Socket函数时，socket执行体将建立一个Socket，实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。<br>　　两个网络程序之间的一个网络连接包括五种信息：通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。</p>
<p><strong>Socket配置</strong><br>
通过socket调用返回一个socket描述符后，在使用socket进行网络传输以前，必须配置该socket。面向连接的socket客户端通过
调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用
bind函数来配置本地信息。<br>Bind函数将socket与本机上的一个端口相关联，随后你就可以在该端口监听服务请求。Bind函数原型为：<br>　　 int bind(int sockfd,struct sockaddr *my_addr, int addrlen);<br>　　 Sockfd是调用socket函数返回的socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针；addrlen常被设置为sizeof(struct sockaddr)。<br>　　 struct sockaddr结构类型是用来保存socket信息的：<br>　　 struct sockaddr {<br>　　 unsigned short sa_family; /* 地址族， AF_xxx */<br>char sa_data[14]; /* 14 字节的协议地址 */<br>};<br>　　 sa_family一般为AF_INET，代表Internet（TCP/IP）地址族；sa_data则包含该socket的IP地址和端口号。<br>　　 另外还有一种结构类型：<br>　　 struct sockaddr_in {<br>　　 short int sin_family; /* 地址族 */<br>　　 unsigned short int sin_port; /* 端口号 */<br>　　 struct in_addr sin_addr; /* IP地址 */<br>　　 unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */<br>　　 };<br>
这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct
sockaddr同样的长度，可以用bzero()或memset()函数将其置为零。指向sockaddr_in
的指针和指向sockaddr的指针可以相互转换，这意味着如果一个函数所需参数类型是sockaddr时，你可以在函数调用的时候将一个指向
sockaddr_in的指针转换为指向sockaddr的指针；或者相反。<br>　　使用bind函数时，可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号：<br>　　 my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */<br>　　 my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */<br>通过将my_addr.sin_port置为0，函数会自动为你选择一个未占用的端口来使用。同样，通过将my_addr.sin_addr.s_addr置为INADDR_ANY，系统会自动填入本机IP地址。<br>注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序；而sin_addr则不需要转换。<br>　　计算机数据存储有两种字节优先顺序：高位字节优先和低位字节优先。Internet上数据以高位字节优先顺序在网络上传输，所以对于在内部是以低位字节优先方式存储数据的机器，在Internet上传输数据时就需要进行转换，否则就会出现数据不一致。<br>　　 下面是几个字节顺序转换函数：<br>&#183;htonl()：把32位值从主机字节序转换成网络字节序<br>&#183;htons()：把16位值从主机字节序转换成网络字节序<br>&#183;ntohl()：把32位值从网络字节序转换成主机字节序<br>&#183;ntohs()：把16位值从网络字节序转换成主机字节序<br>　　 Bind()函数在成功被调用时返回0；出现错误时返回"-1"并将errno置为相应的错误号。需要注意的是，在调用bind函数时一般不要将端口号置为小于1024的值，因为1到1024是保留端口号，你可以选择大于1024中的任何一个没有被占用的端口号。</p>
<p><strong>连接建立</strong><br>　　面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接，其函数原型为：<br>　　 int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);<br>Sockfd
是socket函数返回的socket描述符；serv_addr是包含远端主机IP地址和端口号的指针；addrlen是远端地质结构的长度。
Connect函数在出现错误时返回-1，并且设置errno为相应的错误码。进行客户端程序设计无须调用bind()，因为这种情况下只需知道目的机器
的IP地址，而客户通过哪个端口与服务器建立连接并不需要关心，socket执行体为你的程序自动选择一个未被占用的端口，并通知你的程序数据什么时候到
打断口。<br>　　 Connect函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。无连接协议从不建立直接连接。面向连接的服务器也从不启动一个连接，它只是被动的在协议端口监听客户的请求。<br>　　 Listen函数使socket处于被动的监听模式，并为该socket建立一个输入数据队列，将到达的服务请求保存在此队列中，直到程序处理它们。<br>　　 int listen(int sockfd， int backlog);<br>Sockfd
是Socket系统调用返回的socket
描述符；backlog指定在请求队列中允许的最大请求数，进入的连接请求将在队列中等待accept()它们（参考下文）。Backlog对队列中等待
服务的请求的数目进行了限制，大多数系统缺省值为20。如果一个服务请求到来时，输入队列已满，该socket将拒绝连接请求，客户将收到一个出错信息。<br>当出现错误时listen函数返回-1，并置相应的errno错误码。<br>　　 accept()函数让服务器接收客户的连接请求。在建立好输入队列后，服务器就调用accept函数，然后睡眠并等待客户的连接请求。<br>　　 int accept(int sockfd, void *addr, int *addrlen);<br>
sockfd是被监听的socket描述符，addr通常是一个指向sockaddr_in变量的指针，该变量用来存放提出连接请求服务的主机的信息（某
台主机从某个端口发出该请求）；addrten通常为一个指向值为sizeof(struct
sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。<br>　　首先，当accept函数监视的
socket收到连接请求时，socket执行体将建立一个新的socket，执行体将这个新socket和请求连接进程的地址联系起来，收到服务请求的
初始socket仍可以继续在以前的 socket上监听，同时可以在新的socket描述符上进行数据传输操作。</p>
<p><strong>数据传输</strong><br>　　 Send()和recv()这两个函数用于面向连接的socket上进行数据传输。<br>　　 Send()函数原型为：<br>　　 int send(int sockfd, const void *msg, int len, int flags);<br>Sockfd是你想用来传输数据的socket描述符；msg是一个指向要发送数据的指针；Len是以字节为单位的数据的长度；flags一般情况下置为0（关于该参数的用法可参照man手册）。<br>　　 Send()函数返回实际上发送出的字节数，可能会少于你希望发送的数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send()返回值与len不匹配时，应该对这种情况进行处理。<br>char *msg = "Hello!";<br>int len, bytes_sent;<br>&#8230;&#8230;<br>len = strlen(msg);<br>bytes_sent = send(sockfd, msg,len,0);<br>&#8230;&#8230;<br>　　 recv()函数原型为：<br>　　 int recv(int sockfd,void *buf,int len,unsigned int flags);<br>　　 Sockfd是接受数据的socket描述符；buf 是存放接收数据的缓冲区；len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数，当出现错误时，返回-1并置相应的errno值。<br>Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接，所以在发送数据时应指明目的地址。<br>sendto()函数原型为：<br>　　 int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);<br>　　该函数比send()函数多了两个参数，to表示目地机的IP地址和端口号信息，而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。<br>　　 Recvfrom()函数原型为：<br>　　 int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);<br>
from是一个struct sockaddr类型的变量，该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct
sockaddr)。当recvfrom()返回时，fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或
当出现错误时返回-1，并置相应的errno。<br>如果你对数据报socket调用了connect()函数时，你也可以利用send()和recv()进行数据传输，但该socket仍然是数据报socket，并且利用传输层的UDP服务。但在发送或接收数据报时，内核会自动为之加上目地和源地址信息。</p>
<p><strong>结束传输</strong><br>　　当所有的数据操作结束以后，你可以调用close()函数来释放该socket，从而停止在该socket上的任何数据操作：<br>close(sockfd);<br>　　你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输，而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据，直至读入所有数据。<br>　　 int shutdown(int sockfd,int how);<br>　　 Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式：<br>　　 &#183;0-------不允许继续接收数据<br>　　 &#183;1-------不允许继续发送数据<br>　　 &#183;2-------不允许继续发送和接收数据，<br>　　 &#183;均为允许则调用close ()<br>　　 shutdown在操作成功时返回0，在出现错误时返回-1并置相应errno。</p>
<p><strong>Socket编程实例</strong><br>　　 代码实例中的服务器通过socket连接向客户端发送字符串"Hello, you are connected!"。只要在服务器上运行该服务器软件，在客户端运行客户软件，客户端就会收到该字符串。<br>　　 该服务器软件代码如下：<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;netinet/in.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#include &lt;sys/wait.h&gt;<br>#define SERVPORT 3333 /*服务器监听端口号 */<br>#define BACKLOG 10 /* 最大同时连接请求数 */<br>main()<br>{<br>　 int sockfd,client_fd; /*sock_fd：监听socket；client_fd：数据传输socket */<br>　 struct sockaddr_in my_addr; /* 本机地址信息 */<br>　 struct sockaddr_in remote_addr; /* 客户端地址信息 */<br>　 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {<br>　　 perror("socket创建出错！"); exit(1);<br>　 }<br>　 my_addr.sin_family=AF_INET;<br>　 my_addr.sin_port=htons(SERVPORT);<br>　 my_addr.sin_addr.s_addr = INADDR_ANY;<br>　 bzero(&amp;(my_addr.sin_zero),8);<br>　 if (bind(sockfd, (struct sockaddr *)&amp;my_addr, sizeof(struct sockaddr)) == -1) {<br>　 　 perror("bind出错！");<br>　 　 exit(1);<br>　 }<br>　 if (listen(sockfd, BACKLOG) == -1) {<br>　 　 perror("listen出错！");<br>　 　 exit(1);<br>　 }<br>　 while(1) {<br>　　 sin_size = sizeof(struct sockaddr_in);<br>　　 if ((client_fd = accept(sockfd, (struct sockaddr *)&amp;remote_addr, &amp;sin_size)) == -1) {<br>　 　 　 perror("accept出错");<br>　 　 　 continue;<br>　 　}<br>　　 printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));<br>　 　if (!fork()) { /* 子进程代码段 */<br>　　 　 if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)<br>　　 　 perror("send出错！");<br>　 　 　close(client_fd);<br>　 　 　exit(0);<br>　 　}<br>　　 close(client_fd);<br>　　 }<br>　 }<br>}<br>
服务器的工作流程是这样的：首先调用socket函数创建一个Socket，然后调用bind函数将其与本机地址以及一个本地端口号绑定，然后调用
listen在相应的socket上监听，当accpet接收到一个连接服务请求时，将生成一个新的socket。服务器显示该客户机的IP地址，并通过
新的socket向客户端发送字符串"Hello，you are connected!"。最后关闭该socket。<br>　　代码实例中的fork()函数生成一个子进程来处理数据传输部分，fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分，它与if语句后面的父进程代码部分是并发执行的。</p>
<p>客户端程序代码如下：<br>#include&lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;netdb.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;netinet/in.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#define SERVPORT 3333<br>#define MAXDATASIZE 100 /*每次最大数据传输量 */<br>main(int argc, char *argv[]){<br>　 int sockfd, recvbytes;<br>　 char buf[MAXDATASIZE];<br>　 struct hostent *host;<br>　 struct sockaddr_in serv_addr;<br>　 if (argc &lt; 2) {<br>fprintf(stderr,"Please enter the server's hostname!\n");<br>exit(1);<br>}<br>　 if((host=gethostbyname(argv[1]))==NULL) {<br>herror("gethostbyname出错！");<br>exit(1);<br>}<br>　 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){<br>perror("socket创建出错！");<br>exit(1);<br>}<br>　 serv_addr.sin_family=AF_INET;<br>　 serv_addr.sin_port=htons(SERVPORT);<br>　 serv_addr.sin_addr = *((struct in_addr *)host-&gt;h_addr);<br>　 bzero(&amp;(serv_addr.sin_zero),8);<br>　 if (connect(sockfd, (struct sockaddr *)&amp;serv_addr, \<br>　　 sizeof(struct sockaddr)) == -1) {<br>perror("connect出错！");<br>exit(1);<br>}<br>　 if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {<br>perror("recv出错！");<br>exit(1);<br>}<br>　 buf[recvbytes] = '\0';<br>　 printf("Received: %s",buf);<br>　 close(sockfd);<br>}<br>　　客户端程序首先通过服务器域名获得服务器的IP地址，然后创建一个socket，调用connect函数与服务器建立连接，连接成功之后接收从服务器发送过来的数据，最后关闭socket。<br>　　函数gethostbyname()是完成域名转换的。由于IP地址难以记忆和读写，所以为了方便，人们常常用域名来表示主机，这就需要进行域名和IP地址的转换。函数原型为：<br>　　 struct hostent *gethostbyname(const char *name);<br>　　 函数返回为hosten的结构类型，它的定义如下：<br>　　 struct hostent {<br>　 char *h_name; /* 主机的官方域名 */<br>　　 char **h_aliases; /* 一个以NULL结尾的主机别名数组 */<br>　　 int h_addrtype; /* 返回的地址类型，在Internet环境下为AF-INET */<br>　　 int h_length; /* 地址的字节长度 */<br>　　 char **h_addr_list; /* 一个以0结尾的数组，包含该主机的所有地址*/<br>　　 };<br>　　 #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/<br>　　 当 gethostname()调用成功时，返回指向struct hosten的指针，当调用失败时返回-1。当调用gethostbyname时，你不能使用perror()函数来输出错误信息，而应该使用herror()函数来输出。</p>
<p>　　无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的，两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接，而且在发送接收数据时，需要指定远端机的地址。</p>
<p>阻塞和非阻塞<br>
阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如，程序执行一个读数据的函数调用时，在此函数完成读操作以前将不会执行下一程序语句。当
服务器运行到accept语句时，而没有客户连接服务请求到来，服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞
（blocking）。而非阻塞操作则可以立即完成。比如，如果你希望服务器仅仅注意检查是否有客户在等待连接，有就接受连接，否则就继续做其他事情，则
可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。<br>　　 #include &lt;unistd.h&gt;<br>　　 #include &lt;fcntl.h&gt;<br>　　 &#8230;&#8230;<br>sockfd = socket(AF_INET,SOCK_STREAM,0);<br>fcntl(sockfd,F_SETFL,O_NONBLOCK)；<br>&#8230;&#8230;<br>
通过设置socket为非阻塞方式，可以实现"轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时，函数将立即返
回，返回值为-1，并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式，从而降低性能，浪费系统资源。而调用
select()会有效地解决这个问题，它允许你把进程本身挂起来，而同时使系统内核监听所要求的一组文件描述符的任何活动，只要确认在任何被监控的文件
描述符上出现活动，select()调用将返回指示该文件描述符已准备好的信息，从而实现了为进程选出随机的变化，而不必由进程本身对输入进行测试而浪费
CPU开销。Select函数原型为:<br>int select(int numfds,fd_set *readfds,fd_set *writefds，<br>fd_set *exceptfds,struct timeval *timeout);<br>
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以
从标准输入和某个socket描述符读取数据，你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中；numfds的值
是需要检查的号码最高的文件描述符加1，这个例子中numfds的值应为sockfd+1；当select返回时，readfds将被修改，指示某个文件
描述符已经准备被读取，你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试，它提供了一组宏：<br>　　 FD_ZERO(fd_set *set)----清除一个文件描述符集；<br>　　 FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中；<br>　　 FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除；<br>　　 FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。<br>　　 Timeout参数是一个指向struct timeval类型的指针，它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为：<br>　　 struct timeval {<br>　　 int tv_sec; /* seconds */<br>　　 int tv_usec; /* microseconds */<br>};</p>
<p>POP3客户端实例<br>　　下面的代码实例基于POP3的客户协议，与邮件服务器连接并取回指定用户帐号的邮件。与邮件服务器交互的命令存储在字符串数组POPMessage中，程序通过一个do-while循环依次发送这些命令。<br>#include&lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;netdb.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;netinet/in.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#define POP3SERVPORT 110<br>#define MAXDATASIZE 4096</p>
<p>main(int argc, char *argv[]){<br>int sockfd;<br>struct hostent *host;<br>struct sockaddr_in serv_addr;<br>char *POPMessage[]={<br>"USER userid\r\n",<br>"PASS password\r\n",<br>"STAT\r\n",<br>"LIST\r\n",<br>"RETR 1\r\n",<br>"DELE 1\r\n",<br>"QUIT\r\n",<br>NULL<br>};<br>int iLength;<br>int iMsg=0;<br>int iEnd=0;<br>char buf[MAXDATASIZE];</p>
<p>if((host=gethostbyname("your.server"))==NULL) {<br>perror("gethostbyname error");<br>exit(1);<br>}<br>if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){<br>perror("socket error");<br>exit(1);<br>}<br>serv_addr.sin_family=AF_INET;<br>serv_addr.sin_port=htons(POP3SERVPORT);<br>serv_addr.sin_addr = *((struct in_addr *)host-&gt;h_addr);<br>bzero(&amp;(serv_addr.sin_zero),8);<br>if (connect(sockfd, (struct sockaddr *)&amp;serv_addr,sizeof(struct sockaddr))==-1){<br>perror("connect error");<br>exit(1);<br>}</p>
<p>do {<br>send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);<br>printf("have sent: %s",POPMessage[iMsg]);</p>
<p>iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);<br>iEnd+=iLength;<br>buf[iEnd]='\0';<br>printf("received: %s,%d\n",buf,iMsg);</p>
<p>iMsg++;<br>} while (POPMessage[iMsg]);</p>
<p>close(sockfd);<br>}</p>
<br>
<p><br></p>
<br>
<p><a  href="http://blog.csdn.net/Philip_Chen/archive/2005/06/01/385481.aspx">Unix/Linux环境下的Socket编程</a></p>
<p><font color="#000000">网络的Socket数据传输是一种特殊的I/O，<font color="#ff0000">Socket也是一种文件描述符。</font>
Socket也具有一个类似于打开文件的函数调用Socket()，该函数返回一个整型的Socket描述符，随后的连接建立、数据传输等操作都是通过该
Socket实现的。常用的Socket类型有两种：流式Socket
（SOCK_STREAM）和数据报式Socket（SOCK_DGRAM）。流式是一种面向连接的Socket，针对于面向连接的TCP服务应用；数据
报式Socket是一种无连接的Socket，对应于无连接的UDP服务应用。</font></p>
<p><font color="#000000">&nbsp;&nbsp;&nbsp; Socket描述符是一个指向内部数据结构的指针，它指向描述符表入口。调用Socket函数时，socket执行体将建立一个Socket，实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。</font><font color="#000000">两个网络程序之间的一个网络连接包括五种信息：通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。Socket数据结构中包含这五种信息。 </font></p>
<p><font color="#000000">&nbsp;&nbsp; struct&nbsp;sockaddr结构类型是用来保存socket信息的： <br><font color="#0000ff">&nbsp;&nbsp;&nbsp; struct&nbsp;sockaddr&nbsp;{ <br>　&nbsp;&nbsp;&nbsp; unsigned&nbsp;short&nbsp;sa_family;&nbsp;/*&nbsp;地址族，&nbsp;AF_xxx&nbsp;*/&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;sa_data[14];&nbsp;/*&nbsp;14&nbsp;字节的协议地址&nbsp;*/ <br>&nbsp;&nbsp;&nbsp; };</font> <br>　　sa_family一般为AF_INET，代表Internet（TCP/IP）地址族；sa_data则包含该socket的IP地址和 <br>端口号。 <br>&nbsp;&nbsp; 另外还有一种结构类型： <br></font><font color="#000000"><font color="#0000ff">&nbsp;&nbsp;&nbsp; struct&nbsp;sockaddr_in&nbsp;{ <br>　　&nbsp;short&nbsp;int&nbsp;sin_family;&nbsp;/*&nbsp;地址族&nbsp;*/ <br>　　&nbsp;unsigned&nbsp;short&nbsp;int&nbsp;sin_port;&nbsp;/*&nbsp;端口号&nbsp;*/ <br>　　&nbsp;struct&nbsp;in_addr&nbsp;sin_addr;&nbsp;/*&nbsp;IP地址&nbsp;*/ <br>　　&nbsp;unsigned&nbsp;char&nbsp;sin_zero[8];&nbsp;/*&nbsp;填充0&nbsp;以保持与struct&nbsp;sockaddr同样大小&nbsp;*/&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; };</font> <br></font></p>
<p><font color="#cccccc">&nbsp;&nbsp;&nbsp; </font><font color="#000000">可以用bzero()或memset()函数将其置为零。指向sockaddr_in&nbsp;的指针和指向sockaddr的指针可以相 <br>互转换，这意味着如果一个函数所需参数类型是sockaddr时，</font><font color="#ff0000">你可以在函数调用的时候将一个指向 <br>sockaddr_in的指针转换为指向sockaddr的指针；或者相反。</font></p>
<p><font color="#ff0000">&nbsp;&nbsp; </font><font color="#cccccc"><font color="#000000">在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序；而sin_addr则不需要转换。</font> </font></p>
<p><font color="#cccccc">&nbsp;&nbsp;<font color="#000000"> 计算机数据存储有两种字节优先顺序：高位字节优先和低位字节优先。Internet上数据以高位字节 <br>优先顺序在网络上传输，所以对于在内部是以低位字节优先方式存储数据的机器，在Internet上传输数 <br>据时就需要进行转换，否则就会出现数据不一致。 <br>　　下面是几个字节顺序转换函数： <br>&#183;htonl()：把32位值从主机字节序转换成网络字节序 <br>&#183;htons()：把16位值从主机字节序转换成网络字节序 <br>&#183;ntohl()：把32位值从网络字节序转换成主机字节序 <br>&#183;ntohs()：把16位值从网络字节序转换成主机字节序</font> </font></p>
<p><font color="#cccccc"><font color="#000000">Bind()函数在成功被调用时返回0；出现错误时返回"-1"并将errno置为相应的错误号。需要注意的 <br>是，<font color="#ff0000">在调用bind函数时一般不要将端口号置为小于1024的值，因为1到1024是保留端口号</font>，你可以选择 <br>大于1024中的任何一个没有被占用的端口号。</font> </font></p>
<p><font color="#cccccc"><font color="#000000"><font color="#3366ff">连接建立</font> <br>　　面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接，其函数原型为：</font><font color="#0000ff">int&nbsp;connect(int&nbsp;sockfd,&nbsp;struct&nbsp;sockaddr&nbsp;*serv_addr,int&nbsp;addrlen);</font> </font><font color="#000000">Sockfd是socket函数返回的socket描述符；serv_addr是包含远端主机IP地址和端口号的指针；addrlen是远端地址结构的长度。Connect函数在出现错误时返回-1，并且设置errno为相应的错误码。</font><font color="#ff0000">进行客户端程序设计无须调用bind()，因为这种情况下只需知道目的机器的IP地址，而客户通过哪个端口与服务器建立连接并不需要关心，socket执行体为你的程序自动选择一个未被占用的端口，并通知你的程序数据什么时候到打断口。</font><font color="#000000">Connect
函数启动和远端主机的直接连接。只有面向连接的客户程序使用socket时才需要将此socket与远端主机相连。无连接协议从不建立直接连接。面向连接
的务器也从不启动一个连接，它只是被动的在协议端口监听客户的请求。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#cccccc">&nbsp;&nbsp;&nbsp; </font></font></p>
<p><font color="#000000"><font color="#cccccc">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#000000">Listen函数使socket处于被动的监听模式，并为该socket建立一个输入数据队列，将到达的服务请求 <br>保存在此队列中，直到程序处理它们。</font></font></font><font color="#0000ff"> int&nbsp;listen(int&nbsp;sockfd，&nbsp;int&nbsp;backlog); </font><font color="#000000">Sockfd
是Socket系统调用返回的socket&nbsp;描述符；backlog指定在请求队列中允许的最大请求数，进入的连接请求将在队列中等待accept()它
们（参考下文）。Backlog对队列中等待服务的请求的数目进行了限制，大多数系统缺省值为20。如果一个服务请求到来时，输入队列已满，该
socket将拒绝连接请求，客户将收到一个出错信息。<font color="#000000">当出现错误时listen函数返回-1，并置相应的errno错误码。<br></font></font></p>
<p><font color="#000000"><font color="#cccccc">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#000000">accept()函数让服务器接收客户的连接请求。在建立好输入队列后，服务器就调用accept函数，然后 <br>睡眠并等待客户的连接请求。</font><font color="#cccccc"><font color="#0000ff">int&nbsp;accept(int&nbsp;sockfd,&nbsp;void&nbsp;*addr,&nbsp;int&nbsp;*addrlen);</font> </font><font color="#000000">sockfd是被监听的socket描述符，addr通常是一个指向sockaddr_in变量的指针，<font color="#ff0000">该变量用来存放提出连接请求服务的主机的信息（某台主机从某个端口发出该请求）</font>；addrlen通常为一个指向值为sizeof(struct&nbsp;sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。</font><font color="#000000">当accept函数监视的socket收到连接请求时，socket执行体将建立一个新的socket，执行体将这</font><font color="#000000">个新socket和请求连接进程的地址联系起来，收到服务请求的初始socket仍可以继续在以前的&nbsp;socket上监听，<font color="#000000">同时可以在新的socket描述符上进行数据传输操作。</font><br></font></font></p>
<p><font color="#cccccc"><font color="#3366ff">数据传输</font>&nbsp;</font></p>
<p>(面向连接TCP)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#000000">send()和recv()这两个函数用于面向连接的socket上进行数据传输。</font><font color="#000000">Send()函数原型为： <br>　　<font color="#0000ff">int&nbsp;send(int&nbsp;sockfd,&nbsp;const&nbsp;void&nbsp;*msg,&nbsp;int&nbsp;len,&nbsp;int&nbsp;flags);</font> Sockfd是你想用来传输数据的socket描述符；msg是一个指向要发送数据的指针；Len是以字节为单位的数据的长度；flags一般情况下置为0。</font><font color="#000000">Send()函数返回实际上发送出的字节数，可能会少于你希望发送的数据。在程序中应该将send()的返回值与欲发送的字节数进行比较。当send()返回值与len不匹配时，应该对这种情况进行处理。<font color="#cccccc"> <br></font></font><font color="#000000"><font color="#cccccc">　　<font color="#0000ff">int&nbsp;recv(int&nbsp;sockfd,void&nbsp;*buf,int&nbsp;len,unsigned&nbsp;int&nbsp;flags);</font> </font><font color="#000000">Sockfd是接受数据的socket描述符；buf&nbsp;是存放接收数据的缓冲区；len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数，当出现错误时，返回-1并置相应的errno值。 </font></font></p>
<p><font color="#000000"><font color="#000000">(无连接UDP)</font></font></p>
<p><font color="#000000"><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#000000">sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接，所以在发送数据时应指明目的地址。</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">&nbsp; int&nbsp;sendto(int&nbsp;sockfd,&nbsp;const&nbsp;void&nbsp;*msg,int&nbsp;len,unsigned&nbsp;int&nbsp;flags,const&nbsp;struct&nbsp;sockaddr&nbsp;*to,&nbsp;int&nbsp;tolen);</font><font color="#cccccc"><font color="#000000">该函数比send()函数多了两个参数，to表示目地机的IP地址和端口号信息，而tolen常常被赋值为sizeof&nbsp;(struct&nbsp;sockaddr)。sendto函数也返回实际发送的数据字节长度或在出现发送错误时返回-1</font></font></font></font><font color="#000000">。</font></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#0000ff">int&nbsp;recvfrom(int&nbsp;sockfd,void&nbsp;*buf,int&nbsp;len,unsigned&nbsp;int&nbsp;flags,struct&nbsp;sockaddr&nbsp;*from,int&nbsp;*fromlen); </font><font color="#0000ff"><font color="#000000">from是一个struct&nbsp;sockaddr类型的变量，该变量保存源机的IP地址及端口号。fromlen常置为sizeof&nbsp;(struct&nbsp;sockaddr)。当recvfrom()返回时，fromlen包含实际存入from中的数据字节数。 <br>Recvfrom()函数返回接收到的字节数或当出现错误时返回-1，并置相应的errno。 </font></font></p>
<p><font color="#0000ff"><font color="#000000"><font color="#3366ff">结束传输 </font></font></font></p>
<p><font color="#0000ff"><font color="#000000"><font color="#3366ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#cccccc"><font color="#000000">当所有的数据操作结束以后，你可以调用close()函数来释放该socket，从而停止在该socket上的任 <br>何数据操作：<font color="#0000ff">close(sockfd);</font> 也可以调用<font color="#0000ff">shutdown()</font>函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输，而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据，直至读入所有数据。</font><font color="#000000">Sockfd
是需要关闭的socket的描述符。参数&nbsp;how允许为shutdown操作选择以下几种方式：0-------不允许继续接收数据
1-------不允许继续发送数据 2-------不允许继续发送和接收数据，均为允许则调用close&nbsp;()
shutdown在操作成功时返回0，在出现错误时返回-1并置相应errno。</font> </font></font></font></font></p>
</div><img src ="http://www.cnitblog.com/zouzheng/aggbug/25019.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-02 00:13 <a href="http://www.cnitblog.com/zouzheng/articles/25019.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++基本功：全面掌握const、volatile和mutable关键字</title><link>http://www.cnitblog.com/zouzheng/articles/21774.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 12:14:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21774.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21774.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21774.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21774.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%; TEXT-ALIGN: left" align="left">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">C++</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">程式设计过程中</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的使用可以频度是非常高的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">它在保证程式安全方面起到了不可估量的作用</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br /></font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">用一句话来表达最确切不过了：”小兵立大功”</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">有了</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">那么</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">mutable</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">当然缺不了</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">然作为</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的同胞兄弟</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">却在很多人的视野中消失</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">其实</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">担负的责任有何尝小呢</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">?<br />   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">自然</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">它们的用法多样而灵巧</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">以至新手迷惑久久</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">下面就来系统的探讨总结一下吧：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
						<p>
						</p>
				</span>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">一</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">一般应用</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">1.const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰各种变量的用法</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   a.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">取代</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">define<br />     #define D_INT 100<br />     #define D_LONG 100.29<br />     ………<br />     const int D_INT = 100;<br />     const D_INT = 100;     //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如果定义的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">int</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">类型</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">可省略</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">int.<br />     const long D_LONG = 100.29;<br />     ………<br />     const int&amp; a = 100; <br />     const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">替代</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">define</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">虽然增加分配空间</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">可它却保证了类型安全</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">在</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">C</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">标准中</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">定义的数据相当于全局的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">C++</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">中视声明的位置而定</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   b.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰指针相关的变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">以三组简单的定义示意：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     Group1:   <br />     int a = 0;    <br />     const int* b = &amp;a;------------  [1]                <br />     int const *b = &amp;a;------------  [2]                     <br />     const int* const b = &amp;a;---- [4]   <br />          <br />     Group2:  <br />     const char *p = "const";--------------[1] <br />     char const *p = "const";--------------[2]   <br />     char* const p = "const";--------------[3]   <br />     const char * const p = "const";----[4]      <br />     <br />     Group3:<br />      int a=0;<br />        const int &amp;b = a;---------------[1]<br />     int const &amp;b = a;---------------[2]  <br />     int &amp; const b = a;--------------[3]  //---&gt;</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰引用时</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">被忽略</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     const int &amp; const b = a;-----[4]<br />     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">总结：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     1.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如果</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">位于星号左侧</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">则</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">用来修饰指针所指向的变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,<br />       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">即指针指向的为不可变的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />     2.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如果</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">位于星号右侧</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">就是修饰指针本身</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">即指针本身是</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">不可变的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">因此</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,[1]</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">和</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">[2]</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的情况相同</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">指针所指向内容不可变</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">(const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">放在变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">声明符的位置无关</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">),<br />       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这种情况下不允许对内容进行更改</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如不能</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">*a = 3 ;<br />     3.[3]</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">中指针本身是不可变的，而指针所指向的内容是可变的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这种情况</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">下不能对指针本身</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">进行更改操作</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">a++</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">是错误的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     4.[4]</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">中指针本身和指向的内容均为常量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.(</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">引用特殊：引用在使用增加</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">遇义时</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">增加它代表的变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">所以</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">qualifiers on reference are ignoredv.<br />       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">延伸点</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">: <br />       </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">注意示例</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">:<br />       1.const int&amp; reference = 1000; <br />       2.char* p = "const"<br />         char*&amp; q ;<br />   <br />2.const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">在函数环境下的各种应用</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">常用法示例如下：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   const A&amp;  _Fun(const  A&amp; _in);  //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰引用型传入参数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   // A  _Fun(const A&amp; _in);<br />   //A&amp; _Fun(const A&amp; _in);<br />   //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">上面的两种</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">在函数内部有特殊的步骤</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这里不详提了…</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">..<br />  <br />   const  A*  _Fun( const  A* _in);   //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰指针型传入参数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   void _Fun( ) const;   //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">class</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">成员函数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   const  A&amp;  _Fun(A&amp; _in );  //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰返回值</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">   const A &amp; operator(const A&amp; _in);  //</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">同时修饰传入参数和返回值</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
						<p>
						</p>
				</span>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">   a.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰参数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">void _Fun(const A* _in)</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">或</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman"> void _Fun(const A&amp; _in);<br />     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">它们被修饰后</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">在函数执行期间行为特性同于上面的讲解</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,<br />     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">注意：这不会改变原来数据的是否是</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的属性</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">.</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">   b.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰函数返回值</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">    const A&amp;  _Fun( )<br />    const A*   _Fun( );<br />    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">注意：由于生命期不同步的问题</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">不可将局部的变量的指针或引用返回</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">(static</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">除外</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">).<br />    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">另外</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">传出来的视情况</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">代表不同的意思…</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对于</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">A&amp;</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">返回类型</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">你若将之赋与其它变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">那么它实际执行的是将返回的变量</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">    (</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">或引用</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">)</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">代表的数据赋出</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">..</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而你若将其它值赋予之</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">那么被赋予的是变量或引</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">用代表的数据</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">. </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const A&amp; </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">一般是防止之做为左值被赋值</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">.</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这个地方还有很多的细节问题</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">(</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">譬如在连续赋值、返回的临时对象的处理、</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">    </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">重载的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">和非</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">cosnt</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">运算符等等</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">),</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">读者自己在实践中需要多多总结</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">.</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">二、难点</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">3. </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰类成员函数的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const.<br />   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">形如</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">:void _Fun() const { };<br />   </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">你需要知道的几点规则：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
						<p>
						</p>
				</span>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">   a.const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对象只能访问</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">成员函数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而非</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对象可以访问任意</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的成员函数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">包括</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">成员函数</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   b.const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对象的成员是不可修改的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">然而</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对象通过指针维护的对象却</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">是可以修改的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   c.const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">成员函数不可以修改对象的数据</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">不管对象是否具有</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">性质</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">它在</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">编译时</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">以是否修改成员数据为依据</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">进行检查</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />   e.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">然而加上</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">mutable</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰符的数据成员</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对于任何情况下通过任何手段</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">     </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">都可修改</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">自然此时的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">成员函数是可以修改它的…</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
						<p>
						</p>
				</span>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">4.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">谈谈</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">和”完全</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对象”</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">一个有</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰的类只允许访问其接口的一个子集，这个子集由类的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">实现者来控制</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">用户只有用</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const_cast</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">才可以访问这个类型的全部接口</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而且</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,<br />  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">象</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">一样，类的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">属性会传递给它的成员</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">想象</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰的对</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">象</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">它的成员变量是不可修改的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而它通过指针维护的对象或原生变量是可</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修改</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">那么我们想</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">:</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">如果对象维护一个</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">char* ,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">则它相当于</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">char* <br />  const chrptr ;</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而不是</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const char* cosnt chrptr;</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">对于类中的指针你需要</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这样修饰以防止它或它维护的资源：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">cosnt x* xptr;</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">而不是</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">x*const xptr; <br />  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">因为</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">cosnt </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰的对象它默认</span>
				<span style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">
						</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的行为是延续变量：</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">x* cosnt xptr;</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">更重要的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">修饰的数据</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">编译器不可对其进行执行期寄存于寄存器的优化</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这种特性</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">,</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">是为了多线程同步的需要</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">有兴趣者看参看</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">Andrei</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">GP</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">系列文章</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">.</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">5.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">谈谈</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const_cast</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">转换运算符</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<br />
						<font face="Times New Roman">  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">这个关键字最基础的用法是：去掉数据的</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">性质</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">
						<font face="Times New Roman">.<br />  </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: 新宋体">值得注意的是：它只对指针、引用和其它的具有指向性质的类型</span>
				<font face="Times New Roman">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: black">.<br /><br /></span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: navy">
								<p>
								</p>
						</span>
				</font>
		</p>
		<p>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; LINE-HEIGHT: 180%">
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">参考：</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: maroon">
						<br />
						<font face="Times New Roman">    1.</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">《</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: maroon">
						<font face="Times New Roman">Effective C++ </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">》关于</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: maroon">
						<font face="Times New Roman">const</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">两种语义的论述</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: maroon">
						<br />
						<font face="Times New Roman">    2.Andrei Alexandrescu </font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">《</span>
				<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: maroon">
						<font face="Times New Roman">volatile</font>
				</span>
				<span style="FONT-SIZE: 10pt; COLOR: maroon; FONT-FAMILY: 新宋体">——编写多线程程序的好帮手》</span>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 20:14 <a href="http://www.cnitblog.com/zouzheng/articles/21774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下GetModuleFileName的四种写法</title><link>http://www.cnitblog.com/zouzheng/articles/21773.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 12:11:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21773.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21773.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21773.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21773.html</trackback:ping><description><![CDATA[
		<p>问题的起因是要把一个东东从Windows移植到基于Linux的嵌入式系统上。移植过程中，遇到了GetModuleFileName的问题。为了解决这个问题，花了不少的时间，也走了不少弯路。下面是整理的结果。</p>
		<p>　　首先摘录一段文字，来源《UNIX Programming FAQ 中文版》<br /> <br /><font face="楷体_GB2312">1.14. 我怎样找到进程的相应可执行文件？<br />　　这个问题可以作为'常见未回答问题'('Frequently Unanswered Questions')的一个好候选，因为事实上提出这个问题经常意味着程序的设计有缺陷。:) <br />　　你能作的'最佳猜测'('best guess')是通过审视'argv[0]'的值而获得。如果它包括一个'/'，那么它可能是可执行程序的绝对或相对(对于在程序开始时的当前目录而言) 路径。如果不包括，那么你可以仿效shell对于'PATH'变量的查询来查找这个程序。但是，不能保证成功，因为有可能执行程序时'argv[0]'是一些任意值，也不排除这个可执行文件在执行后可能已经被更名或删除的情况。 <br />　　如果所有你想做的只是能打印一个和错误消息一起出现的合适的名字，那么最好的方法在'main()'函数中将'argv[0]'的值保存在全局变量中以供整个程序使用。虽然没有保证说'argv[0]'的值总是有意义，但在大多数情况下它是最好的选择。 <br />　　人们询问这个问题的最普通原因是意图定位他们程序的配置文件。这被认为是不好的形式；包含可执行文件的目录应当*只*包含可执行文件，而且基于管理的要求经常试图将配置文件放置在和可执行文件不同的文件系统。 <br />　　试图做这个的一个比较不普通但更正规的理由是允许程序调用'exec()'执行它自己；这是一种用来完全重新初始化进程(比如被用于一些'sendmail'的版本)的办法(比如当一个守护程序捕获一个'SIGHUP'信号)。</font></p>
		<p>　　完全同意上面的观点的！所以并不建议在Linux下去实现GetModuleFileName，不过出于技术的角度，讨论一下这个问题也是可以的。<br /> <br />　　好，下面说说茴字的四种写法。哦，不，是GetModuleFileName的四种写法。</p>
		<p>
				<br />
				<strong>
						<font size="4">GetModuleFileName的四种写法<br /></font>
				</strong>
				<br />
				<strong>方法一：从PATH入手</strong>
				<br />说明：上文提供的思路<br /><br />int GetModuleFileName1( char* sModuleName, char* sFileName, int nSize)<br />{<br /> int ret = -1;<br /> if( strchr( sModuleName,'/' ) != NULL )<br />  strcpy( sFileName, sModuleName );<br /> else<br /> {<br />  char* sPath = getenv("PATH");<br />  char* pHead = sPath;<br />  char* pTail = NULL;<br />  while( pHead != NULL &amp;&amp; *pHead != '\x0' )<br />  {<br />   pTail = strchr( pHead, ':' );<br />   if( pTail != NULL )<br />   {<br />    strncpy( sFileName, pHead, pTail-pHead );<br />    sFileName[pTail-pHead] = '\x0';<br />    pHead = pTail+1;<br />   }<br />   else<br />   {<br />    strcpy( sFileName, pHead );<br />    pHead = NULL;<br />   }<br />   <br />   int nLen = strlen(sFileName);<br />   if( sFileName[nLen] != '/' )sFileName[nLen] = '/';<br />   strcpy( sFileName+nLen+1,sModuleName);<br />   if( 0 == access( sFileName, F_OK ) )<br />   {<br />    ret = 0;<br />    break;<br />   }<br />  }<br /> }<br /> return ret;<br />}</p>
		<p>
				<strong>方法二：利用which命令<br /></strong>说明：  与方法一相比，完全是换汤不换药，之所以放在这里，是因为其中用到了一个小技巧：<strong>即利用popen()实现在代码中执行一段command，并得到其执行的结果。<br /></strong>        <br />int GetModuleFileName2( char* sModuleName, char* sFileName, int nSize)<br />{<br /> int ret = 0;<br /> if( strchr( sModuleName,'/' ) != NULL )<br />  strcpy( sFileName, sModuleName );<br /> else<br /> {<br />  char sBuffer[256] = { 0, };<br />  char sCommand[256] = { 0, };<br />  FILE* fp = NULL;<br />  sprintf( sCommand, "which %s", sModuleName );<br />  if((fp = popen(sCommand,"r")) == NULL){ <br />   ret = -1; <br />  }    <br />  else<br />  {<br />   sFileName[0] = '\x0';<br />   while(!feof(fp)){ <br />    if(fgets(sBuffer,nSize-1,fp) == NULL){ <br />     continue; <br />    } <br />    strcat( sFileName, sBuffer ); <br />   }<br />  }<br />     int nLen = strlen( sFileName );<br />  if( 0 == nLen )<br />   ret = -1;<br />  else<br />   if( sFileName[nLen-1] = '\n' ) sFileName[nLen-1] = '\x0';<br /> }<br /> return ret;<br />}</p>
		<p>
				<strong>方法二：获取环境变量"_"</strong>
		</p>
		<p>int GetModuleFileName3( char* sModuleName, char* sFileName, int nSize)<br />{<br /> int ret = -1;<br />    char* p = getenv("_");<br /> if( p != NULL &amp;&amp; strstr( p, sModuleName ) != NULL )<br /> {<br />  ret = 0;<br />  strcpy( sFileName, p );<br /> }<br /> return ret;<br />}</p>
		<p>
				<strong>方法四：读取/proc/self/maps</strong>
				<br />说明：  细节请参阅 <a href="http://autopackage.org/docs/binreloc/">http://autopackage.org/docs/binreloc/</a><br />int GetModuleFileName4( char* sModuleName, char* sFileName, int nSize)<br />{<br /> int ret = -1;<br /> char sLine[1024] = { 0 };<br /> void* pSymbol = (void*)"";<br /> FILE *fp;<br /> char *pPath;</p>
		<p> fp = fopen ("/proc/self/maps", "r");<br /> if ( fp != NULL )<br /> {<br />  while (!feof (fp))<br />  {<br />   unsigned long start, end;</p>
		<p>   if ( !fgets (sLine, sizeof (sLine), fp))<br />    continue;<br />   if ( !strstr (sLine, " r-xp ") || !strchr (sLine, '/'))<br />    continue;</p>
		<p>   sscanf (sLine, "%lx-%lx ", &amp;start, &amp;end);<br />   if (pSymbol &gt;= (void *) start &amp;&amp; pSymbol &lt; (void *) end)<br />   {<br />    char *tmp;<br />    size_t len;</p>
		<p>    /* Extract the filename; it is always an absolute path */<br />    pPath = strchr (sLine, '/');</p>
		<p>    /* Get rid of the newline */<br />    tmp = strrchr (pPath, '\n');<br />    if (tmp) *tmp = 0;</p>
		<p>    /* Get rid of "(deleted)" */<br />    //len = strlen (pPath);<br />    //if (len &gt; 10 &amp;&amp; strc </p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 20:11 <a href="http://www.cnitblog.com/zouzheng/articles/21773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#pragma中一些常用的参数</title><link>http://www.cnitblog.com/zouzheng/articles/21772.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 12:09:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21772.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21772.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21772.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21772.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21772.html</trackback:ping><description><![CDATA[
		<p>#pragma中一些常用的参数<br />在所有的预处理指令中，#Pragma 指令可能是最复杂的了，它的作用是设定编译器的状态或<br />者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与<br />C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器<br />或操作系统专有的,且对于每个编译器都是不同的。<br />其格式一般为: #Pragma Para<br />其中Para 为参数，下面来看一些常用的参数。</p>
		<p>(1)message 参数。 Message 参数是我最喜欢的一个参数，它能够在编译信息输出窗<br />口中输出相应的信息，这对于源代码信息的控制是非常重要的。其使用方法为：<br />#Pragma message(“消息文本”)<br />当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。<br />当我们在程序中定义了许多宏来控制源代码版本的时候，我们自己有可能都会忘记有没有正<br />确的设置这些宏，此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自<br />己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法<br />#ifdef _X86<br />#Pragma message(“_X86 macro activated!”)<br />#endif<br />当我们定义了_X86这个宏以后，应用程序在编译时就会在编译输出窗口里显示“_<br />X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了<br />。</p>
		<p>(2)另一个使用得比较多的pragma参数是code_seg。格式如：<br />#pragma code_seg( ["section-name"[,"section-class"] ] )<br />它能够设置程序中函数代码存放的代码段，当我们开发驱动程序的时候就会使用到它。</p>
		<p>(3)#pragma once (比较常用）<br />只要在头文件的最开始加入这条指令就能够保证头文件被编译一次，这条指令实际上在VC6(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )<br />等价于：<br />#pragma warning(disable:4507 34) // 不显示4507和34号警告信息<br />#pragma warning(once:4385) // 4385号警告信息仅报告一次<br />#pragma warning(error:164) // 把164号警告信息作为一个错误。<br />同时这个pragma warning 也支持如下格式：<br />#pragma warning( push [ ,n ] )<br />#pragma warning( pop )<br />这里n代表一个警告等级(1---4)。<br />#pragma warning( push )保存所有警告信息的现有的警告状态。<br />#pragma warning( push, n)保存所有警告信息的现有的警告状态，并且把全局警告<br />等级设定为n。<br />#pragma warning( pop )向栈中弹出最后一个警告信息，在入栈和出栈之间所作的<br />一切改动取消。例如：<br />#pragma warning( push )<br />#pragma warning( disable : 4705 )<br />#pragma warning( disable : 4706 )<br />#pragma warning( disable : 4707 )<br />//.......<br />#pragma warning( pop )<br />在这段代码的最后，重新保存所有的警告信息(包括4705，4706和4707)。<br />（7）pragma comment(...)<br />该指令将一个注释记录放入一个对象文件或可执行文件中。<br />常用的lib关键字，可以帮我们连入一个库文件。</p>
		<p>
				<br />补充一个 #pragma pack 1 将c/c++结构体里面的数据按照1字节对齐。这条指令在通信的时<br />候很常用</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21772.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 20:09 <a href="http://www.cnitblog.com/zouzheng/articles/21772.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用GDB调试程序</title><link>http://www.cnitblog.com/zouzheng/articles/21771.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 12:07:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21771.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21771.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21771.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21771.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21771.html</trackback:ping><description><![CDATA[
		<p align="center">
				<font face="Courier New" size="4">
						<strong>用GDB调试程序</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">
						<strong>GDB概述<br />————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">GDB 是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许，各位比较喜欢那种图形界面方式的，像VC、BCB等IDE的调试，但如果你是在 UNIX平台下做软件，你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长，尺有所短”就是这个道理。</font>
		</p>
		<p>
				<font face="Courier New">一般来说，GDB主要帮忙你完成下面四个方面的功能：</font>
		</p>
		<p>
				<font face="Courier New">    1、启动你的程序，可以按照你的自定义的要求随心所欲的运行程序。<br />    2、可让被调试的程序在你所指定的调置的断点处停住。（断点可以是条件表达式）<br />    3、当程序被停住时，可以检查此时你的程序中所发生的事。<br />    4、动态的改变你程序的执行环境。</font>
		</p>
		<p>
				<font face="Courier New">从上面看来，GDB和一般的调试工具没有什么两样，基本上也是完成这些功能，不过在细节上，你会发现GDB这个调试工具的强大，大家可能比较习惯了图形化的调试工具，但有时候，命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>一个调试示例<br />——————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">源程序：tst.c</font>
		</p>
		<p>
				<font face="Courier New">     1 #include &lt;stdio.h&gt;<br />     2<br />     3 int func(int n)<br />     4 {<br />     5         int sum=0,i;<br />     6         for(i=0; i&lt;n; i++)<br />     7         {<br />     8                 sum+=i;<br />     9         }<br />    10         return sum;<br />    11 }<br />    12<br />    13<br />    14 main()<br />    15 {<br />    16         int i;<br />    17         long result = 0;<br />    18         for(i=1; i&lt;=100; i++)<br />    19         {<br />    20                 result += i;<br />    21         }<br />    22<br />    23        printf("result[1-100] = %d \n", result );<br />    24        printf("result[1-250] = %d \n", func(250) );<br />    25 }</font>
		</p>
		<p>
				<font face="Courier New">编译生成执行文件：（Linux下）<br />    hchen/test&gt; cc -g tst.c -o tst</font>
		</p>
		<p>
				<font face="Courier New">使用GDB调试：</font>
		</p>
		<p>
				<font face="Courier New">hchen/test&gt; gdb tst  &lt;---------- 启动GDB<br />GNU gdb 5.1.1<br />Copyright 2002 Free Software Foundation, Inc.<br />GDB is free software, covered by the GNU General Public License, and you are<br />welcome to change it and/or distribute copies of it under certain conditions.<br />Type "show copying" to see the conditions.<br />There is absolutely no warranty for GDB.  Type "show warranty" for details.<br />This GDB was configured as "i386-suse-linux"...<br />(gdb) l     &lt;-------------------- l命令相当于list，从第一行开始例出原码。<br />1        #include &lt;stdio.h&gt;<br />2<br />3        int func(int n)<br />4        {<br />5                int sum=0,i;<br />6                for(i=0; i&lt;n; i++)<br />7                {<br />8                        sum+=i;<br />9                }<br />10               return sum;<br />(gdb)       &lt;-------------------- 直接回车表示，重复上一次命令<br />11       }<br />12<br />13<br />14       main()<br />15       {<br />16               int i;<br />17               long result = 0;<br />18               for(i=1; i&lt;=100; i++)<br />19               {<br />20                       result += i;    <br />(gdb) break 16    &lt;-------------------- 设置断点，在源程序第16行处。<br />Breakpoint 1 at 0x8048496: file tst.c, line 16.<br />(gdb) break func  &lt;-------------------- 设置断点，在函数func()入口处。<br />Breakpoint 2 at 0x8048456: file tst.c, line 5.<br />(gdb) info break  &lt;-------------------- 查看断点信息。<br />Num Type           Disp Enb Address    What<br />1   breakpoint     keep y   0x08048496 in main at tst.c:16<br />2   breakpoint     keep y   0x08048456 in func at tst.c:5<br />(gdb) r           &lt;--------------------- 运行程序，run命令简写<br />Starting program: /home/hchen/test/tst</font>
		</p>
		<p>
				<font face="Courier New">Breakpoint 1, main () at tst.c:17    &lt;---------- 在断点处停住。<br />17               long result = 0;<br />(gdb) n          &lt;--------------------- 单条语句执行，next命令简写。<br />18               for(i=1; i&lt;=100; i++)<br />(gdb) n<br />20                       result += i;<br />(gdb) n<br />18               for(i=1; i&lt;=100; i++)<br />(gdb) n<br />20                       result += i;<br />(gdb) c          &lt;--------------------- 继续运行程序，continue命令简写。<br />Continuing.<br />result[1-100] = 5050       &lt;----------程序输出。</font>
		</p>
		<p>
				<font face="Courier New">Breakpoint 2, func (n=250) at tst.c:5<br />5                int sum=0,i;<br />(gdb) n<br />6                for(i=1; i&lt;=n; i++)<br />(gdb) p i        &lt;--------------------- 打印变量i的值，print命令简写。<br />$1 = 134513808<br />(gdb) n<br />8                        sum+=i;<br />(gdb) n<br />6                for(i=1; i&lt;=n; i++)<br />(gdb) p sum<br />$2 = 1<br />(gdb) n<br />8                        sum+=i;<br />(gdb) p i<br />$3 = 2<br />(gdb) n<br />6                for(i=1; i&lt;=n; i++)<br />(gdb) p sum<br />$4 = 3<br />(gdb) bt        &lt;--------------------- 查看函数堆栈。<br />#0  func (n=250) at tst.c:5<br />#1  0x080484e4 in main () at tst.c:24<br />#2  0x400409ed in __libc_start_main () from /lib/libc.so.6<br />(gdb) finish    &lt;--------------------- 退出函数。<br />Run till exit from #0  func (n=250) at tst.c:5<br />0x080484e4 in main () at tst.c:24<br />24              printf("result[1-250] = %d \n", func(250) );<br />Value returned is $6 = 31375<br />(gdb) c     &lt;--------------------- 继续运行。<br />Continuing.<br />result[1-250] = 31375    &lt;----------程序输出。</font>
		</p>
		<p>
				<font face="Courier New">Program exited with code 027. &lt;--------程序退出，调试结束。<br />(gdb) q     &lt;--------------------- 退出gdb。<br />hchen/test&gt;</font>
		</p>
		<p>
				<font face="Courier New">好了，有了以上的感性认识，还是让我们来系统地认识一下gdb吧。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<br />
				<font face="Courier New">
						<strong>使用GDB<br />————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序，首先在编译时，我们必须要把调试信息加到可执行文件中。使用编译器（cc/gcc/g++）的 -g 参数可以做到这一点。如：</font>
		</p>
		<p>
				<font face="Courier New">    &gt; cc -g hello.c -o hello<br />    &gt; g++ -g hello.cpp -o hello</font>
		</p>
		<p>
				<font face="Courier New">如果没有-g，你将看不见程序的函数名、变量名，所代替的全是运行时的内存地址。当你用-g把调试信息加入之后，并成功编译目标代码以后，让我们来看看如何用gdb来调试他。</font>
		</p>
		<p>
				<font face="Courier New">启动GDB的方法有以下几种：</font>
		</p>
		<p>
				<font face="Courier New">    1、gdb &lt;program&gt; <br />       program也就是你的执行文件，一般在当然目录下。</font>
		</p>
		<p>
				<font face="Courier New">    2、gdb &lt;program&gt; core<br />       用gdb同时调试一个运行程序和core文件，core是程序非法执行后core dump后产生的文件。</font>
		</p>
		<p>
				<font face="Courier New">    3、gdb &lt;program&gt; &lt;PID&gt;<br />       如果你的程序是一个服务程序，那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去，并调试他。program应该在PATH环境变量中搜索得到。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<font face="Courier New">GDB启动时，可以加上一些GDB的启动开关，详细的开关可以用gdb -help查看。我在下面只例举一些比较常用的参数：</font>
		</p>
		<p>
				<font face="Courier New">    -symbols &lt;file&gt; <br />    -s &lt;file&gt; <br />    从指定文件中读取符号表。</font>
		</p>
		<p>
				<font face="Courier New">    -se file <br />    从指定文件中读取符号表信息，并把他用在可执行文件中。</font>
		</p>
		<p>
				<font face="Courier New">    -core &lt;file&gt;<br />    -c &lt;file&gt; <br />    调试时core dump的core文件。</font>
		</p>
		<p>
				<font face="Courier New">    -directory &lt;directory&gt;<br />    -d &lt;directory&gt;<br />    加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。</font>
		</p>
		<p>
				<font face="Courier New">
						<strong>GDB的命令概貌<br />———————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">启动gdb后，就你被带入gdb的调试环境中，就可以使用gdb的命令开始调试程序了，gdb的命令可以使用help命令来查看，如下所示：</font>
		</p>
		<p>
				<font face="Courier New">    /home/hchen&gt; gdb<br />    GNU gdb 5.1.1<br />    Copyright 2002 Free Software Foundation, Inc.<br />    GDB is free software, covered by the GNU General Public License, and you are<br />    welcome to change it and/or distribute copies of it under certain conditions.<br />    Type "show copying" to see the conditions.<br />    There is absolutely no warranty for GDB.  Type "show warranty" for details.<br />    This GDB was configured as "i386-suse-linux".<br />    (gdb) help<br />    List of classes of commands:</font>
		</p>
		<p>
				<font face="Courier New">    aliases -- Aliases of other commands<br />    breakpoints -- Making program stop at certain points<br />    data -- Examining data<br />    files -- Specifying and examining files<br />    internals -- Maintenance commands<br />    obscure -- Obscure features<br />    running -- Running the program<br />    stack -- Examining the stack<br />    status -- Status inquiries<br />    support -- Support facilities<br />    tracepoints -- Tracing of program execution without stopping the program<br />    user-defined -- User-defined commands</font>
		</p>
		<p>
				<font face="Courier New">    Type "help" followed by a class name for a list of commands in that class.<br />    Type "help" followed by command name for full documentation.<br />    Command name abbreviations are allowed if unambiguous.<br />    (gdb)</font>
		</p>
		<p>
				<font face="Courier New">gdb 的命令很多，gdb把之分成许多个种类。help命令只是例出gdb的命令种类，如果要看种类中的命令，可以使用help &lt;class&gt; 命令，如：help breakpoints，查看设置断点的所有命令。也可以直接help &lt;command&gt;来查看命令的帮助。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">gdb中，输入命令时，可以不用打全命令，只用打命令的前几个字符就可以了，当然，命令的前几个字符应该要标志着一个唯一的命令，在Linux下，你可以敲击两次TAB键来补齐命令的全称，如果有重复的，那么gdb会把其例出来。<br />    <br />    示例一：在进入函数func时，设置一个断点。可以敲入break func，或是直接就是b func<br />    (gdb) b func<br />    Breakpoint 1 at 0x8048458: file hello.c, line 10.<br /> <br />    示例二：敲入b按两次TAB键，你会看到所有b打头的命令：<br />    (gdb) b<br />    backtrace  break      bt<br />    (gdb)</font>
		</p>
		<p>
				<font face="Courier New">    示例三：只记得函数的前缀，可以这样：<br />    (gdb) b make_ &lt;按TAB键&gt;<br />    （再按下一次TAB键，你会看到:）<br />    make_a_section_from_file     make_environ<br />    make_abs_section             make_function_type<br />    make_blockvector             make_pointer_type<br />    make_cleanup                 make_reference_type<br />    make_command                 make_symbol_completion_list<br />    (gdb) b make_<br />    GDB把所有make开头的函数全部例出来给你查看。</font>
		</p>
		<p>
				<font face="Courier New">    示例四：调试C++的程序时，有可以函数名一样。如：<br />    (gdb) b 'bubble( M-? <br />    bubble(double,double)    bubble(int,int)<br />    (gdb) b 'bubble(<br />    你可以查看到C++中的所有的重载函数及参数。（注：M-?和“按两次TAB键”是一个意思）</font>
		</p>
		<p>
				<font face="Courier New">要退出gdb时，只用发quit或命令简称q就行了。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<font face="Courier New">
						<strong>GDB中运行UNIX的shell程序<br />————————————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">在gdb环境中，你可以执行UNIX的shell的命令，使用gdb的shell命令来完成：</font>
		</p>
		<p>
				<font face="Courier New">    shell &lt;command string&gt;<br />    调用UNIX的shell来执行&lt;command string&gt;，环境变量SHELL中定义的UNIX的shell将会被用来执行&lt;command string&gt;，如果SHELL没有定义，那就使用UNIX的标准shell：/bin/sh。（在Windows中使用Command.com或 cmd.exe）</font>
		</p>
		<p>
				<font face="Courier New">还有一个gdb命令是make：<br />    make &lt;make-args&gt; <br />    可以在gdb中执行make命令来重新build自己的程序。这个命令等价于“shell make &lt;make-args&gt;”。 </font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<br />
				<font face="Courier New">
						<strong>在GDB中运行程序<br />————————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">当以gdb &lt;program&gt;方式启动gdb后，gdb会在PATH路径和当前目录中搜索&lt;program&gt;的源文件。如要确认gdb是否读到源文件，可使用l或list命令，看看gdb是否能列出源代码。</font>
		</p>
		<p>
				<font face="Courier New">在gdb中，运行程序使用r或是run命令。程序的运行，你有可能需要设置下面四方面的事。</font>
		</p>
		<p>
				<font face="Courier New">1、程序运行参数。<br />    set args 可指定运行时参数。（如：set args 10 20 30 40 50）<br />    show args 命令可以查看设置好的运行参数。</font>
		</p>
		<p>
				<font face="Courier New">2、运行环境。<br />    path &lt;dir&gt; 可设定程序的运行路径。<br />    show paths 查看程序的运行路径。<br />    set environment varname [=value] 设置环境变量。如：set env USER=hchen<br />    show environment [varname] 查看环境变量。</font>
		</p>
		<p>
				<font face="Courier New">3、工作目录。<br />    cd &lt;dir&gt; 相当于shell的cd命令。<br />    pwd 显示当前的所在目录。</font>
		</p>
		<p>
				<font face="Courier New">4、程序的输入输出。<br />    info terminal 显示你程序用到的终端的模式。<br />    使用重定向控制程序输出。如：run &gt; outfile<br />    tty命令可以指写输入输出的终端设备。如：tty /dev/ttyb</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>调试已运行的程序<br />————————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">两种方法：<br />1、在UNIX下用ps查看正在运行的程序的PID（进程ID），然后用gdb &lt;program&gt; PID格式挂接正在运行的程序。<br />2、先用gdb &lt;program&gt;关联上源代码，并进行gdb，在gdb中用attach命令来挂接进程的PID。并用detach来取消挂接的进程。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<font face="Courier New">
						<strong>暂停 / 恢复程序运行<br />—————————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">调试程序中，暂停程序运行是必须的，GDB可以方便地暂停程序的运行。你可以设置程序的在哪行停住，在什么条件下停住，在收到什么信号时停往等等。以便于你查看运行时的变量，以及运行时的流程。</font>
		</p>
		<p>
				<font face="Courier New">当进程被gdb停住时，你可以使用info program 来查看程序的是否在运行，进程号，被暂停的原因。</font>
		</p>
		<p>
				<font face="Courier New">在gdb中，我们可以有以下几种暂停方式：断点（BreakPoint）、观察点（WatchPoint）、捕捉点（CatchPoint）、信号（Signals）、线程停止（Thread Stops）。如果要恢复程序运行，可以使用c或是continue命令。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>一、设置断点（BreakPoint）</strong>
						<br />    <br />    我们用break命令来设置断点。正面有几点设置断点的方法：<br />    <br />    break &lt;function&gt; <br />        在进入指定函数时停住。C++中可以使用class::function或function(type,type)格式来指定函数名。</font>
		</p>
		<p>
				<font face="Courier New">    break &lt;linenum&gt;<br />        在指定行号停住。</font>
		</p>
		<p>
				<font face="Courier New">    break +offset <br />    break -offset <br />        在当前行号的前面或后面的offset行停住。offiset为自然数。</font>
		</p>
		<p>
				<font face="Courier New">    break filename:linenum <br />        在源文件filename的linenum行处停住。</font>
		</p>
		<p>
				<font face="Courier New">    break filename:function <br />        在源文件filename的function函数的入口处停住。</font>
		</p>
		<p>
				<font face="Courier New">    break *address<br />        在程序运行的内存地址处停住。</font>
		</p>
		<p>
				<font face="Courier New">    break <br />        break命令没有参数时，表示在下一条指令处停住。</font>
		</p>
		<p>
				<font face="Courier New">    break ... if &lt;condition&gt;<br />        ...可以是上述的参数，condition表示条件，在条件成立时停住。比如在循环境体中，可以设置break if i=100，表示当i为100时停住程序。</font>
		</p>
		<p>
				<font face="Courier New">    查看断点时，可使用info命令，如下所示：（注：n表示断点号）<br />    info breakpoints [n] <br />    info break [n] <br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>二、设置观察点（WatchPoint）</strong>
						<br />    <br />    观察点一般来观察某个表达式（变量也是一种表达式）的值是否有变化了，如果有变化，马上停住程序。我们有下面的几种方法来设置观察点：<br />    <br />    watch &lt;expr&gt;<br />        为表达式（变量）expr设置一个观察点。一量表达式值有变化时，马上停住程序。<br />        <br />    rwatch &lt;expr&gt;<br />        当表达式（变量）expr被读时，停住程序。<br />        <br />    awatch &lt;expr&gt;<br />        当表达式（变量）的值被读或被写时，停住程序。<br />    <br />    info watchpoints<br />        列出当前所设置了的所有观察点。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>三、设置捕捉点（CatchPoint）</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    你可设置捕捉点来补捉程序运行时的一些事件。如：载入共享库（动态链接库）或是C++的异常。设置捕捉点的格式为：<br />    <br />    catch &lt;event&gt;<br />        当event发生时，停住程序。event可以是下面的内容：<br />        1、throw 一个C++抛出的异常。（throw为关键字）<br />        2、catch 一个C++捕捉到的异常。（catch为关键字）<br />        3、exec 调用系统调用exec时。（exec为关键字，目前此功能只在HP-UX下有用）<br />        4、fork 调用系统调用fork时。（fork为关键字，目前此功能只在HP-UX下有用）<br />        5、vfork 调用系统调用vfork时。（vfork为关键字，目前此功能只在HP-UX下有用）<br />        6、load 或 load &lt;libname&gt; 载入共享库（动态链接库）时。（load为关键字，目前此功能只在HP-UX下有用）<br />        7、unload 或 unload &lt;libname&gt; 卸载共享库（动态链接库）时。（unload为关键字，目前此功能只在HP-UX下有用）</font>
		</p>
		<p>
				<font face="Courier New">    tcatch &lt;event&gt; <br />        只设置一次捕捉点，当程序停住以后，应点被自动删除。</font>
		</p>
		<p>
				<strong>
						<font face="Courier New">四、维护停止点</font>
				</strong>
		</p>
		<p>
				<font face="Courier New">上面说了如何设置程序的停止点，GDB中的停止点也就是上述的三类。在GDB中，如果你觉得已定义好的停止点没有用了，你可以使用delete、clear、disable、enable这几个命令来进行维护。</font>
		</p>
		<p>
				<font face="Courier New">    clear<br />        清除所有的已定义的停止点。</font>
		</p>
		<p>
				<font face="Courier New">    clear &lt;function&gt;<br />    clear &lt;filename:function&gt;<br />        清除所有设置在函数上的停止点。</font>
		</p>
		<p>
				<font face="Courier New">    clear &lt;linenum&gt;<br />    clear &lt;filename:linenum&gt;<br />        清除所有设置在指定行上的停止点。</font>
		</p>
		<p>
				<font face="Courier New">    delete [breakpoints] [range...]<br />        删除指定的断点，breakpoints为断点号。如果不指定断点号，则表示删除所有的断点。range 表示断点号的范围（如：3-7）。其简写命令为d。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">比删除更好的一种方法是disable停止点，disable了的停止点，GDB不会删除，当你还需要时，enable即可，就好像回收站一样。</font>
		</p>
		<p>
				<font face="Courier New">    disable [breakpoints] [range...]<br />        disable所指定的停止点，breakpoints为停止点号。如果什么都不指定，表示disable所有的停止点。简写命令是dis.</font>
		</p>
		<p>
				<font face="Courier New">    enable [breakpoints] [range...]<br />        enable所指定的停止点，breakpoints为停止点号。</font>
		</p>
		<p>
				<font face="Courier New">    enable [breakpoints] once range...<br />        enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动disable。</font>
		</p>
		<p>
				<font face="Courier New">    enable [breakpoints] delete range...<br />        enable所指定的停止点一次，当程序停止后，该停止点马上被GDB自动删除。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<font face="Courier New">
						<strong>五、停止条件维护</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">前面在说到设置断点时，我们提到过可以设置一个条件，当条件成立时，程序自动停止，这是一个非常强大的功能，这里，我想专门说说这个条件的相关维护命令。一般来说，为断点设置一个条件，我们使用if关键词，后面跟其断点条件。并且，条件设置好后，我们可以用condition命令来修改断点的条件。（只有 break和watch命令支持if，catch目前暂不支持if）</font>
		</p>
		<p>
				<font face="Courier New">    condition &lt;bnum&gt; &lt;expression&gt;<br />        修改断点号为bnum的停止条件为expression。</font>
		</p>
		<p>
				<font face="Courier New">    condition &lt;bnum&gt;<br />        清除断点号为bnum的停止条件。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">还有一个比较特殊的维护命令ignore，你可以指定程序运行时，忽略停止条件几次。</font>
		</p>
		<p>
				<font face="Courier New">    ignore &lt;bnum&gt; &lt;count&gt;<br />        表示忽略断点号为bnum的停止条件count次。</font>
		</p>
		<p>
				<font face="Courier New">
				</font> </p>
		<p>
				<font face="Courier New">
						<strong>六、为停止点设定运行命令</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">我们可以使用GDB提供的command命令来设置停止点的运行命令。也就是说，当运行的程序在被停止住时，我们可以让其自动运行一些别的命令，这很有利行自动化调试。对基于GDB的自动化调试是一个强大的支持。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">    commands [bnum]<br />    ... command-list ...<br />    end</font>
		</p>
		<p>
				<font face="Courier New">    为断点号bnum指写一个命令列表。当程序被该断点停住时，gdb会依次运行命令列表中的命令。</font>
		</p>
		<p>
				<font face="Courier New">    例如：</font>
		</p>
		<p>
				<font face="Courier New">        break foo if x&gt;0<br />        commands<br />        printf "x is %d\n",x<br />        continue<br />        end<br />        断点设置在函数foo中，断点条件是x&gt;0，如果程序被断住后，也就是，一旦x的值在foo函数中大于0，GDB会自动打印出x的值，并继续运行程序。</font>
		</p>
		<p>
				<font face="Courier New">如果你要清除断点上的命令序列，那么只要简单的执行一下commands命令，并直接在打个end就行了。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>七、断点菜单</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">在C ++中，可能会重复出现同一个名字的函数若干次（函数重载），在这种情况下，break &lt;function&gt;不能告诉GDB要停在哪个函数的入口。当然，你可以使用break &lt;function(type)&gt;也就是把函数的参数类型告诉GDB，以指定一个函数。否则的话，GDB会给你列出一个断点菜单供你选择你所需要的断点。你只要输入你菜单列表中的编号就可以了。如：</font>
		</p>
		<p>
				<font face="Courier New">    (gdb) b String::after<br />    [0] cancel<br />    [1] all<br />    [2] file:String.cc; line number:867<br />    [3] file:String.cc; line number:860<br />    [4] file:String.cc; line number:875<br />    [5] file:String.cc; line number:853<br />    [6] file:String.cc; line number:846<br />    [7] file:String.cc; line number:735<br />    &gt; 2 4 6<br />    Breakpoint 1 at 0xb26c: file String.cc, line 867.<br />    Breakpoint 2 at 0xb344: file String.cc, line 875.<br />    Breakpoint 3 at 0xafcc: file String.cc, line 846.<br />    Multiple breakpoints were set.<br />    Use the "delete" command to delete unwanted<br />     breakpoints.<br />    (gdb)</font>
		</p>
		<p>
				<font face="Courier New">可见，GDB列出了所有after的重载函数，你可以选一下列表编号就行了。0表示放弃设置断点，1表示所有函数都设置断点。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>八、恢复程序运行和单步调试</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">当程序被停住了，你可以用continue命令恢复程序的运行直到程序结束，或下一个断点到来。也可以使用step或next命令单步跟踪程序。</font>
		</p>
		<p>
				<font face="Courier New">    continue [ignore-count]<br />    c [ignore-count]<br />    fg [ignore-count]<br />        恢复程序运行，直到程序结束，或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue，c，fg三个命令都是一样的意思。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">    step &lt;count&gt;<br />        单步跟踪，如果有函数调用，他会进入该函数。进入函数的前提是，此函数被编译有debug信息。很像VC等工具中的step in。后面可以加count也可以不加，不加表示一条条地执行，加表示执行后面的count条指令，然后再停住。</font>
		</p>
		<p>
				<font face="Courier New">    next &lt;count&gt;<br />        同样单步跟踪，如果有函数调用，他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加，不加表示一条条地执行，加表示执行后面的count条指令，然后再停住。</font>
		</p>
		<p>
				<font face="Courier New">    set step-mode<br />    set step-mode on<br />        打开step-mode模式，于是，在进行单步跟踪时，程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码。</font>
		</p>
		<p>
				<font face="Courier New">    set step-mod off<br />        关闭step-mode模式。</font>
		</p>
		<p>
				<font face="Courier New">    finish<br />        运行程序，直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。</font>
		</p>
		<p>
				<font face="Courier New">    until 或 u<br />        当你厌倦了在一个循环体内单步跟踪时，这个命令可以运行程序直到退出循环体。</font>
		</p>
		<p>
				<font face="Courier New">    stepi 或 si<br />    nexti 或 ni<br />        单步跟踪一条机器指令！一条程序代码有可能由数条机器指令完成，stepi和nexti可以单步执行机器指令。与之一样有相同功能的命令是 “display/i $pc” ，当运行完这个命令后，单步跟踪会在打出程序代码的同时打出机器指令（也就是汇编代码）</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>九、信号（Signals）</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">信号是一种软中断，是一种处理异步事件的方法。一般来说，操作系统都支持许多信号。尤其是UNIX，比较重要应用程序一般都会处理信号。UNIX定义了许多信号，比如SIGINT表示中断字符信号，也就是Ctrl+C的信号，SIGBUS表示硬件故障的信号；SIGCHLD表示子进程状态改变信号； SIGKILL表示终止程序运行的信号，等等。信号量编程是UNIX下非常重要的一种技术。</font>
		</p>
		<p>
				<font face="Courier New">GDB有能力在你调试程序的时候处理任何一种信号，你可以告诉GDB需要处理哪一种信号。你可以要求GDB收到你所指定的信号时，马上停住正在运行的程序，以供你进行调试。你可以用GDB的handle命令来完成这一功能。</font>
		</p>
		<p>
				<font face="Courier New">    handle &lt;signal&gt; &lt;keywords...&gt;<br />        在GDB中定义一个信号处理。信号&lt;signal&gt;可以以SIG开头或不以SIG开头，可以用定义一个要处理信号的范围（如：SIGIO- SIGKILL，表示处理从SIGIO信号到SIGKILL的信号，其中包括SIGIO，SIGIOT，SIGKILL三个信号），也可以使用关键字 all来标明要处理所有的信号。一旦被调试的程序接收到信号，运行程序马上会被GDB停住，以供调试。其&lt;keywords&gt;可以是以下几种关键字的一个或多个。</font>
		</p>
		<p>
				<font face="Courier New">        nostop<br />            当被调试的程序收到信号时，GDB不会停住程序的运行，但会打出消息告诉你收到这种信号。<br />        stop<br />            当被调试的程序收到信号时，GDB会停住你的程序。<br />        print<br />            当被调试的程序收到信号时，GDB会显示出一条信息。<br />        noprint<br />            当被调试的程序收到信号时，GDB不会告诉你收到信号的信息。<br />        pass<br />        noignore<br />            当被调试的程序收到信号时，GDB不处理信号。这表示，GDB会把这个信号交给被调试程序会处理。<br />        nopass<br />        ignore<br />            当被调试的程序收到信号时，GDB不会让被调试程序来处理这个信号。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">    info signals<br />    info handle<br />        查看有哪些信号在被GDB检测中。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>十、线程（Thread Stops）</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">如果你程序是多线程的话，你可以定义你的断点是否在所有的线程上，或是在某个特定的线程。GDB很容易帮你完成这一工作。</font>
		</p>
		<p>
				<font face="Courier New">    break &lt;linespec&gt; thread &lt;threadno&gt;<br />    break &lt;linespec&gt; thread &lt;threadno&gt; if ...<br />        linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID，注意，这个ID是GDB分配的，你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定thread &lt;threadno&gt;则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如：<br />    <br />        (gdb) break frik.c:13 thread 28 if bartab &gt; lim</font>
		</p>
		<p>
				<font face="Courier New">    当你的程序被GDB停住时，所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时，所有的线程也会被恢复运行。那怕是主进程在被单步调试时。</font>
		</p>
		<p>
				<strong>
						<font face="Courier New">查看栈信息<br />—————</font>
				</strong>
		</p>
		<p>
				<font face="Courier New">当程序被停住了，你需要做的第一件事就是查看程序是在哪里停住的。当你的程序调用了一个函数，函数的地址，函数参数，函数内的局部变量都会被压入“栈”（Stack）中。你可以用GDB命令来查看当前的栈中的信息。</font>
		</p>
		<p>
				<font face="Courier New">下面是一些查看函数调用栈信息的GDB命令：</font>
		</p>
		<p>
				<font face="Courier New">    backtrace <br />    bt <br />        打印当前的函数调用栈的所有信息。如：<br />        <br />        (gdb) bt<br />        #0  func (n=250) at tst.c:6<br />        #1  0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30<br />        #2  0x400409ed in __libc_start_main () from /lib/libc.so.6<br />        <br />        从上可以看出函数的调用栈信息：__libc_start_main --&gt; main() --&gt; func()<br />        <br />    <br />    backtrace &lt;n&gt;<br />    bt &lt;n&gt; <br />        n是一个正整数，表示只打印栈顶上n层的栈信息。</font>
		</p>
		<p>
				<font face="Courier New">    backtrace &lt;-n&gt; <br />    bt &lt;-n&gt; <br />        -n表一个负整数，表示只打印栈底下n层的栈信息。<br />        <br />如果你要查看某一层的信息，你需要在切换当前的栈，一般来说，程序停止时，最顶层的栈就是当前栈，如果你要查看栈下面层的详细信息，首先要做的是切换当前栈。</font>
		</p>
		<p>
				<font face="Courier New">    frame &lt;n&gt; <br />    f &lt;n&gt; <br />        n是一个从0开始的整数，是栈中的层编号。比如：frame 0，表示栈顶，frame 1，表示栈的第二层。<br />    <br />    up &lt;n&gt;<br />        表示向栈的上面移动n层，可以不打n，表示向上移动一层。 <br />        <br />    down &lt;n&gt; <br />        表示向栈的下面移动n层，可以不打n，表示向下移动一层。 <br />        </font>
		</p>
		<p>
				<font face="Courier New">    上面的命令，都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令：<br />    <br />            select-frame &lt;n&gt; 对应于 frame 命令。<br />            up-silently &lt;n&gt; 对应于 up 命令。<br />            down-silently &lt;n&gt; 对应于 down 命令。</font>
		</p>
		<p>
				<font face="Courier New">    <br />查看当前栈层的信息，你可以用以下GDB命令：</font>
		</p>
		<p>
				<font face="Courier New">    frame 或 f <br />        会打印出这些信息：栈的层编号，当前的函数名，函数参数值，函数所在文件及行号，函数执行到的语句。<br />    <br />    info frame <br />    info f <br />        这个命令会打印出更为详细的当前栈层的信息，只不过，大多数都是运行时的内内地址。比如：函数地址，调用函数的地址，被调用函数的地址，目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。如：<br />            (gdb) info f<br />            Stack level 0, frame at 0xbffff5d4:<br />             eip = 0x804845d in func (tst.c:6); saved eip 0x8048524<br />             called by frame at 0xbffff60c<br />             source language c.<br />             Arglist at 0xbffff5d4, args: n=250<br />             Locals at 0xbffff5d4, Previous frame's sp is 0x0<br />             Saved registers:<br />              ebp at 0xbffff5d4, eip at 0xbffff5d8<br />              <br />     info args<br />        打印出当前函数的参数名及其值。<br />     <br />     info locals<br />        打印出当前函数中所有局部变量及其值。<br />        <br />     info catch<br />        打印出当前的函数中的异常处理信息。<br />        <br />        <br />        <br />        <br /><strong>查看源程序<br />—————</strong></font>
		</p>
		<p>
				<font face="Courier New">
						<strong>一、显示源代码</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    GDB 可以打印出所调试程序的源代码，当然，在程序编译时一定要加上-g的参数，把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后， GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。还是来看一看查看源代码的GDB命令吧。<br />    <br />    list &lt;linenum&gt;<br />        显示程序第linenum行的周围的源程序。<br />    <br />    list &lt;function&gt; <br />        显示函数名为function的函数的源程序。<br />        <br />    list <br />        显示当前行后面的源程序。<br />    <br />    list - <br />        显示当前行前面的源程序。</font>
		</p>
		<p>
				<font face="Courier New">一般是打印当前行的上5行和下5行，如果显示函数是是上2行下8行，默认是10行，当然，你也可以定制显示的范围，使用下面命令可以设置一次显示源程序的行数。</font>
		</p>
		<p>
				<font face="Courier New">    set listsize &lt;count&gt;<br />        设置一次显示源代码的行数。<br />        <br />    show listsize<br />        查看当前listsize的设置。<br />        </font>
		</p>
		<p>
				<font face="Courier New">list命令还有下面的用法：</font>
		</p>
		<p>
				<font face="Courier New">    list &lt;first&gt;, &lt;last&gt;<br />        显示从first行到last行之间的源代码。<br />    <br />    list , &lt;last&gt;<br />        显示从当前行到last行之间的源代码。<br />        <br />    list +<br />        往后显示源代码。<br />        </font>
		</p>
		<p>
				<font face="Courier New">一般来说在list后面可以跟以下这们的参数：</font>
		</p>
		<p>
				<font face="Courier New">    &lt;linenum&gt;   行号。<br />    &lt;+offset&gt;   当前行号的正偏移量。<br />    &lt;-offset&gt;   当前行号的负偏移量。<br />    &lt;filename:linenum&gt;  哪个文件的哪一行。<br />    &lt;function&gt;  函数名。<br />    &lt;filename:function&gt; 哪个文件中的哪个函数。<br />    &lt;*address&gt;  程序运行时的语句在内存中的地址。<br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>二、搜索源代码</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">不仅如此，GDB还提供了源代码搜索的命令：</font>
		</p>
		<p>
				<font face="Courier New">    forward-search &lt;regexp&gt; <br />    search &lt;regexp&gt;<br />        向前面搜索。</font>
		</p>
		<p>
				<font face="Courier New">    reverse-search &lt;regexp&gt; <br />        全部搜索。<br />        <br />其中，&lt;regexp&gt;就是正则表达式，也主一个字符串的匹配模式，关于正则表达式，我就不在这里讲了，还请各位查看相关资料。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>三、指定源文件的路径</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">某些时候，用-g编译过后的执行程序中只是包括了源文件的名字，没有路径名。GDB提供了可以让你指定源文件的路径的命令，以便GDB进行搜索。</font>
		</p>
		<p>
				<font face="Courier New">    directory &lt;dirname ... &gt;<br />    dir &lt;dirname ... &gt;<br />        加一个源文件路径到当前路径的前面。如果你要指定多个路径，UNIX下你可以使用“:”，Windows下你可以使用“;”。<br />    directory <br />        清除所有的自定义的源文件搜索路径信息。<br />    <br />    show directories <br />        显示定义了的源文件搜索路径。<br />        </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>四、源代码的内存</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">你可以使用info line命令来查看源代码在内存中的地址。info line后面可以跟“行号”，“函数名”，“文件名:行号”，“文件名:函数名”，这个命令会打印出所指定的源码在运行时的内存地址，如：</font>
		</p>
		<p>
				<font face="Courier New">        (gdb) info line tst.c:func<br />        Line 5 of "tst.c" starts at address 0x8048456 &lt;func+6&gt; and ends at 0x804845d &lt;func+13&gt;.</font>
		</p>
		<p>
				<font face="Courier New">还有一个命令（disassemble）你可以查看源程序的当前执行时的机器码，这个命令会把目前内存中的指令dump出来。如下面的示例表示查看函数func的汇编代码。</font>
		</p>
		<p>
				<font face="Courier New">        (gdb) disassemble func<br />        Dump of assembler code for function func:<br />        0x8048450 &lt;func&gt;:       push   %ebp<br />        0x8048451 &lt;func+1&gt;:     mov    %esp,%ebp<br />        0x8048453 &lt;func+3&gt;:     sub    $0x18,%esp<br />        0x8048456 &lt;func+6&gt;:     movl   $0x0,0xfffffffc(%ebp)<br />        0x804845d &lt;func+13&gt;:    movl   $0x1,0xfffffff8(%ebp)<br />        0x8048464 &lt;func+20&gt;:    mov    0xfffffff8(%ebp),%eax<br />        0x8048467 &lt;func+23&gt;:    cmp    0x8(%ebp),%eax<br />        0x804846a &lt;func+26&gt;:    jle    0x8048470 &lt;func+32&gt;<br />        0x804846c &lt;func+28&gt;:    jmp    0x8048480 &lt;func+48&gt;<br />        0x804846e &lt;func+30&gt;:    mov    %esi,%esi<br />        0x8048470 &lt;func+32&gt;:    mov    0xfffffff8(%ebp),%eax<br />        0x8048473 &lt;func+35&gt;:    add    %eax,0xfffffffc(%ebp)<br />        0x8048476 &lt;func+38&gt;:    incl   0xfffffff8(%ebp)<br />        0x8048479 &lt;func+41&gt;:    jmp    0x8048464 &lt;func+20&gt;<br />        0x804847b &lt;func+43&gt;:    nop<br />        0x804847c &lt;func+44&gt;:    lea    0x0(%esi,1),%esi<br />        0x8048480 &lt;func+48&gt;:    mov    0xfffffffc(%ebp),%edx<br />        0x8048483 &lt;func+51&gt;:    mov    %edx,%eax<br />        0x8048485 &lt;func+53&gt;:    jmp    0x8048487 &lt;func+55&gt;<br />        0x8048487 &lt;func+55&gt;:    mov    %ebp,%esp<br />        0x8048489 &lt;func+57&gt;:    pop    %ebp<br />        0x804848a &lt;func+58&gt;:    ret<br />        End of assembler dump.</font>
		</p>
		<p>
				<font face="Courier New">
						<strong>查看运行时数据<br />———————</strong>
						<br />    <br />    在你调试程序时，当程序被停住时，你可以使用print命令（简写命令为p），或是同义命令inspect来查看当前程序的运行数据。print命令的格式是：<br />    <br />    print &lt;expr&gt;<br />    print /&lt;f&gt; &lt;expr&gt;<br />        &lt;expr&gt;是表达式，是你所调试的程序的语言的表达式（GDB可以调试多种编程语言），&lt;f&gt;是输出的格式，比如，如果要把表达式按16进制的格式输出，那么就是/x。<br />        <br />    <br /><strong>一、表达式</strong></font>
		</p>
		<p>
				<font face="Courier New">    print和许多GDB的命令一样，可以接受一个表达式，GDB会根据当前的程序运行的数据来计算这个表达式，既然是表达式，那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。<br />    <br />    表达式的语法应该是当前所调试的语言的语法，由于C/C++是一种大众型的语言，所以，本文中的例子都是关于C/C++的。（而关于用GDB调试其它语言的章节，我将在后面介绍）<br />    <br />    在表达式中，有几种GDB所支持的操作符，它们可以用在任何一种语言中。<br />    <br />    @<br />        是一个和数组有关的操作符，在后面会有更详细的说明。<br />        <br />    ::<br />        指定一个在文件或是一个函数中的变量。<br />        <br />    {&lt;type&gt;} &lt;addr&gt;<br />        表示一个指向内存地址&lt;addr&gt;的类型为type的一个对象。<br />        <br />        <br /><strong>二、程序变量</strong></font>
		</p>
		<p>
				<font face="Courier New">    在GDB中，你可以随时查看以下三种变量的值：<br />        1、全局变量（所有文件可见的）<br />        2、静态全局变量（当前文件可见的）<br />        3、局部变量（当前Scope可见的）<br />        <br />    如果你的局部变量和全局变量发生冲突（也就是重名），一般情况下是局部变量会隐藏全局变量，也就是说，如果一个全局变量和一个函数中的局部变量同名时，如果当前停止点在函数中，用print显示出的变量的值会是函数中的局部变量的值。如果此时你想查看全局变量的值时，你可以使用“::”操作符：<br />    <br />        file::variable<br />    function::variable<br />    可以通过这种形式指定你所想查看的变量，是哪个文件中的或是哪个函数中的。例如，查看文件f2.c中的全局变量x的值：<br />    <br />    gdb) p 'f2.c'::x<br />    <br />    当然，“::”操作符会和C++中的发生冲突，GDB能自动识别“::” 是否C++的操作符，所以你不必担心在调试C++程序时会出现异常。<br />    <br />    另外，需要注意的是，如果你的程序编译时开启了优化选项，那么在用GDB调试被优化过的程序时，可能会发生某些变量不能访问，或是取值错误码的情况。这个是很正常的，因为优化程序会删改你的程序，整理你程序的语句顺序，剔除一些无意义的变量等，所以在GDB调试这种程序时，运行时的指令和你所编写指令就有不一样，也就会出现你所想象不到的结果。对付这种情况时，需要在编译程序时关闭编译优化。一般来说，几乎所有的编译器都支持编译优化的开关，例如，GNU 的C/C++编译器GCC，你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数，还请查看编译器的使用说明文档。<br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>三、数组</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    有时候，你需要查看一段连续的内存空间的值。比如数组的一段，或是动态分配的数据的大小。你可以使用GDB的“@”操作符，“@”的左边是第一个内存的地址的值，“@”的右边则你你想查看内存的长度。例如，你的程序中有这样的语句：<br />     <br />        int *array = (int *) malloc (len * sizeof (int));<br />        <br />    于是，在GDB调试过程中，你可以以如下命令显示出这个动态数组的取值：</font>
		</p>
		<p>
				<font face="Courier New">        p </font>
				<font face="Courier New">*array@len</font>
		</p>
		<p>
				<font face="Courier New">    @的左边是数组的首地址的值，也就是变量array所指向的内容，右边则是数据的长度，其保存在变量len中，其输出结果，大约是下面这个样子的：<br />    <br />        (gdb) p </font>
				<font face="Courier New">*array@len</font>
				<br />
				<font face="Courier New">        $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}</font>
		</p>
		<p>
				<font face="Courier New">    如果是静态数组的话，可以直接用print数组名，就可以显示数组中所有数据的内容了。</font>
		</p>
		<p>
				<font face="Courier New">    <br /><strong>四、输出格式</strong></font>
		</p>
		<p>
				<font face="Courier New">    一般来说，GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如，你想输出一个整数的十六进制，或是二进制来查看这个整型变量的中的位的情况。要做到这样，你可以使用GDB的数据显示格式：<br />    <br />    x  按十六进制格式显示变量。<br />    d  按十进制格式显示变量。<br />    u  按十六进制格式显示无符号整型。<br />    o  按八进制格式显示变量。<br />    t  按二进制格式显示变量。 <br />    a  按十六进制格式显示变量。<br />    c  按字符格式显示变量。<br />    f  按浮点数格式显示变量。</font>
		</p>
		<p>
				<font face="Courier New">        (gdb) p i<br />        $21 = 101    <br />        <br />        (gdb) p/a i<br />        $22 = 0x65<br />        <br />        (gdb) p/c i<br />        $23 = 101 'e'<br />        <br />        (gdb) p/f i<br />        $24 = 1.41531145e-43<br />        <br />        (gdb) p/x i<br />        $25 = 0x65<br />        <br />        (gdb) p/t i<br />        $26 = 1100101</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>五、查看内存</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    你可以使用examine命令（简写是x）来查看内存地址中的值。x命令的语法如下所示：<br />    <br />    x/&lt;n/f/u&gt; &lt;addr&gt; <br />    <br />    n、f、u是可选的参数。<br />    <br />    n 是一个正整数，表示显示内存的长度，也就是说从当前地址向后显示几个地址的内容。<br />    f 表示显示的格式，参见上面。如果地址所指的是字符串，那么格式可以是s，如果地十是指令地址，那么格式可以是i。<br />    u 表示从当前地址往后请求的字节数，如果不指定的话，GDB默认是4个bytes。u参数可以用下面的字符来代替，b表示单字节，h表示双字节，w表示四字节，g表示八字节。当我们指定了字节长度后，GDB会从指内存定的内存地址开始，读写指定字节，并把其当作一个值取出来。<br />    <br />    &lt;addr&gt;表示一个内存地址。</font>
		</p>
		<p>
				<font face="Courier New">    n/f/u三个参数可以一起使用。例如：<br />    <br />    命令：x/3uh 0x54320 表示，从内存地址0x54320读取内容，h表示以双字节为一个单位，3表示三个单位，u表示按十六进制显示。<br />    <br />    <br /><strong>六、自动显示</strong></font>
		</p>
		<p>
				<font face="Courier New">    你可以设置一些自动显示的变量，当程序停住时，或是在你单步跟踪时，这些变量会自动显示。相关的GDB命令是display。<br />    <br />    display &lt;expr&gt; <br />    display/&lt;fmt&gt; &lt;expr&gt; <br />    display/&lt;fmt&gt; &lt;addr&gt;<br />    <br />    expr是一个表达式，fmt表示显示的格式，addr表示内存地址，当你用display设定好了一个或多个表达式后，只要你的程序被停下来，GDB会自动显示你所设置的这些表达式的值。<br />    <br />    格式i和s同样被display支持，一个非常有用的命令是：<br />    <br />        display/i $pc<br />    <br />    $pc是GDB的环境变量，表示着指令的地址，/i则表示输出格式为机器指令码，也就是汇编。于是当程序停下后，就会出现源代码和机器指令码相对应的情形，这是一个很有意思的功能。<br />    <br />    下面是一些和display相关的GDB命令：<br />    <br />    undisplay &lt;dnums...&gt;<br />    delete display &lt;dnums...&gt;<br />    删除自动显示，dnums意为所设置好了的自动显式的编号。如果要同时删除几个，编号可以用空格分隔，如果要删除一个范围内的编号，可以用减号表示（如：2-5）<br />    <br />    disable display &lt;dnums...&gt;<br />    enable display &lt;dnums...&gt;<br />    disable和enalbe不删除自动显示的设置，而只是让其失效和恢复。<br />    <br />    info display<br />    查看display设置的自动显示的信息。GDB会打出一张表格，向你报告当然调试中设置了多少个自动显示设置，其中包括，设置的编号，表达式，是否enable。</font>
		</p>
		<p>
				<font face="Courier New">
						<strong>七、设置显示选项</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    GDB中关于显示的选项比较多，这里我只例举大多数常用的选项。</font>
		</p>
		<p>
				<font face="Courier New">    set print address <br />    set print address on <br />        打开地址输出，当程序显示函数信息时，GDB会显出函数的参数地址。系统默认为打开的，如：<br />        <br />        (gdb) f<br />        #0  set_quotes (lq=0x34c78 "&lt;&lt;", rq=0x34c88 "&gt;&gt;")<br />            at input.c:530<br />        530         if (lquote != def_lquote)</font>
		</p>
		<p>
				<br />
				<font face="Courier New">    set print address off <br />        关闭函数的参数地址显示，如：<br />        <br />        (gdb) set print addr off<br />        (gdb) f<br />        #0  set_quotes (lq="&lt;&lt;", rq="&gt;&gt;") at input.c:530<br />        530         if (lquote != def_lquote)</font>
		</p>
		<p>
				<font face="Courier New">    show print address <br />        查看当前地址显示选项是否打开。<br />        <br />    set print array <br />    set print array on <br />        打开数组显示，打开后当数组显示时，每个元素占一行，如果不打开的话，每个元素则以逗号分隔。这个选项默认是关闭的。与之相关的两个命令如下，我就不再多说了。<br />        <br />    set print array off <br />    show print array </font>
		</p>
		<p>
				<font face="Courier New">    set print elements &lt;number-of-elements&gt;<br />        这个选项主要是设置数组的，如果你的数组太大了，那么就可以指定一个&lt;number-of-elements&gt;来指定数据显示的最大长度，当到达这个长度时，GDB就不再往下显示了。如果设置为0，则表示不限制。<br />        <br />    show print elements <br />        查看print elements的选项信息。<br />        <br />    set print null-stop &lt;on/off&gt;<br />        如果打开了这个选项，那么当显示字符串时，遇到结束符则停止显示。这个选项默认为off。<br />        <br />    set print pretty on <br />        如果打开printf pretty这个选项，那么当GDB显示结构体时会比较漂亮。如：</font>
		</p>
		<p>
				<font face="Courier New">            $1 = {<br />              next = 0x0,<br />              flags = {<br />                sweet = 1,<br />                sour = 1<br />              },<br />              meat = 0x54 "Pork"<br />            }</font>
		</p>
		<p>
				<font face="Courier New">    set print pretty off<br />        关闭printf pretty这个选项，GDB显示结构体时会如下显示：<br />        <br />            $1 = {next = 0x0, flags = {sweet = 1, sour = 1}, meat = 0x54 "Pork"}<br />            <br />    show print pretty <br />        查看GDB是如何显示结构体的。<br />        <br />    <br />    set print sevenbit-strings &lt;on/off&gt;<br />        设置字符显示，是否按“\nnn”的格式显示，如果打开，则字符串或字符数据按\nnn显示，如“\065”。<br />    <br />    show print sevenbit-strings<br />        查看字符显示开关是否打开。 <br />        <br />    set print union &lt;on/off&gt;<br />        设置显示结构体时，是否显式其内的联合体数据。例如有以下数据结构：<br />        <br />        typedef enum {Tree, Bug} Species;<br />        typedef enum {Big_tree, Acorn, Seedling} Tree_forms;<br />        typedef enum {Caterpillar, Cocoon, Butterfly}<br />                      Bug_forms;<br />        <br />        struct thing {<br />          Species it;<br />          union {<br />            Tree_forms tree;<br />            Bug_forms bug;<br />          } form;<br />        };<br />        <br />        struct thing foo = {Tree, {Acorn}};</font>
		</p>
		<p>
				<font face="Courier New">        当打开这个开关时，执行 p foo 命令后，会如下显示：<br />            $1 = {it = Tree, form = {tree = Acorn, bug = Cocoon}}<br />        <br />        当关闭这个开关时，执行 p foo 命令后，会如下显示：<br />            $1 = {it = Tree, form = {...}}</font>
		</p>
		<p>
				<font face="Courier New">    show print union<br />        查看联合体数据的显示方式<br />        <br />    set print object &lt;on/off&gt;<br />        在C++中，如果一个对象指针指向其派生类，如果打开这个选项，GDB会自动按照虚方法调用的规则显示输出，如果关闭这个选项的话，GDB就不管虚函数表了。这个选项默认是off。<br />    <br />    show print object<br />        查看对象选项的设置。<br />        <br />    set print static-members &lt;on/off&gt;<br />        这个选项表示，当显示一个C++对象中的内容是，是否显示其中的静态数据成员。默认是on。<br />    <br />    show print static-members<br />        查看静态数据成员选项设置。<br />        <br />    set print vtbl &lt;on/off&gt;<br />        当此选项打开时，GDB将用比较规整的格式来显示虚函数表时。其默认是关闭的。<br />        <br />    show print vtbl<br />        查看虚函数显示格式的选项。<br />        <br /><strong>        <br />八、历史记录</strong></font>
		</p>
		<p>
				<font face="Courier New">    当你用GDB的print查看程序运行时的数据时，你每一个print都会被GDB记录下来。GDB会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是，你可以使用这个编号访问以前的表达式，如$1。这个功能所带来的好处是，如果你先前输入了一个比较长的表达式，如果你还想查看这个表达式的值，你可以使用历史记录来访问，省去了重复输入。<br />    <br />    <br /><strong>九、GDB环境变量</strong></font>
		</p>
		<p>
				<font face="Courier New">    你可以在GDB的调试环境中定义自己的变量，用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样，也是以$起头。如：<br />    <br />    set $foo = *object_ptr<br />    <br />    使用环境变量时，GDB会在你第一次使用时创建这个变量，而在以后的使用中，则直接对其賦值。环境变量没有类型，你可以给环境变量定义任一的类型。包括结构体和数组。<br />    <br />    show convenience <br />        该命令查看当前所设置的所有的环境变量。<br />        <br />    这是一个比较强大的功能，环境变量和程序变量的交互使用，将使得程序调试更为灵活便捷。例如：<br />    <br />        set $i = 0<br />        print bar[$i++]-&gt;contents<br />    <br />    于是，当你就不必，print bar[0]-&gt;contents, print bar[1]-&gt;contents地输入命令了。输入这样的命令后，只用敲回车，重复执行上一条语句，环境变量会自动累加，从而完成逐个输出的功能。<br />    <br />    <br /><strong>十、查看寄存器</strong></font>
		</p>
		<p>
				<font face="Courier New">    要查看寄存器的值，很简单，可以使用如下命令：<br />    <br />    info registers <br />        查看寄存器的情况。（除了浮点寄存器）<br />    <br />    info all-registers<br />        查看所有寄存器的情况。（包括浮点寄存器）<br />    <br />    info registers &lt;regname ...&gt;<br />        查看所指定的寄存器的情况。<br />        <br />    寄存器中放置了程序运行时的数据，比如程序当前运行的指令地址（ip），程序的当前堆栈地址（sp）等等。你同样可以使用print命令来访问寄存器的情况，只需要在寄存器名字前加一个$符号就可以了。如：p $eip。</font>
		</p>
		<p>
				<strong>
						<font face="Courier New">改变程序的执行<br />———————</font>
				</strong>
		</p>
		<p>
				<font face="Courier New">    一旦使用GDB挂上被调试程序，当程序运行起来后，你可以根据自己的调试思路来动态地在GDB中更改当前被调试程序的运行线路或是其变量的值，这个强大的功能能够让你更好的调试你的程序，比如，你可以在程序的一次运行中走遍程序的所有分支。<br />    <br /></font>
				<strong>
						<font face="Courier New">    <br />一、修改变量值</font>
				</strong>
		</p>
		<p>
				<font face="Courier New">    修改被调试程序运行时的变量值，在GDB中很容易实现，使用GDB的print命令即可完成。如：<br />    <br />        (gdb) print x=4<br />    <br />    x=4这个表达式是C/C++的语法，意为把变量x的值修改为4，如果你当前调试的语言是Pascal，那么你可以使用Pascal的语法：x:=4。<br />    <br />    在某些时候，很有可能你的变量和GDB中的参数冲突，如：<br />    <br />        (gdb) whatis width<br />        type = double<br />        (gdb) p width<br />        $4 = 13<br />        (gdb) set width=47<br />        Invalid syntax in expression.</font>
		</p>
		<p>
				<font face="Courier New">    因为，set width是GDB的命令，所以，出现了“Invalid syntax in expression”的设置错误，此时，你可以使用set var命令来告诉GDB，width不是你GDB的参数，而是程序的变量名，如：<br />    <br />        (gdb) set var width=47<br />        <br />    另外，还可能有些情况，GDB并不报告这种错误，所以保险起见，在你改变程序变量取值时，最好都使用set var格式的GDB命令。<br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>二、跳转执行</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    一般来说，被调试程序会按照程序代码的运行顺序依次执行。GDB提供了乱序执行的功能，也就是说，GDB可以修改程序的执行顺序，可以让程序执行随意跳跃。这个功能可以由GDB的jump命令来完：<br />    <br />    jump &lt;linespec&gt;<br />    指定下一条语句的运行点。&lt;linespce&gt;可以是文件的行号，可以是file:line格式，可以是+num这种偏移量格式。表式着下一条运行语句从哪里开始。<br />    <br />    jump &lt;address&gt;<br />    这里的&lt;address&gt;是代码行的内存地址。<br />    <br />    注意，jump命令不会改变当前的程序栈中的内容，所以，当你从一个函数跳到另一个函数时，当函数运行完返回时进行弹栈操作时必然会发生错误，可能结果还是非常奇怪的，甚至于产生程序Core Dump。所以最好是同一个函数中进行跳转。<br />    <br />    熟悉汇编的人都知道，程序运行时，有一个寄存器用于保存当前代码所在的内存地址。所以，jump命令也就是改变了这个寄存器中的值。于是，你可以使用“set $pc”来更改跳转执行的地址。如：<br />    <br />    set $pc = 0x485</font>
		</p>
		<p>
				<br />
				<font face="Courier New">
						<strong>三、产生信号量</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    使用singal命令，可以产生一个信号量给被调试的程序。如：中断信号Ctrl+C。这非常方便于程序的调试，可以在程序运行的任意位置设置断点，并在该断点用GDB产生一个信号量，这种精确地在某处产生信号非常有利程序的调试。<br />    <br />    语法是：signal &lt;singal&gt;，UNIX的系统信号量通常从1到15。所以&lt;singal&gt;取值也在这个范围。<br />    <br />    single命令和shell的kill命令不同，系统的kill命令发信号给被调试程序时，是由GDB截获的，而single命令所发出一信号则是直接发给被调试程序的。<br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>四、强制函数返回</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    如果你的调试断点在某个函数中，并还有语句没有执行完。你可以使用return命令强制函数忽略还没有执行的语句并返回。<br />    <br />    return<br />    return &lt;expression&gt;<br />    使用return命令取消当前函数的执行，并立即返回，如果指定了&lt;expression&gt;，那么该表达式的值会被认作函数的返回值。<br />    <br />    <br /><strong>五、强制调用函数</strong></font>
		</p>
		<p>
				<font face="Courier New">    call &lt;expr&gt;<br />    表达式中可以一是函数，以此达到强制调用函数的目的。并显示函数的返回值，如果函数返回值是void，那么就不显示。<br />    <br />    另一个相似的命令也可以完成这一功能——print，print后面可以跟表达式，所以也可以用他来调用函数，print和call的不同是，如果函数返回void，call则不显示，print则显示函数返回值，并把该值存入历史数据中。</font>
		</p>
		<p>
				<font face="Courier New">
						<strong>
						</strong>
				</font> </p>
		<p>
				<font face="Courier New">
						<strong>在不同语言中使用GDB<br />——————————</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">GDB 支持下列语言：C, C++, Fortran, PASCAL, Java, Chill, assembly, 和 Modula-2。一般说来，GDB会根据你所调试的程序来确定当然的调试语言，比如：发现文件名后缀为“.c”的，GDB会认为是C程序。文件名后缀为 “.C, .cc, .cp, .cpp, .cxx, .c++”的，GDB会认为是C++程序。而后缀是“.f, .F”的，GDB会认为是Fortran程序，还有，后缀为如果是“.s, .S”的会认为是汇编语言。</font>
		</p>
		<p>
				<font face="Courier New">也就是说，GDB会根据你所调试的程序的语言，来设置自己的语言环境，并让GDB的命令跟着语言环境的改变而改变。比如一些GDB命令需要用到表达式或变量时，这些表达式或变量的语法，完全是根据当前的语言环境而改变的。例如C/C++中对指针的语法是*p，而在Modula-2中则是p^。并且，如果你当前的程序是由几种不同语言一同编译成的，那到在调试过程中，GDB也能根据不同的语言自动地切换语言环境。这种跟着语言环境而改变的功能，真是体贴开发人员的一种设计。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">下面是几个相关于GDB语言环境的命令：</font>
		</p>
		<p>
				<font face="Courier New">    show language <br />        查看当前的语言环境。如果GDB不能识为你所调试的编程语言，那么，C语言被认为是默认的环境。<br />        <br />    info frame<br />        查看当前函数的程序语言。<br />        <br />    info source<br />        查看当前文件的程序语言。<br />    <br />如果GDB没有检测出当前的程序语言，那么你也可以手动设置当前的程序语言。使用set language命令即可做到。</font>
		</p>
		<p>
				<font face="Courier New">    当set language命令后什么也不跟的话，你可以查看GDB所支持的语言种类：<br />    <br />        (gdb) set language<br />        The currently understood settings are:<br />        <br />        local or auto    Automatic setting based on source file<br />        c                Use the C language<br />        c++              Use the C++ language<br />        asm              Use the Asm language<br />        chill            Use the Chill language<br />        fortran          Use the Fortran language<br />        java             Use the Java language<br />        modula-2         Use the Modula-2 language<br />        pascal           Use the Pascal language<br />        scheme           Use the Scheme language<br />        <br />    于是你可以在set language后跟上被列出来的程序语言名，来设置当前的语言环境。<br />    <br />    </font>
		</p>
		<p>
				<font face="Courier New">
						<strong>后记<br />——</strong>
				</font>
		</p>
		<p>
				<font face="Courier New">    GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于，其可以形成执行序列，形成脚本。UNIX下的软件全是命令行的，这给程序开发提代供了极大的便利，命令行软件的优势在于，它们可以非常容易的集成在一起，使用几个简单的已有工具的命令，就可以做出一个非常强大的功能。<br />    <br />    于是UNIX下的软件比Windows下的软件更能有机地结合，各自发挥各自的长处，组合成更为强劲的功能。而Windows下的图形软件基本上是各自为营，互相不能调用，很不利于各种软件的相互集成。在这里并不是要和Windows做个什么比较，所谓“寸有所长，尺有所短”，图形化工具还是有不如命令行的地方。（看到这句话时，希望各位千万再也不要认为我就是“鄙视图形界面”，和我抬杠了 ）<br />    <br />    我是根据版本为5.1.1的GDB所写的这篇文章，所以可能有些功能已被修改，或是又有更为强劲的功能。而且，我写得非常仓促，写得比较简略，并且，其中我已经看到有许多错别字了（我用五笔，所以错字让你看不懂），所以，我在这里对我文中的差错表示万分的歉意。<br />    <br />    文中所罗列的GDB的功能时，我只是罗列了一些带用的GDB的命令和使用方法，其实，我这里只讲述的功能大约只占GDB所有功能的60%吧，详细的文档，还是请查看GDB的帮助和使用手册吧，或许，过段时间，如果我有空，我再写一篇GDB的高级使用。<br />    <br />    我个人非常喜欢GDB的自动调试的功能，这个功能真的很强大，试想，我在UNIX下写个脚本，让脚本自动编译我的程序，被自动调试，并把结果报告出来，调试成功，自动checkin源码库。一个命令，编译带着调试带着checkin，多爽啊。只是GDB对自动化调试目前支持还不是很成熟，只能实现半自动化，真心期望着GDB的自动化调试功能的成熟。<br />    <br />    如果各位对GDB或是别的技术问题有兴趣的话，欢迎和我讨论交流。本人目前主要在UNIX下做产品软件的开发，所以，对UNIX下的软件开发比较熟悉，当然，不单单是技术，对软件工程实施，软件设计，系统分析，项目管理我也略有心得。欢迎大家找我交流，（QQ是：753640，MSN是： haoel@hotmail.com）</font>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21771.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 20:07 <a href="http://www.cnitblog.com/zouzheng/articles/21771.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对const声明变量的奇异行为的探讨</title><link>http://www.cnitblog.com/zouzheng/articles/21738.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:20:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21738.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21738.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21738.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21738.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<span id="ArticleContent1_ArticleContent1_lblContent">
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<font size="3">The information in this article applies to:</font>
						</div>
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<span style="FONT-SIZE: 10pt">- C/C++</span>
						</div>
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<font size="3">----------------------------------------------------------------</font>
						</div>
						<div style="MARGIN: 13pt 0cm">
								<strong>
										<font size="5">
												<a name="OLE_LINK2">
														<span style="COLOR: black">奇异的现象：</span>
												</a>
										</font>
								</strong>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">我把这个试验的源代码列出来：</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">int main(int argc, char* argv[])</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">{</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       const int x=10000;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       int *y=0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       y=(int*)&amp;x;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       *y=10;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       printf("%d\n", x);</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       printf("%d\n", *y);</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       return 0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">}</font>
								</span>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">首先我们声明了一个const变量x，初始化为10000。然后让一个int指针y指向x。通过给*y赋值，从而改变了x的实际值！</font>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">
										<strong>虽然在</strong>
										<strong>Watch</strong>
										<strong>窗口中你明明看到</strong>
										<strong>x</strong>
										<strong>的值确实是</strong>
										<strong>10</strong>
										<strong>，但是</strong>
										<strong>printf</strong>
										<strong>出来的</strong>
										<strong>x</strong>
										<strong>的值却偏偏是</strong>
										<strong>10000</strong>
										<strong>！！</strong>
								</font>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">可是，这个已经被彻底抹去的10000，又是从哪里被找回来的呢？</font>
						</div>
						<div>
								<font size="3">
								</font> </div>
						<div style="MARGIN: 13pt 0cm">
								<strong>
										<font size="5">
												<span style="COLOR: black">我的解释：</span>
										</font>
								</strong>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">这样的代码经过VC编译器的Debug版本的编译，最后生成的完整的汇编代码为(我做了注释，可以参考一下)：</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">11:   int main(int argc, char* argv[])</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">12:   {</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401250   push        ebp</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第一步，将</span>
										<span style="COLOR: red">基址寄存器</span>
										<span style="COLOR: red">(EBP) </span>
										<span style="COLOR: red">压入堆栈</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401251   mov         ebp,esp          </font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第二步，</span>
										<span style="COLOR: red">把当前的栈顶指针</span>
										<span style="COLOR: red">(ESP)</span>
										<span style="COLOR: red">拷贝到</span>
										<span style="COLOR: red">EBP</span>
										<span style="COLOR: red">，做为新的基地址</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401253   sub         esp,48h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第三步，</span>
										<span style="COLOR: red">把</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">减去一个数值，用来为本地变量留出一定空间。这里减去</span>
										<span style="COLOR: red">48h</span>
										<span style="COLOR: red">，也就是</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">\\ 72 .</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这里对前面的三步说明一下：</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">和</span>
										<span style="COLOR: red">EBP</span>
										<span style="COLOR: red">寄存器是堆栈专用的。堆栈基址指针</span>
										<span style="COLOR: red">(EBP)</span>
										<span style="COLOR: red">寄</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">存器确定堆栈帧的起始位置，而堆栈指针</span>
										<span style="COLOR: red">(ESP)</span>
										<span style="COLOR: red">寄存器执行当前堆栈顶。在函数的入口处，</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">当前堆栈基址指针被压到了堆栈中，并且当前堆栈指针成为新的堆栈基址指针。局部变</span>
										<span style="COLOR: red"> </span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">量的存储空间、函数使用的各种需要保存的寄存器的存储空间在函数入口处也被预留出</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">来。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">所以也就有了下面的三个压栈行为。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">                     </font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">下面是连续三个压栈，</span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">步：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401256   push        ebx</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">ebx</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">EBX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为基址</span>
										<span style="COLOR: red"> DS </span>
										<span style="COLOR: red">数据段；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401257   push        esi</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">esi</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">ESI</span>
										<span style="COLOR: red">寄存器是指针寄存器的一种。是内存移动和比较操作的源地址寄</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">存器；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401258   push        edi</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">edi</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">EDI</span>
										<span style="COLOR: red">寄存器是指针寄存器的一种。是内存移动和比较操作的目标地址</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">寄存器</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">以上四步执行完之后，函数入口处的堆栈帧结构如下所示：</span>
								</font>
						</div>
						<div> </div>
						<div> </div>
						<div> </div>
						<div> </div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">值得注意的是，上面所说的对于</span>
										<span style="COLOR: red">Debug</span>
										<span style="COLOR: red">版本才是正确的，对于</span>
										<span style="COLOR: red">Release</span>
										<span style="COLOR: red">版本可不一定对。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ Release </span>
										<span style="COLOR: red">版本也许已经把堆栈基址指针优化掉了。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401259   lea         edi,[ebp-48h]</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">5</span>
										<span style="COLOR: red">步，</span>
										<span style="COLOR: red">lea</span>
										<span style="COLOR: red">指令装入有效地址，用来得到局部变量和函数参数的指针。这里</span>
										<span style="COLOR: red">[ebp-48h]</span>
										<span style="COLOR: red">就是基地址再向下偏移</span>
										<span style="COLOR: red">48h</span>
										<span style="COLOR: red">，就是前面说的为本地变量留出的空间的起始地址；将这个值装载入</span>
										<span style="COLOR: red">edi</span>
										<span style="COLOR: red">寄存器，从而得到局部变量的地址；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">下面的这第六步可是非常的重要，请记住：</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第六步，给段寄存器预先赋值：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040125C   mov         ecx,12h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ ECX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为计数器</span>
										<span style="COLOR: red"> SS </span>
										<span style="COLOR: red">堆栈段。设为</span>
										<span style="COLOR: red">12h</span>
										<span style="COLOR: red">。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401261   mov         eax,0CCCCCCCCh</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ EAX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为累加器</span>
										<span style="COLOR: red"> CS </span>
										<span style="COLOR: red">代码段；设为</span>
										<span style="COLOR: red">0CCCCCCCCh</span>
										<span style="COLOR: red">。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401266   rep stos    dword ptr [edi]</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这句话是干吗的？</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">下面开始我们的代码了：</span>
										</strong>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">13:       const int x=10000;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401268   mov         dword ptr [ebp-4], 2710h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第一步，在基地址向下偏移</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">个字节所指向的地址，将</span>
										<span style="COLOR: red">10000</span>
										<span style="COLOR: red">这个</span>
										<span style="COLOR: red">DWORD</span>
										<span style="COLOR: red">数值放进\\去；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">可以看出的是，对于一个普通的</span>
										<span style="COLOR: red">int z = 10000;</span>
										<span style="COLOR: red">汇编代码依然是这个样子。说明从这句</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">
										</span>
								</font>
								<font size="3">
										<span style="COLOR: red">\\ 话</span>
										<span style="COLOR: red">是无法分清楚局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量的初始化和普通变量的初始化的！这一点很重要！就</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">
										</span>
								</font>
								<font size="3">
										<span style="COLOR: red">\\ 是说编译器</span>
										<span style="COLOR: red">从表面上是无法分清楚一个局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量和一个普通变量的。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">14:       int *y=0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040126F   mov         dword ptr [ebp-8],0</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">15:       y=(int*)&amp;x;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401276   lea         eax,[ebp-4]</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401279   mov         dword ptr [ebp-8],eax</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">2</span>
										<span style="COLOR: red">步，将</span>
										<span style="COLOR: red">x</span>
										<span style="COLOR: red">的地址装载到</span>
										<span style="COLOR: red">EAX</span>
										<span style="COLOR: red">寄存器；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">3</span>
										<span style="COLOR: red">步，再把这个地址作为一个数值导到</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">的地址，这样</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">就指向了</span>
										<span style="COLOR: red">x</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这是局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量声明的情况！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">而对于全局</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量声明的情况，这句</span>
										<span style="COLOR: red">y=(int*)&amp;x;</span>
										<span style="COLOR: red">的汇编却是：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">\\ 00401276   mov         dword ptr [ebp-8],offset x (0043101c)</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">一个很显著的区别！</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">16:       *y=10;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040127C   mov         ecx,dword ptr [ebp-8]</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040127F   mov         dword ptr [ecx],0Ah</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">步，通过</span>
										<span style="COLOR: red">ECX</span>
										<span style="COLOR: red">寄存器倒手，将</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">所指向的地址的数值修改为</span>
										<span style="COLOR: red">0Ah</span>
										<span style="COLOR: red">，也就是</span>
										<span style="COLOR: red">10</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">编译器之所以允许这种修改</span>
										</strong>
										<strong>
												<span style="COLOR: purple">const</span>
										</strong>
										<strong>
												<span style="COLOR: purple">变量值的非法情况，是因为编译器并不知道这\\是一个</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ const</span>
										</strong>
										<strong>
												<span style="COLOR: purple">变量，它实在是和普通的变量太像了！</span>
										</strong>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">17:</font>
								</span>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">18:       printf("%d\n", x);</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">00401285   push        2710h</font>
										</span>
								</strong>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">5</span>
										<span style="COLOR: red">步，将</span>
										<span style="COLOR: red">10000</span>
										<span style="COLOR: red">数值压栈！按照惯例，这个</span>
										<span style="COLOR: red">2710h</span>
										<span style="COLOR: red">会被存在当前栈顶指针前</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">个字节</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">处。原来</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">指向</span>
										<span style="COLOR: red">0012FF2C</span>
										<span style="COLOR: red">，所以现在指向</span>
										<span style="COLOR: red">0012FF28</span>
										<span style="COLOR: red">了。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">编译器为什么会直接</span>
										</strong>
										<strong>
												<span style="COLOR: purple">push</span>
										</strong>
										<strong>
												<span style="COLOR: purple">一个常量入栈呢？</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">我觉得可能是这样：制定</span>
										<span style="COLOR: red">C++</span>
										<span style="COLOR: red">编译器规则的人想反正都是</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量了，它的值肯定不</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">能变。</span>
										<span style="COLOR: red">printf</span>
										<span style="COLOR: red">一个普通变量是倒手两个寄存器后把</span>
										<span style="COLOR: red">EAX</span>
										<span style="COLOR: red">寄存器的内容压栈，多影响效率</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">还不如直接将这个</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量的值压栈呢。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040128A   push        offset string "%d\n" (0042f01c)</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">再把格式化压栈；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这样，</span>
										<span style="COLOR: red">printf</span>
										<span style="COLOR: red">函数将取栈顶的内容打印，当然是按照</span>
										<span style="COLOR: red">%d\n</span>
										<span style="COLOR: red">来打印的，所以只会再取栈顶</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ 0x</span>
										<span style="COLOR: red">0012FF28</span>
										<span style="COLOR: red">指向的内容</span>
										<span style="COLOR: red">；所以打印出来的就是上面压栈的常量</span>
										<span style="COLOR: red">2710h</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这就是我给出的解释。请高手们指正。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040128F   call        printf (004082f0)</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401294   add         esp,8</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">19:</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">20:       printf("%d\n", *y);</font>
								</span>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">00401297   mov         edx,dword ptr [ebp-8]</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">0040129A   mov         eax,dword ptr [edx]</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">0040129C   push        eax</font>
										</span>
								</strong>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: red">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: red">看，对于一个普通变量的</span>
										</strong>
										<strong>
												<span style="COLOR: red">printf</span>
										</strong>
								</font>
						</div>
				</span>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:20 <a href="http://www.cnitblog.com/zouzheng/articles/21738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2410自带ad</title><link>http://www.cnitblog.com/zouzheng/articles/18588.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 10:54:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18588.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18588.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18588.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18588.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18588.html</trackback:ping><description><![CDATA[
		<font color="#555555">2410自带了8个ad通道，10bit，500ksps。在网上查了资料，很少有人用它做ad转换的，原因是精度不高。不过对于一些要求不是很高的地方，还是值得一用。<br />在/kernel/drivers/char中已经有了ad不完整的驱动，基本框架搭好了，只要加上文件系统那部分就可以直接使用了。使用时一定要在Vref上加上电压，否则ad就不工作了。现在就把驱动和应用程序贴出来，供大家参考学习。<br />应用程序的作用是要测量的通道号告诉驱动，驱动得到通道号经过ad转换后将数字量传给应用程序。<br />驱动程序：<br />/*<br />* s3c2410-adc.c<br />*<br />* S3C2410 ADC <br />*  exclusive with s3c2410-ts.c<br />*/<br />#include linux/config.h&gt;<br />#include linux/module.h&gt;<br />#include linux/kernel.h&gt;<br />#include linux/init.h&gt;<br />#include linux/sched.h&gt;<br />#include linux/irq.h&gt;<br />#include linux/delay.h&gt;<br />#include asm/hardware.h&gt;<br />#include asm/semaphore.h&gt;<br />#include asm/uaccess.h&gt;<br />#include asm/io.h&gt; </font>
		<p>#define DEVICE_NAME"s3c2410-adc"<br />#define ADCRAW_MINOR1<br />#define ADC_WRITE(ch, prescale)((ch)&lt;&lt;16|(prescale))<br />#define ADC_WRITE_GETCH(data)(((data)&gt;&gt;16)&amp;0x7)   //得到通道号<br />#define ADC_WRITE_GETPRE(data)((data)&amp;0xff)      //得到转换的比例因子 
</p>
		<p>static int adcMajor = 0; 
</p>
		<p>typedef struct {<br />struct semaphore lock;<br />wait_queue_head_t wait;<br />int channel;<br />int prescale;<br />}ADC_DEV; 
</p>
		<p>static ADC_DEV adcdev; 
</p>
		<p>#define START_ADC_AIN(ch, prescale) \<br />do{ \<br />ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \<br />ADCCON |= ADC_START; \<br />}while(0) 
</p>
		<p>static void adcdone_int_handler(int irq, void *dev_id, struct pt_regs *reg)<br />{<br />wake_up(&amp;adcdev.wait);<br />} 
</p>
		<p>static ssize_t s3c2410_adc_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)<br />{<br />int data; 
</p>
		<p>if(count!=sizeof(data)){<br />printk("the size of  input data must be %d\n", sizeof(data));<br />return 0;<br />}<br />copy_from_user(&amp;data, buffer, count);<br />adcdev.channel=ADC_WRITE_GETCH(data);<br />adcdev.prescale=ADC_WRITE_GETPRE(data);<br />DPRINTK("set adc channel=%d, prescale=0x%x\n", adcdev.channel, adcdev.prescale);<br />return count;<br />} 
</p>
		<p>static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)<br />{<br />int ret = 0;<br />if (down_interruptible(&amp;adcdev.lock))<br />return -ERESTARTSYS;<br />START_ADC_AIN(adcdev.channel, adcdev.prescale);<br />interruptible_sleep_on(&amp;adcdev.wait);<br />printk("in read channel=%d\n",adcdev.channel);<br />ret = ADCDAT0;<br />printk("ADCDAT0=%x\n",ADCDAT0);<br />while((ADCCON &amp; 0x80)!=1)<br />udelay(100);<br />ret &amp;= 0x3ff;<br />copy_to_user(buffer, (char *)&amp;ret, sizeof(ret));<br />up(&amp;adcdev.lock);<br />return sizeof(ret);<br />} 
</p>
		<p>static int s3c2410_adc_open(struct inode *inode, struct file *filp)<br />{<br />init_MUTEX(&amp;adcdev.lock);<br />init_waitqueue_head(&amp;(adcdev.wait)); 
</p>
		<p>adcdev.channel=0;<br />adcdev.prescale=0xff; 
</p>
		<p>MOD_INC_USE_COUNT;<br />printk("adc opened!\n");<br />return 0;<br />} 
</p>
		<p>static int s3c2410_adc_release(struct inode *inode, struct file *filp)<br />{<br />MOD_DEC_USE_COUNT;<br />printk( "adc closed\n");<br />return 0;<br />} 
</p>
		<p>
				<br />static struct file_operations s3c2410_fops = {<br />owner:THIS_MODULE,<br />open:s3c2410_adc_open,<br />read:s3c2410_adc_read,<br />write:s3c2410_adc_write,<br />release:s3c2410_adc_release,<br />}; 
</p>
		<p>#ifdef CONFIG_DEVFS_FS<br />static devfs_handle_t devfs_adc_dir, devfs_adcraw;<br />#endif 
</p>
		<p>int __init s3c2410_adc_init(void)<br />{<br />int ret; 
</p>
		<p>/* normal ADC */<br />ADCTSC = 0;<br />ret = request_irq(IRQ_ADC_DONE, adcdone_int_handler, SA_INTERRUPT, DEVICE_NAME, NULL);<br />if (ret) {<br />return ret;<br />} 
</p>
		<p>ret = register_chrdev(0, DEVICE_NAME, &amp;s3c2410_fops);<br />if (ret &lt; 0) {<br />printk(DEVICE_NAME " can't get major number\n");<br />return ret;<br />}<br />adcMajor=ret; 
</p>
		<p>#ifdef CONFIG_DEVFS_FS<br />devfs_adc_dir = devfs_mk_dir(NULL, "adc", NULL);<br />devfs_adcraw = devfs_register(devfs_adc_dir, "0raw", DEVFS_FL_DEFAULT,<br />adcMajor, ADCRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR, &amp;s3c2410_fops, NULL);<br />#endif<br />printk (DEVICE_NAME"\tinitialized\n");<br />printk ("initialized\n"); 
</p>
		<p>printk("inilized ADCDAT0=%x\n",ADCDAT0);<br />printk("inilized ADCCON=%x\n",ADCCON);<br />return 0;<br />} 
</p>
		<p>module_init(s3c2410_adc_init); 
</p>
		<p>#ifdef MODULE<br />void __exit s3c2410_adc_exit(void)<br />{<br />#ifdef CONFIG_DEVFS_FS<br />devfs_unregister(devfs_adcraw);<br />devfs_unregister(devfs_adc_dir);<br />#endif<br />unregister_chrdev(adcMajor, DEVICE_NAME); 
</p>
		<p>free_irq(IRQ_ADC_DONE, NULL);<br />} 
</p>
		<p>module_exit(s3c2410_adc_exit);<br />MODULE_LICENSE("GPL");<br />#endif 
</p>
		<p>应用程序：<br />#include stdio.h&gt;<br />#include unistd.h&gt;<br />#include sys/types.h&gt;<br />#include sys/ipc.h&gt;<br />#include sys/ioctl.h&gt;<br />#include fcntl.h&gt; 
</p>
		<p>#define ADC_DEV"/dev/adc/0raw"<br />#define ADC_WRITE(ch, prescale)((ch)&lt;&lt;16|(prescale))<br />#define ADC_WRITE_GETCH(data)(((data)&gt;&gt;16)&amp;0x7)   //得到通道号<br />#define ADC_WRITE_GETPRE(data)((data)&amp;0xff)      //得到转换的比例因子 
</p>
		<p>static int get(int channel)<br />{<br />int PRESCALE=0XFF;<br />int data=ADC_WRITE(channel, PRESCALE);<br />write(adc_fd, &amp;data, sizeof(data));<br />read(adc_fd, &amp;data, sizeof(data));<br />return data;<br />} 
</p>
		<p>main()<br />{<br />int i;<br />int fd;<br />float d;<br />void * retval;<br />   <br />if((fd=open(ADC_DEV, O_RDWR))&lt;0)<br />{<br />printf("Error opening %s adc device\n", ADC_DEV);<br />return -1;<br />}<br />for(i=0; i&lt;=7; i++)<br />{<br />d=((float)get(i)*3.3)/1024.0;<br />printf("a[%d]=%8.4f\n",i,d);<br />}<br />}<br /></p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/18588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 18:54 <a href="http://www.cnitblog.com/zouzheng/articles/18588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于ARM9的多功能硬盘MP3播放器的设计</title><link>http://www.cnitblog.com/zouzheng/articles/18581.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 03:10:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18581.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18581.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18581.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18581.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18581.html</trackback:ping><description><![CDATA[很难给这个设计方案定义一个确切的名称，是硬盘MP3播放器，还是数码相机伴侣，还是是电子相框，甚至叫做移动硬盘？其实这几项功能这款设计方案都具备了。不过通常来说，作为MP3使用的频率更高一些，因此我们姑且还是将它称作多功能硬盘MP3播放器。 <br />这款多功能MP3播放器的核心是三星公司的S3C2410芯片。该芯片是基于ARM920T而开发的一款面向消费类电子产品的多功能SOC。除具备一般嵌入式芯片所具有的总线，SDRAM控制器，3个串口等外设之外，S3C2410还具有TFT LCD控制器、USB Slave、USB Host、I2C总线控制器、SPI控制器、IIS音频接口、SD &amp; MMC存储卡接口等丰富的扩展功能。芯片工作电压3.3/1.8V，最高运行速度可达200MHz。 <br /><br />　　这款设计最大的特点就是功能丰富。除了可以作为iPod那样的硬盘MP3播放器外，还可以作为数码相机伴侣、电子相框和移动硬盘。特别值得一提的是，在作为数码相机伴侣使用的时候，与现有的同类产品需要将卡从DC种取出再插入设备上众多插槽中的一个不同，该设计本身可以作为一个USB Host，并且支持USB Mass Storage Class，因此对于多数数码相机，只要用USB电缆将相机和播放器相连，就可以自动将相机内的所有照片拷贝到设备内置的硬盘之中。对于不是标准Mass Storage类的相机也没有关系，该设计可以配合任意一款读卡器进行使用，只要将读卡器连接到设备上再将卡插入读卡器就可以了。这种设计能大幅度缩小数码相机伴侣的体积，并且能够给使用带来极大的方便。 <br /><br />　　下图是简化的硬盘MP3播放器的硬件结构框图： <br /><br /><p align="center"><img height="276" alt="" src="http://www.gd-emb.org/UserFiles/Image/tech_doc/js138.gif" width="432" /></p><br /><br />　　从图中可以看到，系统可以分为核心部分、硬盘控制、音频编解码、液晶控制以及触摸屏和键盘控制等几个部分。此外还有必不可少的电源管理等部分。硬盘可以采用2.5英寸或者1.8英寸的小型硬盘。音频编解码可以采用PHILIPS的UDA1344等芯片。LCD可以采用320X240的TFT液晶显示器，电源则宜用可充电锂电池。 <br />　　<br />　　再来看软件结构： <br /><p align="center"><img height="215" alt="" src="http://www.gd-emb.org/UserFiles/Image/tech_doc/js139.gif" width="448" /></p><br /><br /><br />　　软件的核心是Linux操作系统，一切功能都是基于Linux上完成的。首先需要设备驱动程序，包括USB、硬盘控制、音频控制、LCD等等。然后是文件系统。该设计采用的是通用的FAT32文件系统，在对Windows平台有很好的兼容性。基于QT的图形界面为用户提供了良好的GUI，配上触摸屏，可以形成一个很好的人机交互界面。最上层是应用层，例如MP3编码/解码，JPEG解码甚至MPEG4解码等等。 <br /><br />　　由于S3C2410以及Linux的功能都非常强大，因此除了实现上述功能，该款设计还可以有其它很多扩充功能，例如添加WLAN，GPS，GPRS以及照相模块等，以适应不同的应用需求。上述功能在产品研发过程中已经分别被我们很好地得以实现。 <br /><br />　　由于三星公司的S3C2410有很高的性价比，因此该款设计的产品生产成本并不高，其性价比非常优异。它的另外一个优点是功耗很小，根据我们测试，一块7.2V800mAh的电池可以听MP3长达10个小时以上。 <br /><br />　　我们将现在常见的一些产品做一个对比，如下表所示： <br /><p align="center"><img height="247" alt="" src="http://www.gd-emb.org/UserFiles/Image/tech_doc/js140.gif" width="528" /></p><img src ="http://www.cnitblog.com/zouzheng/aggbug/18581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 11:10 <a href="http://www.cnitblog.com/zouzheng/articles/18581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ARM MPlayer移植过程</title><link>http://www.cnitblog.com/zouzheng/articles/18579.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 03:06:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18579.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18579.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18579.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18579.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18579.html</trackback:ping><description><![CDATA[
		<p>○<font face="Times New Roman">. </font>移植前的准备</p>
		<p>源代码包选用目前最新的<font face="Times New Roman">MPlayer-1.0pre7try2.tar.bz2</font>，这些很容易从网上找到来就不给出联结了。编译工具选择<font face="Times New Roman">arm-linux-gcc-3.3.2.tar.bz2</font>，这个有<font face="Times New Roman">70</font>多<font face="Times New Roman">M</font>，也忘了当初从哪里收集回来的，本人就有这些收破烂的嗜好，没想到这次管用了：）<font face="Times New Roman">2.95.3</font>的在编译<font face="Times New Roman">MPlayer-1.0pre7try2</font>这个版本时会有问题，另外网上常见的<font face="Times New Roman">gcc3.4.1</font>版在编译<font face="Times New Roman">libavcodec/snow.c</font>文件时会挂掉，不知道是<font face="Times New Roman">GNU</font>的<font face="Times New Roman">BUG</font>还是<font face="Times New Roman">MPlayer</font>的<font face="Times New Roman">BUG</font>，没时间深究了，那位高人有结论了麻烦通报一下。补丁用不到，可以一帆风顺的完成编译的过程。</p>
		<p>
				<font face="Times New Roman">
				</font>
		</p>
		<p> </p>
		<p> </p>
		<p>一.    安装工具及解压缩源代码</p>
		<p>将<font face="Times New Roman">arm-linux-gcc</font>安装到<font face="Times New Roman">/usr/local/arm/3.3.2/bin</font>下，确保你现在有<font face="Times New Roman">root</font>权限哦，假设你的<font face="Times New Roman">arm-linux-gcc</font>工具包放在<font face="Times New Roman">~/src</font>目录下，执行以下的操作。当然你也可以将<font face="Times New Roman">arm-linux-gcc</font>安装到其它地方。</p>
		<p>
				<font face="Times New Roman">cd /</font>
		</p>
		<p>
				<font face="Times New Roman">tar jxvf ~/src/ arm-linux-gcc-3.3.2.tar.bz2</font>
		</p>
		<p>修改<font face="Times New Roman">$PATH</font>变量包含<font face="Times New Roman">arm-linux-gcc</font>，我个人习惯修改当前用户<font face="Times New Roman">home</font>目录下的<font face="Times New Roman">.bash_profile</font>文件。在<font face="Times New Roman">.bash_profile</font>文件中增加一行</p>
		<p>
				<font face="Times New Roman">$PATH=/usr/local/arm/3.3.2/bin: $PATH</font>
		</p>
		<p>也可以不做这一步在以后用的地方用绝对路径。</p>
		<p>以上完成了交叉编译工具的安装，接下来该解压<font face="Times New Roman">MPlayer</font>的源代码了。将<font face="Times New Roman">MPlayer-1.0pre7try2.tar.bz2</font>拷贝到工作目录<font face="Times New Roman">(</font>比如<font face="Times New Roman">~/develop)</font>下。</p>
		<p>
				<font face="Times New Roman">cp ~/src/MPlayer-1.0pre7try2.tar.bz2 ~/develop</font>
		</p>
		<p>
				<font face="Times New Roman">cd ~/develop</font>
		</p>
		<p>
				<font face="Times New Roman">tar jxvf MPlayer-1.0pre7try2.tar.bz2</font>
		</p>
		<p>
				<font face="Times New Roman">mv MPlayer-1.0pre7try2 MPlayer (</font>这里只是改个名字，原来的实在太长了<font face="Times New Roman">)</font></p>
		<p>上面应该不会有什么问题的，接下来就要开始编译了，各位看官可要看好了哈，来点掌声鼓励嘛<font face="Times New Roman">…..</font>哈哈。</p>
		<p>
				<font face="Times New Roman">
				</font>
		</p>
		<p> </p>
		<p> </p>
		<p>二.    开始编译</p>
		<p>首先是配置，这里有几个要注意的地方，命令如下：</p>
		<p>
				<font face="Times New Roman">./configure --host-cc=gcc --cc=arm-linux-gcc --target=arm-armv4l-linux --enable-static --prefix=/tmp/mplayer --disable-win32  --disable-dvdread --enable-fbdev --disable-mencoder --disable-live 2&gt;&amp;1 | tee logfile</font>
		</p>
		<p>
				<font face="Times New Roman">--host-cc=gcc</font>是用来编译一些需要在<font face="Times New Roman">host</font>上执行的中间文件的，如<font face="Times New Roman">codec-cfg</font>，切记不能少了或搞错了！网上的《<font face="Times New Roman">mplayer</font>在<font face="Times New Roman">ARM9(s3c2410)</font>上的移植》<font face="Times New Roman">(</font>以下简称《移》文<font face="Times New Roman">)</font>一文介绍说遇到提示<font face="Times New Roman">codec-cfg</font>不能被执行出错时的解决方法是先将<font face="Times New Roman">codec-cfg</font>编译成<font face="Times New Roman">i386</font>平台的在再这里停下来时用<font face="Times New Roman">i386</font>的<font face="Times New Roman">codec-cfg</font>替代从而使编译继续。通过实验设置了这个参数就不用那么麻烦了，而且整个过程也显得比较幽雅了。</p>
		<p>
				<font face="Times New Roman">--cc=arm-linux-gcc</font>这个没什么好介绍的。如果上面没有将<font face="Times New Roman">arm-linux-gcc</font>的位置加入到<font face="Times New Roman">$PATH</font>中的话，在这个指定绝对路径好了。</p>
		<p>
				<font face="Times New Roman">--target=arm-armv4l-linux</font>这个参数要注意的是一个分三部分，第一部分的<font face="Times New Roman">arm</font>是指<font face="Times New Roman">arch</font>，这里设定为<font face="Times New Roman">arm</font>；第二部分的<font face="Times New Roman">armv4l</font>是指具体的版本，这个要注意了，一定要跟<font face="Times New Roman">libavcodec</font>目录下的平台目录名一致，否则为这个平台的优化代码没办法编译进去<font face="Times New Roman">(</font>据我观察是这样的<font face="Times New Roman">^_@ )</font>；第三部分是系统平台。</p>
		<p>
				<font face="Times New Roman">--enable-static</font>是设定静态连接，不需要一堆乱七八糟的动态库，尤其对我们这些新手来说省了很多的麻烦。如果设置了这个参数就不用设置<font face="Times New Roman">—prefix</font>了，另外也不用执行<font face="Times New Roman">make install</font>。</p>
		<p>最后的一个<font face="Times New Roman">2&gt;&amp;1 | tee logfile</font>意思是将执行的情况在输出到屏幕的同时记录到<font face="Times New Roman">logfile</font>文件中，在控制台下编译比较有用。</p>
		<p>剩余的几个参数没什么好介绍的了，记住<font face="Times New Roman">--disable-mencoder</font>要加上，<font face="Times New Roman">mencoder</font>在这里编译会有问题，还没有时间去研究呢。如果不清楚其它的参数的意思自己看<font face="Times New Roman">configure</font>文件吧，里面都有介绍。</p>
		<p>配置完成了就该编译了。执行</p>
		<p>
				<font face="Times New Roman">make</font>
		</p>
		<p>这里在我的机器上大概花费了<font face="Times New Roman">2-3</font>分钟的时间。这样在当前目录下就得到可执行的<font face="Times New Roman">mplayer</font>文件。</p>
		<p list="" level1="" l0="">三.    试运行</p>
		<p>上面我们得到了可以在<font face="Times New Roman">2410</font>上执行的<font face="Times New Roman">mplayer</font>了，我是迫不得已的要跑一跑看效果如何。</p>
		<p>现介绍一下<font face="Times New Roman">host</font>的设置。配置<font face="Times New Roman">host</font>的<font face="Times New Roman">nfs</font>服务，使<font face="Times New Roman">host</font>作为一台服务器能够被<font face="Times New Roman">2410</font>挂上<font face="Times New Roman">(2410</font>上的内核需要支持<font face="Times New Roman">nfs</font>哦<font face="Times New Roman">)</font>。以上的一些步骤很多资料上都有，我就摘录一些了，嘿嘿。</p>
		<p>修改<font face="Times New Roman">/etc/export</font>文件，在其中添加一行<font face="Times New Roman">: /nfs *(rw,sync,no_root_squash)</font></p>
		<p>关掉防火墙<font face="Times New Roman">: iptables –F</font></p>
		<p>让<font face="Times New Roman">nfs</font>服务开机自动执行<font face="Times New Roman">: setup </font>后选<font face="Times New Roman">service</font></p>
		<p>上面的需要重新启动后<font face="Times New Roman">nfs</font>才能运行起来，如果你现在还不想重起的话，咱们手动让它跑起来<font face="Times New Roman">: /etc/rc.d/init.d/nfs start</font></p>
		<p>现在可以进行下一步了，将<font face="Times New Roman">mplayer</font>拷贝到<font face="Times New Roman">host</font>的<font face="Times New Roman">nfs</font>根目录里，将要播放的文件也一起拷贝过来</p>
		<p>
				<font face="Times New Roman">cp mplayer /nfs</font>
		</p>
		<p>
				<font face="Times New Roman">cp xxx.avi /nfs</font>
		</p>
		<p>从<font face="Times New Roman">2410</font>上将主机的<font face="Times New Roman">/nfs</font>目录挂到开发板上没有使用的一个空目录上来，一般我选<font face="Times New Roman">/mnt</font>下的目录，如<font face="Times New Roman">/mnt/nfs</font></p>
		<p>
				<font face="Times New Roman">mount –t nfs 192.168.1.222:/nfs /mnt/nfs</font>
		</p>
		<p>现在试试看有没有成功<font face="Times New Roman">,</font>在终端里输入</p>
		<p>
				<font face="Times New Roman">cd /mnt/nfs</font>
		</p>
		<p>
				<font face="Times New Roman">./mplayer xxx.avi</font>
		</p>
		<p>只见终端稀里哗啦的现实一堆的东西就停下来了。惨了！我是新手啊，让我怎么处理呢。别急慢慢来，先看看提示信息吧。提示信息中大概有这些</p>
		<p>
				<font face="Times New Roman">can’t open /dev/fb0</font>
		</p>
		<p>
				<font face="Times New Roman">vo[null]</font>
		</p>
		<p>
				<font face="Times New Roman">can’t open /dev/dsp</font>
		</p>
		<p>
				<font face="Times New Roman">ao[null]</font>
		</p>
		<p>先把图像弄出来吧，这样可以证明编译和基本设置都没有问题，声音下一步再处理了。看<font face="Times New Roman">mplayer</font>手册介绍使用的视频输出是<font face="Times New Roman">fb.</font>去<font face="Times New Roman">/dev</font>下面看看发现只有<font face="Times New Roman">/dev/fb</font>一个而没有<font face="Times New Roman">/dev/fb0</font>啊。先看看有效的输出设备吧，用<font face="Times New Roman">mplayer –vo help</font>，挨个挨个试<font face="Times New Roman">mplayer –vo xx xxx.avi</font>都还是不行。难道是编译过程有问题？解码器什么的各个参数组合都试了一遍，还是一样，<font face="Times New Roman">LCD</font>没有动静。折腾了几个晚上，我打算放弃了<font face="Times New Roman">……</font></p>
		<p>一日在网上闲逛，看了一片文章介绍<font face="Times New Roman">Linux</font>的一些技巧，如<font face="Times New Roman">cp /dev/fb myimage</font>相当于抓屏。突然我在想那么是不是<font face="Times New Roman">cp myimage /dev/fb</font>就相当于显示<font face="Times New Roman">myimage</font>呢？不管它试试再说。果然<font face="Times New Roman">LCD</font>开始有动作了，虽不是正常的将图片显示出来，但还是可以三个相同的轮廓。此证明<font face="Times New Roman">LCD</font>的驱动是没有问题的。而<font face="Times New Roman">mplayer</font>使用的是<font face="Times New Roman">/dev/fb0</font>我<font face="Times New Roman">cp</font>的是<font face="Times New Roman">/dev/fb</font>！而且这个<font face="Times New Roman">/dev/fb0</font>我好像在什么地方见到过。仔细这么一想，以前内核是有这个<font face="Times New Roman">/dev/fb0</font>的，现在使用的内核是我自己编译的，而且有<font face="Times New Roman">/dev/fb</font>这个节点说明<font face="Times New Roman">framebuff</font>驱动没有问题，那么新旧内核的差别在什么地方？这是我想到好像在编译内核的时候我没有按实际修改<font face="Times New Roman">mtd</font>的分区参数会不会是这个在作怪呢？</p>
		<p>修改<font face="Times New Roman">drivers\mtd\nand\s3c2410_nand.c</font>文件中的参数为实际对应的参数重新编译内核下载，去<font face="Times New Roman">/dev</font>目录下一看果然<font face="Times New Roman">/dev/fb0</font>出现了。<font face="Times New Roman">….(</font>此处省略<font face="Times New Roman">xxx</font>字节<font face="Times New Roman">)</font>追查到<font face="Times New Roman">/etc/init.d/rcS</font>文件<font face="Times New Roman">(</font>每个人的配置不一样，请查看你对应的文件<font face="Times New Roman">)</font>一切真相大白，各位自己去研究这个文件吧。由于文件分区信息不对，内核不能执行<font face="Times New Roman">/etc/init.d/rcS</font>，导致一些系统初始化动作没有执行。</p>
		<p>再试着执行<font face="Times New Roman">./mplayer xxx.avi</font>还是不对，难道难道<font face="Times New Roman">…</font>试一试《移》文中的只播放图像不播放声音，<font face="Times New Roman">./mplayer –nosound xxx.avi</font>哈哈正常了，在<font face="Times New Roman">320x240</font>的分辨率下相当的流畅，没什么好说的，按《移》文修改声音驱动程序重新编译内核。</p>
		<p>到这里本应该结束了，可是<font face="Times New Roman">…</font>可是<font face="Times New Roman">…</font>唉，嵌入式就是这样的，成功总是一模一样，错误总是千差万别啊。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/18579.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 11:06 <a href="http://www.cnitblog.com/zouzheng/articles/18579.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跟我一起写 Makefile </title><link>http://www.cnitblog.com/zouzheng/articles/18578.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 03:03:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18578.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18578.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18578.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18578.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18578.html</trackback:ping><description><![CDATA[
		<div class="twikiToc">
				<ul>
						<li>
								<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#Makefile_Makefile">
										<font color="#000080">Makefile学习教程: 跟我一起写 Makefile</font>
								</a>
								<ul>
										<li>
												<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#0_Makefile">
														<font color="#000080">0 Makefile概述</font>
												</a>
												<ul>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#0_1">
																		<font color="#000080">0.1 关于程序的编译和链接</font>
																</a>
														</li>
												</ul>
										</li>
										<li>
												<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_Makefile">
														<font color="#000080">1 Makefile 介绍</font>
												</a>
												<ul>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_1_Makefile">
																		<font color="#000080">1.1 Makefile的规则</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_2">
																		<font color="#000080">1.2 一个示例</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_3_make">
																		<font color="#000080">1.3 make是如何工作的</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_4_makefile">
																		<font color="#000080">1.4 makefile中使用变量</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_5_make">
																		<font color="#000080">1.5 让make自动推导</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_6_makefile">
																		<font color="#000080">1.6 另类风格的makefile</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#1_7">
																		<font color="#000080">1.7 清空目标文件的规则</font>
																</a>
														</li>
												</ul>
										</li>
										<li>
												<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_Makefile">
														<font color="#000080">2 Makefile 总述</font>
												</a>
												<ul>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_1_Makefile">
																		<font color="#000080">2.1 Makefile里有什么？</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_2Makefile">
																		<font color="#000080">2.2Makefile的文件名</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_3_Makefile">
																		<font color="#000080">2.3 引用其它的Makefile</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_4_MAKEFILES">
																		<font color="#000080">2.4 环境变量 MAKEFILES </font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#2_5_make">
																		<font color="#000080">2.5 make的工作方式</font>
																</a>
														</li>
												</ul>
										</li>
										<li>
												<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_Makefile">
														<font color="#000080">3 Makefile书写规则</font>
												</a>
												<ul>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_1">
																		<font color="#000080">3.1 规则举例</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_2">
																		<font color="#000080">3.2 规则的语法</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_3">
																		<font color="#000080">3.3 在规则中使用通配符</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_4">
																		<font color="#000080">3.4 文件搜寻</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_5">
																		<font color="#000080">3.5 伪目标</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_6">
																		<font color="#000080">3.6 多目标</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_7">
																		<font color="#000080">3.7 静态模式</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#3_8">
																		<font color="#000080">3.8 自动生成依赖性</font>
																</a>
														</li>
												</ul>
										</li>
										<li>
												<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_Makefile">
														<font color="#000080">4 Makefile 书写命令</font>
												</a>
												<ul>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_1">
																		<font color="#000080">4.1 显示命令</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_2">
																		<font color="#000080">4.2 命令执行</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_3">
																		<font color="#000080">4.3 命令出错</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_4_make">
																		<font color="#000080">4.4 嵌套执行make</font>
																</a>
														</li>
														<li>
																<a href="http://www.stlchina.org/twiki/bin/view.pl/ScriptProgram/LearnMakefile#4_5">
																		<font color="#000080">4.5 定义命令包</font>
																</a>
														</li>
												</ul>
										</li>
								</ul>
						</li>
				</ul>
		</div>
		<h3>
				<a name="0_Makefile">
				</a>
				<a name="0_Makefile_">
				</a>0 Makefile概述 </h3>
		<hr />
什么是makefile？或许很多Winodws的程序员都不知道这个东西，因为那些Windows的IDE都为你做了这个工作，但我觉得要作一个好的和professional的程序员，makefile还是要懂。这就好像现在有这么多的HTML的编辑器，但如果你想成为一个专业人士，你还是要了解HTML的标识的含义。特别在Unix下的软件编译，你就不能不自己写makefile了，会不会写makefile，从一个侧面说明了一个人是否具备完成大型工程的能力。 
<p>因为，makefile关系到了整个工程的编译规则。一个工程中的源文件不计数，其按类型、功能、模块分别放在若干个目录中，makefile定义了一系列的规则来指定，哪些文件需要先编译，哪些文件需要后编译，哪些文件需要重新编译，甚至于进行更复杂的功能操作，因为makefile就像一个Shell脚本一样，其中也可以执行操作系统的命令。 </p><p>makefile带来的好处就是——“自动化编译”，一旦写好，只需要一个make命令，整个工程完全自动编译，极大的提高了软件开发的效率。make是一个命令工具，是一个解释makefile中指令的命令工具，一般来说，大多数的IDE都有这个命令，比如：Delphi的make，Visual C++的nmake，Linux下GNU的make。可见，makefile都成为了一种在工程方面的编译方法。 </p><p>现在讲述如何写makefile的文章比较少，这是我想写这篇文章的原因。当然，不同产商的make各不相同，也有不同的语法，但其本质都是在“文件依赖性”上做文章，这里，我仅对GNU的make进行讲述，我的环境是RedHat Linux 8.0，make的版本是3.80。必竟，这个make是应用最为广泛的，也是用得最多的。而且其还是最遵循于IEEE 1003.2-1992 标准的（POSIX.2）。 </p><p>在这篇文档中，将以C/C++的源码作为我们基础，所以必然涉及一些关于C/C++的编译的知识，相关于这方面的内容，还请各位查看相关的编译器的文档。这里所默认的编译器是UNIX下的GCC和CC。 </p><p> </p><h4><a name="0_1"></a><a name="0_1_"></a>0.1 关于程序的编译和链接 </h4>在此，我想多说关于程序编译的一些规范和方法，一般来说，无论是C、C++、还是pas，首先要把源文件编译成中间代码文件，在Windows下也就是 .obj 文件，UNIX下是 .o 文件，即 Object File，这个动作叫做编译（compile）。然后再把大量的Object File合成执行文件，这个动作叫作链接（link）。 
<p>编译时，编译器需要的是语法的正确，函数与变量的声明的正确。对于后者，通常是你需要告诉编译器头文件的所在位置（头文件中应该只是声明，而定义应该放在C/C++文件中），只要所有的语法正确，编译器就可以编译出中间目标文件。一般来说，每个源文件都应该对应于一个中间目标文件（O文件或是OBJ文件）。 </p><p>链接时，主要是链接函数和全局变量，所以，我们可以使用这些中间目标文件（O文件或是OBJ文件）来链接我们的应用程序。链接器并不管函数所在的源文件，只管函数的中间目标文件（Object File），在大多数时候，由于源文件太多，编译生成的中间目标文件太多，而在链接时需要明显地指出中间目标文件名，这对于编译很不方便，所以，我们要给中间目标文件打个包，在Windows下这种包叫“库文件”（Library File)，也就是 .lib 文件，在UNIX下，是Archive File，也就是 .a 文件。 </p><p>总结一下，源文件首先会生成中间目标文件，再由中间目标文件生成执行文件。在编译时，编译器只检测程序语法，和函数、变量是否被声明。如果函数未被声明，编译器会给出一个警告，但可以生成Object File。而在链接程序时，链接器会在所有的Object File中找寻函数的实现，如果找不到，那到就会报链接错误码（Linker Error），在VC下，这种错误一般是：Link 2001错误，意思说是说，链接器未能找到函数的实现。你需要指定函数的Object File. </p><p>好，言归正传，GNU的make有许多的内容，闲言少叙，还是让我们开始吧。 </p><p> </p><h3><a name="1_Makefile"></a><a name="1_Makefile_"></a>1 Makefile 介绍 </h3><hr />
make命令执行时，需要一个 Makefile 文件，以告诉make命令需要怎么样的去编译和链接程序。 
<p>首先，我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册，在这个示例中，我们的工程有8个C文件，和3个头文件，我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是： </p><ol><li>如果这个工程没有编译过，那么我们的所有C文件都要编译并被链接。 
</li><li>如果这个工程的某几个C文件被修改，那么我们只编译被修改的C文件，并链接目标程序。 
</li><li>如果这个工程的头文件被改变了，那么我们需要编译引用了这几个头文件的C文件，并链接目标程序。 </li></ol><p>只要我们的Makefile写得够好，所有的这一切，我们只用一个make命令就可以完成，make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译，从而自己编译所需要的文件和链接目标程序。 </p><h4><a name="1_1_Makefile"></a><a name="1_1_Makefile_"></a>1.1 Makefile的规则 </h4>在讲述这个Makefile之前，还是让我们先来粗略地看一看Makefile的规则。<pre>target ... : prerequisites ...   command   ...   ...</pre>target也就是一个目标文件，可以是Object File，也可以是执行文件。还可以是一个标签（Label），对于标签这种特性，在后续的“伪目标”章节中会有叙述。 
<p>prerequisites就是，要生成那个target所需要的文件或是目标。 </p><p>command也就是make需要执行的命令。（任意的Shell命令） </p><p>这是一个文件的依赖关系，也就是说，target这一个或多个的目标文件依赖于prerequisites中的文件，其生成规则定义在command中。说白一点就是说，prerequisites中如果有一个以上的文件比target文件要新的话，command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。 </p><p>说到底，Makefile的东西就是这样一点，好像我的这篇文档也该结束了。呵呵。还不尽然，这是Makefile的主线和核心，但要写好一个Makefile还不够，我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。：） </p><h4><a name="1_2"></a><a name="1_2_"></a>1.2 一个示例 </h4>正如前面所说的，如果一个工程有3个头文件，和8个C文件，我们为了完成前面所述的那三个规则，我们的Makefile应该是下面的这个样子的。<pre>    edit : main.o kbd.o command.o display.o            insert.o search.o files.o utils.o            cc -o edit main.o kbd.o command.o display.o                        insert.o search.o files.o utils.o    main.o : main.c defs.h            cc -c main.c    kbd.o : kbd.c defs.h command.h            cc -c kbd.c    command.o : command.c defs.h command.h            cc -c command.c    display.o : display.c defs.h buffer.h            cc -c display.c    insert.o : insert.c defs.h buffer.h            cc -c insert.c    search.o : search.c defs.h buffer.h            cc -c search.c    files.o : files.c defs.h buffer.h command.h            cc -c files.c    utils.o : utils.c defs.h            cc -c utils.c    clean :            rm edit main.o kbd.o command.o display.o                insert.o search.o files.o utils.o</pre>反斜杠（\）是换行符的意思。这样比较便于Makefile的易读。我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中，然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件，那么，只要简单地执行一下“make clean”就可以了。 
<p>在这个makefile中，目标文件（target）包含：执行文件edit和中间目标文件（*.o），依赖文件（prerequisites）就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件，而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的，换言之，目标文件是哪些文件更新的。 </p><p>在定义好依赖关系后，后续的那一行定义了如何生成目标文件的操作系统命令，一定要以一个Tab键作为开头。记住，make并不管命令是怎么工作的，他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期，如果prerequisites文件的日期要比targets文件的日期要新，或者target不存在的话，那么，make就会执行后续定义的命令。 </p><p>这里要说明一点的是，clean不是一个文件，它只不过是一个动作名字，有点像C语言中的lable一样，其冒号后什么也没有，那么，make就不会自动去找文件的依赖性，也就不会自动执行其后所定义的命令。要执行其后的命令，就要在make命令后明显得指出这个lable的名字。这样的方法非常有用，我们可以在一个makefile中定义不用的编译或是和编译无关的命令，比如程序的打包，程序的备份，等等。 </p><p> </p><h4><a name="1_3_make"></a><a name="1_3_make_"></a>1.3 make是如何工作的 </h4>在默认的方式下，也就是我们只输入make命令。那么， 
<p> </p><ol><li>make会在当前目录下找名字叫“Makefile”或“makefile”的文件。 
</li><li>如果找到，它会找文件中的第一个目标文件（target），在上面的例子中，他会找到“edit”这个文件，并把这个文件作为最终的目标文件。 
</li><li>如果edit文件不存在，或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新，那么，他就会执行后面所定义的命令来生成edit这个文件。 
</li><li>如果edit所依赖的.o文件也存在，那么make会在当前文件中找目标为.o文件的依赖性，如果找到则再根据那一个规则生成.o文件。（这有点像一个堆栈的过程） 
</li><li>当然，你的C文件和H文件是存在的啦，于是make会生成 .o 文件，然后再用 .o 文件生命make的终极任务，也就是执行文件edit了。 </li></ol><p>这就是整个make的依赖性，make会一层又一层地去找文件的依赖关系，直到最终编译出第一个目标文件。在找寻的过程中，如果出现错误，比如最后被依赖的文件找不到，那么make就会直接退出，并报错，而对于所定义的命令的错误，或是编译不成功，make根本不理。make只管文件的依赖性，即，如果在我找了依赖关系之后，冒号后面的文件还是不在，那么对不起，我就不工作啦。 </p><p>通过上述分析，我们知道，像clean这种，没有被第一个目标文件直接或间接关联，那么它后面所定义的命令将不会被自动执行，不过，我们可以显示要make执行。即命令——“make clean”，以此来清除所有的目标文件，以便重编译。 </p><p>于是在我们编程中，如果这个工程已被编译过了，当我们修改了其中一个源文件，比如file.c，那么根据我们的依赖性，我们的目标file.o会被重编译（也就是在这个依性关系后面所定义的命令），于是file.o的文件也是最新的啦，于是file.o的文件修改时间要比edit要新，所以edit也会被重新链接了（详见edit目标文件后定义的命令）。 </p><p>而如果我们改变了“command.h”，那么，kdb.o、command.o和files.o都会被重编译，并且，edit会被重链接。 </p><h4><a name="1_4_makefile"></a><a name="1_4_makefile_"></a>1.4 makefile中使用变量 </h4>在上面的例子中，先让我们看看edit的规则：<pre>      edit : main.o kbd.o command.o display.o                   insert.o search.o files.o utils.o            cc -o edit main.o kbd.o command.o display.o                        insert.o search.o files.o utils.o</pre>我们可以看到[.o]文件的字符串被重复了两次，如果我们的工程需要加入一个新的[.o]文件，那么我们需要在两个地方加（应该是三个地方，还有一个地方在clean中）。当然，我们的makefile并不复杂，所以在两个地方加也不累，但如果makefile变得复杂，那么我们就有可能会忘掉一个需要加入的地方，而导致编译失败。所以，为了makefile的易维护，在makefile中我们可以使用变量。makefile的变量也就是一个字符串，理解成C语言中的宏可能会更好。 
<p>比如，我们声明一个变量，叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ，反正不管什么啦，只要能够表示obj文件就行了。我们在makefile一开始就这样定义： </p><pre>     objects = main.o kbd.o command.o display.o               insert.o search.o files.o utils.o</pre>于是，我们就可以很方便地在我们的makefile中以“$(objects)”的方式来使用这个变量了，于是我们的改良版makefile就变成下面这个样子：<pre>    objects = main.o kbd.o command.o display.o               insert.o search.o files.o utils.o    edit : $(objects)            cc -o edit $(objects)    main.o : main.c defs.h            cc -c main.c    kbd.o : kbd.c defs.h command.h            cc -c kbd.c    command.o : command.c defs.h command.h            cc -c command.c    display.o : display.c defs.h buffer.h            cc -c display.c    insert.o : insert.c defs.h buffer.h            cc -c insert.c    search.o : search.c defs.h buffer.h            cc -c search.c    files.o : files.c defs.h buffer.h command.h            cc -c files.c    utils.o : utils.c defs.h            cc -c utils.c    clean :            rm edit $(objects)</pre><p>于是如果有新的 .o 文件加入，我们只需简单地修改一下 objects 变量就可以了。 </p><p>关于变量更多的话题，我会在后续给你一一道来。 </p><h4><a name="1_5_make"></a><a name="1_5_make_"></a>1.5 让make自动推导 </h4>GNU的make很强大，它可以自动推导文件以及文件依赖关系后面的命令，于是我们就没必要去在每一个[.o]文件后都写上类似的命令，因为，我们的make会自动识别，并自己推导命令。 
<p>只要make看到一个[.o]文件，它就会自动的把[.c]文件加在依赖关系中，如果make找到一个whatever.o，那么whatever.c，就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来，于是，我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。 </p><pre>    objects = main.o kbd.o command.o display.o               insert.o search.o files.o utils.o    edit : $(objects)            cc -o edit $(objects)    main.o : defs.h    kbd.o : defs.h command.h    command.o : defs.h command.h    display.o : defs.h buffer.h    insert.o : defs.h buffer.h    search.o : defs.h buffer.h    files.o : defs.h buffer.h command.h    utils.o : defs.h    .PHONY : clean    clean :            rm edit $(objects)</pre>这种方法，也就是make的“隐晦规则”。上面文件内容中，“.PHONY”表示，clean是个伪目标文件。 
<p>关于更为详细的“隐晦规则”和“伪目标文件”，我会在后续给你一一道来。 </p><h4><a name="1_6_makefile"></a>1.6 另类风格的makefile </h4>即然我们的make可以自动推导命令，那么我看到那堆[.o]和[.h]的依赖就有点不爽，那么多的重复的[.h]，能不能把其收拢起来，好吧，没有问题，这个对于make来说很容易，谁叫它提供了自动推导命令和文件的功能呢？来看看最新风格的makefile吧。<pre>    objects = main.o kbd.o command.o display.o               insert.o search.o files.o utils.o    edit : $(objects)            cc -o edit $(objects)    $(objects) : defs.h    kbd.o command.o files.o : command.h    display.o insert.o search.o files.o : buffer.h    .PHONY : clean    clean :            rm edit $(objects)</pre>这种风格，让我们的makefile变得很简单，但我们的文件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的，一是文件的依赖关系看不清楚，二是如果文件一多，要加入几个新的.o文件，那就理不清楚了。 
<h4><a name="1_7"></a><a name="1_7_"></a>1.7 清空目标文件的规则 </h4>每个Makefile中都应该写一个清空目标文件（.o和执行文件）的规则，这不仅便于重编译，也很利于保持文件的清洁。这是一个“修养”（呵呵，还记得我的《编程修养》吗）。一般的风格都是：<pre>        clean:            rm edit $(objects)</pre>更为稳健的做法是：<pre>        .PHONY : clean        clean :                -rm edit $(objects)</pre>前面说过，.PHONY意思表示clean是一个“伪目标”，。而在rm命令前面加了一个小减号的意思就是，也许某些文件出现问题，但不要管，继续做后面的事。当然，clean的规则不要放在文件的开头，不然，这就会变成make的默认目标，相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。 
<p> </p><p>上面就是一个makefile的概貌，也是makefile的基础，下面还有很多makefile的相关细节，准备好了吗？准备好了就来。 </p><hr /><h3><a name="2_Makefile"></a><a name="2_Makefile_"></a>2 Makefile 总述 </h3><h4><a name="2_1_Makefile"></a><a name="2_1_Makefile_"></a>2.1 Makefile里有什么？ </h4>Makefile里主要包含了五个东西：显式规则、隐晦规则、变量定义、文件指示和注释。 
<p> </p><ol><li>显式规则。显式规则说明了，如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出，要生成的文件，文件的依赖文件，生成的命令。 
</li><li>隐晦规则。由于我们的make有自动推导的功能，所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile，这是由make所支持的。 
</li><li>变量的定义。在Makefile中我们要定义一系列的变量，变量一般都是字符串，这个有点你C语言中的宏，当Makefile被执行时，其中的变量都会被扩展到相应的引用位置上。 
</li><li>文件指示。其包括了三个部分，一个是在一个Makefile中引用另一个Makefile，就像C语言中的include一样；另一个是指根据某些情况指定Makefile中的有效部分，就像C语言中的预编译#if一样；还有就是定义一个多行的命令。有关这一部分的内容，我会在后续的部分中讲述。 
</li><li>注释。Makefile中只有行注释，和UNIX的Shell脚本一样，其注释是用“#”字符，这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符，可以用反斜框进行转义，如：“\#”。 </li></ol><p>最后，还值得一提的是，在Makefile中的命令，必须要以[Tab]键开始。 </p><h4><a name="2_2Makefile"></a><a name="2_2Makefile_"></a>2.2Makefile的文件名 </h4>默认的情况下，make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件，找到了解释这个文件。在这三个文件名中，最好使用“Makefile”这个文件名，因为，这个文件名第一个字符为大写，这样有一种显目的感觉。最好不要用“GNUmakefile”，这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感，但是基本上来说，大多数的make都支持“makefile”和“Makefile”这两种默认文件名。 
<p>当然，你可以使用别的文件名来书写Makefile，比如：“Make.Linux”，“Make.Solaris”，“Make.AIX”等，如果要指定特定的Makefile，你可以使用make的“-f”和“--file”参数，如：make -f Make.Linux或make --file Make.AIX。 </p><h4><a name="2_3_Makefile"></a>2.3 引用其它的Makefile </h4>在Makefile使用include关键字可以把别的Makefile包含进来，这很像C语言的#include，被包含的文件会原模原样的放在当前文件的包含位置。include的语法是： 
<div class="fragment"><pre>include &lt;filename&gt;</pre><pre> </pre></div>filename可以是当前操作系统Shell的文件模式（可以保含路径和通配符） 
<p>在include前面可以有一些空字符，但是绝不能是[Tab]键开始。include和<filename></filename>可以用一个或多个空格隔开。举个例子，你有这样几个Makefile：a.mk、b.mk、c.mk，还有一个文件叫foo.make，以及一个变量$(bar)，其包含了e.mk和f.mk，那么，下面的语句： </p><pre>    include foo.make *.mk $(bar)</pre>等价于：<pre>    include foo.make a.mk b.mk c.mk e.mk f.mk</pre>make命令开始时，会把找寻include所指出的其它Makefile，并把其内容安置在当前的位置。就好像C/C++的#include指令一样。如果文件都没有指定绝对路径或是相对路径的话，make会在当前目录下首先寻找，如果当前目录下没有找到，那么，make还会在下面的几个目录下找： 
<p> </p><ol><li>如果make执行时，有“-I”或“--include-dir”参数，那么make就会在这个参数所指定的目录下去寻找。 
</li><li>如果目录 <prefix></prefix>/include（一般是：/usr/local/bin或/usr/include）存在的话，make也会去找。 </li></ol><p>如果有文件没有找到的话，make会生成一条警告信息，但不会马上出现致命错误。它会继续载入其它的文件，一旦完成makefile的读取，make会再重试这些没有找到，或是不能读取的文件，如果还是不行，make才会出现一条致命信息。如果你想让make不理那些无法读取的文件，而继续执行，你可以在include前加一个减号“-”。如： </p><div class="fragment"><pre>-include &lt;filename&gt;</pre><pre> </pre></div>其表示，无论include过程中出现什么错误，都不要报错继续执行。和其它版本make兼容的相关命令是sinclude，其作用和这一个是一样的。 
<h4><a name="2_4_MAKEFILES"></a>2.4 环境变量 MAKEFILES </h4>如果你的当前环境中定义了环境变量MAKEFILES，那么，make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile，用空格分隔。只是，它和include不同的是，从这个环境变中引入的Makefile的“目标”不会起作用，如果环境变量中定义的文件发现错误，make也会不理。 
<p>但是在这里我还是建议不要使用这个环境变量，因为只要这个变量一被定义，那么当你使用make时，所有的Makefile都会受到它的影响，这绝不是你想看到的。在这里提这个事，只是为了告诉大家，也许有时候你的Makefile出现了怪事，那么你可以看看当前环境中有没有定义这个变量。 </p><h4><a name="2_5_make"></a><a name="2_5_make_"></a>2.5 make的工作方式 </h4>GNU的make工作时的执行步骤入下：（想来其它的make也是类似） 
<p> </p><ol><li>读入所有的Makefile。 
</li><li>读入被include的其它Makefile。 
</li><li>初始化文件中的变量。 
</li><li>推导隐晦规则，并分析所有规则。 
</li><li>为所有的目标文件创建依赖关系链。 
</li><li>根据依赖关系，决定哪些目标要重新生成。 
</li><li>执行生成命令。 </li></ol><p>1-5步为第一个阶段，6-7为第二个阶段。第一个阶段中，如果定义的变量被使用了，那么，make会把其展开在使用的位置。但make并不会完全马上展开，make使用的是拖延战术，如果变量出现在依赖关系的规则中，那么仅当这条依赖被决定要使用了，变量才会在其内部展开。 </p><p>当然，这个工作方式你不一定要清楚，但是知道这个方式你也会对make更为熟悉。有了这个基础，后续部分也就容易看懂了。 </p><h3><a name="3_Makefile"></a><a name="3_Makefile_"></a>3 Makefile书写规则 </h3><hr />
规则包含两个部分，一个是依赖关系，一个是生成目标的方法。 
<p>在Makefile中，规则的顺序是很重要的，因为，Makefile中只应该有一个最终目标，其它的目标都是被这个目标所连带出来的，所以一定要让make知道你的最终目标是什么。一般来说，定义在Makefile中的目标可能会有很多，但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个，那么，第一个目标会成为最终的目标。make所完成的也就是这个目标。 </p><p>好了，还是让我们来看一看如何书写规则。 </p><h4><a name="3_1"></a><a name="3_1_"></a>3.1 规则举例 </h4><pre> foo.o : foo.c defs.h       # foo模块            cc -c -g foo.c</pre>看到这个例子，各位应该不是很陌生了，前面也已说过，foo.o是我们的目标，foo.c和defs.h是目标所依赖的源文件，而只有一个命令“cc -c -g foo.c”（以Tab键开头）。这个规则告诉我们两件事： 
<p> </p><ol><li>文件的依赖关系，foo.o依赖于foo.c和defs.h的文件，如果foo.c和defs.h的文件日期要比foo.o文件日期要新，或是foo.o不存在，那么依赖关系发生。 
</li><li>如果生成（或更新）foo.o文件。也就是那个cc命令，其说明了，如何生成foo.o这个文件。（当然foo.c文件include了defs.h文件） </li></ol><p> </p><h4><a name="3_2"></a><a name="3_2_"></a>3.2 规则的语法 </h4><pre>      targets : prerequisites        command        ...</pre>或是这样： <pre>      targets : prerequisites ; command            command            ...</pre>targets是文件名，以空格分开，可以使用通配符。一般来说，我们的目标基本上是一个文件，但也有可能是多个文件。 
<p>command是命令行，如果其不与“target:prerequisites”在一行，那么，必须以[Tab键]开头，如果和prerequisites在一行，那么可以用分号做为分隔。（见上） </p><p>prerequisites也就是目标所依赖的文件（或依赖目标）。如果其中的某个文件要比目标文件要新，那么，目标就被认为是“过时的”，被认为是需要重生成的。这个在前面已经讲过了。 </p><p>如果命令太长，你可以使用反斜框（‘\’）作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事，文件的依赖关系和如何成成目标文件。 </p><p>一般来说，make会以UNIX的标准Shell，也就是/bin/sh来执行命令。 </p><h4><a name="3_3"></a><a name="3_3_"></a>3.3 在规则中使用通配符 </h4><p>如果我们想定义一系列比较类似的文件，我们很自然地就想起使用通配符。make支持三各通配符：“*”，“?”和“[...]”。这是和Unix的B-Shell是相同的。 </p><p>波浪号（“~”）字符在文件名中也有比较特殊的用途。如果是“~/test”，这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。（这些都是Unix下的小知识了，make也支持）而在Windows或是MS-DOS下，用户没有宿主目录，那么波浪号所指的目录则根据环境变量“HOME”而定。 </p><p>通配符代替了你一系列的文件，如“*.c”表示所以后缀为c的文件。一个需要我们注意的是，如果我们的文件名中有通配符，如：“*”，那么可以用转义字符“\”，如“\*”来表示真实的“*”字符，而不是任意长度的字符串。 </p><p>好吧，还是先来看几个例子吧： </p><pre>    clean:         rm -f *.o</pre>上面这个例子我不不多说了，这是操作系统Shell所支持的通配符。这是在命令中的通配符。 <pre>    print: *.c         lpr -p $?         touch print</pre>上面这个例子说明了通配符也可以在我们的规则中，目标print依赖于所有的[.c]文件。其中的“$?”是一个自动化变量，我会在后面给你讲述。 <pre>    objects = *.o</pre>上面这个例子，表示了，通符同样可以用在变量中。并不是说[*.o]会展开，不！objects的值就是“*.o”。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开，也就是让objects的值是所有[.o]的文件名的集合，那么，你可以这样： <pre>    objects := $(wildcard *.o)</pre>这种用法由关键字“wildcard”指出，关于Makefile的关键字，我们将在后面讨论。 
<h4><a name="3_4"></a><a name="3_4_"></a>3.4 文件搜寻 </h4><p>在一些大的工程中，有大量的源文件，我们通常的做法是把这许多的源文件分类，并存放在不同的目录中。所以，当make需要去找寻文件的依赖关系时，你可以在文件前加上路径，但最好的方法是把一个路径告诉make，让make在自动去找。 </p><p>Makefile文件中的特殊变量“VPATH”就是完成这个功能的，如果没有指明这个变量，make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量，那么，make就会在当当前目录找不到的情况下，到所指定的目录中去找寻文件了。 </p><pre>    VPATH = src:../headers</pre>上面的的定义指定两个目录，“src”和“../headers”，make会按照这个顺序进行搜索。目录由“冒号”分隔。（当然，当前目录永远是最高优先搜索的地方） 
<p>另一个设置文件搜索路径的方法是使用make的“vpath”关键字（注意，它是全小写的），这不是变量，这是一个make的关键字，这和上面提到的那个VPATH变量很类似，但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种： </p><ol><li>vpath &lt; pattern&gt; &lt; directories&gt; <br />为符合模式&lt; pattern&gt;的文件指定搜索目录&lt; directories&gt;。 
</li><li>vpath &lt; pattern&gt;<br />清除符合模式&lt; pattern&gt;的文件的搜索目录。 
</li><li>vpath <br />清除所有已被设置好了的文件搜索目录。 </li></ol><p>vapth使用方法中的&lt; pattern&gt;需要包含“%”字符。“%”的意思是匹配零或若干字符，例如，“%.h”表示所有以“.h”结尾的文件。&lt; pattern&gt;指定了要搜索的文件集，而&lt; directories&gt;则指定了 <pattern></pattern>的文件集的搜索的目录。例如： </p><pre>    vpath %.h ../headers</pre>该语句表示，要求make在“../headers”目录下搜索所有以“.h”结尾的文件。（如果某文件在当前目录没有找到的话） 
<p>我们可以连续地使用vpath语句，以指定不同搜索策略。如果连续的vpath语句中出现了相同的&lt; pattern&gt;，或是被重复了的&lt; pattern&gt;，那么，make会按照vpath语句的先后顺序来执行搜索。如： </p><pre>    vpath %.c foo    vpath %   blish    vpath %.c bar</pre>其表示“.c”结尾的文件，先在“foo”目录，然后是“blish”，最后是“bar”目录。 <pre>    vpath %.c foo:bar    vpath %   blish</pre>而上面的语句则表示“.c”结尾的文件，先在“foo”目录，然后是“bar”目录，最后才是“blish”目录。 
<h4><a name="3_5"></a><a name="3_5_"></a>3.5 伪目标 </h4><p>最早先的一个例子中，我们提到过一个“clean”的目标，这是一个“伪目标”， </p><pre>    clean:            rm *.o temp</pre>正像我们前面例子中的“clean”一样，即然我们生成了许多文件编译文件，我们也应该提供一个清除它们的“目标”以备完整地重编译而用。 （以“make clean”来使用该目标） 
<p>因为，我们并不生成“clean”这个文件。“伪目标”并不是一个文件，只是一个标签，由于“伪目标”不是文件，所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然，“伪目标”的取名不能和文件名重名，不然其就失去了“伪目标”的意义了。 </p><p>当然，为了避免和文件重名的这种情况，我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”，向make说明，不管是否有这个文件，这个目标就是“伪目标”。 </p><pre>    .PHONY : clean</pre>只要有这个声明，不管是否有“clean”文件，要运行“clean”这个目标，只有“make clean”这样。于是整个过程可以这样写： <pre>     .PHONY: clean    clean:            rm *.o temp</pre>伪目标一般没有依赖的文件。但是，我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”，只要将其放在第一个。一个示例就是，如果你的Makefile需要一口气生成若干个可执行文件，但你只想简单地敲一个make完事，并且，所有的目标文件都写在一个Makefile中，那么你可以使用“伪目标”这个特性： <pre>    all : prog1 prog2 prog3    .PHONY : all    prog1 : prog1.o utils.o            cc -o prog1 prog1.o utils.o    prog2 : prog2.o            cc -o prog2 prog2.o    prog3 : prog3.o sort.o utils.o            cc -o prog3 prog3.o sort.o utils.o</pre>我们知道，Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标，其依赖于其它三个目标。由于伪目标的特性是，总是被执行的，所以其依赖的那三个目标就总是不如“all”这个目标新。所以，其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。 
<p>随便提一句，从上面的例子我们可以看出，目标也可以成为依赖。所以，伪目标同样也可成为依赖。看下面的例子： </p><pre>    .PHONY: cleanall cleanobj cleandiff    cleanall : cleanobj cleandiff            rm program    cleanobj :            rm *.o    cleandiff :            rm *.diff</pre>“make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的 
<p> </p><h4><a name="3_6"></a><a name="3_6_"></a>3.6 多目标 </h4><p>Makefile的规则中的目标可以不止一个，其支持多目标，有可能我们的多个目标同时依赖于一个文件，并且其生成的命令大体类似。于是我们就能把其合并起来。当然，多个目标的生成规则的执行命令是同一个，这可能会可我们带来麻烦，不过好在我们的可以使用一个自动化变量“$@”（关于自动化变量，将在后面讲述），这个变量表示着目前规则中所有的目标的集合，这样说可能很抽象，还是看一个例子吧。 </p><pre>    bigoutput littleoutput : text.g            generate text.g -$(subst output,,$@) &gt; $@<pre>    上述规则等价于：</pre>    bigoutput : text.g    generate text.g -big &gt; bigoutput    littleoutput : text.g    generate text.g -little &gt; littleoutput    </pre>其中，-$(subst output,,$@)中的“$”表示执行一个Makefile的函数，函数名为subst，后面的为参数。关于函数，将在后面讲述。这里的这个函数是截取字符串的意思，“$@”表示目标的集合，就像一个数组，“$@”依次取出目标，并执于命令。 
<p> </p><p> </p><h4><a name="3_7"></a><a name="3_7_"></a>3.7 静态模式 </h4><p>静态模式可以更加容易地定义多目标的规则，可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法： </p><div class="fragment"><pre>&lt;targets ...&gt;: &lt;target-pattern&gt;: &lt;prereq-patterns ...&gt;　　　&lt;commands&gt;...</pre><pre> </pre></div><p>targets定义了一系列的目标文件，可以有通配符。是目标的一个集合。 </p><p>target-parrtern是指明了targets的模式，也就是的目标集模式。 </p><p>prereq-parrterns是目标的依赖模式，它对target-parrtern形成的模式再进行一次依赖目标的定义。 </p><p>这样描述这三个东西，可能还是没有说清楚，还是举个例子来说明一下吧。如果我们的<target-parrtern></target-parrtern>定义成“%.o”，意思是我们的<target></target>集合中都是以“.o”结尾的，而如果我们的 <prereq-parrterns></prereq-parrterns>定义成“%.c”，意思是对<target-parrtern></target-parrtern>所形成的目标集进行二次定义，其计算方法是，取<target-parrtern></target-parrtern>模式中的“%”（也就是去掉了[.o]这个结尾），并为其加上[.c]这个结尾，形成的新集合。 </p><p>所以，我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符，如果你的文件名中有“%”那么你可以使用反斜杠“\”进行转义，来标明真实的“%”字符。 </p><p>看一个例子： </p><pre>    objects = foo.o bar.o    all: $(objects)    $(objects): %.o: %.c            $(CC) -c $(CFLAGS) ___FCKpd___34lt; -o $@</pre>上面的例子中，指明了我们的目标从$object中获取，“%.o”表明要所有以“.o”结尾的目标，也就是“foo.o bar.o”，也就是变量$object集合的模式，而依赖模式“%.c”则取模式“%.o”的“%”，也就是“foo bar”，并为其加下“.c”的后缀，于是，我们的依赖目标就是“foo.c bar.c”。而命令中的“$&lt;”和“$@”则是自动化变量，“$&lt;”表示所有的依赖目标集（也就是“foo.c bar.c”），“$@”表示目标集（也褪恰癴oo.o bar.o”）。于是，上面的规则展开后等价于下面的规则： <pre>    foo.o : foo.c            $(CC) -c $(CFLAGS) foo.c -o foo.o    bar.o : bar.c            $(CC) -c $(CFLAGS) bar.c -o bar.o</pre>试想，如果我们的“%.o”有几百个，那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则，实在是太有效率了。“静态模式规则”的用法很灵活，如果用得好，那会一个很强大的功能。再看一个例子： <pre>    files = foo.elc bar.o lose.o    $(filter %.o,$(files)): %.o: %.c            $(CC) -c $(CFLAGS) ___FCKpd___36lt; -o $@    $(filter %.elc,$(files)): %.elc: %.el            emacs -f batch-byte-compile ___FCKpd___36lt;</pre><p>$(filter %.o,$(files))表示调用Makefile的filter函数，过滤“$filter”集，只要其中模式为“%.o”的内容。其的它内容，我就不用多说了吧。这个例字展示了Makefile中更大的弹性。 </p><h4><a name="3_8"></a><a name="3_8_"></a>3.8 自动生成依赖性 </h4><p>在Makefile中，我们的依赖关系可能会需要包含一系列的头文件，比如，如果我们的main.c中有一句“#include "defs.h"”，那么我们的依赖关系应该是： </p><pre>    main.o : main.c defs.h</pre>但是，如果是一个比较大型的工程，你必需清楚哪些C文件包含了哪些头文件，并且，你在加入或删除头文件时，也需要小心地修改Makefile，这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情，我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个“-M”的选项，即自动找寻源文件中包含的头文件，并生成一个依赖关系。例如，如果我们执行下面的命令： <pre>    cc -M main.c</pre>其输出是： <pre>    main.o : main.c defs.h</pre>于是由编译器自动生成的依赖关系，这样一来，你就不必再手动书写若干文件的依赖关系，而由编译器自动生成了。需要提醒一句的是，如果你使用GNU的C/C++编译器，你得用“-MM”参数，不然，“-M”参数会把一些标准库的头文件也包含进来。 
<p>gcc -M main.c的输出是： </p><pre>    main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h          /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h          /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h          /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h          /usr/include/bits/sched.h /usr/include/libio.h          /usr/include/_G_config.h /usr/include/wchar.h          /usr/include/bits/wchar.h /usr/include/gconv.h          /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h          /usr/include/bits/stdio_lim.h</pre>gcc -MM main.c的输出则是： <pre>    main.o: main.c defs.h</pre>那么，编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来，我们的Makefile也要根据这些源文件重新生成，让Makefile自已依赖于源文件？这个功能并不现实，不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中，为每一个“name.c”的文件都生成一个“name.d”的Makefile文件，[.d]文件中就存放对应[.c]文件的依赖关系。 
<p>于是，我们可以写出[.c]文件和[.d]文件的依赖关系，并让make自动更新或自成[.d]文件，并把其包含在我们的主Makefile中，这样，我们就可以自动化地生成每个文件的依赖关系了。 </p><p>这里，我们给出了一个模式规则来产生[.d]文件： </p><pre>    %.d: %.c            @set -e; rm -f $@;              $(CC) -M $(CPPFLAGS) ___FCKpd___42lt; &gt; $@.$$;              sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' &lt; $@.$$ &gt; $@;              rm -f $@.$$</pre><p>这个规则的意思是，所有的[.d]文件依赖于[.c]文件，“rm -f $@”的意思是删除所有的目标，也就是[.d]文件，第二行的意思是，为每个依赖文件“$&lt;”，也就是[.c]文件生成依赖文件，“$@”表示模式“%.d”文件，如果有一个C文件是name.c，那么“%”就是“name”，“$$$$”意为一个随机编号，第二行生成的文件有可能是“name.d.12345”，第三行使用sed命令做了一个替换，关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。 </p><p>总而言之，这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖，即把依赖关系： </p><pre>    main.o : main.c defs.h</pre>转成： <pre>    main.o main.d : main.c defs.h</pre>于是，我们的[.d]文件也会自动更新了，并会自动生成了，当然，你还可以在这个[.d]文件中加入的不只是依赖关系，包括生成的命令也可一并加入，让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作，接下来，我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令，来引入别的Makefile文件（前面讲过），例如： <pre>    sources = foo.c bar.c    include $(sources:.c=.d)</pre>上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换，把变量$(sources)所有[.c]的字串都替换成[.d]，关于这个“替换”的内容，在后面我会有更为详细的讲述。当然，你得注意次序，因为include是按次来载入文件，最先载入的[<no></no>.d]文件中的目标会成为默认目标 
<h3><a name="4_Makefile"></a><a name="4_Makefile_"></a>4 Makefile 书写命令 </h3><hr /><p>每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令，每条命令的开头必须以[Tab]键开头，除非，命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略，但是如果该空格或空行是以Tab键开头的，那么make会认为其是一个空命令。 </p><p>我们在UNIX下可能会使用不同的Shell，但是make的命令默认是被“/bin/sh”——UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中，“#”是注释符，很像C/C++中的“//”，其后的本行字符都被注释。 </p><p> </p><h4><a name="4_1"></a><a name="4_1_"></a>4.1 显示命令 </h4><p>通常，make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前，那么，这个命令将不被make显示出来，最具代表性的例子是，我们用这个功能来像屏幕显示一些信息。如： </p><pre>    @echo 正在编译XXX模块......</pre>当make执行时，会输出“正在编译XXX模块......”字串，但不会输出命令，如果没有“@”，那么，make将输出： <pre>    echo 正在编译XXX模块......    正在编译XXX模块......</pre>如果make执行时，带入make参数“-n”或“--just-print”，那么其只是显示命令，但不会执行命令，这个功能很有利于我们调试我们的Makefile，看看我们书写的命令是执行起来是什么样子的或是什么顺序的。 
<p>而make参数“-s”或“--slient”则是全面禁止命令的显示。 </p><p> </p><h4><a name="4_2"></a><a name="4_2_"></a>4.2 命令执行 </h4><p>当依赖目标新于目标时，也就是当规则的目标需要被更新时，make会一条一条的执行其后的命令。需要注意的是，如果你要让上一条命令的结果应用在下一条命令时，你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令，你希望第二条命令得在cd之后的基础上运行，那么你就不能把这两条命令写在两行上，而应该把这两条命令写在一行上，用分号分隔。如： </p><pre>    示例一：        exec:                cd /home/hchen                pwd    示例二：        exec:                cd /home/hchen; pwd</pre><p>当我们执行“make exec”时，第一个例子中的cd没有作用，pwd会打印出当前的Makefile目录，而第二个例子中，cd就起作用了，pwd会打印出“/home/hchen”。 </p><p>make一般是使用环境变量SHELL中所定义的系统Shell来执行命令，默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。但在MS-DOS下有点特殊，因为MS-DOS下没有SHELL环境变量，当然你也可以指定。如果你指定了UNIX风格的目录形式，首先，make会在SHELL所指定的路径中找寻命令解释器，如果找不到，其会在当前盘符中的当前目录中寻找，如果再找不到，其会在PATH环境变量中所定义的所有路径中寻找。MS-DOS中，如果你定义的命令解释器没有找到，其会给你的命令解释器加上诸如“.exe”、“.com”、“.bat”、“.sh”等后缀。 </p><p> </p><h4><a name="4_3"></a><a name="4_3_"></a>4.3 命令出错 </h4><p>每当命令运行完后，make会检测每个命令的返回码，如果命令返回成功，那么make会执行下一条命令，当规则中所有的命令成功返回后，这个规则就算是成功完成了。如果一个规则中的某个命令出错了（命令退出码非零），那么make就会终止执行当前规则，这将有可能终止所有规则的执行。 </p><p>有些时候，命令的出错并不表示就是错误的。例如mkdir命令，我们一定需要建立一个目录，如果目录不存在，那么mkdir就成功执行，万事大吉，如果目录存在，那么就出错了。我们之所以使用mkdir的意思就是一定要有这样的一个目录，于是我们就不希望mkdir出错而终止规则的运行。 </p><p>为了做到这一点，忽略命令的出错，我们可以在Makefile的命令行前加一个减号“-”（在Tab键之后），标记为不管命令出不出错都认为是成功的。如： </p><pre>   clean:            -rm -f *.o</pre>还有一个全局的办法是，给make加上“-i”或是“--ignore-errors”参数，那么，Makefile中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的，那么这个规则中的所有命令将会忽略错误。这些是不同级别的防止命令出错的方法，你可以根据你的不同喜欢设置。 
<p>还有一个要提一下的make的参数的是“-k”或是“--keep-going”，这个参数的意思是，如果某规则中的命令出错了，那么就终目该规则的执行，但继续执行其它规则。 </p><h4><a name="4_4_make"></a>4.4 嵌套执行make </h4><p>在一些大的工程中，我们会把我们不同模块或是不同功能的源文件放在不同的目录中，我们可以在每个目录中都书写一个该目录的Makefile，这有利于让我们的Makefile变得更加地简洁，而不至于把所有的东西全部写在一个Makefile中，这样会很难维护我们的Makefile，这个技术对于我们模块编译和分段编译有着非常大的好处。 </p><p>例如，我们有一个子目录叫subdir，这个目录下有个Makefile文件，来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写： </p><pre>    subsystem:            cd subdir &amp;&amp; $(MAKE)其等价于：    subsystem:            $(MAKE) -C subdir<pre>定义$(MAKE)宏变量的意思是，也许我们的make需要一些参数，所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录，然后执行make命令。我们把这个Makefile叫做“总控Makefile”，总控Makefile的变量可以传递到下级的Makefile中（如果你显示的声明），但是不会覆盖下层的Makefile中所定义的变量，除非指定了“-e”参数。如果你要传递变量到下级Makefile中，那么你可以使用这样的声明：<div class="fragment"><pre>export &lt;variable ...&gt;</pre></div></pre></pre>如果你不想让某些变量传递到下级Makefile中，那么你可以这样声明： 
<div class="fragment"><pre>unexport &lt;variable ...&gt;</pre><pre> </pre></div>如： <pre>        示例一：        export variable = value        其等价于：        variable = value        export variable        其等价于：        export variable := value        其等价于：        variable := value        export variable    示例二：        export variable += value        其等价于：        variable += value        export variable</pre>如果你要传递所有的变量，那么，只要一个export就行了。后面什么也不用跟，表示传递所有的变量。 
<p>需要注意的是，有两个变量，一个是SHELL，一个是MAKEFLAGS，这两个变量不管你是否export，其总是要传递到下层Makefile中，特别是MAKEFILES变量，其中包含了make的参数信息，如果我们执行“总控Makefile”时有make参数或是在上层Makefile中定义了这个变量，那么MAKEFILES变量将会是这些参数，并会传递到下层Makefile中，这是一个系统级的环境变量。 </p><p>但是make命令中的有几个参数并不往下传递，它们是“-C”,“-f”,“-h”“-o”和“-W”（有关Makefile参数的细节将在后面说明），如果你不想往下层传递参数，那么，你可以这样来： </p><pre>        subsystem:            cd subdir &amp;&amp; $(MAKE) MAKEFLAGS=</pre>如果你定义了环境变量MAKEFLAGS，那么你得确信其中的选项是大家都会用到的，如果其中有“-t”,“-n”,和“-q”参数，那么将会有让你意想不到的结果，或许会让你异常地恐慌。 
<p>还有一个在“嵌套执行”中比较有用的参数，“-w”或是“--print-directory”会在make的过程中输出一些信息，让你看到目前的工作目录。比如，如果我们的下级make目录是“/home/hchen/gnu/make”，如果我们使用“make -w”来执行，那么当进入该目录时，我们会看到： </p><pre>        make: Entering directory `/home/hchen/gnu/make'.</pre>而在完成下层make后离开目录时，我们会看到： <pre>        make: Leaving directory `/home/hchen/gnu/make'</pre>当你使用“-C”参数来指定make下层Makefile时，“-w”会被自动打开的。如果参数中有“-s”（“--slient”）或是“--no-print-directory”，那么，“-w”总是失效的。 
<h4><a name="4_5"></a><a name="4_5_"></a>4.5 定义命令包 </h4><p>如果Makefile中出现一些相同命令序列，那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始，以“endef”结束，如： </p><pre>    define run-yacc    yacc $(firstword $^)    mv y.tab.c $@    endef</pre>这里，“run-yacc”是这个命令包的名字，其不要和Makefile中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序，因为Yacc程序总是生成“y.tab.c”的文件，所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。 <pre>    foo.c : foo.y            $(run-yacc)</pre>我们可以看见，要使用这个命令包，我们就好像使用变量一样。在这个命令包的使用中，命令包“run-yacc”中的“$^”就是“foo.y”，“$@”就是“foo.c”（有关这种以“$”开头的特殊变量，我们会在后面介绍），make在执行命令包时，命令包中的每个命令会被依次独立执行。<img src ="http://www.cnitblog.com/zouzheng/aggbug/18578.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 11:03 <a href="http://www.cnitblog.com/zouzheng/articles/18578.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.6内核SD卡的驱动程序</title><link>http://www.cnitblog.com/zouzheng/articles/15533.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 13:21:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15533.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15533.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15533.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15533.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15533.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: diff -NurbBpP linux-2.6.11.11-h1940-2/arch/arm/mach-s3c2410/mach-h1940.c linux-2.6.11.11-h1940-3.3/arch/arm/mach-s3c2410/mach-h1940.c--- linux-2.6.11.11-h1940-2/arch/arm/mach-s3c2410/mach-h1940.c    2...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/15533.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/15533.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 21:21 <a href="http://www.cnitblog.com/zouzheng/articles/15533.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 2.6内核，嵌入式系统的福音？陷阱</title><link>http://www.cnitblog.com/zouzheng/articles/15531.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 13:14:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15531.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15531.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15531.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15531.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15531.html</trackback:ping><description><![CDATA[
		<span class="title">Linux 2.6内核，嵌入式系统的福音？陷阱？<br /><span class="style1">Linux 2.6内核性能评估报告</span></span>
		<!-- InstanceEndEditable -->
		<!-- InstanceBeginEditable name="author" -->
		<p class="auther">作者 Ray</p>
		<!-- InstanceEndEditable -->
		<!-- InstanceBeginEditable name="copyR" -->
		<p class="copyR">RTEMS版权所有，转载请注明：来源 http://www.rtems.net，作者 ray@rtems.net</p>
		<!-- InstanceEndEditable -->
		<!-- InstanceBeginEditable name="abstract" -->
		<p class="abstract" align="left">        笔者受公司委托对 2.6
的内核作了一个性能评估，评测的对象包括进程切换性能，线程切换性能，网络性能，内存读取速度，文件系统性能几个方面。结果表明，在工作站的模式下
2.6 的内核大多数指标比不上 2.4 内核的性能。在服务器模式下， 2.6 内核才开始有优势。同时，本文使用 FreeBSD
作为参照比较对象。 </p>
		<!-- InstanceEndEditable -->
		<p class="abstract" align="left">   <!-- InstanceBeginEditable name="abs_body" --><!-- InstanceEndEditable --></p>
		<!-- InstanceBeginEditable name="artilce" -->
		<h1>Linux2.6 内核的新特性 </h1>
		<p>网上不少文章介绍了2.6内核的新特性，这里我就照搬过来。</p>
		<h2>内存管理 </h2>
		<h3>1 ．抢占式内存 </h3>
		<p class="text">2.6 版本以前的 Linux
内核是不允许对一个被系统调用并处于运行状态的进程进行调度的。这意味着一旦系统调用中有某个任务正在执行，那么该任务会控制处理器直到系统调用结束，而
不管其使用处理器时间的长短。（2.4是分时的，为什么网上的系统说“直到系统调用结束” ？？） </p>
		<p class="text">2.6 版内核中内存是可抢占的。一些人认为可抢占式内存使 Linux 2.6 内核在一些时效性较强的事件中比
2.4 版内核具有更好的响应能力。为实现抢占式内存， Linux 2.6
内核中的代码被设置了抢占点，这意味着调度程序会中止正在运行的进程，而来执行优先级更高的进程。在系统调用过程中， Linux 2.6
内核会定时地检查抢占点，以避免不合理的延迟发生。而在检查过程中，调度进程很可能就会中止当前进程来让另一个进程运行。 </p>
		<p class="text">实际上，测试表明抢占内核使工作站环境下的应用程序速度变慢。 </p>
		<h3>2 ．引入内存池机制 </h3>
		<p class="text">2.6 版内核的开发过程引入了内存池以满足不间断地进行内存分配。其思想是预先分配一个内存池，并保留到真正需要的时候。 哈哈，其结果是系统一启动，大量的内存就没有了。</p>
		<h3>3 ．改善虚拟内存 </h3>
		<p class="text">2.6 版内核改善了虚拟内存在一定程度负载下的性能。 通过反向地址映射减少了系统的开销。 </p>
		<p class="text">Linux
内核工作于虚拟内存模式时，每一个虚拟页对应一个相应系统内存的物理页。虚拟页和物理页之间的地址转换由硬件页表来完成。但是这种“虚拟到物理”的页映射
不总是一一对应的，多个虚拟页有可能指向同一个物理页。这种情况下，内核想要释放特定的物理页就必须遍历所有的进程页表记录来查找指向这个物理页的引用，
只有在引用数达到 0 时才能释放该物理页。负载较高时，这会让虚拟内存变得非常慢。 </p>
		<p class="text">反向地址映射补丁通过在结构页引入一个叫做 pte_chain 的数据结构来解决这一问题。 </p>
		<p class="text">不过，这种方法存在一个指针开销问题。系统中的每一个结构页都必须有一个额外的、用于 pte_chain
的结构。一个 256MB 内存的系统有 64K 个物理页，这就需要有 64KB* （ sizeof （ struct pte_chain
））的内存被分配用于 pte_chain 的结构。 </p>
		<p class="text">使用了这项技术， r-map 的性能，尤其是高负载的高端系统，相对于 2.4 版内核的虚拟内存系统还是有了显著地提高。 </p>
		<h3>4 ．共享内存的改进 </h3>
		<p class="text">Linux 2.6 内核为多程序提供了一种不同的途径，即 NUMA （ Non Uniform Memory
Access
）。这种方法中，内存和处理器是相互连接的，但对于每一个处理器，某些内存是“关闭”的，而某些内存则“更远”。这意味着当内存竞争出现时，“更近”的处
理器对就近的内存有更高的使用权。 2.6
版内核提供了一套功能来定义内存和处理器之间的拓扑关系。调度程序可以利用这些信息来为任务分配本地内存。这样将减少内存竞争造成的瓶颈，提高吞吐量。
</p>
		<h2>驱动程序 </h2>
		<h3>1 ．中断例程的改进 </h3>
		<p class="text">在 2.6 版内核中，驱动程序如果要从一个设备上发出中断需要返回 IRQ_HANDLED ，否则返回
IRQ_NONE 。这样可以帮助内核的 IRQ 层清楚地识别出哪个驱动程序正在处理哪个特定的中断。
如果一个中断请求不断到来，而且没有注册那个设备的处理程序，内核就会忽略来自该设备的中断。 </p>
		<h3>2 ．驱动程序移植 </h3>
		<p class="text">相对于 2.4 版内核来说， 2.6 版内核改进了内核编译系统，从而获得更快的编译速度。 2.6 版内核的编译系统加入了改进的图形化工具 make xconfig （需要 Qt 库）和 make gconfig </p>
		<p>
				<span class="text">内核模块加载器也在 2.6 版内核中被完全实现，使得模块编译机制相对于 2.4
版内核有了很大的不同。原来 2.4 版内核中的模块工具不能用来加载或卸载 2.6
版内核的模块，需要一组新的模块工具来完成模块的加载和卸载。这个新的模块加载工具会尽量减少在一个设备仍被使用的情况下，相应的模块却被卸载的冲突发
生，而是在确认这些模块已经没有任何设备在使用后再卸载掉。产生这种冲突的原因之一是，模块使用计数是由模块代码自己来控制的。在 2.6
版内核中，模块不再需要对引用计数进行加减，这些工作将在模块代码外部进行。</span>
		</p>
		<h2>进程管理 </h2>
		<h3>1 ．新调度器算法 </h3>
		<p class="text">2.6 版本的 Linux 内核使用了新的调度器算法，称为 O （ 1 ）算法，它在高负载的情况下执行得非常出色，并在有多个处理器时能够很好地扩展。 </p>
		<p class="text">2.4
版本的调度器中，时间片重算算法要求在所有的进程都用尽它们的时间片后，新时间片才会被重新计算。在一个多处理器系统中，当进程用完它们的时间片后不得不
等待重算，以得到新的时间片，从而导致大部分处理器处于空闲状态，影响 SMP
的效率。此外，当空闲处理器开始执行那些时间片尚未用尽的、处于等待状态的进程时，会导致进程开始在处理器之间“跳跃”。当一个高优先级进程或交互式进程
发生跳跃时，整个系统的性能就会受到影响。 </p>
		<p class="text">新调度器解决上述问题的方法是，基于每个 CPU
来分布时间片，并取消全局同步和重算循环。调度器使用了两个优先级数组，即活动数组和过期数组，可以通过指针来访问它们。活动数组中包含所有映射到某个
CPU
且时间片尚未用尽的任务。过期数组中包含时间片已经用尽的所有任务的有序列表。如果所有活动任务的时间片都已用尽，那么指向这两个数组的指针互换，包含准
备运行任务的过期数组成为活动数组，而空的活动数组成为包含过期任务的新数组。数组的索引存储在一个 64
位的位图中，所以很容易找到最高优先级的任务。 </p>
		<p class="text">新调度器的主要优点包括： </p>
		<p class="text">◆ SMP 效率 如果有工作需要完成，所有处理器都会工作。 </p>
		<p class="text">◆ 等待进程 没有进程需要长时间地等待处理器，也没有进程会无端地占用大量的 CPU 时间。 </p>
		<p class="text">◆ SMP 进程映射 进程只映射到一个 CPU ，而且不会在 CPU 之间跳跃。 </p>
		<p class="text">◆ 优先级 非重要任务的优先级低，反之亦然。 </p>
		<p class="text">◆ 负载平衡 调度器会降低那些超出处理器负载能力的进程的优先级。 </p>
		<p class="text">◆ 交互性能 即使在高负载的情况下，系统花费很长时间来响应鼠标点击或键盘输入的情况也不会再发生。 </p>
		<h2>2 ．高效的调度程序 </h2>
		<p class="text">2.6
版内核中，进程调度经过重新编写，调度程序不需每次都扫描所有的任务，而是在一个任务变成就绪状态时将其放到一个名为“当前”的队列中。当进程调度程序运
行时，只选择队列中最有利的任务来执行。这样，调度可以在一个恒定的时间里完成。当任务执行时，它会得到一个时间段，或者在其转到另一线程之前得到一段时
间的处理器使用权。当时间段用完后，任务会被转移到另一个名为“过期”的队列中。在该队列中，任务会根据其优先级进行排序。 </p>
		<p class="text">从某种意义上说，所有位于“当前”队列的任务都将被执行，并被转移到“过期”队列中。当这种事情发生时，队列就会进行切
换，原来的“过期”队列成为“当前”队列，而空的“当前”队列则变成“过期”队列。由于在新的“当前”队列中，任务已经被排列好，调度程序现在使用简单的
队列算法，即总是取当前队列的第一个任务进行执行。这个新过程要比老过程快得多。 </p>
		<h3>3 ．新的同步措施 </h3>
		<p class="text">多进程应用程序有时需要共享一些资源，比如共享内存或设备。为了避免竞争的出现，程序员会使用一个名为互斥的功能来确保
同一时刻只有一个任务在使用资源。到目前为止， Linux
还是通过一个包含在内核中的系统调用来完成互斥的实现，并由该系统调用决定一个线程是等待还是继续执行。 </p>
		<p>
				<span class="text">Linux 2.6 内核支持 FUSM （ Fast User-Space Mutex
）。这个新功能会检查用户的空间，查看是否有等待的情况出现，并且只有在线程需要等待时才进行系统调用。这样当不需要等待时，就会避免不必要的系统调用，
以节约时间。该功能也使用优先级调度，以便在出现竞争时决定哪一个线程可以被执行。</span>
		</p>
		<h1>测试报告 </h1>
		<h2>测试平台： </h2>
		<h3>硬件： </h3>
		<p class="text">2 AMD XP 2500+ / Intel PV 3G (HT支持) </p>
		<p class="text">2 DDR RAM 256M 333Mhz </p>
		<p class="text">2 硬盘 Maxtor 40G 7200rpm/s </p>
		<p class="text">2 主板 MSI KT4AV </p>
		<p class="text">2 100M 以太网卡 </p>
		<h3>软件 </h3>
		<p class="text">2 GCC 3.4.2 </p>
		<p class="text">2 Slackware 10.1 </p>
		<p class="text">2 文件系统 ReiserFS </p>
		<h3>参考平台 </h3>
		<p class="text">FreeBSD5.3 </p>
		<h3>比较对象； </h3>
		<p class="text">2 kernel 2.4.29 </p>
		<p class="text">2 kernel 2.6.10 </p>
		<p class="text">2 FreeBSD 5.3 </p>
		<h3>说明： </h3>
		<p class="text">所有内核都根据平台属性重新编译 </p>
		<h3>测试工具 </h3>
		<p class="text">2 lmbench 用于系统性能整体测试 </p>
		<p class="text">2 forkbomb 进程压力测试 </p>
		<p class="text">2 Netperf 网路性能测试 </p>
		<h2>Linux 2.6 编译 </h2>
		<p class="text">首先获取最新的 kernel 代码 </p>
		<p class="text">linux-2.6.10.tar.bz2 </p>
		<p class="text">解压源代码 </p>
		<p class="text">tar zjvf linux-2.6.10.tar.bz2 </p>
		<p class="text">进行配置： </p>
		<p class="text">make xconfig </p>
		<p class="text">测试使用 SSH 客户端在字符界面下进行。 </p>
		<h2>比较结果 </h2>
		<h3>内核的大小 </h3>
		<p class="text">根据机器的硬件状况重新编译了内核，编译的结果如下：（未压缩）</p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image002.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image002.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="280" width="300" />
				</a>
		</p>
		<h3>启动内存大小 </h3>
		<p class="text">系统启动后，开启了12路的VoIP网关程序。结果，内存的消耗状况如下，大家不要害怕，这还能称为嵌入式系统吗？</p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image0010.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image0010.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="236" width="300" />
				</a>
		</p>
		<h3>线程创建速度 </h3>
		<p class="text">pthread 线程创建测试， BSD 是绝对的赢家，几乎不需要时间 </p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image004.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image004.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="215" width="300" />
				</a>
		</p>
		<h3>内存访问速度 </h3>
		<p class="text">内存访问测试，三者速度差多，整体来看 2.4 快于 bsd 快于 2.6 </p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image006.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image006.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="215" width="300" />
				</a>
		</p>
		<h3>文件访问 </h3>
		<p class="text">文件访问速度 BSD&gt;2.4&gt;2.6 </p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image008.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image008.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="294" width="300" />
				</a>
		</p>
		<h3>网络流量 </h3>
		<p class="text">网络速度： BSD 明显优势 </p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image010.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image010.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="215" width="300" />
				</a>
		</p>
		<h3>socket 创建速度 </h3>
		<p class="text">socket 函数创建的时间： BSD 优于 2.6 优于 2.4 </p>
		<p class="text">
				<a href="http://www.rtems.net/Documnet/other/2.6review%20files/image012.gif" target="_blank">
						<img src="http://www.rtems.net/Documnet/other/2.6review%20files/image012.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" border="0" height="237" width="300" />
				</a>
		</p>
		<h3>bind 函数访问时间 </h3>
		<p class="text">在小负载下，三者表现比较接近，时间是常数在大负载下 2.4.29 变慢 </p>
		<table cellpadding="0" cellspacing="0">
				<tbody>
						<tr>
								<td valign="bottom" width="253">
										<p>bind 函数平均时间 () </p>
								</td>
								<td valign="bottom" width="172">
										<p>(3000socket) </p>
								</td>
								<td valign="bottom" width="163">
										<p>(10000socket) </p>
								</td>
						</tr>
						<tr>
								<td valign="bottom">
										<p>2.4.29 </p>
								</td>
								<td valign="bottom">
										<p>O(1) </p>
								</td>
								<td valign="bottom">
										<p>O(n) </p>
								</td>
						</tr>
						<tr>
								<td valign="bottom">
										<p>2.6.10 </p>
								</td>
								<td valign="bottom">
										<p>O(1) </p>
								</td>
								<td valign="bottom">
										<p>O(1) </p>
								</td>
						</tr>
						<tr>
								<td valign="bottom">
										<p>FreeBSD5.3 </p>
								</td>
								<td valign="bottom">
										<p>O(1) </p>
								</td>
								<td valign="bottom">
										<p>O(1) </p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15531.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 21:14 <a href="http://www.cnitblog.com/zouzheng/articles/15531.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于volatile和(*(unsigned long *)&amp;jiffies)++</title><link>http://www.cnitblog.com/zouzheng/articles/15527.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 13:02:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15527.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15527.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15527.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15527.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15527.html</trackback:ping><description><![CDATA[volatile中文的意思是易于挥发的。在C语言中，如果一个变量被声明为volatile，则说明这个变量每回都要从内存读到寄存器中，操作完以后，
再将值写回到内存中，编译器（例如gcc)并不将值cache在寄存器中。对于多线程或SMP系统，要注意使用volatile。 <br />int wait = TRUE; <br /><br /><br />thread1() thread2() <br />{ { <br />while (wait) { wait = FALSE; <br />sleep(1000); ... <br />} } <br />} <br /><br />当
编译器看到thread1调用sleep的时候，它认为sleep是一个外部模块的函数，不可能改变wait的值，所以它"可能"就进行代码优化，将
wait的值cache在寄存器中，以提高效率。假如另外一个线程thread2开始运行，将wait改成FALSE，如果CPU没有预测到wait变量
的依赖关系的话，thread1就会一直sleep下去。在这种情况下，volatile可以强制编译器不将wait缓存到寄存器中。注意，一般情况下，
CPU是能检测到wait被改过了，从而将寄存器刷新，但是这和CPU的体系结构有关，Linux是能支持多种体系结构的，所以经常使用
volatile。 <br />有网友问为什么在do_timer()函数中，有一句(*(unsigned long *)&amp;jiffies)++，为什么不直接用jiffies++呢？ 这个问题和两个原因有关： <br />* jiffiers是volatile变量 <br />* CPU可能会重新排序指令 <br /><br /><br />我编了3个小程序来测试， <br /><br />jiffier1.c <br /><br /><br />int main() <br />{ <br />unsigned long jiffiers = 777; <br /><br /><br />jiffiers++; <br />return 0; <br />} <br /><br />gcc -S jiffer1.c，生成的汇编为 <br /><br />.file "jiffier1.c" <br />.version "01.01" <br />gcc2_compiled.: <br />.text <br />.align 4 <br />.globl main <br />.type main,@function <br />main: <br />pushl %ebp <br />movl %esp, %ebp <br />subl , %esp <br />movl 7, -4(%ebp) <br />leal -4(%ebp), %eax <br />incl (%eax) <br />movl , %eax <br />leave <br />ret <br />.Lfe1: <br />.size main,.Lfe1-main <br />.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)" <br /><br /><br />从汇编中，可以看到jiffiers的值是直接在内存里加一的。 <br /><br />jiffier2.c <br /><br /><br />int main() <br />{ <br />volatile unsigned long jiffiers = 777; <br />jiffiers++; <br />return 0; <br />} <br /><br />生成的汇编为 <br /><br />.file "jiffier2.c" <br />.version "01.01" gcc2_compiled.: <br />.text <br />.align 4 <br />.globl main <br />.type main,@function <br />main: <br />pushl %ebp <br />movl %esp, %ebp <br />subl , %esp <br />movl 7, -4(%ebp) <br />movl -4(%ebp), %eax <br />incl %eax <br />movl %eax, -4(%ebp) <br />movl , %eax <br />leave <br />ret <br />.Lfe1: <br />.size main,.Lfe1-main <br />.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)" <br /><br />当jiffiers被声明为volatile时，在加一之前，会先从内存中将值读到寄存器%eax中， 直接都寄存器操作，完了以后不做cache，将值写回内存。它需要3条指令才完成了加一 操作， <br /><br />movl -4(%ebp), %eax <br />incl %eax <br />movl %eax, -4(%ebp) <br /><br /><br />也就是说，jiffiers++并不是原子操作，在多处理器环境中， <br />问题就出来了，jiffers有可能被加了两次，内存中却只是加了1。 <br />另外的原因是这3条指令执行的时候，可能会被重新排序，从而破坏了操作的原子性。 <br />当然我们也可以直接调用汇编指令加一，但是do_timers函数是独立于体系结构的， <br />所以Linux使用的一种最简单的方法，也就是(*(unsigned long *)&amp;jiffiers)++ <br />来解决这些问题。 <br /><br />jiffier3.c <br /><br /><br />int main() <br />{ <br />volatile unsigned long jiffiers = 777; <br /><br /><br />(*(unsigned long *)&amp;jiffiers)++; <br /><br /><br />return 0; <br />} <br /><br />生成的汇编为 <br />.file "jiffier3.c" <br />.version "01.01" gcc2_compiled.: <br />.text <br />.align 4 <br />.globl main <br />.type main,@function <br />main: <br />pushl %ebp <br />movl %esp, %ebp <br />subl , %esp <br />movl 7, -4(%ebp) <br />leal -4(%ebp), %eax <br />incl (%eax) <br />movl , %eax <br />leave <br />ret <br />.Lfe1: .size main,.Lfe1-main <br />.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)" <br /><br />可
以看出来，生成的指令和jiffer1.c的一模一样，也就是说， (*(unsigned long
*)&amp;jiffiers)++将volatile的jiffiers转换成一般的内存变量，
避免了用寄存器做cache，从而保证了jiffiers加一操作的原子性。 <br />Linux一开始也是直接用jiffiers++的，到后来的版本才改成(*(unsigned long *)&amp;jiffiers)++， 可想而知写一个牢固的OS是不容易的，要解决的隐含细节非常多。 <br />Good Luck, Linux! <img src ="http://www.cnitblog.com/zouzheng/aggbug/15527.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 21:02 <a href="http://www.cnitblog.com/zouzheng/articles/15527.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux内核模块和驱动程序的编写</title><link>http://www.cnitblog.com/zouzheng/articles/15526.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 12:54:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15526.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15526.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15526.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15526.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15526.html</trackback:ping><description><![CDATA[linux内核是一个整体是结构.因此向内核添加任何东西.或者删除某些功能 ,都十分困难.为了解决这个问题. <br />引入了内核机制.从而可以动态的想内核中添加或者删除模块. <br /><br />模块不被编译在内核中,因而控制了内核的大小.然而模块一旦被插入内核,他就和内核其他部分一样.这样一来 <br />就会曾家一部分系统开销.同时,如果模块出现问题.,也许会带来系统的崩溃. <br /><br />1.1模块的实现机制: <br /><br />启动时,由函数 void inti_modules() 来初始化模块,.因为启动事很多时候没有模块.这个函数往往把内核自 <br />身当作一个虚模块. <br /><br />如由系统需要,则调用一系列以sys 开头的函数,对模块进行操作. 如:sys_creat_modules(),sys_inti_modules() , <br />sys_deldte_modules()等等. <br /><br />这里会用到一些模块的数据就结构,在/usr/scr/linux/include/linux/module.h 中,有兴趣的朋友可以找出来一看 <br /><br />块的加入有两种方法:一是手动加入:如:insmod modulename.另一种是根据需要,动态的加载模块.如你执行命令: <br />$mount -t msdos /dev/hdd /mnt/d 时.系统便自动加载 FAT模块,以支持MSDOS的文件系统. <br /><br />1.2 模块编程 <br /><br />写一个模块,必须有一定的多进程编程基础.因为你变得程序不是以一个独立的程序的来运行的.另外,因为,模块需要 <br />在内核模式下运行,会遇到在内和空间和用户空间数据交换的问题.一般的数据复制函数无法完成这一个过程.因此系 <br />统已入了一些特殊的函数以用来完成内核空间和用户空间数据的交换. <br /><br />这些函数有:void put _user (type valude,type *u_addr) <br /><br />memcpy_tofs() <br /><br />等等,有兴趣的朋友可以仔细的看看所有的函数,以及他们的用法.需要说明的是.模块编程河内核的版本有很大的关系. <br />如果版本不通可能造成,内核模块不能编译,或者.在运行这个模块时,出现不可测结果.如:系统崩溃等. <br /><br />明白了这些以后.你就可以尝试着编写内核模块了.对于每一个内核模块来说.必定包含两个函数 <br /><br />int init_module() 这个函数在插入内核时启动,在内核中注册一定的功能函数,或者用他的代码代替内和中某些函数 <br />的内容(估计这些函数是空的).因此,内和可以安全的卸载.(个人猜测) <br /><br />int cleanup_module() 当内核模块谢载时,调用.将模块从内核中清除. <br /><br />同其他的程序设计教程一样 ,我们给出一个hello world 的例子 <br /><br />/*hello.c a module programm*/ <br /><br />/* the program runing under kernel mod and it is a module*/ <br /><br />#include\" linux/kernerl.h\" <br /><br />#include\"llinux/module.h\" <br /><br />/* pross the CONFIG_MODVERSIONS*/ <br /><br />#if CONFIG_MODVERSIONS==1 <br /><br />#define MODVERSIONS <br /><br />#include\"\"linux/modversions.h\" <br /><br />#end if <br /><br />　 <br /><br />/* the init function*/ <br /><br />int init_module() <br /><br />{ <br /><br />printk(\" hello world !\\n\'); <br /><br />printd(\" I have runing in a kerner mod@!!\\n\"); <br /><br />return 1; <br /><br />} <br /><br />/* the distory function*/ <br /><br />int cleanup_module() <br /><br />{ <br /><br />printk(\" I will shut down myself in kernerl mod /n)\"; <br /><br />retutn 0; <br /><br />} <br /><br />这样一个例子就完成了.我们也写一个makefile 的例子,以适于我们在大程序重的应用.一下是makfile 文件的内容 <br /><br /># a makefile for a module <br /><br />CC=gcc <br /><br />MODCFLAGS:= -Wall _DMODULE -D_KERNEL_ -Dlinux <br /><br />hello.o hello.c /usr/inculde?linux/version.h <br /><br />CC $(MODCFLAGS) 0c hello.c <br /><br />echo the module is complie completely <br /><br />然后你运行make 命令 得到hello.o 这个模块.运行 <br /><br />$insmod hello.o <br /><br />hello world! <br /><br />I will shut down myself in kernerl mod <br /><br />$lsmod <br /><br />hello (unused) <br /><br />…. <br /><br />$remmod <br /><br />I will shut down myself in kernerl mod <br /><br />这样你的模块就可以随意的插入和删除了. <br /><br /><br /><br /><br /><br /><br />linux中的大部分驱动程序,是以模块的形式编写的.这些驱动程序源码可以修改到内核中,也可以把他们编译成模 <br />块形势,在需要的时候动态加载. <br /><br />一个典型的驱动程序,大体上可以分为这么几个部分: <br /><br />1,注册设备 <br /><br />在系统初启,或者模块加载时候,必须将设备登记到相应的设备数组,并返回设备的主驱动号,例如:对快设备来说调 <br />用 refister_blkdec()将设备添加到数组blkdev中.并且获得该设备号.并利用这些设备号对此数组进行索引.对于 <br />字符驱动设备来说,要使用 module_register_chrdev()来获得祝设备的驱动号.然后对这个设备的所有调用都用这 <br />个设备号来实现 <br /><br />2,定义功能函数 <br /><br />对于每一个驱动函数来说.都有一些和此设备密切相关的功能函数.那最常用的块设备或者字符设备来说.都存在着 <br />诸如 open() read() write() ioctrol()这一类的操作.当系统社用这些调用时.将自动的使用驱动函数中特定的模 <br />块.来实现具体的操作.而对于特定的设备.上面的系统调用对应的函数是一定的. <br /><br />如:在块驱动设备中.当系统试图读取这个设备(即调用read()时),就会运行驱动程序中的block_read() 这个函数. <br /><br />打开新设备时会调用这个设备驱动程序的device_open() 这个函数. <br /><br />3,谢载模块 <br /><br />在不用这个设备时,可以将他卸载.主要是从/proc 中取消这个设备的特殊文件.可用特定的函数实现. <br /><br />下面我们列举一个字符设备驱动程序的框架.来说明这个过程. <br /><br />/* a module of a character device */ <br /><br />　 <br /><br />/* some include files*/ <br /><br />#include\"param.h\" <br /><br />#include\"user.h\" <br /><br />#include\"tty.h\" <br /><br />#include\"dir.h\" <br /><br />#include”fs.h\" <br /><br />　 <br /><br />/* the include files modules need*/ <br /><br />#include\"linux/kernel.h\" <br /><br />#include\"linux/module.h\" <br /><br />#if CONFIG_MODBERSIONS==1 <br /><br />degine MODBERSIONS <br /><br />#include\" linux.modversions.h\" <br /><br />#endif <br /><br />#difine devicename mydevice <br /><br />/* the init funcion*/ <br /><br />int init_module() <br /><br />{ <br /><br />int tag=module_register_chrdev(0,mydevice,&amp;Fops); <br /><br />if (tag&lt;0) <br /><br />{ <br /><br />printk(\"the device init is erro!\\n\"); <br /><br />return 1; <br /><br />} <br /><br />return 0; <br /><br />} <br /><br />　 <br /><br />/*the funcion which the device will be used */ <br /><br />int device_open () <br /><br />{ <br /><br />……. <br /><br />} <br /><br />int device_read () <br /><br />{ <br /><br />……. <br /><br />} <br /><br />int device_write () <br /><br />{ <br /><br />……. <br /><br />} <br /><br />int device_ioctl () <br /><br />{ <br /><br />……. <br /><br />} <br /><br />…… <br /><br />/* the deltter function of this module*/ <br /><br />int cleanup_module() <br /><br />{ <br /><br />int re=module_unregister_chrdev(tag,mydevice); <br /><br />if( re&lt;0) <br /><br />{ <br /><br />printk(\"erro unregister the module !!\\n\"); <br /><br />return 1; <br /><br />} <br /><br />return 0; <br /><br />} <img src ="http://www.cnitblog.com/zouzheng/aggbug/15526.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 20:54 <a href="http://www.cnitblog.com/zouzheng/articles/15526.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于Arm的Linux内核编译指导</title><link>http://www.cnitblog.com/zouzheng/articles/15525.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 12:52:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15525.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15525.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15525.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15525.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15525.html</trackback:ping><description><![CDATA[
		<p>This guide will give you step by step instructions about compiling a
kernel for ARM machines on the target platform. If you find something
missing or unclear, please send a mail to the linux-arm list. </p>
		<p>Here are some initial notes to help you understand the terminology and conventions used in this document: 
</p>
		<ul>
				<li>"bash$" and "bash#" are shell prompts, not commands to be typed. 
</li>
				<li>"host" means the machine you are building the ARM kernel on. 
</li>
				<li>"target" means the machine you are building the ARM kernel for. </li>
		</ul>
		<ol>
				<li>
						<h3>Decide where to build your kernel</h3>Firstly, you need to decide
where you are going to build your ARM Linux kernel. A good place to
build the kernel is in your home directory, which we will refer to as <tt>$HOME</tt> thoughout this document. If you wish to use some other location, replace <tt>$HOME</tt> as appropriate. 
<p>However, please note that building a kernel in <tt>/usr/src/linux</tt> is highly discouraged. 
</p></li>
				<li>
						<h3>Deciding on a kernel version</h3>Firstly, you need to decide which
version of the Linux kernel you wish to compile. Most people will want
the latest stable kernel release. To help you identify a particular
release, a system of version numbers is used by the kernel developers. <p>For any kernel version x.y.z, 
</p><p></p><ul><li>x - This is the major revision number 
</li><li>y - This is the minor revision number, where:<br />Even numbers indicate "stable" kernel releases<br />Odd numbers indicate "development" or "beta" kernel releases which may be less stable. 
</li><li>z - This is the patch level of the kernel </li></ul><p>This version number represents the main line kernel version. 
</p><p>At the time of writing this document, the latest stable kernel on ARM is 2.6.6. 
</p><p>Under the ARM kernel tree, you will find a suffix to the kernel version number: <tt>-rmkN</tt>, or <tt>-vrsN</tt>
where 'N' is the patch release number. For instance, 2.4.26-vrs1. This
indicates the version of the main ARM kernel patch which should be
applied. (<b>note:</b> kernels later than 2.6.0-test2 do not require a <tt>-rmk</tt> or <tt>-vrs</tt> patch to be applied since ARM architecture support is already merged.) 
</p><p>Other maintainers, such as Nicolas Pitre, may produce additional
patches, and these will add an additional suffix to denote their
version. Nicolas Pitre's patches add a <tt>-np</tt> suffix, eg 2.4.21-rmk2-np1. 
</p><p>This means that 2.4.21-rmk2-np1 is based upon 2.4.21-rmk2, which
in turn is based upon the 2.4.21 kernel. Therefore, to get the kernel
source for <tt>2.4.21-rmk2-np1</tt>, you need the 2.4.21 main line kernel source, the 2.4.21-rmk2 patch and the 2.4.21-rmk2-np1 patch. 
</p><p>Therefore, if you need extra patches from other maintainers, you
have to be careful to choose an appropriate kernel version. You will
need to locate the maintainer patches first, and then work your way up
through the version number towards the mainline kernel version. </p></li>
				<li>
						<h3>Downloading the maintainer-specific patch.</h3>In some
circumstances, you will need to patch the kernel with a maintainer
specific patch. These patches add extra features or other device
drivers which may be specific to various machines. However, as a
general rule, maintainers forward upstream parts of their patches into
the -rmk or -vrs trees as and when they are happy with the change. <p>Please refer to the <a href="file:///E:/developer/machines/">machine list</a> for information concerning extra patches. 
</p></li>
				<li>
						<h3>Downloading an ARM patch.</h3>(You only need this step if you are using a kernel prior to 2.6.0-test2. There are no <tt>-rmk</tt> or <tt>-vrs</tt> patches for later kernels.) 
<p>You may need to download a kernel patch, which contains all the ARM
specific updates for a particular kernel version. These can be found in
<a href="ftp://ftp.arm.linux.org.uk/pub/armlinux/source/kernel-patches/">ftp://ftp.arm.linux.org.uk/pub/armlinux/source/kernel-patches/</a>. The kernel releases are separated out into directories corresponding to the major and minor version numbers of the kernel. 
</p><p>The individual files are named <tt>patch-x.y.z-rmkN.gz</tt> or <tt>patch-x.y.z-vrsN.gz</tt>,
where 'x', 'y', 'z' and 'N' are the version numbers mentioned above.
You should select and download the latest patch for the kernel into the
<tt>$HOME</tt> directory. This is the one which will have either the
most features, or the most bug fixes in. You will need the version of
the patch later when downloading the main kernel source. </p><p><b>Note:</b> Some files may be named (eg) <tt>pre-patch-x.y.z-rmkN.gz</tt>.
These are alpha or beta patches, which are probably unstable. You
should not use these unless you are sure that you know what you are
doing, and you don't mind the target system being unstable. However,
they are useful when new ideas need to be tested out. </p><p><b>Note2:</b> Some kernels are based on the Alan Cox series of kernels. These have names similar to <tt>patch-x.y.z-acm-rmkN.gz</tt> where <tt>x.y.z</tt> is Linus' version number and <tt>m</tt>
is Alan's version number. In this case, you will need to obtain Alan
Cox's corresponding patch from the kernel.org servers, in the directory
<tt>/pub/linux/kernel/people/alan/linux-2.4/</tt>. 
</p></li>
				<li>
						<h3>Downloading the main line kernel source</h3>A patch file on its own
usually does not contain any compilable code. It is a machine-readable
description of changes to make to a set of text files (in this case,
the kernel source.) You need to obtain the main kernel source tree. <p>The kernel source can be found on one of the <b>kernel.org</b> FTP
sites. There are many sites scattered around the world, and are named
according to a unified naming scheme. All sites start with 'ftp.' and
end in '.kernel.org'. In the middle is placed a country identifier. For
example: </p><ul><li>ftp.uk.kernel.org 
</li><li>ftp.us.kernel.org 
</li><li>ftp.de.kernel.org </li></ul><p>and so forth. You can find out more information on these sites by looking at the main <a href="http://www.kernel.org/">www.kernel.org</a> site. 
</p><p>Once you have selected a site, you need to find the kernel sources. They will be stored in the subdirectories of <tt>/pub/linux/kernel</tt>. Each kernel release is accompanied by several files: 
</p><ul><li>linux-x.y.z.tar.gz 
</li><li>linux-x.y.z.tar.bz2 
</li><li>patch-x.y.z.gz 
</li><li>patch-x.y.z.bz2 </li></ul><p>You will want to download the linux-x.y.z.tar.gz file, again into your <tt>$HOME</tt>
directory. Again, you should look for a version which matches the
version of the patch you obtained above. These files are large (about
14MB or more), so if you are on a slow connection, be prepared for it
to take some time. </p></li>
				<li>
						<h3>Unpacking the ARM kernel source</h3>Unpack the tar archive you downloaded above using:<pre>	bash$ cd $HOME<br />	bash$ tar zxvf linux-x.y.z.tar.gz<br /></pre>This will create a directory called <tt>linux</tt> or <tt>linux-x.y.z</tt> in your home directory. Change into the newly created directory and apply the patch files, eg:<pre>	bash$ cd linux-2.4.26<br />	bash$ zcat ../patch-2.4.26-vrs1.gz | patch -p1<br /></pre>The patches are heirarchial, so you need to apply them in the
correct order. The patch files with more extensions depend on the ones
with less extensions, so you need to apply, for example, the -rmk patch
before the -rmk-np patch. <p>The kernel source tree is now ready to be configured. 
</p></li>
				<li>
						<h3>Configuration of the kernel build environment</h3>Normally, the
kernel build system will build the kernel for the native machine
architecture. This is not appropriate when cross compiling, so you will
need to change two lines in the top level kernel Makefile. Examine the
top level <tt>Makefile</tt> in an editor and find the definitions for <tt>ARCH</tt> and <tt>CROSS_COMPILE</tt>. On 2.4.x kernels, they will look like this:<pre>ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)<br /></pre>[...]<pre>CROSS_COMPILE   =<br /></pre>and on 2.6.x kernels:<pre>ARCH            ?= $(SUBARCH)<br />CROSS_COMPILE   ?=<br /></pre><p>Edit these two lines to read:</p><pre>ARCH		?= arm<br />CROSS_COMPILE	?= /usr/local/bin/arm-linux-<br /></pre><p>replacing <tt>/usr/local/bin/arm-linux-</tt> with the path to your ARM Linux toolchain. 
</p><p>This completes the configuration of the top level kernel
makefile. The next step is to configure the kernel build to select the
drivers that your platform requires. </p><p>You may like to read <a href="http://linux.bkbits.net:8080/linux-2.5/hist/README?nav=index.html%7Csrc/"><tt>linux/README</tt></a> and <a href="http://linux.bkbits.net:8080/linux-2.5/hist/Documentation/arm/README?nav=index.html%7Csrc/%7Csrc/Documentation%7Csrc/Documentation/arm"><tt>linux/Documentation/arm/README</tt></a> before proceeding. Both these files provide further useful information which may be specific to your kernel version. 
</p></li>
				<li>
						<h3>Configuration of the kernel sources</h3>There are a range of 'make'
targets which allow a set of defaults to be selected for the particular
machine you are compiling the source for. The process is much simpler
for 2.6 kernels. <p></p><h4>Configuration of 2.4 kernels</h4>For 2.4 kernels use <tt>&lt;machinename&gt;_config</tt> format, for example: 
<ul><li>a5k_config 
</li><li>ebsa110_config 
</li><li>netwinder_config 
</li><li>rpc_config 
</li><li>assabet_config </li></ul><p>You should select one of these as the "basic" configuration as follows, and run <tt>make oldconfig</tt> immediately afterwards:</p><pre>	bash$ make netwinder_config<br />	bash$ make oldconfig<br /></pre>The oldconfig step will prompt you for any new configuration
options which may have been added since the default machine
configuration file was submitted. It is normally safe to say 'N' to
these new options. <p><i>Note:</i> If you want to change the configuration via make xxx_config, please remove the file <tt>linux/.config</tt> immediately prior to executing this command. 
</p><p></p><h4>Configuration of 2.6 kernels</h4>For 2.6 kernels, the process is similar. Use <tt>&lt;machinename&gt;_defconfig</tt> to select the machine, eg:<pre>	bash$ make netwinder_defconfig<br /></pre>In this case, there is no need to run a separate <tt>oldconfig</tt> step. 
</li>
				<li>
						<h3>Compiling the kernel source</h3>If you are only installing the
kernel source tree for other programs, then you have finished. If you
want to compile up a new kernel, type the following commands:<pre>	bash$ make clean<br />	bash$ make dep<br />	bash$ make zImage<br />	bash$ make modules<br /></pre>The final two commands will actually compile the kernel and the kernel modules. 
<p><b>Note:</b> With 2.6 kernels, the <tt>make dep</tt> stage is not necessary. </p></li>
				<li>
						<h3>Installing the kernel</h3>After the kernel has successfully compiled, you should have the kernel image, <tt>arch/arm/boot/zImage</tt>. What you do next depends if you are cross compiling or not. 
<p>If you are cross compiling, goto the section "<a href="file:///E:/arm9%E5%B5%8C%E5%85%A5%E5%BC%8F%E6%8A%80%E6%9C%AF/%E5%8D%8E%E6%81%92/%E5%8D%8E%E6%81%92/Kernel%20Compilation.htm#crossinstall">Installing a cross compiled kernel</a>". 
</p><p>If you are building natively (ie, for the target on the target), <a href="file:///E:/arm9%E5%B5%8C%E5%85%A5%E5%BC%8F%E6%8A%80%E6%9C%AF/%E5%8D%8E%E6%81%92/%E5%8D%8E%E6%81%92/Kernel%20Compilation.htm#nativeinstall">continue</a>. 
</p></li>
				<li>
						<a name="nativeinstall">
						</a>
						<h3>Installing a native kernel</h3>Since you are about to upgrade system files, you need to become 'root'. To do this, type:<pre>	bash$ su<br />	Password:<br />	bash#<br /></pre>It is highly advisable to keep a backup of your current kernel
and modules. What you need to do is machine dependent. Note that it is
a good idea to always keep a known good previous version of the kernel
and modules in case you need to back down to a previous version. <p>The following is given as an example (for a 2.4.3-rmk1 kernel):</p><pre>	bash# cd /lib/modules<br />	bash# mv 2.4.3-rmk1 2.4.3-rmk1.old<br />	bash# cd /boot<br />	bash# mv vmlinuz vmlinuz.bak<br />	bash# mv System.map System.map.bak<br />	bash#<br /></pre>Now, install the new kernel modules:<pre>	bash# cd $HOME/linux<br />	bash# make modules_install<br />	bash#<br /></pre>This will copy the modules into the <tt>/lib/modules/</tt>x.y.z directory. Next, install the kernel image (normally into /boot):<pre>	bash# cd /boot<br />	bash# cat $HOME/linux/arch/arm/boot/zImage &gt;vmlinuz<br />	bash# cp $HOME/linux/System.map .<br />	bash#<br /></pre><p>Note that the command to copy the new kernel image is <tt>cat</tt> and is not the usual <tt>cp</tt>.
Unix traditionally will not allocate space on the filesystem to
sections of files containing zero data, but instead creates "holes" in
the file. Some kernel loaders do not understand files with holes in,
and therefore using <tt>cat</tt> in this way ensures that this does not happen. 
</p></li>
				<li>
						<h3>Running loadmap</h3>Loadmap is part of the Linux loader on Acorn
machines, as well as EBSA285 machines using EBSA285BIOS with an IDE
disk. For other machines, please refer to the documentation for your
machine. <p>Edit the loader configuration file <tt>/etc/boot.conf</tt> so that you can boot either the <tt>vmlinuz.bak</tt> or <tt>vmlinuz</tt> images. If you place the <tt>vmlinuz</tt> kernel first, then this will be the default kernel which the kernel loader will use. 
</p><p>More information can be found by typing <tt>man boot.conf</tt>. 
</p><p>Run the boot loader map utility:</p><pre>	bash# loadmap -v<br />	bash#<br /></pre>to update the maps. 
<p>You have finished, and are now ready to reboot your machine and try
out your new kernel! If you experience problems, please go to the "<a href="file:///E:/arm9%E5%B5%8C%E5%85%A5%E5%BC%8F%E6%8A%80%E6%9C%AF/%E5%8D%8E%E6%81%92/%E5%8D%8E%E6%81%92/Kernel%20Compilation.htm#problems">Problems</a>" step below. 
</p></li>
				<li>
						<a name="crossinstall">
						</a>
						<h3>Installing a cross compiled kernel</h3>Kernel modules are installed into the <tt>/lib/modules/x.y.z</tt>
directory on the target system, though this will normally be a
different directory on the host system. Where this directory is depends
on your setup, but we will call it $TARGETDIR. <p>Install the modules into <tt>$TARGETDIR</tt> as follows:</p><pre>	bash$ make modules_install INSTALL_MOD_PATH=$TARGETDIR<br />	bash$<br /></pre>This will place the modules into the <tt>$TARGETDIR/lib/modules/x.y.z</tt>
directory on the host, which can then be placed into an suitable
filesystem, or transferred to the target machine. Please also note that
you must not install these kernel modules into the hosts root
filesystem (eg by omitting INSTALL_MOD_PATH or giving $TARGETDIR as
"/"), since they are incompatible with your host kernel and therefore
may leave you with an unbootable host system. <p>The kernel will be available in <tt>$HOME/linux/arch/arm/boot/zImage</tt> and the kernel symbol information in <tt>$HOME/linux/System.map</tt>. Exactly how do install this is outside the scope of this document. 
</p><p>It is important that you keep the <tt>System.map</tt> file safe
- it contains the symbolic information for this kernel, which will be
required if you need to debug or report a problem. </p></li>
				<li>
						<a name="problems">
						</a>
						<h3>Reporting Problems</h3>Please follow the <a href="http://linux.bkbits.net:8080/linux-2.5/anno/REPORTING-BUGS@1.2?nav=index.html%7Csrc/">REPORTING-BUGS</a> guide in the kernel source tree. However, please use the <a href="file:///E:/mailinglists/">linux-arm-kernel</a> mailing list to report problems, rather than the linux-kernel mailing list. </li>
		</ol>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15525.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 20:52 <a href="http://www.cnitblog.com/zouzheng/articles/15525.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>触摸屏驱动移植</title><link>http://www.cnitblog.com/zouzheng/articles/15524.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 12:50:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15524.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15524.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15524.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15524.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15524.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一、添加文件								1				、将				s3c2410_ts.c				文件拷贝到				drivers/input/touchscreen/				目录下，其内容为：								/*																				* This program is free software; you can redistri...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/15524.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/15524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 20:50 <a href="http://www.cnitblog.com/zouzheng/articles/15524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LCD驱动移植笔记-</title><link>http://www.cnitblog.com/zouzheng/articles/15523.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 12:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15523.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15523.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15523.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15523.html</trackback:ping><description><![CDATA[
		<p>LCD驱动程序往2.6.11内核<br />的移植总结<br />硬件环境：SBC-2410X开发板（CPU：S3C2410X）<br />内核版本：2.6.11.1<br />运行环境：Debian2.6.8<br />交叉编译环境：gcc-3.3.4-glibc-2.3.3<br />注：本驱动移植是基于s3c2400 framebuffer 的驱动。</p>
		<p>一、从网上将Linux内核源代码下载到本机上，并将其解压：<br />#tar jxf linux-2.6.11.1.tar.bz2<br />二、打开内核顶层目录中的Makefile文件，这个文件中需要修改的内容包括以下两个方面。<br />（1）指定目标平台。<br /> 移植前：<br />  ARCH      ?= $(SUBARCH)<br /> 移植后：<br /> ARCH            :=arm<br />（2）指定交叉编译器。<br />  移植前：<br /> CROSS_COMPILE  ?=<br /> 移植后：<br /> CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu-<br />注：这里假设编译器就放在本机的那个目录下。<br />三、添加并修改驱动程序源代码，这涉及到以下几个方面。<br />（1）、将开发板配带的LCD驱动程序s3c2400fb.c、s3c2400fb.h源程序放到drivers/video/目录下，并修改名字为s3c2410fb.c\s3c2400fb.h。<br />#cp s3c2400fb.c . drivers/video/s3c2410fb.c<br />（2）、
在s3c2410fb.c驱动程序里面添加：sbc_gpio_con_set()、sbc_gpio_pullup_set()、
sbc_gpio_function_set()的声明以及实现代码用以替代2.4.18代码中的write_gpio_bit()、
set_gpio_ctrl()函数，因为在2.4.18中这两个函数都是用指针的方式对CPU寄存器进行设置，而在2.6.11的驱动程序里面用了
__raw_writel()的方式对寄存器设置进行了封装。<br />在驱动程序移植过程中由于是基于S3C2400的驱动，所以主要的修改工作就是根据所用开发板的硬件修改相应的寄存器的设置。<br />主
要的修改有：s3c2410fb_mach_info结构，这个结果主要定义了所用显示屏的一些信息，如时钟、大小等；修改
c2400fb_activate_var函数中关于寄存器的设置，这个函数涉及到了S3C2410
LCD控制器的有关设置，这些寄存器的设置要根据所用的屏幕（TFT/CSTN）来进行设置；修改
s3c2400fb_set_controller_regs和s3c2400fb_lcd_init函数，这个函数涉及到了CPU与LCD的物理连接，
要根据LCD与CPU的具体连接来设置各个CPIO寄存器。<br />注：具体修改详见驱动程序。<br />（3）、修改arch/arm/mach-s3c2410/s3c2410.c，在s3c2410_iodesc结构中添加：IODESC_ENT(LCD)<br />注：以上添加的语句就是为了将CPU的LCD寄存器的物理地址映射到所指向的虚拟地址上去，上面的结构还定义了虚拟地址所占用的区间，并指定了该区间所指向的域（的属性）。<br />（4）、修改drivers/video目录下的Kconfig文件，在最后添加如下内容：<br />config FB_S3C2410<br /> tristate "S3C2410 LCD support"<br /> depends on FB &amp;&amp; ARM &amp;&amp; ARCH_S3C2410<br /> help<br />  
This is a framebuffer device for the S3C2410 LCD Controller.   If you
plan to use the LCD display with your S3C2410 system, say   Y here.<br />（5）、修改drivers/video目录下的Makefile文件，在最后添加如下内容:<br />obj-$(CONFIG_FB_S3C2410)        += s3c2410fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o<br />四、配置、编译内核。在内核顶层目录当中键入：<br />  #make smdk2410_defconfig<br />由于2.6的内核默认就支持了S3C2410，所以就有一个默认的内核配置文件。里面只是包括了一个简单的配置，要使LCD驱动编译进内核，还要进行手工配置。<br />#make menuconfig</p>
		<p>  Graphics support  ---&gt;    <br />       [*] Support for frame buffer devices<br />     [*] S3C2410 LCD support(BASED ON S3C2400)<br />将刚才添加的LCD驱动程序静态添加到内核当中。<br />最后进行内核编译。<br />#make<br />然后将镜像下载到开发板中去.<br />而且在LCD显示屏上的左上角会显示一个小企鹅的图标。查看设备文件。 <br />[root@fa /]# ls -al /dev/fb/0<br />由此可见，LCD已经成功驱动，要测试驱动程序可以用 自己写（见附件test.c）在显示屏上显示任意颜色的线条。<br />问题解析<br />在LCD驱动程序移植的过程中，出现的问题主要就是由于寄存器设置不正确而造成的问题。<br />在
对驱动程序进行了函数替代以及改写了一些函数之后，将驱动程序编译进内核里，内核可以正确的编译连接并生成镜像文件，把镜像文件下载到开发板上，可以看到
drivers/video目录下看到系统注册的一个设备文件，但是在系统启动之后就是无法看见小企鹅的图标并且用测试程序去测试，LCD屏幕上也无法显
示任何有色的线条。通过多次查阅源代码，才发现原来就是CPU有关LCD的8个寄存器的设置以及对GPC和GPD寄存器的设置不正确。后来对这几个寄存器
进行了正确的设置就可以在系统启动之后看到小企鹅的图标。由于屏幕的背景是蓝色的，所以该图标的颜色显示不正确，但是用自己写的测试程序去画设置好的颜色
的线条，在屏幕上总能正确的显示出来，所以至于这个屏幕的颜色问题至今尚未解决。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15523.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 20:49 <a href="http://www.cnitblog.com/zouzheng/articles/15523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.6.14kernel linux usb host host driver</title><link>http://www.cnitblog.com/zouzheng/articles/15112.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 10 Aug 2006 12:07:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15112.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15112.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15112.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15112.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15112.html</trackback:ping><description><![CDATA[
		<font color="#555555">一、代码修改<br />主要是按照这个贴来做：<br /><a href="http://www.hfrk.net/S3C2410/kaifa/063152202483252_37.htm" target="_blank">http://www.hfrk.net/S3C2410/kaifa/063152202483252_37.htm</a><br />我也看过其它不同版本的内核关于usb驱动的移植，移植方法几乎一样，只是修改的文件不同而已。上面的贴子有不少头文件没有例出来，以下是我按照以上贴子添加的代码（好像在很多论坛上包括头文件部分都显示不出来，在这里把include前面的#给删了，希望有帮助）：</font>
		<p>
				<font color="#555555">/*add by lfc*/<br />include <asm><br />include <asm><br />include <linux><br />include <linux><br />/*end add*/</linux></linux></asm></asm></font>
		</p>
		<p>
				<font color="#555555">/**********************add by lfc*************************************/<br />static struct s3c2410_hcd_info usb_sbc2410_info = {<br />        .port[0]        = {<br />                .flags  = S3C_HCDFLG_USED<br />        }<br />};</font>
		</p>
		<p>
				<font color="#555555">int usb_sbc2410_init(void)<br />{<br /> unsigned long upllvalue = (0x78&lt;&lt;12)|(0x02&lt;&lt;4)|(0x03);<br /> printk("USB Control, (c) 2006 sbc2410\n");<br /> s3c_device_usb.dev.platform_data = &amp;usb_sbc2410_info;<br /> while(upllvalue!=__raw_readl(S3C2410_UPLLCON))<br />{<br /> __raw_writel(upllvalue,S3C2410_UPLLCON);<br /> mdelay(1);<br /> }<br /> return 0;<br />}</font>
		</p>
		<p>
				<font color="#555555">/***************************end add**********************/</font>
		</p>
		<p>
				<font color="#555555">static void __init smdk2410_map_io(void)<br />{<br />s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));<br />s3c24xx_init_clocks(0);<br />s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));<br />s3c24xx_set_board(&amp;smdk2410_board);<br />/*************************add by lfc****************************/<br />        usb_sbc2410_init();<br />/*************************end add*******************************/<br />}</font>
		</p>
		<p>
				<font color="#555555">上面的修改其实也参考了这个牛贴一下，里面有大虾们对USB驱动移植的讨论，还不错：<br /><a href="http://www.linuxforum.net/forum/showflat.php?Cat=&amp;Board=embedded&amp;Number=556915&amp;page=0&amp;view=collapsed&amp;sb=5&amp;o=0&amp;fpart=" target="_blank">http://www.linuxforum.net/forum/showflat.php?Cat=&amp;Board=embedded&amp;Number=556915&amp;page=0&amp;view=collapsed&amp;sb=5&amp;o=0&amp;fpart=</a></font>
		</p>
		<p>
				<font color="#555555">其实到了这里，要修改的代码已经修改完了，比添加Nand flash的支持修改的地方还要少^_^，不过我一直以为还没修改好，最后发现原来是没配置好~_~</font>
		</p>
		<p>
				<font color="#555555">二、内核配置<br />下面说一下郁闷了我好一阵子的内核配置（支持USB）问题，这个就比Nand flash的配置要复杂多了。<br />１、让内核支持热插拔<br />│                General setup  ---&gt;  <br />  │ │[*] Support for hot-pluggable devices</font>
		</p>
		<p>
				<font color="#555555">２、USB驱动设置，可能有些不选也行，不过没时间去试，至于为什么要选这些选项的话可以看一下这个贴（Linux下的硬件驱动——USB设备）：<br /><a href="http://www-128.ibm.com/developerworks/cn/linux/l-usb/index1.html" target="_blank">http://www-128.ibm.com/developerworks/cn/linux/l-usb/index1.html</a></font>
		</p>
		<p>
				<font color="#555555">  │ │                Device Drivers  ---&gt; <br /> │ │              Generic Driver Options  ---&gt;           <br /> │&lt;*&gt; Hotplug firmware loading support    <br />  │ │              Block devices  ---&gt;   <br />  │ │              &lt;*&gt; Low Performance USB Block driver   <br />  │ │              SCSI device support  ---&gt;   <br />  │ │  &lt;*&gt;   SCSI generic support  <br />│ │  [*]   Probe all LUNs on each SCSI device    <br />  │ │              USB support  ---&gt;        <br />  │ │&lt;*&gt; Support for Host-side USB    <br /> │ │[*]   USB device filesystem <br />  │ │&lt;*&gt;   OHCI HCD support  <br /> │ │&lt;*&gt;   USB Mass Storage support <br />  │ │[*]   USB Monitor</font>
		</p>
		<p>
				<font color="#555555">３、加入了MSDOS fs和VFAT fs的支持。</font>
		</p>
		<p>
				<font color="#555555">  │ │                File systems  ---&gt;      <br />  │ │        DOS/FAT/NT Filesystems  ---&gt;   <br />  │ ┌─────────────────────────────────────────────────────────────────────┐ │<br />  │ │                 &lt;*&gt; MSDOS fs support                                │ │<br />  │ │                 &lt;*&gt; VFAT (Windows-95) fs support                    │ │<br />  │ │                 (936) Default codepage for FAT                      │ │<br />  │ │                 (cp936) Default iocharset for FAT                   │ │<br />  │ │                 &lt; &gt; NTFS file system support</font>
		</p>
		<p>
				<font color="#555555">做完这些后，插入u盘后，内核应该可以识别到u盘，出现：<br />usb 1-1: new full speed USB device using s3c2410-ohci and address 3<br />ub(1.3): GetMaxLUN returned 0, using 1 LUNs</font>
		</p>
		<p>
				<font color="#555555">但是，还有下面一句出错提示：<br />/dev/ub/a: unknown partition table</font>
		</p>
		<p>
				<font color="#555555">再次查看了贴子上大虾们的讨论，提到：“使能CONFIG_MSDOS_PARTITION选项”，再仔细查找，发现配置选项如下：<br />│ │                File systems  ---&gt;<br />│ │                Partition Types  ---&gt;  <br />  │ │     [*]   PC BIOS (MSDOS partition tables) support<br />加上这个后应该就可以挂载usb上的MSDOS分区了</font>
		</p>
		<p>
				<font color="#555555">以下是我的内核插入u盘后的提示信息：<br />usb 1-1: new full speed USB device using s3c2410-ohci and 2<br />ub(1.2): GetMaxLUN returned 0, using 1 LUNs<br /> /dev/ub/a: p1<br />表示usb设备已经挂载到/dev/ub/a/part1设备文件下</font>
		</p>
		<p>
				<font color="#555555">4.加入中文字体库（可惜在我的板上还是没能正常显示中文~_~，知道的朋友麻烦告诉我一声，大家一起探讨）<br />  │ │        Native Language Support  ---&gt;     <br />  │ │&lt;*&gt;   Simplified Chinese charset (CP936, GB2312)  <br />  │ │&lt;*&gt;   NLS UTF8</font>
		</p>
		<p>
				<font color="#555555">以下是挂载usb设备后的显示：<br />[root@luofuchong /]# mount -t vfat -o iocharset=cp936 /dev/ub/a/part1 /mnt<br />[root@luofuchong /]# ls /mnt<br />cramfs-1.1.tar.gz        netkit-base-0.17.tar.gz  thttpd-2.25b.tar.gz<br />lfc                      settings.dat             . . I. ..  . .txt</font>
		</p>
		<p>
				<font color="#555555">三、一点心得。</font>
		</p>
		<p>
				<font color="#555555">如果想知道内核有没有识别出u盘的话可以执行命令：cat /proc/partitions ，看看插入USB前后分区信息有什么不同就知道了。</font>
		</p>
		<p>
				<font color="#555555">另外，我在贴上看到其它大虾移植了USB驱动的内核，在插入U盘后内核都把它当成SCSI设备来处理的，不过在我这里却没有，这点我倒不是很明白，难道是我u盘的问题？</font>
		</p>
		<p>
				<font color="#555555">还
有就是，usb设备的挂载点比较特别，在我移植的系统中是：/dev/ub/a/part1。刚开始我不知道，以为/dev/ub/a就是了，结果当然挂
载不了，差点就重编内核了，所以如果内核提示挂载usb设备成功的话，最好进入对应的目录仔细看清楚。如果是当成SCSI设备的话挂载点就在
/dev/scsi/host0/bus0/target0/lun0之类吧。其实插入u盘的时候都有提示，自己看着办吧。</font>
		</p>
		<p>
				<font color="#555555">还
有一个问题，为了统一起见，一般都会新建一个/dev/sda1的链接指向usb设备的挂载点的，但是我的内核是采用devfs的，文件系统的/dev目
录空空如也，而且/dev/ub/a/part1是插入u盘后才有的挂载点，那请问我应该怎样去新建这个链接呢？希望各位大虾知道的话可以告知我，感激不
尽！</font>
		</p>
		<font color="#555555">asm/arch/regs-clock.h　asm/arch/usb-control.h　linux/device.h　linux/delay.h<br /><br /><br /></font>
		<p class="post">
搞定了这个问题，没有加入对应文件系统的language编码支持。
<br />配置如下：
<br />File systems --&gt;
<br />DOS/FAT/NT Filesystems ---&gt;
</p>
		<table border="0" cellpadding="3" cellspacing="1" width="100%">
				<tbody>
						<tr>
								<td class="lighttable" width="83%">
								</td>
						</tr>
				</tbody>
				<li> MSDOS fs support
</li>
				<li> VFAT (Windows-95) fs support
<br />(437) Default codepage for FAT
<br />(iso8859-1) Default iocharset for FAT
<br />Native Language Support --&gt;
<br />(iso8859-1) Default NLS Option
</li>
				<li> Codepage 437 (United States, Canada)
</li>
				<li> NLS ISO 8859-1 (Latin 1; Western European Languages)</li>
		</table>
		<br />
		<br />两路USB问题的解决<br />
观查2.4 有点启发
<br />加
<br />/*sun add*/
<br />	#if 1      //2*usbhost	
<br />	s3c2410_misccr=ioremap_nocache(S3C2410_MISCCR,0x00000004);
<br />	printk("\nMISCCR=%x----sun\n",readl(s3c2410_misccr));	 
<br />	outl(readl(s3c2410_misccr)|(1&lt;&lt;3),s3c2410_misccr);	
<br />	printk("\nMISCCR=%x----sun\n",readl(s3c2410_misccr));	   	
<br />  #endif
<br />/*sun add over*/
<br /><br />这么简单就好用了：
<br />  插入第一个盘
<br /> ~ # usb 1-1: new full speed USB device using s3c2410-ohci and address 7
<br />usb 1-1: Product: Flash Disk
<br />usb 1-1: Manufacturer: Generic
<br />scsi5 : SCSI emulation for USB Mass Storage devices
<br />  Vendor: Generic   Model: USB Flash Drive   Rev:
<br />  Type:   Direct-Access                      ANSI SCSI revision: 02
<br />SCSI device sda: 128640 512-byte hdwr sectors (66 MB)
<br />sda: Write Protect is off
<br />sda: assuming drive cache: write through
<br />SCSI device sda: 128640 512-byte hdwr sectors (66 MB)
<br />sda: Write Protect is off
<br />sda: assuming drive cache: write through
<br /> sda:&lt;7&gt;usb-storage: queuecommand called
<br /> sda1
<br />sd 5:0:0:0: Attached scsi removable disk sda
<br />sd 5:0:0:0: Attached scsi generic sg0 type 0
<br /><br />~ #
<br />插入第二个盘
<br /><br />~ # usb 1-2: new full speed USB device using s3c2410-ohci and address 8
<br />usb 1-2: Product: Intelligent Stick
<br />usb 1-2: Manufacturer: Intelligent Stick
<br />usb 1-2: SerialNumber: 20031228011512-01
<br />scsi6 : SCSI emulation for USB Mass Storage devices
<br />  Vendor: USB Card  Model: IntelligentStick  Rev: 2.23
<br />  Type:   Direct-Access                      ANSI SCSI revision: 02
<br />SCSI device sdb: 260288 512-byte hdwr sectors (133 MB)
<br />sdb: Write Protect is off
<br />sdb: assuming drive cache: write through
<br />SCSI device sdb: 260288 512-byte hdwr sectors (133 MB)
<br />sdb: Write Protect is off
<br />sdb: assuming drive cache: write through
<br /> sdb:&lt;7&gt;usb-storage: queuecommand called
<br /> sdb1
<br />sd 6:0:0:0: Attached scsi removable disk sdb
<br />sd 6:0:0:0: Attached scsi generic sg1 type 0
<br /><br />~ #
<br /><br />~ # mount -t vfat -o iocharset=cp936 /dev/sda1 /mnt/sda1
<br />~ # mount -t vfat -o iocharset=cp936 /dev/sdb1 /mnt/sdb1
<br /><br />~ # cd mnt
<br />/mnt # ls
<br />sda1  sdb1
<br />/mnt # cd sda1
<br />/mnt/sda1 # ls
<br />2005-10-8                 autorun.inf               ~tmp2
<br />HY57V281620HCT-H.pdf      desktop.ini               动态滤波器的fpga实现.doc
<br />_探头参数                 modbus程序要求.doc        沟通.doc
<br />arm_test.txt              mt8816.pdf
<br />/mnt/sda1 # cd ..
<br />/mnt # ls
<br />sda1  sdb1
<br />/mnt # cd sdb1
<br />/mnt/sdb1 # ls
<br />B-doc
<br />ISIS_Proposal.dll
<br />MATLAB命令大全.pdf
<br />MATLAB语言工具箱——TOOLBOX实用指南.pdf
<br />NSFCProposal
<br />const.dat
<br />matlab信号处理详解.pdf
<br />数字信号处理及其matlab实现.pdf
<br />王强.doc
<br />新建文件夹
<br />增 益 控 制 表A.doc
<br />自然科学基金-芦
<br />/mnt/sdb1 #
<br /><br />
挂载U盘时，弹出几行信息
<br />Vendor: VMU801    Model: USB-Disk          Rev: 1.3A
<br /> Type:   Direct-Access                      ANSI SCSI revision: 02
<br />就没有反应了，好像死了一样，但是回车以后会出现"#"
<br /># cd /proc
<br /># ls
<br />1            27           buddyinfo    filesystems  meminfo      slabinfo
<br />10           28           bus          fs           misc         stat
<br />11           3            cmdline      interrupts   modules      sys
<br />12           4            cpu          iomem        mounts       sysvipc
<br />13           5            cpuinfo      ioports      mtd          tty
<br />14           6            devices      kallsyms     net          uptime
<br />15           7            diskstats    kmsg         partitions   version
<br />2            8            driver       loadavg      scsi         vmstat
<br />26           9            execdomains  locks        self
<br /># cd scsi
<br /># ls
<br />device_info  scsi         usb-storage
<br /># cat scsi
<br />Attached devices:
<br />Host: scsi0 Channel: 00 Id: 00 Lun: 00
<br />  Vendor: VMU801   Model: USB-Disk         Rev: 1.3A
<br />  Type:   Direct-Access                    ANSI SCSI revision: 02
<br /># cd usb-storage/
<br /># ls
<br />0
<br /># cat 0
<br />   Host scsi0: usb-storage
<br />       Vendor: USB Drive
<br />      Product: USB-Disk
<br />Serial Number: 000115610000
<br />     Protocol: Transparent SCSI
<br />    Transport: Bulk
<br />       Quirks:
<br /># cat /proc/partitions
<br />major minor  #blocks  name
<br /><br />  31     0        128 mtdblock0
<br />  31     1         64 mtdblock1
<br />  31     2       2816 mtdblock2
<br />  31     3       2048 mtdblock3
<br />  31     4       7168 mtdblock4
<br /># cd /dev/scsi
<br /># ls
<br /># ls /dev
<br />console   kmem      mtd       null      pts       scsi      urandom   vcc
<br />full      kmsg      mtdblock  port      random    tts       usb       zero
<br />input     mem       nftl      ptmx      root      tty       vc
<br /><br />这里并没有生成文件系统的节点，并且在/proc/partitions里面也没有响应的分区信息，但是在/proc/scsi/scsi和
/proc/scsi/usb-storage可以看出已经注册了，请问各位大侠，我的配置文件里面是不是少选了一些东西，为什么没有在/dev下生成节
点？<br /><table style="table-layout: fixed;" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td style="left: 0px; width: 100%;"><br /></td></tr></tbody></table><img src ="http://www.cnitblog.com/zouzheng/aggbug/15112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-10 20:07 <a href="http://www.cnitblog.com/zouzheng/articles/15112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ARM Linux源代码分析(1)</title><link>http://www.cnitblog.com/zouzheng/articles/14673.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 03 Aug 2006 13:52:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/14673.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/14673.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/14673.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/14673.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/14673.html</trackback:ping><description><![CDATA[
		<p>/*<br />ARMLinux源代码分析(1)--head.S</p>
		<p>TigerZ(<a href="mailto:tigerz@yeah.net">tigerz@yeah.net</a>)<br /><a href="http://emblinux.org/">http://emblinux.org/</a></p>
		<p>1. 分析环境</p>
		<p>    kernel: 2.6.10<br />    board: SMDK2410 1.32, 64M SDRAM, 128M SM卡</p>
		<p>2. head.S<br />*/</p>
		<p>/*<br /> * linux/arch/arm/kernel/head.S<br /> *<br /> *  Copyright (C) 1994-2002 Russell King<br /> *<br /> * This program is free software; you can redistribute it and/or modify<br /> * it under the terms of the GNU General Public License version 2 as<br /> * published by the Free Software Foundation.<br /> *<br /> *  Kernel startup code for all 32-bit CPUs<br /> */<br />#include &lt;linux/config.h&gt;<br />#include &lt;linux/linkage.h&gt;<br />#include &lt;linux/init.h&gt;</p>
		<p>#include &lt;asm/assembler.h&gt;<br />#include &lt;asm/mach-types.h&gt;<br />#include &lt;asm/procinfo.h&gt;<br />#include &lt;asm/ptrace.h&gt;<br />#include &lt;asm/constants.h&gt;</p>
		<p>#ifndef CONFIG_XIP_KERNEL /* 我在的平台, 这个宏没有定义 */<br />/*<br /> * We place the page tables 16K below TEXTADDR.  Therefore, we must make sure<br /> * that TEXTADDR is correctly set.  Currently, we expect the least significant<br /> * 16 bits to be 0x8000, but we could probably relax this restriction to<br /> * TEXTADDR &gt;= PAGE_OFFSET + 0x4000<br /> *<br /> * 页表放在比TEXTADDR低16K的地方, 所以我们必须要确认TEXTADDR被正确设定. 目前,<br /> * 我们希望低16位为0x8000, 不过我们可以放松这个限制为<br /> * TEXTADDR &gt;= PAGE_OFFSET + 0x4000<br /> *<br /> * Note that swapper_pg_dir is the virtual address of the page tables, and<br /> * pgtbl gives us a position-independent reference to these tables.  We can<br /> * do this because stext == TEXTADDR<br /> *<br /> * 注意swapper_pg_dir是页表的虚拟地址, pgtbl给我们一个位置无关的页表地址, 这是<br /> * 因为stext == TEXTADDR.<br /> */<br />#if (TEXTADDR &amp; 0xffff) != 0x8000<br />    #error TEXTADDR must start at 0xXXXX8000<br />#endif</p>
		<p>    /* swapper_pg_dir = 0x30008000 - 0x4000 */<br />    .globl swapper_pg_dir<br />    .equ    swapper_pg_dir, TEXTADDR - 0x4000</p>
		<p>    /* rd = stext - 0x4000, 这里stext == TEXTADDR */<br />    /* adr指令是在pc值上+/-一个标号的偏移得到的, 所以得到的地址只跟pc和标号到<br />     * pc的偏移相关, 跟编译地址无关. 在MMU打开前, 代码要是地址无关的, 会经常<br />     * 用到adr.<br />     */<br />    .macro pgtbl, rd, phys<br />    adr     \rd, stext<br />    sub     \rd, \rd, #0x4000<br />    .endm<br />#else<br />/*<br />* XIP Kernel:<br />*<br />* We place the page tables 16K below DATAADDR.  Therefore, we must make sure<br />* that DATAADDR is correctly set.  Currently, we expect the least significant<br />* 16 bits to be 0x8000, but we could probably relax this restriction to<br />* DATAADDR &gt;= PAGE_OFFSET + 0x4000<br />*<br />* Note that pgtbl is meant to return the physical address of swapper_pg_dir.<br />* We can't make it relative to the kernel position in this case since<br />* the kernel can physically be anywhere.<br />*/<br />#if (DATAADDR &amp; 0xffff) != 0x8000<br />#error DATAADDR must start at 0xXXXX8000<br />#endif</p>
		<p>.globl swapper_pg_dir<br />.equ swapper_pg_dir, DATAADDR - 0x4000</p>
		<p>.macro pgtbl, rd, phys<br />ldr \rd, =((DATAADDR - 0x4000) - VIRT_OFFSET)<br />add \rd, \rd, \phys<br />.endm<br />#endif</p>
		<p>/*<br /> * Kernel startup entry point.<br /> * ---------------------------<br /> * Kernel的启动入口.<br /> *<br /> * This is normally called from the decompressor code.  The requirements<br /> * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,<br /> * r1 = machine nr.<br /> *<br /> * 这里一般由解压缩代码调用. 它需要: MMU = off, D-cache = off,<br /> * I-cache = 不关心, r0 = 0, r1 = 机器号.<br /> *<br /> * This code is mostly position independent, so if you link the kernel at<br /> * 0xc0008000, you call this at __pa(0xc0008000).<br /> *<br /> * 这里的代码通常是位置无关的, 所以如果你在0xc0008000链接kernel, 直接调用<br /> * __pa(0xc0008000).<br /> *<br /> * See linux/arch/arm/tools/mach-types for the complete list of machine<br /> * numbers for r1.<br /> *<br /> * 在linux/arch/arm/tools/mach-types里查看机器号r1的完整列表.<br /> *<br /> * We're trying to keep crap to a minimum; DO NOT add any machine specific<br /> * crap here - that's what the boot loader (or in extreme, well justified<br /> * circumstances, zImage) is for.<br /> *<br /> */<br />    __INIT<br />    .type   stext, #function<br />ENTRY(stext)<br />    mov     r12, r0<br />    mov     r0, #PSR_F_BIT | PSR_I_BIT | MODE_SVC   @ make sure svc mode<br />                                                /* 确认是SVC模式 */<br />    msr     cpsr_c, r0          @ and all irqs disabled<br />                            /* 关闭所有irq */<br />    bl      __lookup_processor_type     /* 查看处理器类型 */<br />    teq     r10, #0             @ invalid processor?<br />                            /* 无效的处理器? */<br />    moveq r0, #'p'        @ yes, error 'p'<br />                            /* 如果是, 错误码'p' */<br />    beq     __error<br />    bl      __lookup_architecture_type /* 查看体系类型 */<br />    teq     r7, #0              @ invalid architecture?<br />                            /* 无效体系? */<br />    moveq r0, #'a'        @ yes, error 'a'<br />                            /* 如果是, 错误码'a' */<br />    beq     __error<br />    bl      __create_page_tables    /* 建立页表 */</p>
		<p>    /*<br />     * The following calls CPU specific code in a position independent<br />     * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of<br />     * xxx_proc_info structure selected by __lookup_architecture_type<br />     * above.  On return, the CPU will be ready for the MMU to be<br />     * turned on, and r0 will hold the CPU control register value.<br />     *<br />     * 下面的代码按位置无关的方式调用特定的CPU代码, 详细信息查看<br />     * arch/arm/mm/proc-*.S. r0 = xxx_proc_info结构起始地址, 该结构由上面<br />     * 的__lookup_architecture_type选择. 在返回时, CPU已经准备好打开MMU,<br />     * r0将保存CPU控制寄存器的值.<br />     */<br />    adr     lr, __turn_mmu_on       @ return (PIC) address<br />                                    /* 调用完下面的__arm920_setup后返回到<br />                                       __turn_mmu_on */<br />    add     pc, r10, #12            /* 调用__arm920_setup , 详见<br />                                       arch/arm/mm/proc-arm920.S */</p>
		<p>    .type   __switch_data, %object<br />__switch_data:<br />    .long   __mmap_switched<br />    .long   __data_loc              @ r2<br />    .long   __data_start            @ r3<br />    .long   __bss_start             @ r4<br />    .long   _end                    @ r5<br />    .long   processor_id            @ r6<br />    .long   __machine_arch_type     @ r7<br />    .long   cr_alignment            @ r8<br />    .long   init_thread_union+8192  @ sp</p>
		<p>    /*<br />     * Enable the MMU.  This completely changes the structure of the visible<br />     * memory space.  You will not be able to trace execution through this.<br />     * If you have an enquiry about this, *please* check the linux-arm-kernel<br />     * mailing list archives BEFORE sending another post to the list.<br />     *<br />     * 打开MMU. 这将完全改变可见存储空间的结构, 你不能跟踪运行情况.<br />     * 如果你有疑问, 请先查看linux-arm-kernel邮件列表, 在你发邮件到这个列表前.<br />     */<br />    .align 5<br />    .type   __turn_mmu_on, %function<br />__turn_mmu_on:<br />    ldr     lr, __switch_data                   /* 完成后跳到__switch_data偏移0存放的地址,<br />                                                   即__mmap_switched */<br />    #ifdef CONFIG_ALIGNMENT_TRAP<br />    orr     r0, r0, #2                          @ ...........A.<br />                                                /* 访问非对齐地址产生一个TRAP */<br />    #endif<br />    mcr     p15, 0, r0, c1, c0, 0               @ write control reg<br />    mrc     p15, 0, r3, c0, c0, 0               @ read id reg<br />    mov     r3, r3<br />    mov     r3, r3<br />    mov     pc, lr</p>
		<p>/*<br /> * The following fragment of code is executed with the MMU on, and uses<br /> * absolute addresses; this is not position independent.<br /> *<br /> * 下面的代码在MMU打开的情况下运行, 是绝对地址, 不是位置无关的.<br /> *  r0  = processor control register<br /> *  r1  = machine ID<br /> *  r9  = processor ID<br /> *  r12 = value of r0 when kernel was called (currently always zero)<br /> *<br /> * 这里主要是为调用C函数start_kernel做准备. 在调用这部分代码前, 我们必须<br /> * 打开MMU, 因为kernel的位置相关代码是要以0xc0008000为起始地址的, 所以在这<br /> * 之前, 我们建立了页表, 并打开MMU.<br /> */<br />    .align 5<br />__mmap_switched:<br />    adr         r2, __switch_data + 4<br />    ldmia       r2, {r2, r3, r4, r5, r6, r7, r8, sp}</p>
		<p>    cmp         r2, r3              @ Copy data segment if needed<br />1:  cmpne       r3, r4<br />    ldrne       fp, [r2], #4<br />    strne       fp, [r3], #4<br />    bne         1b</p>
		<p>    mov         fp, #0              @ Clear BSS (and zero fp)<br />1:  cmp         r4, r5<br />    strcc       fp, [r4],#4<br />    bcc         1b</p>
		<p>    str         r9, [r6]   @ Save processor ID<br />    str         r1, [r7]   @ Save machine type<br />    bic         r2, r0, #2   @ Clear 'A' bit<br />    stmia       r8, {r0, r2}   @ Save control register values<br />    b           start_kernel</p>
		<p> </p>
		<p>
				<br />/*<br /> * Setup the initial page tables.  We only setup the barest<br /> * amount which are required to get the kernel running, which<br /> * generally means mapping in the kernel code.<br /> *<br /> * 设置初始化页表, 我们只设置kernel运行所需少量内存, 通常就是<br /> * 映射kernel的代码.<br /> *<br /> * r5 = physical address of start of RAM<br /> * r6 = physical IO address<br /> * r7 = byte offset into page tables for IO<br /> * r8 = page table flags<br /> */<br />__create_page_tables:<br />        /* 取页表地址到r4, 这里r4 = 0x30004000 */<br />        pgtbl r4, r5                          @ page table address</p>
		<p>        /*<br />         * Clear the 16K level 1 swapper page table<br />         *<br />         * 清空页表<br />         */<br />        mov     r0, r4<br />        mov r3, #0<br />        add r2, r0, #0x4000<br />1: str r3, [r0], #4<br />        str r3, [r0], #4<br />        str r3, [r0], #4<br />        str r3, [r0], #4<br />        teq r0, r2<br />        bne 1b</p>
		<p>        /*<br />         * Create identity mapping for first MB of kernel to<br />         * cater for the MMU enable.  This identity mapping<br />         * will be removed by paging_init().  We use our current program<br />         * counter to determine corresponding section base address.\<br />         *<br />         * kernel的第1M建立等价映射, 为打开MMU做准备. 这个等价映射将在<br />         * paging_init()中移除. 我们用pc来确定对应的段地址.<br />         */<br />        mov r2, pc, lsr #20   @ start of kernel section<br />        add r3, r8, r2, lsl #20  @ flags + kernel base (flags = 0x00000c1e)<br />        str r3, [r4, r2, lsl #2]  @ identity mapping<br />        /*<br />         *           r4           +  (pc &gt;&gt; 20) &lt;&lt; 2<br />         * 31                   14 13           2 1 0<br />         * -------------------------------------------<br />         * |   Translatioin base  | Table index  |0|0|<br />         * -------------------------------------------<br />         * 这一个表项把kernel section的虚地址和实地址等同起来.<br />         * r3 = 0x00000c1e + 0x30000000 = 0x30000c1e<br />         * r4 = 0x30004000<br />         * r2 &gt;&gt; 18 = 0x30000000 &gt;&gt; 18 = 0xc00<br />         * [0x30004000 + 0xc00] = 0x30000c1e<br />         */</p>
		<p>        /*<br />         * Now setup the pagetables for our kernel direct<br />         * mapped region.  We round TEXTADDR down to the<br />         * nearest megabyte boundary.  It is assumed that<br />         * the kernel fits within 4 contigous 1MB sections.<br />         *<br />         * 现在为kernel设置页表, 把TEXTADDR完整成MB, 这里假设<br />         * kernel在4个连续的1MB段内.<br />         */<br />        /* TEXTADDR = 0xc0000000 */<br />        add r0, r4,  #(TEXTADDR &amp; 0xff000000) &gt;&gt; 18 @ start of kernel<br />        /* r0 = 0x30004000 + 0x3000 */<br />        str r3, [r0, #(TEXTADDR &amp; 0x00f00000) &gt;&gt; 18]!<br />        /* [0x30004000 + 0x3000] = 0x30000c1e */<br />        /*<br />         * add r0, r4, #(TEXTADDR &amp; 0xfff00000) &gt;&gt; 18<br />         * str r3, [r0]<br />         * 上2行的意思同这2行相同, 为什么不这样做?<br />         */<br />        add r3, r3, #1 &lt;&lt; 20<br />        str r3, [r0, #4]!   @ KERNEL + 1MB<br />        /* [0x30004000 + 0x3004] = 0x30100c1e */<br />        add r3, r3, #1 &lt;&lt; 20<br />        str r3, [r0, #4]!   @ KERNEL + 2MB<br />        /* [0x30004000 + 0x3008] = 0x30200c1e */<br />        add r3, r3, #1 &lt;&lt; 20<br />        str r3, [r0, #4]   @ KERNEL + 3MB<br />        /* [0x30004000 + 0x300c] = 0x30300c1e */</p>
		<p>        /*<br />         * Then map first 1MB of ram in case it contains our boot params.<br />         *<br />         * 因为RAM的第1MB存放有boot参数, 所以也需要映射. (当前boot参数所在<br />         * 的段与前面pc值所在段相等, 但这不是一定的.<br />         */<br />        add r0, r4, #VIRT_OFFSET &gt;&gt; 18<br />        add r2, r5, r8<br />        str r2, [r0]</p>
		<p>/* 下面代码略过 */<br />#ifdef CONFIG_XIP_KERNEL<br />/*<br />* Map some ram to cover our .data and .bss areas.<br />* Mapping 3MB should be plenty.<br />*/<br />sub r3, r4, r5<br />mov r3, r3, lsr #20<br />add r0, r0, r3, lsl #2<br />add r2, r2, r3, lsl #20<br />str r2, [r0], #4<br />add r2, r2, #(1 &lt;&lt; 20)<br />str r2, [r0], #4<br />add r2, r2, #(1 &lt;&lt; 20)<br />str r2, [r0]<br />#endif</p>
		<p>bic r8, r8, #0x0c   @ turn off cacheable<br />@ and bufferable bits<br />#ifdef CONFIG_DEBUG_LL<br />/*<br />* Map in IO space for serial debugging.<br />* This allows debug messages to be output<br />* via a serial console before paging_init.<br />*/<br />add r0, r4, r7<br />rsb r3, r7, #0x4000   @ PTRS_PER_PGD*sizeof(long)<br />cmp r3, #0x0800<br />addge r2, r0, #0x0800<br />addlt r2, r0, r3<br />orr r3, r6, r8<br />1: str r3, [r0], #4<br />add r3, r3, #1 &lt;&lt; 20<br />teq r0, r2<br />bne 1b<br />#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)<br />/*<br />* If we're using the NetWinder, we need to map in<br />* the 16550-type serial port for the debug messages<br />*/<br />teq r1, #MACH_TYPE_NETWINDER<br />teqne r1, #MACH_TYPE_CATS<br />bne 1f<br />add r0, r4, #0x3fc0   @ ff000000<br />mov r3, #0x7c000000<br />orr r3, r3, r8<br />str r3, [r0], #4<br />add r3, r3, #1 &lt;&lt; 20<br />str r3, [r0], #4<br />1:<br />#endif<br />#endif<br />#ifdef CONFIG_ARCH_RPC<br />/*<br />* Map in screen at 0x02000000 &amp; SCREEN2_BASE<br />* Similar reasons here - for debug.  This is<br />* only for Acorn RiscPC architectures.<br />*/<br />add r0, r4, #0x80   @ 02000000<br />mov r3, #0x02000000<br />orr r3, r3, r8<br />str r3, [r0]<br />add r0, r4, #0x3600   @ d8000000<br />str r3, [r0]<br />#endif<br />        /* 返回 */<br />        mov pc, lr<br />        .ltorg</p>
		<p> </p>
		<p>/*<br /> * Exception handling.  Something went wrong and we can't proceed.  We<br /> * ought to tell the user, but since we don't have any guarantee that<br /> * we're even running on the right architecture, we do virtually nothing.<br /> *<br /> * 异常处理. 一些东西错了就不能继续, 我们应该告诉用户, 但因为我们甚至不能<br /> * 保证运行在正确的体系上, 我们实际在做无用功.<br /> *<br /> * r0 = ascii error character:<br /> * a = invalid architecture<br /> * p = invalid processor<br /> * i = invalid calling convention<br /> *<br /> * Generally, only serious errors cause this.<br /> */<br />__error:<br />        #ifdef CONFIG_DEBUG_LL<br />        mov r8, r0    @ preserve r0<br />        adr r0, err_str<br />        bl printascii<br />        mov r0, r8<br />        bl printch<br />        #endif</p>
		<p>#ifdef CONFIG_ARCH_RPC<br />/*<br />* Turn the screen red on a error - RiscPC only.<br />*/<br />mov r0, #0x02000000<br />mov r3, #0x11<br />orr r3, r3, r3, lsl #8<br />orr r3, r3, r3, lsl #16<br />str r3, [r0], #4<br />str r3, [r0], #4<br />str r3, [r0], #4<br />str r3, [r0], #4<br />#endif</p>
		<p>1: mov r0, r0<br />        b 1b</p>
		<p>#ifdef CONFIG_DEBUG_LL<br />err_str:<br />        .asciz "\nError: "<br />        .align<br />#endif</p>
		<p>/*<br /> * Read processor ID register (CP#15, CR0), and look up in the linker-built<br /> * supported processor list.  Note that we can't use the absolute addresses<br /> * for the __proc_info lists since we aren't running with the MMU on<br /> * (and therefore, we are not in the correct address space).  We have to<br /> * calculate the offset.<br /> *<br /> * 读取处理器ID寄存器(CP#15, CR0), 在链接时建立的已支持处理器列表中查找.<br /> * 注意, 我们不能使用__proc_info的绝对地址, 因为我们还没有运行MMU,<br /> * (因此, 我们不在正确的地址空间). 我们必须计算偏移.<br /> *<br /> * Returns:<br /> * r5, r6, r7 corrupted<br /> * r8  = page table flags<br /> * r9  = processor ID<br /> * r10 = pointer to processor structure<br /> */<br />__lookup_processor_type:<br />        adr r5, 2f                          /* 取2f的相对地址 */<br />        ldmia r5, {r7, r9, r10}               /* r7 = __proc_info_end,<br />                                                   r9 = __proc_info_begin,<br />                                                   r10 = 2 */<br />        sub r5, r5, r10   @ convert addresses<br />                                                /* r5 = 2的相对地址 - 2的绝对地址 */<br />        add r7, r7, r5   @ to our address space<br />                                                /* r7 += r5, 把r7从绝对地址转化为<br />                                                   相对地址 */<br />        add r10, r9, r5                     /* 绝对转相对 */<br />        mrc p15, 0, r9, c0, c0  @ get processor id<br />1: ldmia r10, {r5, r6, r8}  @ value, mask, mmuflags<br />        and r6, r6, r9   @ mask wanted bits<br />        teq r5, r6<br />        moveq pc, lr<br />        add r10, r10, #PROC_INFO_SZ  @ sizeof(proc_info_list)<br />        cmp r10, r7<br />        blt 1b<br />        mov r10, #0    @ unknown processor<br />        mov pc, lr</p>
		<p>        /*<br />         * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for<br />         * more information about the __proc_info and __arch_info structures.<br />         */<br />2: .long __proc_info_end<br />        .long __proc_info_begin<br />        .long 2b<br />        .long __arch_info_begin<br />        .long __arch_info_end</p>
		<p>/*<br /> * Lookup machine architecture in the linker-build list of architectures.<br /> * Note that we can't use the absolute addresses for the __arch_info<br /> * lists since we aren't running with the MMU on (and therefore, we are<br /> * not in the correct address space).  We have to calculate the offset.<br /> *<br /> * 在链接时建立的机器列表中查找机器结构. 注意我们不能使用__arch_info的绝对地址,<br /> * 因为我们没有打开MMU(因此, 我们不在正确的地址空间). 我们必须计算偏移.<br /> *<br /> *  r1 = machine architecture number<br /> * Returns:<br /> *  r2, r3, r4 corrupted<br /> *  r5 = physical start address of RAM<br /> *  r6 = physical address of IO<br /> *  r7 = byte offset into page tables for IO<br /> */<br />__lookup_architecture_type:<br />        adr r4, 2b                          /* 这里的做法同__lookup_processor_type */<br />        ldmia r4, {r2, r3, r5, r6, r7} @ throw away r2, r3<br />        sub r5, r4, r5   @ convert addresses<br />        add r4, r6, r5   @ to our address space<br />        add r7, r7, r5<br />1: ldr r5, [r4]   @ get machine type<br />        teq r5, r1    @ matches loader number?<br />        beq 2f    @ found<br />        add r4, r4, #SIZEOF_MACHINE_DESC @ next machine_desc<br />        cmp r4, r7<br />        blt 1b<br />        mov r7, #0    @ unknown architecture<br />        mov pc, lr<br />2: ldmib r4, {r5, r6, r7}  @ found, get results<br />        mov pc, lr</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/14673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-03 21:52 <a href="http://www.cnitblog.com/zouzheng/articles/14673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux内核 ramdisk busybox</title><link>http://www.cnitblog.com/zouzheng/articles/14423.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 30 Jul 2006 02:08:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/14423.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/14423.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/14423.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/14423.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/14423.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>此文章是关于嵌入式LINUX的</p>
				<p>编译内核 /*linux-2.6.15*/<br />-------------<br />进入内核文件夹</p>
				<p>打补丁使其支持at91rm9200<br />#zcat ../2.6.15-at91.patch.gz | patch -p1</p>
				<p>编辑Makefile<br />ARCH            ?= $(SUBARCH)<br />CROSS_COMPILE   ?=<br />变为<br />ARCH  ?= arm<br />CROSS_COMPILE ?= /usr/local/eldk/usr/bin/arm-linux-<br />保存 </p>
				<p>编辑include/config/cmdline.h /*如果你的ramdisk在3M之内就跳过这一系列操作*/<br />#define CONFIG_CMDLINE "mem=32M console=ttyS0,115200 initrd=0x20410000,3145728 root=/dev/ram0 rw"<br />mem为你的sdmem大小，initrd为你的ramdisk加载地址，ramdisk大小，和加载设备。改为你需要的。<br />#define CONFIG_CMDLINE "mem=32M console=ttyS0,115200 initrd=0x20410000,4096000 root=/dev/ram0 rw"<br />保存</p>
				<p>编辑include/linux/autoconf.h里的CMDLINE<br />同include/config/cmdline.h<br />保存</p>
				<p>#make at91rm9200dk_defconfig</p>
				<p>编辑.config<br />CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x20410000,3145728 root=/dev/ram0 rw"<br />同include/config/cmdline.h<br />保存</p>
				<p>#make clean<br />#make zImage<br />#make modules</p>
				<p>#mkdir images<br />#cp arch/arm/boot/zImage images/zImage-2.6.15<br />#cp vmlinux images/vmlinux-2.6.15<br />#cp System.map images/System.map-2.6.15<br />#cp .config images/2.6.15.config</p>
				<p>#make INSTALL_MOD_PATH=images/modules-2.6.15 modules_install<br />copy busybox/examples 下的depmod.pl到内核文件夹<br />#./depmod.pl -b images/modules-2.6.15/lib/modules -k vmlinux</p>
				<p>
						<br />配置busybox /*busybox-1.1.1*/<br />--------------<br />#make menuconfig</p>
				<p>下面是需要编译进busybox的功能选项,其他的可以根据需要自选。<br />General Configuration应该选的选项<br />Show verbose applet usage messages<br />Runtime SUID/SGID configuration via /etc/busybox.conf</p>
				<p>Build Options<br />----------如果选用glibc则不选------<br />Build BusyBox as a static binary (no shared libs)<br />这个选项是一定要选择的,这样才能把busybox编译成静态链接的可执行文件,运行时才独立于其他函数库。否则必需要其他库文件才能运行,在单一个linux内核不能使他正常工作。<br />------------------------------------<br />选择交叉编译环境</p>
				<p>Installation Options<br />Don't use /usr<br />这个选项也一定要选,否则make install 后busybox将安装在原系统的/usr下,这将覆盖掉系统原有的命令。选择这个选项后,make install后会在busybox目录下生成一个叫_install的目录,里面有busybox和指向他的链接。</p>
				<p>其他选项都是一些linux基本命令选项,自己需要哪些命令就编译进去就可以了。</p>
				<p>配置好后退出并保存。</p>
				<p>4,编译并安装busybox</p>
				<p>#make<br />#make install</p>
				<p>编译好后在busybox目录下生成子目录_install,里面的内容:<br />drwxr-xr-x   2 root root     4096 2006-03-30 00:50 bin<br />drwxr-xr-x   2 root root     4096 2006-03-30 00:50 lib<br />lrwxrwxrwx   1 root root       11 2006-03-30 00:50 linuxrc -&gt; bin/busybox<br />drwxr-xr-x   2 root root     4096 2006-03-30 00:50 sbin</p>
				<p>
						<br />制作根文件系统<br />--------------<br />1,基本目录结构<br />#mkdir /mnt/rootfs<br />#cd /mnt/rootfs<br />#mkdir etc usr var tmp proc home root dev<br />#chmod 1777 tmp<br />进入usr<br />#mkdir bin sbin <br />进入var<br />#mkdir lib lock log run tmp<br />#chmod 1777 tmp <br />进入proc<br />#mkdir self self/fd<br />#mkdir boot mnt opt (可选)<br />进入dev<br />#mkdir pts</p>
				<p>其中etc,proc和dev是一定要建的,bin和sbin不用建,因为busybox中已经有了。<br />其他的可以象征性的建几个就可以了。</p>
				<p>拷贝busybox<br />#cp -a 你的目录/_install/* /mnt/rootfs<br />*/<br />2,建立设备文件名</p>
				<p>#cd /mnt/rootfs/dev<br />你可以用mknod手工建立,也可以直接从原系统的/dev目录下拷贝过来。</p>
				<p>手工建立的方法:<br />#ls -l /dev/console<br />crw------- 1 root root 5, 1 11月 30 09:02 /dev/console<br />这样就查看到了console设备的主设备号是5,辅设备号是1,是一个标记为C的字符设备。<br />于是,我们可以用mknod建立一个同样的设备文件:</p>
				<p>#mknod console c 5 1</p>
				<p>但是手工方法建立太麻烦了,通常直接从/dev下把需要的设备文件拷贝过来。<br />这些设备文件是特殊文件,在拷贝时一定要加上-R参数才能拷贝。</p>
				<p>#cp -R /dev/console ./<br />#cp -R /dev/null ./<br />#cp -R /dev/zero ./<br />...</p>
				<p>另外可以代替：<br />#mknod -m 600 mem c 1 1<br />#mknod -m 644 random c 1 8<br />挂载eldk下面的/usr/local/eldk/arm/images的ramdisk_image.gz<br />#gunzip -v ramdisk_image.gz<br />#mkdir -p /mnt/tmp<br />#mount -o loop ramdisk_image /mnt/tmp<br />拷贝/mnt/tmp/dev到/mnt/rootfs/dev<br />#cp -R /mnt/tmp/dev/* /mnt/rootfs/dev<br />*/<br />进入/dev<br />#cp -R cdrom fd0 hda14 hda4 hdb11 hdb19 hdc hdc16 hdc6 hdd13 hdd3 loop2 ram2 /mnt/rootfs/dev<br />#cp -R console fd0H1440 hda15 hda5 hdb12 hdb2 hdc1 hdc17 hdc7 hdd14 hdd4 loop3 tty0 /mnt/rootfs/dev<br />#cp -R fb hda hda16 hda6 hdb13 hdb3 hdc10 hdc18 hdc8 hdd15 hdd5 loop4 tty1 /mnt/rootfs/dev<br />#cp -R fb0 hda1 hda17 hda7 hdb14 hdb4 hdc11 hdc19 hdd hdd16 hdd6 loop5 tty2 /mnt/rootfs/dev<br />#cp -R fb1 hda10 hda18 hda8 hdb15 hdb5 hdc12 hdc2 hdd1 hdd17 hdd7 null tty3 /mnt/rootfs/dev<br />#cp -R fb2 hda11 hda19 hdb hdb16 hdb6 hdc13 hdc3 hdd10 hdd18 hdd8 ram tty4 /mnt/rootfs/dev<br />#cp -R fb3 hda12 hda2 hdb1 hdb17 hdb7 hdc14 hdc4 hdd11 hdd19 initctl ram0 tty5 /mnt/rootfs/dev<br />#cp -R fb4 hda13 hda3 hdb10 hdb18 hdb8 hdc15 hdc5 hdd12 hdd2 loop1 ram1 zero /mnt/rootfs/dev<br />#cp -R usbdev* /mnt/rootfs/dev<br />#mkdir bus<br />#cp -R bus/* /mnt/rootfs/dev/bus/<br />*/<br />如果“没有找到文件”是可以忽略的<br />#cp -R /dev/pts/0 /mnt/rootfs/dev/pts<br />#ln -s ../proc/self/fd fd<br />#ln -s fd/0 stdin<br />#ln -s fd/1 stdout<br />#ln -s fd/2 stderr<br />进入proc/self/fd/<br />#ln -s ../../../dev/pts/0 0<br />#ln -s ../../../dev/pts/0 1<br />#ln -s ../../../dev/pts/0 2</p>
				<p>以下是我认为可以加入的设备名:<br />cdrom fd0 hda14 hda4 hdb11 hdb19 hdc hdc16 hdc6 hdd13 hdd3 loop2 ram2<br />console fd0H1440 hda15 hda5 hdb12 hdb2 hdc1 hdc17 hdc7 hdd14 hdd4 loop3 tty0<br />fb hda hda16 hda6 hdb13 hdb3 hdc10 hdc18 hdc8 hdd15 hdd5 loop4 tty1<br />fb0 hda1 hda17 hda7 hdb14 hdb4 hdc11 hdc19 hdd hdd16 hdd6 loop5 tty2<br />fb1 hda10 hda18 hda8 hdb15 hdb5 hdc12 hdc2 hdd1 hdd17 hdd7 null tty3<br />fb2 hda11 hda19 hdb hdb16 hdb6 hdc13 hdc3 hdd10 hdd18 hdd8 ram tty4<br />fb3 hda12 hda2 hdb1 hdb17 hdb7 hdc14 hdc4 hdd11 hdd19 initctl ram0 tty5<br />fb4 hda13 hda3 hdb10 hdb18 hdb8 hdc15 hdc5 hdd12 hdd2 loop1 ram1 zero</p>
				<p>其中,fd0,hda,ram0,ttyS0,tty1,null,zero,loop1,fb0,fb等是必备的.<br />其它的hda,hda1,hdb等可以根据实际需要决定.但是上表中的选择是比较合理的,即能满足大部分的需要,有没有不用的设备浪费空间。注意,千万不要把/dev下的设备全拷贝过来,那将产生大约420K的/dev目录,这样来说太大了。</p>
				<p>3,建立etc目录下的配置文件</p>
				<p>busybox.conf group inittab motd passwd resolv.conf<br />fstab init.d issue mtab profile shadow modules.conf</p>
				<p>执行：<br />touch busybox.conf group inittab motd passwd resolv.conf<br />touch fstab issue mtab profile shadow modules.conf</p>
				<p>其中init.d是一个目录,从busybox源代码目录下拷贝过来。</p>
				<p>#cp -R 你的目录/examples/bootflopyp/etc/init.d /mnt/rootfs/etc/</p>
				<p>busybox.conf是一个空文件<br />其他文件的内容如下:</p>
				<p>fstab：<br />/dev/fd0 /  ext2  defaults 0 0<br />none  /proc  proc  defaults 0 0<br />none  /dev/pts devpts  mode=0622 0 0</p>
				<p>group：<br />root:x:0:root<br />bin:x:1:root,bin,daemon<br />daemon:x:2:root,bin,daemon<br />sys:x:3:root,bin<br />tty:x:5:<br />disk:x:6:root<br />lp:x:7:daemon<br />mem:x:8:<br />kmem:x:9:<br />ftp:x:50:<br />nobody:x:99:<br />users:x:100:</p>
				<p>inittab：<br />::sysinit:/etc/init.d/rcS<br />::respawn:/sbin/getty 115200 ttyS0<br />::respawn:/bin/sh<br />::askfirst:/bin/sh<br /># Stuff to do when restarting the init process<br />::restart:/sbin/init<br /># Stuff to do before rebooting<br />::ctrlaltdel:/sbin/reboot<br />::shutdown:/bin/umount -a -r<br />::shutdown:/sbin/swapoff -a</p>
				<p>issue：<br />Lianic Arm Linux release 0.1(2006-04-05)</p>
				<p>motd是一个空文件</p>
				<p>mtab是一个空文件</p>
				<p>passwd：<br />root:x:0:0:root:/:/bin/ash<br />bin:x:1:1:bin:/bin:<br />daemon:x:2:2:daemon:/sbin:<br />halt:x:7:0:halt:/sbin:/sbin/halt<br />ftp:x:14:50:FTP User:/<br />nobody:*:99:99:Nobody:/:</p>
				<p>profile：<br /># /etc/profile: system-wide .profile file for the Bourne shells<br />export PS1="[u@h w]$"<br />echo "Done"<br />alias ll='ls -l'11 ll<br />alias du='du -h'<br />alias df='df -h'<br />alias rm='rm -i'<br />echo<br /># Set path<br />PATH=/bin:/sbin:/usr/bin:/usr/sbin<br />export PATH</p>
				<p>resolv.conf：dns域名解析<br />nameserver 127.0.0.1<br />search localhost</p>
				<p>shadow：根据自己的设置<br />root:$2a$05$xyunjFqZ9SVeqkDHWlmQd.hMEeBteWCFQ.yfkmf5DSrihghkwA.w6:13243::::::<br />bin:*:13080::::::<br />daemon:*:13080::::::<br />ftp:*:13080::::::<br />nobody:*:13080::::::</p>
				<p>其中有很多是从原系统的/etc下拷贝过来修改的,如果你是一个具有中等以上水平的linux爱好者,那么应该一看就明白了,当然,你也可以根据自己的需要修改这些文件。其中最重要的是fstab和inittab,busybox内建的init程序用到的inittab文件的语法和一般的不一样,不能直接把原系统/etc下inittab文件拷贝过来。可以把busybox-1.00目录下的示例文件拷贝过来修改用.具体请看busybox的文档。</p>
				<p>init.d下的文件:rcS<br />rcS的内容:<br />#! /bin/sh<br />mount -o remount,rw /</p>
				<p>/sbin/ifconfig lo 127.0.0.1<br />#/sbin/ifconfig eth0 192.168.1.2</p>
				<p>/bin/mount -a<br />&gt;/etc/mtab<br />echo<br />echo "+++++++++++++ Welcom to Lianic Arm Linux ++++++++++++++++++ "<br />echo "+ This is a arm linux system which is running on at91rm9200."<br />echo "+ It contains more than 100 basic Linux commands and tools."<br />echo "+ All these great features are powered by BusyBox 1.1.1 "<br />echo "+ This is a free system tool developed by Edward Lianic. "<br />echo "+ If you have any problem please mailto : <a href="mailto:Lianic@163.com">Lianic@163.com</a> "<br />echo "+ Enjoy!!!"<br />echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ "<br />echo<br />hostname LianicLinux</p>
				<p>可以自己作相应的修改。</p>
				<p>请确保这个文件是可执行的,否则请改成可执行的:<br />#chmod a+x rcS</p>
				<p>4,建立boot目录下文件(可选,建议但内核启动失败的时候可以加载)<br />进入内核文件夹<br />#cp images/zImage-2.6.15 /mnt/rootfs/boot/<br />#cp images/2.6.15.config /mnt/rootfs/boot/</p>
				<p>5,建立lib目录下文件<br />进入内核文件夹<br />#cp -a images/modules-2.6.15/* /mnt/rootfs/<br />进入/usr/local/eldk/arm/lib /*完整glibc链接库*/<br />#cp *-*.so /mnt/rootfs/lib<br />#cp *.so.[*0-9] /mnt/rootfs/lib<br />#cp libSegFault.so libmemusage.so libpcprofile.so /mnt/rootfs/lib<br />#arm-linux-strip /mnt/rootfs/lib/*.so<br />*/<br />如果拷贝了NSS，如libnss_file，libnss_dns，libnss_nis则执行，否则跳过<br />进入etc<br />#touch nsswitch.conf<br />nsswitch.conf：<br /># ---nsswitch.conf---begin<br /># /etc/nsswitch.conf<br />#<br /># An example Name Service Switch config file. This file should be<br /># sorted with the most-used services at the beginning.<br />#<br /># The entry '[NOTFOUND=return]' means that the search for an<br /># entry should stop if the search in the previous entry turned<br /># up nothing. Note that if the search failed due to some other reason<br /># (like no NIS server responding) then the search continues with the<br /># next entry.<br />#<br /># Legal entries are:<br />#<br />#       compat                  Use compatibility setup<br />#       nisplus                 Use NIS+ (NIS version 3)<br />#       nis                     Use NIS (NIS version 2), also called YP<br />#       dns                     Use DNS (Domain Name Service)<br />#       files                   Use the local files<br />#       [NOTFOUND=return]       Stop searching if not found so far<br />#<br /># For more information, please read the nsswitch.conf.5 manual page.<br />#</p>
				<p># passwd: files nis<br /># shadow: files nis<br /># group:  files nis</p>
				<p>passwd: compat<br />group: compat</p>
				<p>hosts: files dns<br />networks: files dns</p>
				<p>services: files<br />protocols: files<br />rpc: files<br />ethers: files<br />netmasks: files<br />netgroup: files<br />publickey: files</p>
				<p>bootparams: files<br />automount: files nis<br />aliases: files<br /># ---nsswitch.conf---end</p>
				<p>以上是Lianic Arm Linux根文件系统的所有内容,他的总大小应该在6.8M左右。</p>
				<p>
						<br />建立ramdisk<br />--------------<br />1建立新的ramdisk大小为8M<br />#dd if=/dev/zero of=ramdisk bs=1k count=8192<br />2格式为ext2<br />#/sbin/mke2fs -F -m 0 ramdisk<br />3禁止check<br />#/sbin/tune2fs -c 0 ramdisk<br />4挂载<br />#mkdir -p /mnt/ramdisk<br />#mount -o loop ramdisk /mnt/ramdisk<br />5拷贝根文件系统<br />#cp -a /mnt/rootfs/* /mnt/ramdisk/<br />6解挂和压缩<br />#umount /mnt/ramdisk/<br />#gzip -v9 ramdisk</p>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/14423.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-07-30 10:08 <a href="http://www.cnitblog.com/zouzheng/articles/14423.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>成功在uclinux下实现拨号，总结经验</title><link>http://www.cnitblog.com/zouzheng/articles/14422.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 30 Jul 2006 01:52:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/14422.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/14422.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/14422.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/14422.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/14422.html</trackback:ping><description><![CDATA[实现s3c2510拨号全过程小结 <br /><br />                                  <br /><br />    摘要： <br /><br />    实现视频服务器的pppoe拨号功能 <br /><br />    平台： <br /><br />    03版的uclinux <br /><br />    s3c2510的芯片 <br /><br />    编译器原来是03版的，后来我改为04版的arm-elf.....2004.sh <br /><br />    一 配置内核 <br /><br />       配置内核，使系统内核支持ppp协议，这是最基本的工作。 <br /><br />       make menuconfig <br /><br />       选择network device support <br /><br />       然后选择下面所有的ppp协议 <br /><br />       退出 <br /><br />    二 安装pppd,pppoe <br /><br />       我的弯路主要就是在这里 <br /><br />       我一开始不知道uclinux的user里本身自带了pppd,rp-pppoe的安装程序，于是到网上down了安装程序进行 <br /><br />       移植，虽然后来经过升级编译完成了移植，但是最好还是用uclinux自带的程序，这样可以避免很多兼容性的 <br /><br />       问题(uclinux自带了很多非常有用的组件，大部分常用的功能都能从user目录下找到)。安装过程 <br /><br />       make menuconfig <br /><br />       customize vendor/user settings <br /><br />       选择 <br /><br />       network applications <br /><br />         pppd <br /><br />         rp-pppoe(别忘了pppd下面还有这个要选择) <br /><br />       Miscellanenous Applicaions <br /><br />         chat   (这个进程在连接上后有用) <br /><br />       好了，配置好内核后，不要急于make,还有个地方要改 <br /><br />       cd user/pppd/ppd/ <br /><br />       修改lcp.c <br /><br />       把 1459行改为:orc=CONFNAK <br /><br />       好了，接下来回到系统目录make dep -&gt;make <br /><br />       如果在编译过程中出现错误应该根据错误提示进行修改，在这里不排出因为编译器版本过低无法编译pppd的原因， <br /><br />       最好使用04版的arm-elf-gcc。 <br /><br />       编译成功后，应该可以在romfs/bin下找到pppd,pppoe,chat这三个我们要生成的东西了。 <br /><br />       网上有资料说还要生成pppstatus,pppdump这两个东西，但我觉得这两个文件不是必要的，如果真的需要就到/user/ppd/ppd里 <br /><br />       手动生成这两个文件然后，拷贝到romfs/bin目录下。 <br /><br />       现在，通过上面的步骤，我们已经在romfs/bin生成了 <br /><br />       生成了pppd,pppoe,chat,pppstatus,pppdump这五个文件了，除此之外我们还需要一些配置文件，主要用来保存拨号的用户名 <br /><br />       和密码。在romfs目录下建一个ppp目录用来存放配置文件，然后把user/pppd/etc.ppp下的chap-secrets,pap-secrets,options <br /><br />       文件拷贝到我们刚才建的那个目录。在chap-secrets,pap-secrets文件里写下我们要用来拨号的用户名和密码，格式如下 <br /><br />       “test” * “password” <br /><br />       好了，现在我们来确认一下pppd能不能正常启动 <br /><br />       我们把生成的image文件download到目标板上 <br /><br />       运行/bin/pppd，如果能够打印出乱码的话，说明pppd已经移植成功了。 <br /><br />       非常不幸，我在 运行/bin/pppd后没能打出乱码，根本无法启动pppd <br /><br />       是什么原因导致pppd退出呢，系统也没有提示，郁闷！在这里搞了很久，为了弄明白系统在执行pppd的时候到底是什么原因 <br /><br />       退出，我决定在busybox里安装一个syslogd用来显示日志内容。 <br /><br />       安装方法很简单，就在vender /user settings里选busybox然后选择syslogd，重新编译。 <br /><br />       好了，现在我的s3c2510有日志可查了，我再次运行/bin/pppd后，出现提示，找不到/dev/ppp <br /><br />       这时我才知道，原来uclinux是不默认建立ppp的设备节点的，要自己手动建立一个ppp节点。 <br /><br />       手动建立一个ppp节点： <br /><br />       进入linux主机下的/dev <br /><br />       file ppp <br /><br />       得知ppp是字符设备，主设备号是108，次设备号是0 <br /><br />       使用下面这个命令生成 <br /><br />       touch /dev/ppp @ppp,c,108,0 <br /><br />       把@ppp,c,108,0烤到romfs/dev下面，重新编译生成image。 <br /><br />       完成了节点的建立后，再在目标板上运行 /bin/pppd,令人兴奋得乱码终于出现了，和主机上的情况一样，在这里我们就 <br /><br />       姑且认为pppd已经移植成功了。 <br /><br />   三  测试环境的建立 <br /><br />        <br /><br />       在我们真刀真枪地使用modern来拨通ISP之前，我们可以先自己用windowXP建立一个PPPOE服务器，然后使用直连线 <br /><br />       把我们的目标板和XP服务器连接起来，在目标板上对XP进行拨号测试。拨号成功后，我们在使用modern拨ISP。当然拨 <br /><br />       XP服务器和拨ISP的方法基本上是一样的。 <br /><br />        <br /><br />       建立XP服务器 <br /><br />         1 下载RASPPPOE_098B组件 <br /><br />         2  右击网上邻居，选属性，点安装，选择协议，添加，从磁盘安装，然后选择RASPPPOE_098B的目录 <br /><br />            安装好组件后，你便可以在本地连接中看到ppp oever ethernet prototal <br />         3, 建立一个基于ppp的传入连接（我们的目标板拨号连接XP的pppoe服务器，靠的就是这个连接） <br /><br />             1）新建网络连接-&gt;选择设置高级连接-&gt;接受传入的连接-&gt;选择REALTEK ....(这是我的网卡)-&gt;允许虚拟连接 <br />             2）在这里你可以选择或新建一个用户用来被客户端拨号使用 <br />             3）一路next <br />   四  开始进行拨号 <br />      1）利用我们在第三步建立的平台来进行拨号测试 <br />          确定目标板已经可以和XP服务器进行通信，（用直连线相连或连在同一局域网） <br />          在目标板上运行测试命令 <br />          /bin/pppd pty  '/bin/pppoe' user frankie &amp;(这里请用你的chap-secrets的用户，当然这个用户必须是在XP服务器上 <br />          有登记的) <br />          非常不幸，在我运行/bin/pppd pty  '/bin/pppoe' user frankie &amp;后，出现了LCP:timeout config-request sending <br />          等一堆错误。 <br />          在这里卡住之后，查了很多资料，好像比较麻烦。无奈之下，我决定改用05版uclinux的rp-pppoe。 <br />          哈哈，想不到05版的东西那么好用，重新编译后，再允许测试命令。 <br />          终于拨号成功了 <br />          ifconfig后也终于出现了ppp0. <br />       2)由于硬件环境限制，暂时不对moden进行直接拨号测试。 <br />五 参考资料 <br />              参考资料全部来自INTERNET <br />六 后记 <br />           出于用户友好性的考虑，我想可以做成用网页的形式来给客户进行拨号，这样是否更有意思呢<br /><img src ="http://www.cnitblog.com/zouzheng/aggbug/14422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-07-30 09:52 <a href="http://www.cnitblog.com/zouzheng/articles/14422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ArmLinux BOOTLOADER全程详解</title><link>http://www.cnitblog.com/zouzheng/articles/13880.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 20 Jul 2006 03:31:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/13880.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/13880.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/13880.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/13880.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/13880.html</trackback:ping><description><![CDATA[<table cellSpacing=2 cellPadding=2 width="100%" border=0>
    <tbody>
        <tr>
            <td>
            <div class=post_content>ArmLinux BOOTLOADER全程详解<br><br>网上关于<strong class=b>Linux</strong>的<strong class=b>BOOTLOADER</strong>文章不少了,但是大都是vivi,blob等比较庞大的程序,读起来不太方便,编译出的文件也比较大,而且更多的是面向开发用的引导代码,做成产品时还要裁减,这一定程度影响了开发速度,对初学者学习开销也比较大,在此分析一种简单的<strong class=b>BOOTLOADER</strong>,是在三星公司提供的2410 <strong class=b>BOOTLOADER</strong>上稍微修改后的结果,编译出来的文件大小不超过4k,希望对大家有所帮助. <br><strong>1.几个重要的概念 <br></strong>COMPRESSED KERNEL and DECOMPRESSED KERNEL <br>压缩后的KERNEL,按照文档资料,现在不提倡使用DECOMPRESSED KERNEL,而要使用COMPRESSED KERNEL,它包括了解压器.因此要在ram分配时给压缩和解压的KERNEL提供足够空间,这样它们不会相互覆盖.当执行指令跳转到 COMPRESSED KERNEL后,解压器就开始工作,如果解压器探测到解压的代码会覆盖掉COMPRESSED KERNEL,那它会直接跳到COMPRESSED KERNEL后存放数据,并且重新定位KERNEL,所以如果没有足够空间,就会出错. <br><br>Jffs2 File System <br>可以使armlinux应用中产生的数据保存在FLASH上,我的板子还没用到这个. <br><br>RAMDISK <br>使用RAMDISK可以使ROOT FILE SYSTEM在没有其他设备的情况下启动.一般有两种加载方式,我就介绍最常用的吧,把COMPRESSED RAMDISK IMAGE放到指定地址,然后由<strong class=b>BOOTLOADER</strong>把这个地址通过启动参数的方式ATAG_INITRD2传递给KERNEL.具体看代码分析. <br><br>启动参数(摘自IBM developer) <br>在调用内核之前，应该作一步准备工作，即：设置 <strong class=b>Linux</strong> 内核的启动参数。<strong class=b>Linux</strong> 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。启动参数标记列表以标记 ATAG_CORE 开始，以标记 ATAG_NONE 结束。每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。数据结构 tag 和 tag_header 定义在 <strong class=b>Linux</strong> 内核源码的include/asm/setup.h 头文件中. <br>在嵌入式 <strong class=b>Linux</strong> 系统中，通常需要由 <strong class=b>BOOTLOADER</strong> 设置的常见启动参数有：ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。 <br>(注)参数也可以用COMMANDLINE来设定,在我的<strong class=b>BOOTLOADER</strong>里,我两种都用了. <br><br><strong>2.开发环境和开发板配置:</strong><br>CPU:S3C2410,BANK6上有64M的SDRAM(两块),BANK0上有32M NOR FLASH,串口当然是逃不掉的.这样,按照数据手册,地址分配如下: <br>0x4000_0000开始是4k的片内DRAM. <br>0x0000_0000开始是32M FLASH 16bit宽度 <br>0x3000_0000开始是64M SDRAM 32bit宽度 <br>注意:控制寄存器中的BANK6和BANK7部分必须相同. <br>0x4000_0000(片内DRAM)存放4k以内的<strong class=b>BOOTLOADER</strong> IMAGE <br>0x3000_0100开始存放启动参数 <br>0x3120_0000 存放COMPRESSED KERNEL IMAGE <br>0x3200_0000 存放COMPRESSED RAMDISK <br>0x3000_8000 指定为DECOMPRESSED KERNEL IMAGE ADDRESS <br>0x3040_0000 指定为DECOMPRESSED RAMDISK IMAGE ADDRESS <br>开发环境:Redhat <strong class=b>Linux</strong>,armgcc toolchain, armlinux KERNEL <br>如何建立armgcc的编译环境:建议使用toolchain,而不要自己去编译armgcc,偶试过好多次,都以失败告终. <br>先下载arm-gcc 3.3.2 toolchain <br>将arm-linux-gcc-3.3.2.tar.bz2 解压到 /toolchain <br># tar jxvf arm-linux-gcc-3.3.2.tar.bz2 <br># mv /usr/local/arm/3.3.2 /toolchain <br>在makefile 中在把arch=arm CROSS_COMPILE设置成toolchain的路径还有就是INCLUDE = -I ../include -I /root/my/usr/local/arm/3.3.2/include.,否则库函数就不能用了 <br><strong>3.启动方式: <br></strong>可以放在FLASH里启动,或者用Jtag仿真器.由于使用NOR FLASH,根据2410的手册,片内的4K DRAM在不需要设置便可以直接使用,而其他存储器必须先初始化,比如告诉memory controller,BANK6里有两块SDRAM,数据宽度是32bit,= =.否则memory control会按照复位后的默认值来处理存储器.这样读写就会产生错误. <br>所以第一步,通过仿真器把执行代码放到0x4000_0000,(在编译的时候,设定TEXT_BASE=0x40000000) <br>第二步,通过 AxD把linux KERNEL IMAGE放到目标地址(SDRAM)中,等待调用 <br>第三步,执行<strong class=b>BOOTLOADER</strong>代码,从串口得到调试数据,引导armlinux <br><br><strong>4.代码分析</strong><br>讲了那么多执行的步骤,是想让大家对启动有个大概印象,接着就是<strong class=b>BOOTLOADER</strong>内部的代码分析了,<strong class=b>BOOTLOADER</strong>文章内容网上很多,我这里精简了下,删除了不必要的功能. <br><strong class=b>BOOTLOADER</strong>一般分为2部分,汇编部分和c语言部分,汇编部分执行简单的硬件初始化,C部分负责复制数据,设置启动参数,串口通信等功能. <br><strong class=b>BOOTLOADER</strong>的生命周期: <br>1. 初始化硬件,比如设置UART(至少设置一个),检测存储器= =. <br>2. 设置启动参数,这是为了告诉内核硬件的信息,比如用哪个启动界面,波特率 = =. <br>3. 跳转到<strong class=b>Linux</strong> KERNEL的首地址. <br>4. 消亡 <br>当然,在引导阶段,象vivi等,都用虚地址,如果你嫌烦的话,就用实地址,都一样. <br>我们来看代码: <br>2410init.s <br>.global _start//开始执行处 <br>_start: <br>//下面是中断向量 <br>b reset @ Supervisor Mode//重新启动后的跳转 <br>⋯⋯ <br>⋯⋯ <br>reset: <br>ldr r0,=WTCON /WTCON地址为53000000,watchdog的控制寄存器 */ <br>ldr r1,=0x0 /*关watchdog*/ <br>str r1,[r0] <br>ldr r0,=INTMSK <br>ldr r1,=0xffffffff /*屏蔽所有中断*/ <br>str r1,[r0] <br>ldr r0,=INTSUBMSK <br>ldr r1,=0x3ff /*子中断也一样*/ <br>str r1,[r0] <br>/*Initialize Ports...for display LED.*/ <br>ldr r0, =GPFCON <br>ldr r1, =0x55aa <br>str r1, [r0] <br>ldr r0, =GPFUP <br>ldr r1, =0xff <br>str r1, [r0] <br>ldr r0,=GPFDAT <br>ldr r1,=POWEROFFLED1 <br>str r1,[r0] <br>/* Setup clock Divider control register <br>* you must configure CLKDIVN before LOCKTIME or MPLL UPLL <br>* because default CLKDIVN 1,1,1 set the SDMRAM Timing Conflictnop <br>* FCLK:HCLK:PCLK = 1:2:4 in this case <br>*/ <br>ldr r0,=CLKDIVN <br>ldr r1,=0x3 <br>str r1,[r0] <br>/*To reduce PLL lock time, adjust the LOCKTIME register. */ <br>ldr r0,=LOCKTIME <br>ldr r1,=0xffffff <br>str r1,[r0] <br>/*Configure MPLL */ <br>ldr r0,=MPLLCON <br>ldr r1,=((M_MDIV&lt;&lt;12)+(M_PDIV&lt;&lt;4)+M_SDIV) //Fin=12MHz,Fout=203MHz <br>str r1,[r0] <br>ldr r1,=GSTATUS2 <br>ldr r10,[r1] <br>tst r10,#OFFRST <br>bne 1000f <br>//以上这段,我没动,就用三星写的了,下面是主要要改的地方 <br>/* MEMORY C0NTROLLER(MC)设置*/ <br>add r0,pc,#MCDATA - (.+8)// r0指向MCDATA地址,那里存放着MC初始化要用到的数据 <br>ldr r1,=BWSCON // r1指向MC控制器寄存器的首地址 <br>add r2,r0,#52 // 复制次数,偏移52字 <br>1: //按照偏移量进行循环复制 <br>ldr r3,[r0],#4 <br>str r3,[r1],#4 <br>cmp r2,r0 <br>bne 1b <br>.align 2 <br>MCDATA: <br>.word (0+(B1_BWSCON&lt;&lt;4)+(B2_BWSCON&lt;&lt;8)+(B3_BWSCON&lt;&lt;12)+(B4_BWSCON&lt;&lt;16)+(B5_BWSCON&lt;&lt;20)+(B6_BWSCON&lt;&lt;24)+(B7_BWSCON&lt;&lt;28)) <br>上面这行就是BWSCON的数据,具体参数意义如下: <br>需要更改设置DW6 和DW7都设置成10,即32bit,DW0 设置成01,即16bit <br>下面都是每个BANK的控制器数据,大都是时钟相关,可以用默认值,设置完MC后,就跳到调用main函数的部分
            <pre><br>.word ((B0_Tacs&lt;&lt;13)+(B0_Tcos&lt;&lt;11)+(B0_Tacc&lt;&lt;8)+(B0_Tcoh&lt;&lt;6)+(B0_Tah&lt;&lt;4)+(B0_Tacp&lt;&lt;2)+(B0_PMC)) <br>.word ((B1_Tacs&lt;&lt;13)+(B1_Tcos&lt;&lt;11)+(B1_Tacc&lt;&lt;8)+(B1_Tcoh&lt;&lt;6)+(B1_Tah&lt;&lt;4)+(B1_Tacp&lt;&lt;2)+(B1_PMC)) <br>.word ((B2_Tacs&lt;&lt;13)+(B2_Tcos&lt;&lt;11)+(B2_Tacc&lt;&lt;8)+(B2_Tcoh&lt;&lt;6)+(B2_Tah&lt;&lt;4)+(B2_Tacp&lt;&lt;2)+(B2_PMC)) <br>.word ((B3_Tacs&lt;&lt;13)+(B3_Tcos&lt;&lt;11)+(B3_Tacc&lt;&lt;8)+(B3_Tcoh&lt;&lt;6)+(B3_Tah&lt;&lt;4)+(B3_Tacp&lt;&lt;2)+(B3_PMC)) <br>.word ((B4_Tacs&lt;&lt;13)+(B4_Tcos&lt;&lt;11)+(B4_Tacc&lt;&lt;8)+(B4_Tcoh&lt;&lt;6)+(B4_Tah&lt;&lt;4)+(B4_Tacp&lt;&lt;2)+(B4_PMC)) <br>.word ((B5_Tacs&lt;&lt;13)+(B5_Tcos&lt;&lt;11)+(B5_Tacc&lt;&lt;8)+(B5_Tcoh&lt;&lt;6)+(B5_Tah&lt;&lt;4)+(B5_Tacp&lt;&lt;2)+(B5_PMC)) <br>.word ((B6_MT&lt;&lt;15)+(B6_Trcd&lt;&lt;2)+(B6_SCAN)) <br>.word ((B7_MT&lt;&lt;15)+(B7_Trcd&lt;&lt;2)+(B7_SCAN)) <br>.word ((REFEN&lt;&lt;23)+(TREFMD&lt;&lt;22)+(Trp&lt;&lt;20)+(Trc&lt;&lt;18)+(Tchr&lt;&lt;16)+REFCNT) <br>.word 0xB2 /* REFRESH Control Register */ <br>.word 0x30 /* BANKSIZE Register : Burst Mode */ <br>.word 0x30 /* SDRAM Mode Register */ <br>.align 2 <br>.global call_main //调用main函数,函数参数都为0 <br>call_main: <br>ldr sp,STACK_START <br>mov fp,#0 /* no previous frame, so fp=0*/ <br>mov a1, #0 /* set argc to 0*/ <br>mov a2, #0 /* set argv to NUL*/ <br>bl main /* call main*/ <br>STACK_START: <br>.word STACK_BASE <br>undefined_instruction: <br>software_interrupt: <br>prefetch_abort: <br>data_abort: <br>not_used: <br>irq: <br>fiq: </pre>
            <br>/*以上是主要的汇编部分,实现了时钟设置,串口设置watchdog关闭,中断关闭功能(如果有需要还可以降频使用),然后转入main*/
            <pre><br>2410init.c file <br>int main(int argc,char **argv) <br>{ <br>u32 test = 0; <br>void (*theKERNEL)(int zero, int arch, unsigned long params_addr) = (void (*)(int, int, unsigned long))RAM_COMPRESSED_KERNEL <br>_BASE; //压缩后的IMAGE地址 <br>int i,k=0; <br>// downPt=(RAM_COMPRESSED_KERNEL_BASE); <br>chkBs=(_RAM_STARTADDRESS);//SDRAM开始的地方 <br>// fromPt=(FLASH_LINUXKERNEL); <br>MMU_EnableICache(); <br>ChangeClockDivider(1,1); // 1:2:4 <br>ChangeMPllValue(M_MDIV,M_PDIV,M_SDIV); //Fin=12MHz FCLK=200MHz <br>Port_Init();//设置I/O端口,在使用com口前,必须调用这个函数,否则通信芯片根本得不到数据 <br>Uart_Init(PCLK, 115200);//PCLK使用默认的200000,拨特率115200 <br>/*******************(检查ram空间)*******************/ <br>Uart_SendString("nt<strong class=b>Linux</strong> S3C2410 Nor <strong class=b>BOOTLOADER</strong>n"); <br>Uart_SendString("ntChecking SDRAM 2410loader.c...n"); <br>for(;chkBs&lt;0x33FA0140;chkBs=chkBs+0x4,test++)// </pre>
            <br>//根据我的经验,最好以一个字节为递增,我们的板子,在256byte递增检测的时候是没问题的,但是 <br>//以1byte递增就出错了,第13跟数据线随几的会冒&#8221;1&#8221;,检测出来是硬件问题,现象如下 <br>//用仿真器下代码测试SDRAM，开始没贴28F128A3J FLASH片子，测试结果很好，但在上了FLASH片子//之后，测试数据（data）为0x00000400 <br>连续成批写入读出时，操作大约1k左右内存空间就会出错，//而且随机。那个出错数据总是变为0x00002400，数据总线10位和13位又没短路 <br>发生。用其他数据//测试比如0x00000200；0x00000800没这问题。dx帮忙。 <br>//至今没有解决,所以我用不了Flash. <br>{ <br>chkPt1 = chkBs; <br>*(u32 *)chkPt1 = test;//写数据 <br>if(*(u32 *)chkPt1==1024))//读数据和写入的是否一样? <br>{ <br>chkPt1 += 4; <br>Led_Display(1); <br>Led_Display(2); <br>Led_Display(3); <br>Led_Display(4); <br>} <br>else <br>goto error; <br>} <br>Uart_SendString("ntSDRAM Check Successful!ntMemory Maping..."); <br>get_memory_map(); <br>//获得可用memory 信息,做成列表,后面会作为启动参数传给KERNEL <br>//所谓内存映射就是指在4GB 物理地址空间中有哪些地址范围被分配用来寻址系统的 RAM 单元。 <br>Uart_SendString("ntMemory Map Successful!n"); <br>//我用仿真器把KERNEL,RAMDISK直接放在SDRAM上,所以下面这段是不需要的,但是如果KERNEL,RAMDISK在FLASH里,那就需要.
            <pre><br>/*******************(copy linux KERNEL)*******************/ <br>Uart_SendString("tLoading KERNEL IMAGE from FLASH... n "); <br>Uart_SendString("tand copy KERNEL IMAGE to SDRAM at 0x31000000n"); <br>Uart_SendString("ttby LEIJUN DONG dongleijun4000@hotmail.com n"); <br>for(k = 0;k &lt; 196608;k++,downPt += 1,fromPt += 1)//3*1024*1024/32linux KERNEL des,src,length=3M <br>* (u32 *)downPt = * (u32 *)fromPt; <br>/*******************(load RAMDISK)*******************/ <br>Uart_SendString("ttloading COMPRESSED RAMDISK...n"); <br>downPt=(RAM_COMPRESSED_RAMDISK_BASE); <br>fromPt=(FLASH_RAMDISK_BASE); <br>for(k = 0;k &lt; 196608;k++,downPt += 1,fromPt += 1)//3*1024*1024/32linux KERNEL des,src,length=3M <br>* (u32 *)downPt = * (u32 *)fromPt; <br>/******jffs2文件系统,在开发中如果用不到FLASH,这段也可以不要********/ <br>Uart_SendString("ttloading jffs2...n"); <br>downPt=(RAM_JFFS2); <br>fromPt=(FLASH_JFFS2); <br>for(k = 0;k &lt; (1024*1024/32);k++,downPt += 1,fromPt += 1) <br>* (u32 *)downPt = * (u32 *)fromPt; <br>Uart_SendString( "Load Success...Run...n "); <br>/*******************(setup param)*******************/ <br>setup_start_tag();//开始设置启动参数 <br>setup_memory_tags();//内存印象 <br>setup_commandline_tag("console=ttyS0,115200n8");//启动命令行 <br>setup_initrd2_tag();//root device <br>setup_RAMDISK_tag();//ramdisk image <br>setup_end_tag(); <br>/*关I-cache */ <br>asm ("mrc p15, 0, %0, c1, c0, 0": "=r" (i)); <br>i &amp;= ~0x1000; <br>asm ("mcr p15, 0, %0, c1, c0, 0": : "r" (i)); <br>/* flush I-cache */ <br>asm ("mcr p15, 0, %0, c7, c5, 0": : "r" (i)); </pre>
            <br>//下面这行就跳到了COMPRESSED KERNEL的首地址 <br>theKERNEL(0, ARCH_NUMBER, (unsigned long *)(RAM_BOOT_PARAMS)); <br>//启动kernel时候,I-cache可以开也可以关,r0必须是0,r1必须是CPU型号 <br>(可以从linux/arch/arm/tools/mach-types中找到),r2必须是参数的物理开始地址
            <pre><br>/*******************END*******************/ <br>error: <br>Uart_SendString("nnPanic SDRAM check error!n"); <br>return 0; <br>} <br>static void setup_start_tag(void) <br>{ <br>params = (struct tag *)RAM_BOOT_PARAMS;//启动参数开始的地址 <br>params-&gt;hdr.tag = ATAG_CORE; <br>params-&gt;hdr.size = tag_size(tag_core); <br>params-&gt;u.core.flags = 0; <br>params-&gt;u.core.pagesize = 0; <br>params-&gt;u.core.rootdev = 0; <br>params = tag_next(params); <br>} <br>static void setup_memory_tags(void) <br>{ <br>int i; <br>for(i = 0; i &lt; NUM_MEM_AREAS; i++) { <br>if(memory_map[i].used) { <br>params-&gt;hdr.tag = ATAG_MEM; <br>params-&gt;hdr.size = tag_size(tag_mem32); <br>params-&gt;u.mem.start = memory_map[i].start; <br>params-&gt;u.mem.size = memory_map[i].len; <br>params = tag_next(params); <br>} <br>} <br>} <br>static void setup_commandline_tag(char *commandline) <br>{ <br>int i = 0; <br>/* skip non-existent command lines so the kernel will still <br>* use its default command line. <br>*/ <br>params-&gt;hdr.tag = ATAG_CMDLINE; <br>params-&gt;hdr.size = 8; <br>//console=ttyS0,115200n8 <br>strcpy(params-&gt;u.cmdline.cmdline, p); <br>params = tag_next(params); <br>} <br>static void setup_initrd2_tag(void) <br>{ <br>/* an ATAG_INITRD node tells the kernel where the compressed <br>* ramdisk can be found. ATAG_RDIMG is a better name, actually. <br>*/ <br>params-&gt;hdr.tag = ATAG_INITRD2; <br>params-&gt;hdr.size = tag_size(tag_initrd); <br>params-&gt;u.initrd.start = RAM_COMPRESSED_RAMDISK_BASE; <br>params-&gt;u.initrd.size = 2047;//k byte <br>params = tag_next(params); <br>} <br>static void setup_ramdisk_tag(void) <br>{ <br>/* an ATAG_RAMDISK node tells the kernel how large the <br>* decompressed ramdisk will become. <br>*/ <br>params-&gt;hdr.tag = ATAG_RAMDISK; <br>params-&gt;hdr.size = tag_size(tag_ramdisk); <br>params-&gt;u.ramdisk.start = RAM_DECOMPRESSED_RAMDISK_BASE; <br>params-&gt;u.ramdisk.size = 7.8*1024; //k byte <br>params-&gt;u.ramdisk.flags = 1; // automatically load ramdisk <br>params = tag_next(params); <br>} <br>static void setup_end_tag(void) <br>{ <br>params-&gt;hdr.tag = ATAG_NONE; <br>params-&gt;hdr.size = 0; <br>} void Uart_Init(int pclk,int baud)//串口是很重要的 <br>{ <br>int i; <br>if(pclk == 0) <br>pclk = PCLK; <br>rUFCON0 = 0x0; //UART channel 0 FIFO control register, FIFO d<a class=keyurl title=ISA href="http://www.dz863.com/interface-circuits/ISA.htm" target=_blank><font color=#5f9ea0>ISA</font></a>ble <br>rUMCON0 = 0x0; //UART chaneel 0 MODEM control register, AFC d<a class=keyurl title=ISA href="http://www.dz863.com/interface-circuits/ISA.htm" target=_blank><font color=#5f9ea0>ISA</font></a>ble <br>//UART0 <br>rULCON0 = 0x3; //Line control register : Normal,No parity,1 stop,8 bits <br>下面这段samsung好象写的不太对,但是我按照Normal,No parity,1 stop,8 bits算出来的确是0x245 <br>// [10] [9] [8] [7] [6] [5] [4] [3:2] [1:0] <br>// Clock Sel, Tx Int, Rx Int, Rx Time Out, Rx err, Loop-back, Send break, Transmit Mode, Receive Mode <br>// 0 1 0 , 0 1 0 0 , 01 01 <br>// PCLK Level Pulse D<a class=keyurl title=ISA href="http://www.dz863.com/interface-circuits/ISA.htm" target=_blank><font color=#5f9ea0>ISA</font></a>ble Generate Normal Normal Interrupt or Polling <br>rUCON0 = 0x245; // Control register <br>rUBRDIV0=( (int)(PCLK/16./ baud) -1 ); //Baud rate divisior register 0 <br>delay(10); <br>} </pre>
            <br>经过以上的折腾,接下来就是kernel的活了.能不能启动kernel,得看你编译kernel的水平了.&nbsp;<br><br>摘自：<a href="http://www.dz863.com/Embedded-Systems-Design/ARM-Microcontroller/ArmLinux-BOOTLOADER.htm"><font color=#5f9ea0>http://www.dz863.com/Embedded-Systems-Design/ARM-Microcontroller/ArmLinux-BOOTLOADER.htm</font></a><br></div>
            <br>
            <div class=post_footer><a class=post_permalink href="http://deven.yculblog.com/post.1083361.html" rel=follow><font color=#5f9ea0>阅读全文</font> </a>/ <a class=post_comments href="http://deven.yculblog.com/post.1083361.html#followups" rel=follow><font color=#5f9ea0>评论</font></a><span class=post_pm>/ <a class=post_pm href="http://www.ycul.com/pm.php?act=edit&amp;u_id=1036269" rel=follow><font color=#5f9ea0>丢小纸条</font></a></span><span class=post_folder>/ 文件夹: <a class=post_folder href="http://deven.yculblog.com/archive.72442.html" rel=follow><font color=#5f9ea0>嵌入开发——Linux系统构建学习笔记</font></a></span></div>
            <br>&nbsp; </td>
        </tr>
        <tr>
            <td>
            <div><a class=post_title href="http://deven.yculblog.com/post.1083352.html" rel=follow><strong><font color=#000080>【摘】Kernel Memory Layout on ARM Linux</font> </strong></a></div>
            <div><span class=post_user><font color=#778899>棉花糖糖主</font> </span><span class=post_time><font color=#999999>@ 2006-02-02 10:25</font> </span></div>
            <br>
            <div class=post_content><br>
            <pre>		Kernel Memory Layout on ARM Linux
            Russell King &lt;rmk@arm.linux.org.uk&gt;
            November 17, 2005 (2.6.15)
            This document describes the virtual memory layout which the Linux
            kernel uses for ARM processors.  It indicates which regions are
            free for platforms to use, and which are used by generic code.
            The ARM CPU is capable of addressing a maximum of 4GB virtual memory
            space, and this must be shared between user space processes, the
            kernel, and hardware devices.
            As the ARM architecture matures, it becomes necessary to reserve
            certain regions of VM space for use for new facilities; therefore
            this document may reserve more VM space over time.
            Start		End		Use
            --------------------------------------------------------------------------
            ffff8000	ffffffff	copy_user_page / clear_user_page use.
            For SA11xx and Xscale, this is used to
            setup a minicache mapping.
            ffff1000	ffff7fff	Reserved.
            Platforms must not use this address range.
            ffff0000	ffff0fff	CPU vector page.
            The CPU vectors are mapped here if the
            CPU supports vector relocation (control
            register V bit.)
            ffc00000	fffeffff	DMA memory mapping region.  Memory returned
            by the dma_alloc_xxx functions will be
            dynamically mapped here.
            ff000000	ffbfffff	Reserved for future expansion of DMA
            mapping region.
            VMALLOC_END	feffffff	Free for platform use, recommended.
            VMALLOC_END must be aligned to a 2MB
            boundary.
            VMALLOC_START	VMALLOC_END-1	vmalloc() / ioremap() space.
            Memory returned by vmalloc/ioremap will
            be dynamically placed in this region.
            VMALLOC_START may be based upon the value
            of the high_memory variable.
            PAGE_OFFSET	high_memory-1	Kernel direct-mapped RAM region.
            This maps the platforms RAM, and typically
            maps all platform RAM in a 1:1 relationship.
            TASK_SIZE	PAGE_OFFSET-1	Kernel module space
            Kernel modules inserted via insmod are
            placed here using dynamic mappings.
            00001000	TASK_SIZE-1	User space mappings
            Per-thread mappings are placed here via
            the mmap() system call.
            00000000	00000fff	CPU vector page / null pointer trap
            CPUs which do not support vector remapping
            place their vector page here.  NULL pointer
            dereferences by both the kernel and user
            space are also caught via this mapping.
            Please note that mappings which collide with the above areas may result
            in a non-bootable kernel, or may cause the kernel to (eventually) panic
            at run time.
            Since future CPUs may impact the kernel mapping layout, user programs
            must not access any memory which is not mapped inside their 0x0001000
            to TASK_SIZE address range.  If they wish to access these areas, they
            must set up their own mappings using open() and mmap().
            </pre>
            <br><br>摘自：<a href="http://www.arm.linux.org.uk/developer/memory.txt"><font color=#5f9ea0>http://www.arm.linux.org.uk/developer/memory.txt</font></a><br></div>
            <br>
            <div class=post_footer><a class=post_permalink href="http://deven.yculblog.com/post.1083352.html" rel=follow><font color=#5f9ea0>阅读全文</font> </a>/ <a class=post_comments href="http://deven.yculblog.com/post.1083352.html#followups" rel=follow><font color=#5f9ea0>评论</font></a><span class=post_pm>/ <a class=post_pm href="http://www.ycul.com/pm.php?act=edit&amp;u_id=1036269" rel=follow><font color=#5f9ea0>丢小纸条</font></a></span><span class=post_folder>/ 文件夹: <a class=post_folder href="http://deven.yculblog.com/archive.72443.html" rel=follow><font color=#5f9ea0>嵌入开发——Linux内核分析学习笔记</font></a></span></div>
            <br>&nbsp; </td>
        </tr>
        <tr>
            <td>
            <div><a class=post_title href="http://deven.yculblog.com/post.1083334.html" rel=follow><strong><font color=#000080>【摘】Booting ARM Linux</font> </strong></a></div>
            <div><span class=post_user><font color=#778899>棉花糖糖主</font> </span><span class=post_time><font color=#999999>@ 2006-02-02 10:11</font> </span></div>
            <br>
            <div class=post_content>
            <center>
            <h3>Booting ARM Linux</h3>
            </center><ins>Author: Russell King<br>Initial date: May 18, 2002<br>Revision: <font color=#0000ff>1 - 17 September 2004</font><font color=#ff0000>2 - 30 September 2004</font></ins>
            <p>In order to boot ARM Linux, you require a boot loader, which is a small program that runs before the main kernel. The boot loader is expected to initialise various devices, and eventually call the Linux kernel, passing information to the kernel. </p>
            <p>Essentially, the boot loader should provide (as a minimum) the following: </p>
            <ol>
                <li><a href="http://www.arm.linux.org.uk/developer/booting.php#1"><font color=#5f9ea0>Setup and initialise the RAM.</font> </a>
                <li><a href="http://www.arm.linux.org.uk/developer/booting.php#2"><font color=#5f9ea0>Initialise one serial port.</font> </a>
                <li><a href="http://www.arm.linux.org.uk/developer/booting.php#3"><font color=#5f9ea0>Detect the machine type.</font> </a>
                <li><a href="http://www.arm.linux.org.uk/developer/booting.php#4"><font color=#5f9ea0>Setup the kernel tagged list.</font> </a>
                <li><a href="http://www.arm.linux.org.uk/developer/booting.php#5"><font color=#5f9ea0>Call the kernel image.</font> </a></li>
            </ol>
            <a name=1></a><u>1. Setup and initialise RAM</u>
            <p>Existing boot loaders: MANDATORY<br>New boot loaders: MANDATORY </p>
            <p>The boot loader is expected to find and initialise all RAM that the kernel will use for volatile data storage in the system. It performs this in a machine dependent manner. (It may use internal algorithms to automatically locate and size all RAM, or it may use knowledge of the RAM in the machine, or any other method the boot loader designer sees fit.) </p>
            <p><a name=2></a><u>2. Initialise one serial port</u> </p>
            <p>Existing boot loaders: OPTIONAL, RECOMMENDED<br>New boot loaders: OPTIONAL, RECOMMENDED </p>
            <p>The boot loader should initialise and enable one serial port on the target. This allows the kernel serial driver to automatically detect which serial port it should use for the kernel console (generally used for debugging purposes, or communication with the target.) </p>
            <p>As an alternative, the boot loader can pass the relevant 'console=' option to the kernel via the tagged lists specifing the port, and serial format options as described in </p>
            <p>linux/Documentation/kernel-parameters.txt. </p>
            <p><a name=3></a><u>3. Detect the machine type</u> </p>
            <p>Existing boot loaders: OPTIONAL<br>New boot loaders: MANDATORY </p>
            <p>The boot loader should detect the machine type its running on by some method. Whether this is a hard coded value or some algorithm that looks at the connected hardware is beyond the scope of this document. The boot loader must ultimately be able to provide a MACH_TYPE_xxx value to the kernel. (see linux/arch/arm/tools/mach-types). </p>
            <p><a name=4></a><u>4. Setup the kernel tagged list</u> </p>
            <p>Existing boot loaders: OPTIONAL, HIGHLY RECOMMENDED<br>New boot loaders: MANDATORY </p>
            <p>The boot loader must create and initialise the kernel tagged list. A valid tagged list starts with ATAG_CORE and ends with ATAG_NONE. The ATAG_CORE tag may or may not be empty. An empty ATAG_CORE tag has the size field set to '2' (0x00000002). The ATAG_NONE must set the size field to zero. </p>
            <p>Any number of tags can be placed in the list. It is undefined whether a repeated tag appends to the information carried by the previous tag, or whether it replaces the information in its entirety; some tags behave as the former, others the latter. </p>
            <p>The boot loader must pass at a minimum the size and location of the system memory, and root filesystem location. Therefore, the minimum tagged list should look: </p>
            <p>&nbsp;</p>
            <pre>	+-----------+
            base -&gt;	| ATAG_CORE |  |
            +-----------+  |
            | ATAG_MEM  |  | increasing address
            +-----------+  |
            | ATAG_NONE |  |
            +-----------+  v
            </pre>
            The tagged list should be stored in system RAM.
            <p>The tagged list must be placed in a region of memory where neither the kernel decompressor nor initrd 'bootp' program will overwrite it. The recommended placement is in the first 16KiB of RAM. </p>
            <p><a name=5></a><u>5. Calling the kernel image</u></p>
            <p>Existing boot loaders: MANDATORY<br>New boot loaders: MANDATORY </p>
            <p>There are two options for calling the kernel zImage. If the zImage is stored in flash, and is linked correctly to be run from flash, then it is legal for the boot loader to call the zImage in flash directly. </p>
            <p>The zImage may also be placed in system RAM (at any location) and called there. Note that the kernel uses 16K of RAM below the image to store page tables. The recommended placement is 32KiB into RAM. </p>
            <p>In either case, the following conditions must be met: </p>
            <ul>
                <li>CPU register settings
                <ul>
                    <li>r0 = 0.
                    <li>r1 = machine type number discovered in (3) above.
                    <li>r2 = physical address of tagged list in system RAM. </li>
                </ul>
                <li>CPU mode
                <ul>
                    <li>All forms of interrupts must be disabled (IRQs and FIQs.)
                    <li>The CPU must be in SVC mode. (A special exception exists for Angel.) </li>
                </ul>
                <li>Caches, MMUs
                <ul>
                    <li>The MMU must be off.
                    <li>Instruction cache may be on or off.
                    <li>Data cache must be off <ins class=rev1>and must not contain any stale data.</ins></li>
                </ul>
                </li>
            </ul>
            <li><ins class=rev2>Devices</ins>
            <ul>
                <li><ins class=rev2>DMA to/from devices should be quiesced.</ins></li>
            </ul>
            <li>The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image. </li>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/13880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-07-20 11:31 <a href="http://www.cnitblog.com/zouzheng/articles/13880.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>