﻿<?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>C++博客-&lt;H1&gt;&lt;font color=orange&gt;JonsenElizee&lt;/font&gt;&lt;/H1&gt;-随笔分类-Linux.Basic</title><link>http://www.cppblog.com/JonsenElizee/category/13505.html</link><description>Software Developing Blog
&lt;BR&gt;
&lt;BR&gt;
"An idea is fragile . It can be killed by a scornful smile or a yawn .It can be mound down by irony and scared to death by a cold look." 
&lt;BR&gt; 
"Most cultures throughout human history have not liked creative individuals .They ignore them or kill them.It is a very efficient way of stopping creativity."  
&lt;BR&gt; 
&lt;BR&gt;
------Advertising boss Charles Browe and Howard Gardner ,professor at Harvard </description><language>zh-cn</language><lastBuildDate>Sat, 22 Jan 2011 10:04:48 GMT</lastBuildDate><pubDate>Sat, 22 Jan 2011 10:04:48 GMT</pubDate><ttl>60</ttl><item><title>High Memory In The Linux Kernel</title><link>http://www.cppblog.com/JonsenElizee/archive/2011/01/22/139105.html</link><dc:creator>JonsenElizee</dc:creator><author>JonsenElizee</author><pubDate>Sat, 22 Jan 2011 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/JonsenElizee/archive/2011/01/22/139105.html</guid><wfw:comment>http://www.cppblog.com/JonsenElizee/comments/139105.html</wfw:comment><comments>http://www.cppblog.com/JonsenElizee/archive/2011/01/22/139105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/JonsenElizee/comments/commentRss/139105.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/JonsenElizee/services/trackbacks/139105.html</trackback:ping><description><![CDATA[<h2 class="title">Feature: High Memory In The Linux Kernel</h2>
<div class="author">Submitted by <a  href="http://kerneltrap.org/user/1903" title="View user profile.">Amit Shah</a></div>
<div class="date">on February 21, 2004 - 6:02am</div>
<div class="terms">
<ul class="links inline">
    <li class="taxonomy_term_478 first"><a  href="http://kerneltrap.org/taxonomy/term/478" rel="tag" title="">high memory</a></li>
    <li class="taxonomy_term_187"><a  href="http://kerneltrap.org/Linux" rel="tag" title="">Linux</a></li>
    <li class="taxonomy_term_250"><a  href="http://kerneltrap.org/memory" rel="tag" title="">memory</a></li>
    <li class="taxonomy_term_479"><a  href="http://kerneltrap.org/taxonomy/term/479" rel="tag" title="">memory management</a></li>
    <li class="taxonomy_term_1233"><a  href="http://kerneltrap.org/PAE" rel="tag" title="">PAE</a></li>
    <li class="taxonomy_term_477"><a  href="http://kerneltrap.org/taxonomy/term/477" rel="tag" title="">RAM</a></li>
    <li class="taxonomy_term_37 last"><a  href="http://kerneltrap.org/taxonomy/term/37" rel="tag" title="">Linux feature article</a></li>
</ul>
</div>
<p>As RAM increasingly becomes a
commodity, the prices drop and computer users are able to buy more.
32-bit archictectures face certain limitations in regards to accessing
these growing amounts of RAM. To better understand the problem and the
various solutions, we begin with an overview of Linux memory
management. Understanding how basic memory management works, we are
better able to define the problem, and finally to review the various
solutions.</p>
<p>This article was written by examining the Linux 2.6 kernel source code for the x86 architecture types.</p>
<hr>
<strong>Overview of Linux memory management</strong>
<p>32-bit architectures can reference 4 GB of physical memory (2^32).
Processors that have an MMU (Memory Management Unit) support the
concept of virtual memory: page tables are set up by the kernel which
map "virtual addresses" to "physical addresses"; this basically means
that each process can access 4 GB of memory, thinking it's the only
process running on the machine (much like multi-tasking, in which each
process is made to think that it's the only process executing on a CPU).</p>
<p>The virtual address to physical address mappings are done by the
kernel. When a new process is "fork()"ed, the kernel creates a new set
of page tables for the process. The addresses referenced within a
process in user-space are virtual addresses. They do not necessarily
map directly to the same physical address. The virtual address is
passed to the MMU (Memory Management Unit of the processor) which
converts it to the proper physical address based on the tables set up
by the kernel. Hence, two processes can refer to memory address
0x08329, but they would refer to two different locations in memory.</p>
<p>The Linux kernel splits the 4 GB virtual address space of a process
in two parts: 3 GB and 1 GB. The lower 3 GB of the process virtual
address space is accessible as the user-space virtual addresses and the
upper 1 GB space is reserved for the kernel virtual addresses. This is
true for all processes.</p>
<pre>							      <br>      +----------+ 4 GB				      <br>      |          |     				      <br>      |	         |     				      <br>      |	         |     				      <br>      | Kernel   |     				      <br>      |          |     			       +----------+ <br>      |	Virtual  |     			       |          |<br>      |          |     			       |          |<br>      | Space    |     			       | High     |<br>      |          |     			       |          |<br>      | (1 GB)   |     			       | Memory   |<br>      |          |     			       |          |<br>      |	         |     			       | (unused) |<br>      +----------+ 3 GB	   		       +----------+ 1 GB<br>      |          |     	   		       |          |<br>      |          |     	   		       |          |<br>      |	         |     	   		       |          |<br>      |          |     			       | Kernel   |<br>      |          |     			       |          |<br>      |          |     			       | Physical |<br>      |          |     			       |          |<br>      |User-space|     			       | Space    |<br>      |	       	 |     			       |      	  |<br>      | Virtual  |     			       |          |<br>      |          |     			       |          |<br>      | Space    |     			       |          |<br>      |          |     			       |          |    	<br>      | (3 GB)   |     			       +----------+ 0 GB<br>      |          |     				      	 <br>      |          |     			       	 Physical <br>      |          |     			          Memory <br>      |          |     				      	 <br>      |          |     				      	 <br>      |          |     				      	 <br>      |          |     				      	 <br>      |          |     				      	 <br>      +----------+ 0 GB					 <br>			 					 <br>       	Virtual	 					 <br>       	Memory 	 					 <br></pre>
<p>The kernel virtual area (3 - 4 GB address space) maps to the
first 1 GB of physical RAM. The 3 GB addressable RAM available to each
process is mapped to the available physical RAM.</p>
<p><strong>The Problem</strong></p>
<p>So, the basic problem here is, the kernel can just address 1 GB of
virtual addresses, which can translate to a maximum of 1 GB of physical
memory. This is because the kernel directly maps all available kernel
virtual space addresses to the available physical memory.</p>
<p><strong>Solutions</strong></p>
<p>There are some solutions which address this problem:</p>
<ol>
    <li>2G / 2G, 1G / 3G split</li>
    <li>HIGHMEM solution for using up to 4 GB of memory</li>
    <li>HIGHMEM solution for using up to 64 GB of memory</li>
</ol>
<p><strong>1. 2G / 2G, 1G / 3G split</strong></p>
<p>Instead of splitting the virtual address space the traditional way
of 3G / 1G (3 GB for user-space, 1 GB for kernel space), third-party
patches exist to split the virtual address space 2G / 2G or 1G / 3G.
The 1G / 3G split is a bit extreme in that you can map up to 3 GB of
physical memory, but user-space applications cannot grow beyond 1 GB.
It could work for simple applications; but if one has more than 3 GB of
physical RAM, he / she won't run simple applications on it, right?</p>
<p>The 2G / 2G split seems to be a balanced approach to using RAM more
than 1 GB without using the HIGHMEM patches. However, server
applications like databases always want as much virtual addressing
space as possible; so this approach may not work in those scenarios.</p>
<p>There's a patch for 2.4.23 that includes a config-time option of
selecting the user / kernel split values by Andrea Arcangeli. It is
available at <a  href="http://www.kernel.org/pub/linux/kernel/people/andrea/kernels/v2.4/2.4.23aa1/00_3.5G-address-space-5">his kernel page</a>. It's a simple patch and making it work on 2.6 should not be too difficult.</p>
<p>Before looking at solutions 2 &amp; 3, let's take a look at some more Linux Memory Management issues.</p>
<p><strong>Zones</strong></p>
<p>In Linux, the memory available from all banks is classified into
"nodes". These nodes indicate how much memory each bank has. This
classification is mainly useful for NUMA architectures, but it's also
used for UMA architectures, where the number of nodes is just 1.</p>
<p>Memory in each node is divided into "zones". The zones currently defined are ZONE_DMA, ZONE_NORMAL and ZONE_HIGHMEM.</p>
<p>ZONE_DMA is used by some devices for data transfer and is mapped in the lower physical memory range (up to 16 MB).</p>
<p>Memory in the ZONE_NORMAL region is mapped by the kernel in the
upper region of the linear address space. Most operations can only take
place in ZONE_NORMAL; so this is the most performance critical zone.
ZONE_NORMAL goes from 16 MB to 896 MB.</p>
<p>To address memory from 1 GB onwards, the kernel has to map pages from high memory into ZONE_NORMAL.</p>
<p>Some area of memory is reserved for storing several kernel data
structures that store information about the memory map and page tables.
This on x86 is 128 MB. Hence, of the 1 GB physical memory the kernel
can access, 128MB is reserved. This means that the kernel virtual
address in this 128 MB is not mapped to physical memory. This leaves a
maximum of 896 MB for ZONE_NORMAL. So, even if one has 1 GB of physical
RAM, just 896 MB will be actually available.</p>
<p>Back to the solutions:</p>
<p><strong>2. HIGHMEM solution for using up to 4 GB of memory</strong></p>
<p>Since Linux can't access memory which hasn't been directly mapped
into its address space, to use memory &gt; 1 GB, the physical pages
have to be mapped in the kernel virtual address space first. This means
that the pages in ZONE_HIGHMEM have to be mapped in ZONE_NORMAL before
they can be accessed.</p>
<p>The reserved space which we talked about earlier (in case of x86,
128 MB) has an area in which pages from high memory are mapped into the
kernel address space.</p>
<p>To create a permanent mapping, the "kmap" function is used. Since
this function may sleep, it may not be used in interrupt context. Since
the number of permanent mappings is limited (if not, we could've
directly mapped all the high memory in the address space), pages mapped
this way should be "kunmap"ped when no longer needed.</p>
<p>Temporary mappings can be created via "kmap_atomic". This function
doesn't block, so it can be used in interrupt context. "kunmap_atomic"
un-maps the mapped high memory page. A temporary mapping is only
available as long as the next temporary mapping. However, since the
mapping and un-mapping functions also disable / enable preemption, it's
a bug to not kunmap_atomic a page mapped via kmap_atomic.</p>
<p><strong>3. HIGHMEM solution for using 64 GB of memory</strong></p>
<p>This is enabled via the PAE (Physical Address Extension) extension
of the PentiumPro processors. PAE addresses the 4 GB physical memory
limitation and is seen as Intel's answer to AMD 64-bit and AMD x86-64.
PAE allows processors to access physical memory up to 64 GB (36 bits of
address bus). However, since the virtual address space is just 32 bits
wide, each process can't grow beyond 4 GB. The mechanism used to access
memory from 4 GB to 64 GB is essentially the same as that of accessing
the 1 GB - 4 GB RAM via the HIGHMEM solution discussed above.</p>
<p><strong>Should I enable CONFIG_HIGHMEM for my 1 GB RAM system?</strong></p>
<p>It is advised to not enable CONFIG_HIGHMEM in the kernel to utilize
the extra 128 MB you get for your 1 GB RAM system. I/O Devices cannot
directly address high memory from PCI space, so bounce buffers have to
be used. Plus the virtual memory management and paging costs come with
extra mappings. For details on bounce buffers, refer to Mel Gorman's
documentation (link below).</p>
<p><strong>For more information, see</strong></p>
<ul>
    <li>Andrea Arcangeli's <a  href="http://strasbourg.linuxfr.org/jl3/features-2.3-2.html"> article on the original HIGHMEM patches in Linux 2.3</a>
    </li>
    <li>Mel Gorman's <a  href="http://www.skynet.ie/%7Emel/projects/vm/guide/html/understand/">VM Documentation for the 2.4 Linux Memory Management subsystem</a></li>
    <li><a  href="http://lxr.linux.no/source/?v=2.6.1">Linux kernel sources</a></li>
</ul><img src ="http://www.cppblog.com/JonsenElizee/aggbug/139105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/JonsenElizee/" target="_blank">JonsenElizee</a> 2011-01-22 10:44 <a href="http://www.cppblog.com/JonsenElizee/archive/2011/01/22/139105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux lib</title><link>http://www.cppblog.com/JonsenElizee/archive/2010/10/01/128163.html</link><dc:creator>JonsenElizee</dc:creator><author>JonsenElizee</author><pubDate>Thu, 30 Sep 2010 18:10:00 GMT</pubDate><guid>http://www.cppblog.com/JonsenElizee/archive/2010/10/01/128163.html</guid><wfw:comment>http://www.cppblog.com/JonsenElizee/comments/128163.html</wfw:comment><comments>http://www.cppblog.com/JonsenElizee/archive/2010/10/01/128163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/JonsenElizee/comments/commentRss/128163.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/JonsenElizee/services/trackbacks/128163.html</trackback:ping><description><![CDATA[<a title="How does linux lib work?"  href="http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/">How does linux lib work?</a><br><br><br>
<p><a name="dynamicloading">用 Linux 进行动态加载</a></p>
<p>Linux 并不会自动为给定程序加载和链接库，而是与应用程序本身共享该控制权。这个过程就称为<em>动态加载</em>。使用动态加载，应用程序能够先指定要加载的库，然后将该库作为一个可执行文件来使用（即调用其中的函数）。但是正如您在前面所了解到的，用于动态加载的共享库与标准共享库（ELF 共享对象）无异。事实上，<code>ld-linux</code> 动态链接器作为 ELF 加载器和解释器，仍然会参与到这个过程中。</p>
<p>动态加载（Dynamic Loading，DL）API 就是为了动态加载而存在的，它允许共享库对用户空间程序可用。尽管非常小，但是这个 API 提供了所有需要的东西，而且很多困难的工作是在后台完成的。表 1 展示了这个完整的 API。</p>
<br><a name="table1"><strong>表 1. Dl API</strong></a><br>
<table class="data-table-1" summary="DL api" width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <th scope="col">函数</th>
            <th scope="col">描述</th>
        </tr>
        <tr>
            <th class="tb-row" scope="row">dlopen</th>
            <td>使对象文件可被程序访问</td>
        </tr>
        <tr>
            <th class="tb-row" scope="row">dlsym</th>
            <td>获取执行了 <code>dlopen</code> 函数的对象文件中的符号的地址</td>
        </tr>
        <tr>
            <th class="tb-row" scope="row">dlerror</th>
            <td>返回上一次出现错误的字符串错误</td>
        </tr>
        <tr>
            <th class="tb-row" scope="row">dlclose</th>
            <td>关闭目标文件</td>
        </tr>
    </tbody>
</table>
<p>该过程首先是调用 <code>dlopen</code>，提供要访问的文件对象和模式。调用 <code>dlopen</code> 的结果是稍候要使用的对象的句柄。<code>mode</code> 参数通知动态链接器何时执行再定位。有两个可能的值。第一个是 <code>RTLD_NOW</code>，它表明动态链接器将会在调用 <code>dlopen</code> 时完成所有必要的再定位。第二个可选的模式是 <code>RTLD_LAZY</code>，它只在需要时执行再定位。这是通过在内部使用动态链接器重定向所有尚未再定位的请求来完成的。这样，动态链接器就能够在请求时知晓何时发生了新的引用，而且再定位可以正常进行。后面的调用无需重复再定位过程。</p>
<p>还可以选择另外两种模式，它们可以按位 <code>OR</code> 到 <code>mode</code> 参数中。<code>RTLD_LOCAL</code> 表明其他任何对象都无法使加载的共享对象的符号用于再定位过程。如果这正是您想要的的话（例如，为了让共享的对象能够调用原始进程映像中的符号），那就使用 <code>RTLD_GLOBAL</code> 吧。</p>
<p><code>dlopen</code> 函数还会自动解析共享库中的依赖项。这样，如果您打开了一个依赖于其他共享库的对象，它就会自动加载它们。函数返回一个句柄，该句柄用于后续的 API 调用。<code>dlopen</code> 的原型为：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">#include &lt;dlfcn.h&gt;<br><br>void *dlopen( const char *file, int mode );<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>有了 ELF 对象的句柄，就可以通过调用 <code>dlsym</code> 来识别这个对象内的符号的地址了。该函数采用一个符号名称，如对象内的一个函数的名称。返回值为对象符号的解析地址：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">void *dlsym( void *restrict handle, const char *restrict name );<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>如果调用该 API 时发生了错误，可以使用 <code>dlerror</code> 函数返回一个表示此错误的人类可读的字符串。该函数没有参数，它会在发生前面的错误时返回一个字符串，在没有错误发生时返回 NULL：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">char *dlerror();<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>最后，如果无需再调用共享对象的话，应用程序可以调用 <code>dlclose</code> 来通知操作系统不再需要句柄和对象引用了。它完全是按引用来计数的，所以同一个共享对象的多个用户相互间不会发生冲突（只要还有一个用户在使用它，它就会待在内存中）。任何通过已关闭的对象的 <code>dlsym</code> 解析的符号都将不再可用。</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">char *dlclose( void *handle );<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<br>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" height="6" width="8" border="0"></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%"><br>
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" height="16" width="16" border="0"><br></td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name="dlexample">动态加载示例</a></p>
<p>了
解了 API 之后，下面让我们来看一看 DL API 的例子。在这个应用程序中，您主要实现了一个
shell，它允许操作员来指定库、函数和参数。换句话说，也就是用户能够指定一个库并调用该库（先前未链接于该应用程序的）内的任意一个函数。首先使用
DL API 来解析该库中的函数，然后使用用户定义的参数（用来发送结果）来调用它。清单 2 展示了完整的应用程序。</p>
<br><a name="list2"><strong>清单 2. 使用 DL API 的 Shell</strong></a><br>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">	<br>#include &lt;stdio.h&gt;<br>#include &lt;dlfcn.h&gt;<br>#include &lt;string.h&gt;<br><br>#define MAX_STRING      80<br><br><br>void invoke_method( char *lib, char *method, float argument )<br>{<br>  void *dl_handle;<br>  float (*func)(float);<br>  char *error;<br><br>  /* Open the shared object */<br>  dl_handle = dlopen( lib, RTLD_LAZY );<br>  if (!dl_handle) {<br>    printf( "!!! %s\n", dlerror() );<br>    return;<br>  }<br><br>  /* Resolve the symbol (method) from the object */<br>  func = dlsym( dl_handle, method );<br>  error = dlerror();<br>  if (error != NULL) {<br>    printf( "!!! %s\n", error );<br>    return;<br>  }<br><br>  /* Call the resolved method and print the result */<br>  printf("  %f\n", (*func)(argument) );<br><br>  /* Close the object */<br>  dlclose( dl_handle );<br><br>  return;<br>}<br><br><br>int main( int argc, char *argv[] )<br>{<br>  char line[MAX_STRING+1];<br>  char lib[MAX_STRING+1];<br>  char method[MAX_STRING+1];<br>  float argument;<br><br>  while (1) {<br><br>    printf("&gt; ");<br><br>    line[0]=0;<br>    fgets( line, MAX_STRING, stdin);<br><br>    if (!strncmp(line, "bye", 3)) break;<br><br>    sscanf( line, "%s %s %f", lib, method, &amp;argument);<br><br>    invoke_method( lib, method, argument );<br><br>  }<br><br>}<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>要构建这个应用程序，需要通过 GNU Compiler Collection（GCC）使用如下的编译行。选项 <code>-rdynamic</code> 用来通知链接器将所有符号添加到动态符号表中（目的是能够通过使用 <code>dlopen</code> 来实现向后跟踪）。<code>-ldl</code> 表明一定要将 <code>dllib</code> 链接于该程序。</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">gcc -rdynamic -o dl dl.c -ldl<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>再回到 <a href="http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/#list2">清单 2</a>，<code>main</code> 函数仅充当解释器，解析来自输入行的三个参数（库名、函数名和浮点参数）。如果出现 <code>bye</code> 的话，应用程序就会退出。否则的话，这三个参数就会传递给使用 DL API 的 <code>invoke_method</code> 函数。</p>
<p>首先调用 <code>dlopen</code> 来访问目标文件。如果返回 NULL 句柄，表示无法找到对象，过程结束。否则的话，将会得到对象的一个句柄，可以进一步询问对象。然后使用 <code>dlsym</code> API 函数，尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针，或者是得到一个 NULL 并返回一个错误。</p>
<p>在
ELF
对象中解析了符号后，下一步就只需要调用函数。要注意一下这个代码和前面讨论的动态链接的差别。在这个例子中，您强行将目标文件中的符号地址用作函数指
针，然后调用它。而在前面的例子是将对象名作为函数，由动态链接器来确保符号指向正确的位置。虽然动态链接器能够为您做所有麻烦的工作，但这个方法会让您
构建出极其动态的应用程序，它们可以再运行时被扩展。</p>
<p>调用 ELF 对象中的目标函数后，通过调用 <code>dlclose</code> 来关闭对它的访问。</p>
<p>清
单 3 展示了一个如何使用这个测试程序的例子。在这个例子中，首先编译程序而后执行它。接着调用了 math
库（libm.so）中的几个函数。完成演示后，程序现在能够用动态加载来调用共享对象（库）中的任意函数了。这是一个很强大的功能，通过它还能够给程序
扩充新的功能。</p>
<br><a name="list3"><strong>清单 3. 使用简单的程序来调用库函数</strong></a><br>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">	<br>mtj@camus:~/dl$ gcc -rdynamic -o dl dl.c -ldl<br>mtj@camus:~/dl$ ./dl<br>&gt; libm.so cosf 0.0<br>  1.000000<br>&gt; libm.so sinf 0.0<br>  0.000000<br>&gt; libm.so tanf 1.0<br>  1.557408<br>&gt; bye<br>mtj@camus:~/dl$<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<br>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"><br><img alt="" src="http://www.ibm.com/i/c.gif" height="6" width="8" border="0"></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%"><br>
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" height="16" width="16" border="0"><br></td>
                        <td valign="top" align="right"><a href="http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name="tools">工具</a></p>
<p>Linux 提供了很多种查看和解析 ELF 对象（包括共享库）的工具。其中最有用的一个当属 <code>ldd</code> 命令，您可以使用它来发送共享库依赖项。例如，在 <code>dl</code> 应用程序上使用 <code>ldd</code> 命令会显示如下内容：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mtj@camus:~/dl$ ldd dl<br>        linux-gate.so.1 =&gt;  (0xffffe000)<br>        libdl.so.2 =&gt; /lib/tls/i686/cmov/libdl.so.2 (0xb7fdb000)<br>        libc.so.6 =&gt; /lib/tls/i686/cmov/libc.so.6 (0xb7eac000)<br>        /lib/ld-linux.so.2 (0xb7fe7000)<br>mtj@camus:~/dl$<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><code>ldd</code> 所告诉您的是：该 ELF 映像依赖于 linux-gate.so（一个特殊的共享对象，它处理系统调用，它在文件系统中无关联文件）、libdl.so（DL API）、GNU <code>C</code> 库（libc.so）以及 Linux 动态加载器（因为它里面有共享库依赖项）。</p>
<p><code>readelf</code> 命令是一个有很多特性的实用程序，它让您能够解析和读取 ELF 对象。<code>readelf</code> 有一个有趣的用途，就是用来识别对象内可再定位的项。对于我们这个简单的程序来说（<a href="http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/#list2">清单 2</a> 展示的程序），您可以看到需要再定位的符号为：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mtj@camus:~/dl$ readelf -r dl<br><br>Relocation section '.rel.dyn' at offset 0x520 contains 2 entries:<br> Offset     Info    Type            Sym.Value  Sym. Name<br>08049a3c  00001806 R_386_GLOB_DAT    00000000   __gmon_start__<br>08049a78  00001405 R_386_COPY        08049a78   stdin<br><br>Relocation section '.rel.plt' at offset 0x530 contains 8 entries:<br> Offset     Info    Type            Sym.Value  Sym. Name<br>08049a4c  00000207 R_386_JUMP_SLOT   00000000   dlsym<br>08049a50  00000607 R_386_JUMP_SLOT   00000000   fgets<br>08049a54  00000b07 R_386_JUMP_SLOT   00000000   dlerror<br>08049a58  00000c07 R_386_JUMP_SLOT   00000000   __libc_start_main<br>08049a5c  00000e07 R_386_JUMP_SLOT   00000000   printf<br>08049a60  00001007 R_386_JUMP_SLOT   00000000   dlclose<br>08049a64  00001107 R_386_JUMP_SLOT   00000000   sscanf<br>08049a68  00001907 R_386_JUMP_SLOT   00000000   dlopen<br>mtj@camus:~/dl$<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>从这个列表中，您可以看到各种各样的需要再定位（到 libc.so）的 <code>C</code> 库调用，包括对 DL API（libdl.so）的调用。函数
<code>__libc_start_main</code> 是一个
<code>C</code> 库函数，它优先于程序的
<code>main</code> 函数（一个提供必要初始化的 shell）而被调用。</p>
<p>其他操作对象文件的实用程序包括：<code>objdump</code>，它展示了关于对象文件的信息；<code>nm</code>，它列出来自对象文件（包括调试信息）的符号。还可以将 EFL 程序作为参数，直接调用 Linux 动态链接器，从而手动启动映像：</p>
<table width="80%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">mtj@camus:~/dl$ /lib/ld-linux.so.2 ./dl<br>&gt; libm.so expf 0.0<br>  1.000000<br>&gt;<br></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>另外，可以使用 ld-linux.so 的 <code>--list</code> 选项来罗列 ELF 映像的依赖项（<code>ldd</code> 命令也如此）。切记，它仅仅是一个用户空间程序，是由内核在需要时引导的。</p>
<br><img src ="http://www.cppblog.com/JonsenElizee/aggbug/128163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/JonsenElizee/" target="_blank">JonsenElizee</a> 2010-10-01 02:10 <a href="http://www.cppblog.com/JonsenElizee/archive/2010/10/01/128163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>