﻿<?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++博客-iniwf-随笔分类-嵌入式</title><link>http://www.cppblog.com/iniwf/category/9822.html</link><description>风是温柔的，雨是伤心的，云是快乐的，月是多情的，爱是迷失的，恋是醉人的，情是难忘的，天是长久的，地是永恒的</description><language>zh-cn</language><lastBuildDate>Mon, 19 Apr 2010 18:51:00 GMT</lastBuildDate><pubDate>Mon, 19 Apr 2010 18:51:00 GMT</pubDate><ttl>60</ttl><item><title>内核反编译学习笔记3</title><link>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sun, 18 Apr 2010 11:22:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/112924.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/112924.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/112924.html</trackback:ping><description><![CDATA[<p>&nbsp;</p>
<p>本节主要看全局变量和局部变量，程序越来越长，可以跳开查看。</p>
<p>全局变量在程序开始定义赋值的话，存放在Data块，Data块可以通过静态反汇编获得。<br>局部变量定义在函数内部，使用的时候需要类似sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h，开辟空间存放</p>
<p>需要掌握：<br>静态反汇编工具<br>变量存放地点<br>sub</p>
<p>简单了解：<br>读取全局变量的方法：1，获取全局变量存放地址。2，偏移量与实际内存地址关系。</p>
<p><br>不需了解：<br>w32asm反汇编以后，需要复制其中内容的，先保存为alf文件，再用文本读取程序打开。</p>
<p><br>所用程序：bz4</p>
<p><br>#include &lt;ntddk.h&gt;</p>
<p>ULONG au1,au2;<br>ULONG au3 = 7;<br>ULONG au4 = 9;</p>
<p>ULONG MyAdd1(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> return u1+u2;</p>
<p>}<br>&nbsp;<wbr><br>ULONG MyAdd2(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> ULONG u3;<br>&nbsp;<wbr>&nbsp;<wbr> u3 = u1+u2;<br>&nbsp;<wbr>&nbsp;<wbr> return u3;&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr></p>
<p>}</p>
<p>ULONG MyAdd3(ULONG u1,ULONG u2)<br>{<br>&nbsp;<wbr>&nbsp;<wbr> ULONG u3,u4,u5,u6;<br>&nbsp;<wbr>&nbsp;<wbr> u3 = u1+u2;<br>&nbsp;<wbr>&nbsp;<wbr> u4 = u3+u1;<br>&nbsp;<wbr>&nbsp;<wbr> u5 = u1;<br>&nbsp;<wbr>&nbsp;<wbr> u6 = u1+u3;<br>&nbsp;<wbr>&nbsp;<wbr> return u3+u4+u5+u6;&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr></p>
<p>}</p>
<p><br>VOID DriverUnload(PDRIVER_OBJECT driver)<br>{<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("unload&#8230;\r\n");<br>}</p>
<p><br>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)<br>{<br>&nbsp;<wbr>ULONG x1 = 5;<br>&nbsp;<wbr>ULONG x2 = 8;<br>&nbsp;<wbr>ULONG x3 ;<br>&nbsp;<wbr><br>&nbsp;<wbr><br>#if DBG<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> _asm int 3<br>#endif<br>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>au1 = MyAdd1(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au1 Result:%d\n!",au1);<br>&nbsp;<wbr><br>&nbsp;<wbr>au2 = MyAdd2(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au2 Result:%d\n!",au2);<br>&nbsp;<wbr><br>&nbsp;<wbr>x3 = MyAdd3(x1,x2);&nbsp;<wbr> //使用自定义函数，反汇编看看结果<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("Result:%d\n!",x3);<br>&nbsp;<wbr><br>&nbsp;<wbr>DbgPrint("au3 Result:%d\n!",au3);<br>&nbsp;<wbr>DbgPrint("au4 Result:%d\n!",au4);<br>&nbsp;<wbr><br>&nbsp;<wbr>driver-&gt;DriverUnload = DriverUnload;<br>&nbsp;<wbr>return STATUS_SUCCESS;<br>}</p>
<p>接下来要用w32asm和windbg反汇编，看其中对应关系，都是从55F到5B1，显然一一对应，只是前面地址有偏移。<br>我们知道，Data Offset是Data数据段，存放的是全局变量。本程序全局变量是：<br>ULONG au3 = 7;<br>ULONG au4 = 9;<br>显然我们dd 00000700是看不到数据的，要dd f84f7700，等我们运行起来的时候，看看是不是</p>
<p><br>w32asm反汇编：</p>
<p><br>Code Offset = 00000480, Code Size = 00000200<br>Data Offset = 00000700, Data Size = 00000080<br>...............</p>
<p>:0001055F 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx</p>
<p>* Possible StringData Ref from Code Obj -&gt;"au1 Result:%d<br>!"<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:00010560 6850060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010650</p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:00010565 E888000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:0001056A 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008<br>:0001056D 8B45FC&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov eax, dword ptr [ebp-04]<br>:00010570 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push eax<br>:00010571 8B4DF8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov ecx, dword ptr [ebp-08]<br>:00010574 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push ecx<br>:00010575 E836FFFFFF&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call 000104B0<br>:0001057A A310070100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov dword ptr [00010710], eax<br>:0001057F 8B1510070100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov edx, dword ptr [00010710]<br>:00010585 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx<br>:00010586 6840060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010640<br></p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:0001058B E862000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:00010590 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008<br>:00010593 8B45FC&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov eax, dword ptr [ebp-04]<br>:00010596 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push eax<br>:00010597 8B4DF8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov ecx, dword ptr [ebp-08]<br>:0001059A 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push ecx<br>:0001059B E830FFFFFF&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call 000104D0<br>:000105A0 8945F4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov dword ptr [ebp-0C], eax<br>:000105A3 8B55F4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov edx, dword ptr [ebp-0C]<br>:000105A6 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push edx</p>
<p>* Possible StringData Ref from Code Obj -&gt;"Result:%d<br>!"<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:000105A7 6830060100&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push 00010630</p>
<p>* Reference To: ntoskrnl.DbgPrint, Ord:0030h<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>:000105AC E841000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> Call 000105F2<br>:000105B1 83C408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add esp, 00000008</p>
<p>&nbsp;<wbr></p>
<p><br>主函数中主要反汇编代码，也就是调用几个自定义函数的部分：</p>
<p>&nbsp;<wbr>&nbsp;<wbr> 57 f84f755f 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f7560 6850764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7650)<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f7565 e888000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 57 f84f756a 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f756d 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7570 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7571 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7574 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f7575 e836ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!MyAdd2 (f84f74b0)<br>&nbsp;<wbr>&nbsp;<wbr> 59 f84f757a a310774ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [bz4!au2 (f84f7710)],eax<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f757f 8b1510774ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [bz4!au2 (f84f7710)]<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7585 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7586 6840764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7640)<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f758b e862000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 61 f84f7590 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7593 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7596 50&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f7597 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f759a 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f759b e830ffffff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!MyAdd3 (f84f74d0)<br>&nbsp;<wbr>&nbsp;<wbr> 63 f84f75a0 8945f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-0Ch],eax<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a3 8b55f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp-0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a6 52&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75a7 6830764ff8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> offset bz4! ?? ::FNODOBFM::`string' (f84f7630)<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75ac e841000000&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> call&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> bz4!DbgPrint (f84f75f2)<br>&nbsp;<wbr>&nbsp;<wbr> 65 f84f75b1 83c408&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,8</p>
<p><br>三个自定义函数</p>
<p>kd&gt; uf bz4!myadd1<br>bz4!MyAdd1 [d:\mydriver\bz4\bz4.c @ 9]:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7490 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7492 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 9 f84f7493 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84f7495 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 10 f84f7498 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84f749b 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 12 f84f749c c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>kd&gt; uf bz4!myadd2<br>bz4!MyAdd2 [d:\mydriver\bz4\bz4.c @ 15]:<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 15 f84f74b5 51&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74b6 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74b9 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 17 f84f74bc 8945fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],eax<br>&nbsp;<wbr>&nbsp;<wbr> 18 f84f74bf 8b45fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c2 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c4 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 21 f84f74c5 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8<br>kd&gt; uf bz4!myadd3<br>bz4!MyAdd3 [d:\mydriver\bz4\bz4.c @ 24]:<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d0 8bff&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edi,edi<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d2 55&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> push&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d3 8bec&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp,esp<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d5 83ec10&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74d8 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74db 03450c&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 26 f84f74de 8945f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-8],eax<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e1 8b4df8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e4 034d08&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ecx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 27 f84f74e7 894dfc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-4],ecx<br>&nbsp;<wbr>&nbsp;<wbr> 28 f84f74ea 8b5508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> edx,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 28 f84f74ed 8955f0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-10h],edx<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f0 8b4508&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp+8]<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f3 0345f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 29 f84f74f6 8945f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> dword ptr [ebp-0Ch],eax<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74f9 8b45f8&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-8]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74fc 0345fc&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-4]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f74ff 0345f0&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-10h]<br>&nbsp;<wbr>&nbsp;<wbr> 30 f84f7502 0345f4&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> add&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> eax,dword ptr [ebp-0Ch]<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7505 8be5&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> mov&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,ebp<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7507 5d&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pop&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ebp<br>&nbsp;<wbr>&nbsp;<wbr> 33 f84f7508 c20800&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ret&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 8</p>
<p>///////////////////////////////////////////</p>
<p>//终于等到分析了<br>先看下全局变量 ：<br>ULONG au3 = 7;<br>ULONG au4 = 9;</p>
<p>看程序中Data存放的:</p>
<p>kd&gt; dd f84f7700<br>f84f7700&nbsp;<wbr> 00000007 00000009 f84de439 07b21bc6</p>
<p>没错，以后要查看全局变量的值，先反汇编，获得Data的偏移量，dd 地址 就可以看见了。</p>
<p>/////////////////////</p>
<p>kd&gt; uf bz4!myadd3<br>bz4!MyAdd3 [d:\mydriver\bz4\bz4.c @ 24]:<br>.......<br>&nbsp;<wbr>&nbsp;<wbr> 24 f84f74d5 83ec10&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> sub&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> esp,10h<br>.......<br>&nbsp;<wbr>&nbsp;<wbr> 局部变量开辟空间。<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr><br>&nbsp;<wbr>好了，简单赋值，调用就玩到这里，接下来是.....&nbsp;<wbr></p>
<p><br></p>
<img src ="http://www.cppblog.com/iniwf/aggbug/112924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2010-04-18 19:22 <a href="http://www.cppblog.com/iniwf/archive/2010/04/18/112924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>djyos 嵌入式实时操作系统</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:29:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91889.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91889.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91889.html</trackback:ping><description><![CDATA[<a href="http://www.djyos.com/">http://www.djyos.com/</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-08-01 14:29 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源项目：MaxWit Linux 、g-bios</title><link>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Sat, 01 Aug 2009 06:27:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/91888.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/91888.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/91888.html</trackback:ping><description><![CDATA[<a href="http://www.maxwit.com/msg.php/22.html">http://www.maxwit.com/msg.php/22.html</a>
<img src ="http://www.cppblog.com/iniwf/aggbug/91888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-08-01 14:27 <a href="http://www.cppblog.com/iniwf/archive/2009/08/01/91888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下PCI设备驱动程序开发</title><link>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Thu, 02 Jul 2009 03:19:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/89053.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/89053.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/89053.html</trackback:ping><description><![CDATA[转自<a href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html">http://www.ibm.com/developerworks/cn/linux/l-pci/index.html</a><br><br>
<p>2004 年 3 月 09 日</p>
<blockquote>PCI是一种广泛采用的总线标准，它提供了许多优于其它总线标准（如EISA）的新特性，目前已经成为计算机系统中应用最为广泛，并且最为通用的总线标准。Linux的内核能较好地支持PCI总线，本文以Intel 386体系结构为主，探讨了在Linux下开发PCI设备驱动程序的基本框架。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=1><span class=atitle>一、PCI总线系统体系结构</span></a></p>
<p>PCI是外围设备互连（Peripheral Component Interconnect）的简称，作为一种通用的总线接口标准，它在目前的计算机系统中得到了非常广泛的应用。PCI提供了一组完整的总线接口规范，其目的是描述如何将计算机系统中的外围设备以一种结构化和可控化的方式连接在一起，同时它还刻画了外围设备在连接时的电气特性和行为规约，并且详细定义了计算机系统中的各个不同部件之间应该如何正确地进行交互。 </p>
<p>无论是在基于Intel芯片的PC机中，或是在基于Alpha芯片的工作站上，PCI毫无疑问都是目前使用最广泛的一种总线接口标准。同旧式的ISA总线不同，PCI将计算机系统中的总线子系统与存储子系统完全地分开，CPU通过一块称为PCI桥（PCI-Bridge）的设备来完成同总线子系统的交互，如图1所示。 </p>
<br><a name=N10045><strong>图1 PCI子系统的体系结构</strong></a><br><img alt="" src="http://www.ibm.com/developerworks/cn/linux/l-pci/images/1.jpg"> <br>
<p>由于使用了更高的时钟频率，因此PCI总线能够获得比ISA总线更好的整体性能。PCI总线的时钟频率一般在25MHz到33MHz范围内，有些甚至能够达到66MHz或者133MHz，而在64位系统中则最高能达到266MHz。尽管目前PCI设备大多采用32位数据总线，但PCI规范中已经给出了64位的扩展实现，从而使PCI总线能够更好地实现平台无关性，现在PCI总线已经能够用于IA-32、Alpha、PowerPC、SPARC64和IA-64等体系结构中。 </p>
<p>PCI总线具有三个非常显著的优点，使得它能够完成最终取代ISA总线这一历史使命： <br>
<ul>
    <li>在计算机和外设间传输数据时具有更好的性能；
    <li>能够尽量独立于具体的平台；
    <li>可以很方便地实现即插即用。 </li>
</ul>
<p>&#160;</p>
<p>图2是一个典型的基于PCI总线的计算机系统逻辑示意图，系统的各个部分通过PCI总线和PCI-PCI桥连接在一起。从图中不难看出，CPU和RAM需要通过PCI桥连接到PCI总线0（即主PCI总线），而具有PCI接口的显卡则可以直接连接到主PCI总线上。PCI-PCI桥是一个特殊的PCI设备，它负责将PCI总线0和PCI总线1（即从PCI主线）连接在一起，通常PCI总线1称为PCI-PCI桥的下游（downstream），而PCI总线0则称为PCI-PCI桥的上游（upstream）。图中连接到从PCI总线上的是SCSI卡和以太网卡。为了兼容旧的ISA总线标准，PCI总线还可以通过PCI-ISA桥来连接ISA总线，从而能够支持以前的ISA设备。图中ISA总线上连接着一个多功能I/O控制器，用于控制键盘、鼠标和软驱。 </p>
<br><a name=N10069><strong>图2 PCI系统示意图</strong></a><br><img alt="" src="http://www.ibm.com/developerworks/cn/linux/l-pci/images/2.jpg"> <br>
<p>在此我只对PCI总线系统体系结构作了概括性介绍，如果读者想进一步了解，David A Rusling在The Linux Kernel（http://tldp.org/LDP/tlk/dd/pci.html）中对Linux的PCI子系统有比较详细的介绍。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="35" sizcache="2">
    <tbody sizset="35" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="36" sizcache="2">
    <tbody sizset="37" sizcache="2">
        <tr align=right sizset="37" sizcache="2">
            <td sizset="37" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="37" sizcache="2">
                <tbody sizset="37" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=2><span class=atitle>二、Linux驱动程序框架</span></a></p>
<p>Linux将所有外部设备看成是一类特殊文件，称之为&#8220;设备文件&#8221;，如果说系统调用是Linux内核和应用程序之间的接口，那么设备驱动程序则可以看成是Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节，使得应用程序可以像操作普通文件一样来操作外部设备。 </p>
<p><a name=N10080><span class=smalltitle><strong><font face=Arial>1. 字符设备和块设备</font></strong></span></a></p>
<p>Linux抽象了对硬件的处理，所有的硬件设备都可以像普通文件一样来看待：它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作，而驱动程序的主要任务也就是要实现这些系统调用函数。Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示，例如，系统中的第一个IDE硬盘使用/dev/hda表示。每个设备文件对应有两个设备号：一个是主设备号，标识该设备的种类，也标识了该设备所使用的驱动程序；另一个是次设备号，标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致，否则用户进程将无法访问到设备驱动程序。 </p>
<p>在Linux操作系统下有两类主要的设备文件：一类是字符设备，另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备，在对字符设备发出读写请求时，实际的硬件I/O紧接着就发生了，一般来说字符设备中的缓存是可有可无的，而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区，当用户进程对设备进行读写请求时，驱动程序先查看缓冲区中的内容，如果缓冲区中的数据能满足用户的要求就返回相应的数据，否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的，其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来，PCI卡通常都属于字符设备。 </p>
<p>所有已经注册（即已经加载了驱动程序）的硬件设备的主设备号可以从/proc/devices文件中得到。使用mknod命令可以创建指定类型的设备文件，同时为其分配相应的主设备号和次设备号。例如，下面的命令： </p>
<p sizset="38" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="38" sizcache="2">
    <tbody sizset="38" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>[root@gary root]# mknod  /dev/lp0  c  6  0
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>将建立一个主设备号为6，次设备号为0的字符设备文件/dev/lp0。当应用程序对某个设备文件进行系统调用时，Linux内核会根据该设备文件的设备类型和主设备号调用相应的驱动程序，并从用户态进入到核心态，再由驱动程序判断该设备的次设备号，最终完成对相应硬件的操作。 </p>
<p><a name=N10099><span class=smalltitle><strong><font face=Arial>2. 设备驱动程序接口</font></strong></span></a></p>
<p>Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口，这是通过include/linux/fs.h中的数据结构file_operations来完成的： </p>
<p sizset="39" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="39" sizcache="2">
    <tbody sizset="39" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>struct file_operations {
            struct module *owner;
            loff_t (*llseek) (struct file *, loff_t, int);
            ssize_t (*read) (struct file *, char *, size_t, loff_t *);
            ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
            int (*readdir) (struct file *, void *, filldir_t);
            unsigned int (*poll) (struct file *, struct poll_table_struct *);
            int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
            int (*mmap) (struct file *, struct vm_area_struct *);
            int (*open) (struct inode *, struct file *);
            int (*flush) (struct file *);
            int (*release) (struct inode *, struct file *);
            int (*fsync) (struct file *, struct dentry *, int datasync);
            int (*fasync) (int, struct file *, int);
            int (*lock) (struct file *, int, struct file_lock *);
            ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
            ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
            ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
            unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
            <!-- code sample is too wide -->};
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>当应用程序对设备文件进行诸如open、close、read、write等操作时，Linux内核将通过file_operations结构访问驱动程序提供的函数。例如，当应用程序对设备文件执行读操作时，内核将调用file_operations结构中的read函数。 </p>
<p><a name=N100AC><span class=smalltitle><strong><font face=Arial>2. 设备驱动程序模块</font></strong></span></a></p>
<p>Linux下的设备驱动程序可以按照两种方式进行编译，一种是直接静态编译成内核的一部分，另一种则是编译成可以动态加载的模块。如果编译进内核的话，会增加内核的大小，还要改动内核的源文件，而且不能动态地卸载，不利于调试，所有推荐使用模块方式。 </p>
<p>从本质上来讲，模块也是内核的一部分，它不同于普通的应用程序，不能调用位于用户态下的C或者C++库函数，而只能调用Linux内核提供的函数，在/proc/ksyms中可以查看到内核提供的所有函数。 </p>
<p>在以模块方式编写驱动程序时，要实现两个必不可少的函数init_module( )和cleanup_module( )，而且至少要包含&lt;linux/krernel.h&gt;和&lt;linux/module.h&gt;两个头文件。在用gcc编译内核模块时，需要加上-DMODULE -D__KERNEL__ -DLINUX这几个参数，编译生成的模块（一般为.o文件）可以使用命令insmod载入Linux内核，从而成为内核的一个组成部分，此时内核会调用模块中的函数init_module( )。当不需要该模块时，可以使用rmmod命令进行卸载，此进内核会调用模块中的函数cleanup_module( )。任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。 </p>
<p><a name=N100BB><span class=smalltitle><strong><font face=Arial>3. 设备驱动程序结构</font></strong></span></a></p>
<p>了解设备驱动程序的基本结构（或者称为框架），对开发人员而言是非常重要的，Linux的设备驱动程序大致可以分为如下几个部分：驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。 </p>
<ul>
    <li><strong>驱动程序的注册与注销</strong>
    <p>向系统增加一个驱动程序意味着要赋予它一个主设备号，这可以通过在驱动程序的初始化过程中调用register_chrdev( )或者register_blkdev( )来完成。而在关闭字符设备或者块设备时，则需要通过调用unregister_chrdev( )或unregister_blkdev( )从内核中注销设备，同时释放占用的主设备号。 </p>
    <li><strong>设备的打开与释放</strong>
    <p>打开设备是通过调用file_operations结构中的函数open( )来完成的，它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中，open( )通常需要完成下列工作： </p>
    <ol>
        <li>检查设备相关错误，如设备尚未准备好等。
        <li>如果是第一次打开，则初始化硬件设备。
        <li>识别次设备号，如果有必要则更新读写操作的当前位置指针f_ops。
        <li>分配和填写要放在file-&gt;private_data里的数据结构。
        <li>使用计数增1。 </li>
    </ol>
    <p>释放设备是通过调用file_operations结构中的函数release( )来完成的，这个设备方法有时也被称为close( )，它的作用正好与open( )相反，通常要完成下列工作： </p>
    <ol>
        <li>使用计数减1。
        <li>释放在file-&gt;private_data中分配的内存。
        <li>如果使用计算为0，则关闭设备。 </li>
    </ol>
    <li><strong>设备的读写操作</strong>
    <p>字符设备的读写操作相对比较简单，直接使用函数read( )和write( )就可以了。但如果是块设备的话，则需要调用函数block_read( )和block_write( )来进行数据读写，这两个函数将向设备请求表中增加读写请求，以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作的，因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据，或者需要执行写操作将数据写入设备，那么就要执行真正的数据传输，这是通过调用数据结构blk_dev_struct中的函数request_fn( )来完成的。 </p>
    <li><strong>设备的控制操作</strong>
    <p>除了读写操作外，应用程序有时还需要对设备进行控制，这可以通过设备驱动程序中的函数ioctl( )来完成。ioctl( )的用法与具体设备密切关联，因此需要根据设备的实际情况进行具体分析。 </p>
    <li><strong>设备的中断和轮询处理</strong> <br>
    <p>对于不支持中断的硬件设备，读写时需要轮流查询设备状态，以便决定是否继续进行数据传输。如果设备支持中断，则可以按中断方式进行操作。 </p>
    </li>
</ul>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="40" sizcache="2">
    <tbody sizset="40" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="41" sizcache="2">
    <tbody sizset="42" sizcache="2">
        <tr align=right sizset="42" sizcache="2">
            <td sizset="42" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="42" sizcache="2">
                <tbody sizset="42" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=3><span class=atitle>三、PCI驱动程序实现</span></a></p>
<p><a name=N1011D><span class=smalltitle><strong><font face=Arial>1. 关键数据结构</font></strong></span></a></p>
<p>PCI设备上有三种地址空间：PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间，其中I/O空间和存储空间提供给设备驱动程序使用，而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行初始化，配置好所有的PCI设备，包括中断号以及I/O基址，并在文件/proc/pci中列出所有找到的PCI设备，以及这些设备的参数和属性。 </p>
<p>Linux驱动程序通常使用结构（struct）来表示一种设备，而结构体中的变量则代表某一具体设备，该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同种设备，每个设备之间用次设备号进行区分，如果采用结构数据来代表所有能由该驱动程序驱动的设备，那么就可以简单地使用数组下标来表示次设备号。 </p>
<p>在PCI驱动程序中，下面几个关键数据结构起着非常核心的作用： </p>
<ul sizset="43" sizcache="2">
    <li sizset="43" sizcache="2"><strong>pci_driver</strong>
    <p>这个数据结构在文件include/linux/pci.h里，这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的，其中最主要的是用于识别设备的id_table结构，以及用于检测设备的函数probe( )和卸载设备的函数remove( )： </p>
    <p sizset="43" sizcache="2">
    <table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="43" sizcache="2">
        <tbody sizset="43" sizcache="1">
            <tr>
                <td class=code-outline>
                <pre class=displaycode>struct pci_driver {
                struct list_head node;
                char *name;
                const struct pci_device_id *id_table;
                int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);
                void (*remove) (struct pci_dev *dev);
                int  (*save_state) (struct pci_dev *dev, u32 state);
                int  (*suspend)(struct pci_dev *dev, u32 state);
                int  (*resume) (struct pci_dev *dev);
                int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
                };
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></p>
    <li sizset="44" sizcache="2"><strong>pci_dev</strong>
    <p>这个数据结构也在文件include/linux/pci.h里，它详细描述了一个PCI设备几乎所有的硬件信息，包括厂商ID、设备ID、各种资源等： </p>
    <p sizset="44" sizcache="2">
    <table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="44" sizcache="2">
        <tbody sizset="44" sizcache="1">
            <tr>
                <td class=code-outline>
                <pre class=displaycode>struct pci_dev {
                struct list_head global_list;
                struct list_head bus_list;
                struct pci_bus  *bus;
                struct pci_bus  *subordinate;
                void        *sysdata;
                struct proc_dir_entry *procent;
                unsigned int    devfn;
                unsigned short  vendor;
                unsigned short  device;
                unsigned short  subsystem_vendor;
                unsigned short  subsystem_device;
                unsigned int    class;
                u8      hdr_type;
                u8      rom_base_reg;
                struct pci_driver *driver;
                void        *driver_data;
                u64     dma_mask;
                u32             current_state;
                unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];
                unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];
                unsigned int    irq;
                struct resource resource[DEVICE_COUNT_RESOURCE];
                struct resource dma_resource[DEVICE_COUNT_DMA];
                struct resource irq_resource[DEVICE_COUNT_IRQ];
                char        name[80];
                char        slot_name[8];
                int     active;
                int     ro;
                unsigned short  regs;
                int (*prepare)(struct pci_dev *dev);
                int (*activate)(struct pci_dev *dev);
                int (*deactivate)(struct pci_dev *dev);
                };
                </pre>
                </td>
            </tr>
        </tbody>
    </table>
    <br></p>
    </li>
</ul>
<p><a name=N1014F><span class=smalltitle><strong><font face=Arial>2. 基本框架</font></strong></span></a></p>
<p>在用模块方式实现PCI设备驱动程序时，通常至少要实现以下几个部分：初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。下面给出一个典型的PCI设备驱动程序的基本框架，从中不难体会到这几个关键模块是如何组织起来的。 </p>
<p sizset="45" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="45" sizcache="2">
    <tbody sizset="45" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>/* 指明该驱动程序适用于哪一些PCI设备 */
            static struct pci_device_id demo_pci_tbl [] __initdata = {
            {PCI_VENDOR_ID_DEMO, PCI_DEVICE_ID_DEMO,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEMO},
            {0,}
            };
            /* 对特定PCI设备进行描述的数据结构 */
            struct demo_card {
            unsigned int magic;
            /* 使用链表保存所有同类的PCI设备 */
            struct demo_card *next;
            /* ... */
            }
            /* 中断处理模块 */
            static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
            {
            /* ... */
            }
            /* 设备文件操作接口 */
            static struct file_operations demo_fops = {
            owner:      THIS_MODULE,   /* demo_fops所属的设备模块 */
            read:       demo_read,    /* 读设备操作*/
            write:      demo_write,    /* 写设备操作*/
            ioctl:      demo_ioctl,    /* 控制设备操作*/
            mmap:       demo_mmap,    /* 内存重映射操作*/
            open:       demo_open,    /* 打开设备操作*/
            release:    demo_release    /* 释放设备操作*/
            /* ... */
            };
            /* 设备模块信息 */
            static struct pci_driver demo_pci_driver = {
            name:       demo_MODULE_NAME,    /* 设备模块名称 */
            id_table:   demo_pci_tbl,    /* 能够驱动的设备列表 */
            probe:      demo_probe,    /* 查找并初始化设备 */
            remove:     demo_remove    /* 卸载设备模块 */
            /* ... */
            };
            static int __init demo_init_module (void)
            {
            /* ... */
            }
            static void __exit demo_cleanup_module (void)
            {
            pci_unregister_driver(&amp;demo_pci_driver);
            }
            /* 加载驱动程序模块入口 */
            module_init(demo_init_module);
            /* 卸载驱动程序模块入口 */
            module_exit(demo_cleanup_module);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>上面这段代码给出了一个典型的PCI设备驱动程序的框架，是一种相对固定的模式。需要注意的是，同加载和卸载模块相关的函数或数据结构都要在前面加上__init、__exit等标志符，以使同普通函数区分开来。构造出这样一个框架之后，接下去的工作就是如何完成框架内的各个功能模块了。 </p>
<p><a name=N10162><span class=smalltitle><strong><font face=Arial>3. 初始化设备模块</font></strong></span></a></p>
<p>在Linux系统下，想要完成对一个PCI设备的初始化，需要完成以下工作：</p>
<ul>
    <li>检查PCI总线是否被Linux内核支持；
    <li>检查设备是否插在总线插槽上，如果在的话则保存它所占用的插槽的位置等信息。
    <li>读出配置头中的信息提供给驱动程序使用。 </li>
</ul>
<p>当Linux内核启动并完成对所有PCI设备进行扫描、登录和分配资源等初始化操作的同时，会建立起系统中所有PCI设备的拓扑结构，此后当PCI驱动程序需要对设备进行初始化时，一般都会调用如下的代码： </p>
<p sizset="46" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="46" sizcache="2">
    <tbody sizset="46" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int __init demo_init_module (void)
            {
            /* 检查系统是否支持PCI总线 */
            if (!pci_present())
            return -ENODEV;
            /* 注册硬件驱动程序 */
            if (!pci_register_driver(&amp;demo_pci_driver)) {
            pci_unregister_driver(&amp;demo_pci_driver);
            return -ENODEV;
            }
            /* ... */
            return 0;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>驱动程序首先调用函数pci_present( )检查PCI总线是否已经被Linux内核支持，如果系统支持PCI总线结构，这个函数的返回值为0，如果驱动程序在调用这个函数时得到了一个非0的返回值，那么驱动程序就必须得中止自己的任务了。在2.4以前的内核中，需要手工调用pci_find_device( )函数来查找PCI设备，但在2.4以后更好的办法是调用pci_register_driver( )函数来注册PCI设备的驱动程序，此时需要提供一个pci_driver结构，在该结构中给出的probe探测例程将负责完成对硬件的检测工作。 </p>
<p sizset="47" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="47" sizcache="2">
    <tbody sizset="47" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>
            static int __init demo_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
            {
            struct demo_card *card;
            /* 启动PCI设备 */
            if (pci_enable_device(pci_dev))
            return -EIO;
            /* 设备DMA标识 */
            if (pci_set_dma_mask(pci_dev, DEMO_DMA_MASK)) {
            return -ENODEV;
            }
            /* 在内核空间中动态申请内存 */
            if ((card = kmalloc(sizeof(struct demo_card), GFP_KERNEL)) == NULL) {
            printk(KERN_ERR "pci_demo: out of memory\n");
            return -ENOMEM;
            }
            memset(card, 0, sizeof(*card));
            /* 读取PCI配置信息 */
            card-&gt;iobase = pci_resource_start (pci_dev, 1);
            card-&gt;pci_dev = pci_dev;
            card-&gt;pci_id = pci_id-&gt;device;
            card-&gt;irq = pci_dev-&gt;irq;
            card-&gt;next = devs;
            card-&gt;magic = DEMO_CARD_MAGIC;
            /* 设置成总线主DMA模式 */
            pci_set_master(pci_dev);
            /* 申请I/O资源 */
            request_region(card-&gt;iobase, 64, card_names[pci_id-&gt;driver_data]);
            return 0;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N1018B><span class=smalltitle><strong><font face=Arial>4. 打开设备模块</font></strong></span></a></p>
<p>在这个模块里主要实现申请中断、检查读写模式以及申请对设备的控制权等。在申请控制权的时候，非阻塞方式遇忙返回，否则进程主动接受调度，进入睡眠状态，等待其它进程释放对设备的控制权。 </p>
<p sizset="48" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="48" sizcache="2">
    <tbody sizset="48" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_open(struct inode *inode, struct file *file)
            {
            /* 申请中断，注册中断处理程序 */
            request_irq(card-&gt;irq, &amp;demo_interrupt, SA_SHIRQ,
            card_names[pci_id-&gt;driver_data], card)) {
            /* 检查读写模式 */
            if(file-&gt;f_mode &amp; FMODE_READ) {
            /* ... */
            }
            if(file-&gt;f_mode &amp; FMODE_WRITE) {
            /* ... */
            }
            /* 申请对设备的控制权 */
            down(&amp;card-&gt;open_sem);
            while(card-&gt;open_mode &amp; file-&gt;f_mode) {
            if (file-&gt;f_flags &amp; O_NONBLOCK) {
            /* NONBLOCK模式，返回-EBUSY */
            up(&amp;card-&gt;open_sem);
            return -EBUSY;
            } else {
            /* 等待调度，获得控制权 */
            card-&gt;open_mode |= f_mode &amp; (FMODE_READ | FMODE_WRITE);
            up(&amp;card-&gt;open_sem);
            /* 设备打开计数增1 */
            MOD_INC_USE_COUNT;
            /* ... */
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N1019B><span class=smalltitle><strong><font face=Arial>5. 数据读写和控制信息模块</font></strong></span></a></p>
<p>PCI设备驱动程序可以通过demo_fops 结构中的函数demo_ioctl( )，向应用程序提供对硬件进行控制的接口。例如，通过它可以从I/O寄存器里读取一个数据，并传送到用户空间里： </p>
<p sizset="49" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="49" sizcache="2">
    <tbody sizset="49" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
            <!-- code sample is too wide -->{
            /* ... */
            switch(cmd) {
            case DEMO_RDATA:
            /* 从I/O端口读取4字节的数据 */
            val = inl(card-&gt;iobae + 0x10);
            /* 将读取的数据传输到用户空间 */
            return 0;
            }
            /* ... */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>事实上，在demo_fops里还可以实现诸如demo_read( )、demo_mmap( )等操作，Linux内核源码中的driver目录里提供了许多设备驱动程序的源代码，找那里可以找到类似的例子。在对资源的访问方式上，除了有I/O指令以外，还有对外设I/O内存的访问。对这些内存的操作一方面可以通过把I/O内存重新映射后作为普通内存进行操作，另一方面也可以通过总线主DMA（Bus Master DMA）的方式让设备把数据通过DMA传送到系统内存中。 </p>
<p><a name=N101AE><span class=smalltitle><strong><font face=Arial>6. 中断处理模块</font></strong></span></a></p>
<p>PC的中断资源比较有限，只有0~15的中断号，因此大部分外部设备都是以共享的形式申请中断号的。当中断发生的时候，中断处理程序首先负责对中断进行识别，然后再做进一步的处理。 </p>
<p sizset="50" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="50" sizcache="2">
    <tbody sizset="50" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)
            {
            struct demo_card *card = (struct demo_card *)dev_id;
            u32 status;
            spin_lock(&amp;card-&gt;lock);
            /* 识别中断 */
            status = inl(card-&gt;iobase + GLOB_STA);
            if(!(status &amp; INT_MASK))
            {
            spin_unlock(&amp;card-&gt;lock);
            return;  /* not for us */
            }
            /* 告诉设备已经收到中断 */
            outl(status &amp; INT_MASK, card-&gt;iobase + GLOB_STA);
            spin_unlock(&amp;card-&gt;lock);
            /* 其它进一步的处理，如更新DMA缓冲区指针等 */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N101BE><span class=smalltitle><strong><font face=Arial>7. 释放设备模块</font></strong></span></a></p>
<p>释放设备模块主要负责释放对设备的控制权，释放占用的内存和中断等，所做的事情正好与打开设备模块相反：</p>
<p sizset="51" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="51" sizcache="2">
    <tbody sizset="51" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static int demo_release(struct inode *inode, struct file *file)
            {
            /* ... */
            /* 释放对设备的控制权 */
            card-&gt;open_mode &amp;= (FMODE_READ | FMODE_WRITE);
            /* 唤醒其它等待获取控制权的进程 */
            wake_up(&amp;card-&gt;open_wait);
            up(&amp;card-&gt;open_sem);
            /* 释放中断 */
            free_irq(card-&gt;irq, card);
            /* 设备打开计数增1 */
            MOD_DEC_USE_COUNT;
            /* ... */
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p><a name=N101CE><span class=smalltitle><strong><font face=Arial>8. 卸载设备模块</font></strong></span></a></p>
<p>卸载设备模块与初始化设备模块是相对应的，实现起来相对比较简单，主要是调用函数pci_unregister_driver( )从Linux内核中注销设备驱动程序： </p>
<p sizset="52" sizcache="2">
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="52" sizcache="2">
    <tbody sizset="52" sizcache="1">
        <tr>
            <td class=code-outline>
            <pre class=displaycode>static void __exit demo_cleanup_module (void)
            {
            pci_unregister_driver(&amp;demo_pci_driver);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="53" sizcache="2">
    <tbody sizset="53" sizcache="1">
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right sizset="54" sizcache="2">
    <tbody sizset="55" sizcache="2">
        <tr align=right sizset="55" sizcache="2">
            <td sizset="55" sizcache="2"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0 sizset="55" sizcache="2">
                <tbody sizset="55" sizcache="1">
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-pci/index.html#main" cmImpressionSent="1"><strong><u><font color=#800080>回页首</font></u></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=4><span class=atitle>四、小结</span></a></p>
<p>PCI总线不仅是目前应用广泛的计算机总线标准，而且是一种兼容性最强、功能最全的计算机总线。而Linux作为一种新的操作系统，其发展前景是无法估量的，同时也为PCI总线与各种新型设备互连成为可能。由于Linux源码开放，因此给连接到PCI总线上的任何设备编写驱动程序变得相对容易。本文介绍如何编译Linux下的PCI驱动程序，针对的内核版本是2.4。 </p>
<br><br>
<p><a name=resources><span class=atitle>参考资料 </span></a></p>
<ul>
    <li>David A Rusling在 <a href="http://tldp.org/LDP/tlk/dd/pci.html" cmImpressionSent="1"><u><font color=#0000ff>The Linux Kernel</font></u></a>中对Linux的PCI子系统进行了比较详细的介绍。 <br><br>
    <li><a href="http://tldp.org/HOWTO/PCI-HOWTO.html" cmImpressionSent="1"><u><font color=#0000ff>Linux PCI-HOWTO</font></u></a>是了解Linux下PCI设备的最好读物。 <br><br>
    <li>毛德操，胡希明，Linux内核源代码情景分析，杭州：浙江大学出版社，2001<br><br>
    <li>Alessandro Rubini,，Linux Device Drivers（2nd Edition） USA：O&#8217;Reilly，2001<br><br>
    <li>Tomshanley，DonAderson，PCI系统结构（第四版），北京：电子工业出版社，2000<br></li>
</ul>
<br><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0 sizset="56" sizcache="2">
    <tbody sizset="56" sizcache="1">
        <tr>
            <td colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&#160;</p>
            </td>
            <td><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
            <td width="100%">
            <p>肖文鹏，北京理工大学计算机系的一名硕士研究生，主要从事操作系统和分布式计算环境的研究，喜爱Linux和Python。你可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#120;&#105;&#97;&#111;&#119;&#112;&#64;&#50;&#54;&#51;&#46;&#110;&#101;&#116;&#63;&#99;&#99;&#61;" cmImpressionSent="1"><u><font color=#0000ff>xiaowp@263.net</font></u></a>与他取得联系。 </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/iniwf/aggbug/89053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-07-02 11:19 <a href="http://www.cppblog.com/iniwf/archive/2009/07/02/89053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux内核之旅</title><link>http://www.cppblog.com/iniwf/archive/2009/04/07/79211.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Tue, 07 Apr 2009 14:01:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/07/79211.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79211.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/07/79211.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79211.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79211.html</trackback:ping><description><![CDATA[<p>转自<a href="http://www.kerneltravel.net/">http://www.kerneltravel.net/<br></a><br><br></p>
<div style="WIDTH: 100%; TEXT-ALIGN: left" align=left;>
<h5><a href="http://www.kerneltravel.net/"><font color=#0066cc>首页&gt;&gt;</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=8"><font color=#0066cc>2.6内核模块编程实例</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=21"><font color=#0066cc>走进内核</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=12"><font color=#0066cc>电子杂志</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=14"><font color=#0066cc>经验交流</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=16"><font color=#0066cc>Linux杂谈</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=18"><font color=#0066cc>新手上路</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=23"><font color=#0066cc>资料下载</font></a>&nbsp;&nbsp; <a href="http://www.lupaworld.com/bbs/forum-255-1.html"><font color=#0066cc>讨论区</font></a>&nbsp;&nbsp; <a href="http://www.kerneltravel.net/?page_id=2"><font color=#0066cc>关于我们</font></a></h5>
<font color=#0066cc>
<hr>
</font></div>
<div class=narrowcolumn id=content>
<div class=post id=post-5><font color=#0066cc>
<hr width="100%" color=gray SIZE=2 a>
</font>
<div class=entry>
<p><a href="http://www.kerneltravel.net/?page_id=8"><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"><font color=#0066cc>&#8226;</font><span style="COLOR: #ff0000">(new)</span><font color=#0066cc>Linux2.6内核编程实例</font></span></strong></a> 是新开的一个栏目，主要是面向对Linux内核感兴趣,但又不知如何在内核级编程的新手。该栏目收录了：<a href="http://www.kerneltravel.net/?page_id=8"><font color=#0066cc>&#8211;&gt;2.6内核模块编程指导文章；&#8211;&gt;2.6现内核模块实例。</font></a><font color=#0066cc></font></p>
<p><span lang=EN-US style="FONT-SIZE: 9pt"><a href="http://www.kerneltravel.net/?page_id=21"><font color=#0066cc><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">&#8226;走进内核</span></strong><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"> </span></strong></font></a></span><span style="FONT-SIZE: 9pt">主要是面向对Linux内 核感兴趣的内核新手。我们希望通过这个栏目能让更多的人接触和学习Linux内核，加入Linux内核的开发。 该栏目新加了：Andrew Morton的<a href="http://wiki.zh-kernel.org/doc/submittingpatches"><font color=#0066cc>The Perfact Patch</font></a>，Greg KH的<a href="http://www.kerneltravel.net/newbie/dev_howto.html"><font color=#0066cc>HOWTO do Linux kernel development</font></a>。希望会对那些准备加入Linux内核开发的人有所帮助。</span></p>
<hr SIZE=2>
<span lang=EN-US style="FONT-SIZE: 9pt"><a href="http://www.kerneltravel.net/?page_id=12"><font color=#0066cc><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">&#8226;电子杂志</span></strong><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"> </span></strong></font></a></span><span style="FONT-SIZE: 9pt; COLOR: #333333">是我们今后的重点工作，我们希望能以期刊形式发布 关于内核研究与学习的资料， 目前我们已经完成了 : <a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span style="FONT-SIZE: 10.5pt">&#8221; </span><span lang=EN-US style="FONT-SIZE: 10.5pt">走入Linux </span><span lang=EN-US style="FONT-SIZE: 10.5pt">操作系统&#8221; </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span style="FONT-SIZE: 10.5pt">&#8220;I386 </span><span lang=EN-US style="FONT-SIZE: 10.5pt">体系结构&#8221; </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span style="FONT-SIZE: 10.5pt">&#8220;Shell </span><span lang=EN-US style="FONT-SIZE: 10.5pt">解释器 DIY&#8221; </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span style="FONT-SIZE: 10.5pt">&#8220;Linux </span><span lang=EN-US style="FONT-SIZE: 10.5pt">系统调用&#8221; </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8220;Linux </span><span lang=EN-US style="FONT-SIZE: 10.5pt">内存管理&#8221;</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8220;内核中的调度与同步</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a><a href="http://www.kerneltravel.net/journal/v/index.htm"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8221;</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a><a href="http://www.kerneltravel.net/004.htm"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8220;如何实现Linux </span><span lang=EN-US style="FONT-SIZE: 10.5pt">下的文件系统&#8221;</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a><a href="http://www.kerneltravel.net/004.htm"><span lang=EN-US style="FONT-SIZE: 10.5pt"><font color=#0066cc>&#8220;中断处理&#8221;<span style="TEXT-DECORATION: underline"> </span></font></span></a></span><span style="FONT-SIZE: 10.5pt; COLOR: #33cccc"><a href="http://www.kerneltravel.net/?p=281" target=_blank><font color=#0066cc>&#8220;proc文件系统浅析&#8221;</font></a><span style="COLOR: #ff0000"><a href="http://www.kerneltravel.net/?p=281" target=_blank><font color=#0066cc>(new)</font></a></span></span>
<p>&nbsp;</p>
<p>&nbsp;</p>
<hr SIZE=2>
<div class=MsoNormal style="TEXT-ALIGN: center"><span lang=EN-US style="FONT-SIZE: 9pt"><a href="http://www.kerneltravel.net/?page_id=14"><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"><font color=#0066cc>&#8226;经验交流 </font></span></strong></a></span><span style="FONT-SIZE: 9pt">中发表关于 <em><span style="FONT-FAMILY: 宋体">内核开发技 巧等系随笔文章 (<a href="http://www.kerneltravel.net/jiaoliu/timekeep.htm"><font color=#0066cc><span lang=EN-US style="FONT-STYLE: normal">虚拟机中</span><span style="FONT-STYLE: normal">GUEST OS</span><span lang=EN-US style="FONT-STYLE: normal">时钟</span><span style="FONT-STYLE: normal">(TIMEKEEP)</span><span lang=EN-US style="FONT-STYLE: normal">问题的探讨</span></font></a>，</span></em><em><span style="FONT-STYLE: normal; FONT-FAMILY: 宋体"><a href="http://www.kerneltravel.net/jiaoliu/timekeep2.htm"><font color=#0066cc>虚拟机中GUEST OS时钟(TIMEKEEP)问题的探讨续</font></a></span></em><em><span style="FONT-FAMILY: 宋体">，</span></em><a href="http://www.kerneltravel.net/jiaoliu/Data%20Miss%20Exception%20Handle%20For%20E500%20Core.htm"><span style="FONT-SIZE: 10.5pt"><font color=#0066cc>Modification For Linux&#8217;s or vxworks&#8217;s Tlb Data Miss Exception Handle For E500 Core </font></span></a>, <a href="http://www.kerneltravel.net/jiaoliu/OLDOS.htm"><font color=#0066cc><span class=GramE><span lang=EN-US style="FONT-SIZE: 10.5pt">小论遗留</span></span><span lang=EN-US style="FONT-SIZE: 10.5pt">系统向Linux</span><span lang=EN-US style="FONT-SIZE: 10.5pt">系统迁移</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a><a href="http://www.kerneltravel.net/jiaoliu/map.htm"><font color=#0066cc><span class=GramE><span lang=EN-US style="FONT-SIZE: 10.5pt">小论内存</span></span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span><span lang=EN-US style="FONT-SIZE: 10.5pt">映射</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a>、 <a href="http://www.kerneltravel.net/jiaoliu/IPoverpci.htm"><font color=#0066cc><span class=GramE><span lang=EN-US style="FONT-SIZE: 10.5pt">小论</span></span><span style="FONT-SIZE: 10.5pt"> IP-OVER-FIFO </span></font></a>、 <a href="http://www.kerneltravel.net/jiaoliu/clkdebug.htm"><font color=#0066cc><span class=GramE><span lang=EN-US style="FONT-SIZE: 10.5pt">小论时钟</span></span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span><span lang=EN-US style="FONT-SIZE: 10.5pt">调试</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a>等）， 以及翻译的几篇关于 <em><span style="FONT-FAMILY: 宋体">Linux </span></em><em><span style="FONT-FAMILY: 宋体">实时系统 </span></em>的经典文章。感谢网友提供的有关 <a href="http://www.kerneltravel.net/jiaoliu/jffs2.mht"><span style="FONT-SIZE: 10.5pt"><font color=#0066cc>jffs2 </font></span></a>文件系统的文章。</span></div>
<hr SIZE=2>
<div class=MsoNormal style="TEXT-ALIGN: center"><span lang=EN-US style="FONT-SIZE: 9pt"><span lang=EN-US style="FONT-SIZE: 10.5pt"><a href="http://www.kerneltravel.net/kernel-book/%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90Linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81.html"><strong><font color=#0066cc>《深入分析Linux内核源代码》</font></strong></a></span>是由陈莉君老师撰写，现在我们把它以网页的形式形式全部公开，希望对大家学习Linux内核有所帮助。 书的内容可以在这里看到：<a href="http://www.kerneltravel.net/kernel-book/%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90Linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81.html"><font color=#0066cc>深入分析Linux内核源代码</font></a>。</span></div>
<div class=MsoNormal style="TEXT-ALIGN: center"><span lang=EN-US style="FONT-SIZE: 9pt"><a href="http://www.kerneltravel.net/"><font color=#0066cc><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">&#8226;《Understanding the Linux Kernel, 3rd Edition</span></strong><strong><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">》</span></strong></font></a></span><span style="FONT-SIZE: 9pt">一书已经由陈莉君，张琼声，张宏伟翻译完毕。为了答 谢各位的厚爱，我们专门拿出此书中重要的两章──<a href="http://www.kerneltravel.net/books/ulk_ch02.pdf.tar.gz"><em><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"><font color=#0066cc>第二章《内存寻址》</font></span></em></a>和<a href="http://www.kerneltravel.net/books/ch07.pdf"><em><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"><font color=#0066cc>第七章《进程调度》</font></span></em></a>来给大家先睹为快。</span></div>
<hr SIZE=2>
<div class=MsoNormal style="TEXT-ALIGN: center"><strong><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体"><a href="http://www.hzbook.com/books/ShowBooks.aspx?BookID=3307"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8226;《</span><span style="FONT-SIZE: 10.5pt">The Linux Kernel Primer: A Top-Down Approach For X86 and PowerPC Architectures</span><span lang=EN-US style="FONT-SIZE: 10.5pt">》</span></font></a></span></strong><span style="FONT-SIZE: 9pt">也就是《Linux内核编程 》，是由陈莉君，<span class=GramE>贺炎和</span>刘霞林翻译，此书最大的特色是能够把x86和<span class=SpellE>ppc</span>两 个平台结合起来讲述内核，从用户程序讲起，深入到内核，而且每章后面还配有练习题。我们拿出<a href="http://www.kerneltravel.net/books/Chapter_2.pdf"><em><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体"><font color=#0066cc>第二章《内核探索工具集》</font></span></em></a>供大家下载，这一章内容很多，讲述 了开发内核的基础知识和工具，不妨读一下。</span></div>
<hr SIZE=2>
<strong><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体"><a href="http://www.china-pub.com/computers/common/info.asp?id=28429" target=_blank><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">&#8226;《</span><span style="FONT-SIZE: 10.5pt">Linux Kernel Development</span><span lang=EN-US style="FONT-SIZE: 10.5pt">》</span></font></a></span></strong><span style="FONT-SIZE: 9pt">一书已经翻译完毕 <em><span style="FONT-FAMILY: 宋体">( </span></em><em><span style="FONT-FAMILY: 宋体">译者： <a href="http://www.kerneltravel.net/US.htm"><font color=#0066cc><span lang=EN-US style="FONT-SIZE: 10.5pt">陈莉君、康华、张波</span><span lang=EN-US style="FONT-SIZE: 10.5pt"> </span></font></a>） </span></em>，并已出版（机械工业出版社）， 为了让读者先睹为快，我</span></div>
</div>
</div>
<div style="TEXT-ALIGN: left">
<ul>
    <li class="widget widget_recent_entries" id=recent-posts>
    <h2 class=widgettitle>最新文章</h2>
    <ul>
        <li><a href="http://www.kerneltravel.net/?p=379"><font color=#0066cc>Linux 内核代码赏析与应用（三）－链表API之应用 </font></a>
        <li><a href="http://www.kerneltravel.net/?p=369"><font color=#0066cc>Linux内核代码赏析与应用（一）－链表之衍生 </font></a>
        <li><a href="http://www.kerneltravel.net/?p=365"><font color=#0066cc>Linux内核代码赏析与应用（二）－链表之实现 </font></a>
        <li><a href="http://www.kerneltravel.net/?p=356"><font color=#0066cc>虚地址转换为物理地址 </font></a>
        <li><a href="http://www.kerneltravel.net/?p=334"><font color=#0066cc>2.6驱动程序-字符驱动 </font></a>
        <li><a href="http://www.kerneltravel.net/?p=285"><font color=#0066cc>proc文件系统探索 之 以数字命名的目录[一] </font></a>
        <li><a href="http://www.kerneltravel.net/?p=300"><font color=#0066cc>proc文件系统探索 之 proc根目录下的文件[六] </font></a></li>
    </ul>
    </li>
</ul>
</div>
<div style="FONT-SIZE: 10.5pt; TEXT-ALIGN: left"><span style="FONT-SIZE: 10.5pt; LINE-HEIGHT: 2" align="left"><strong>&#247;&#247;友情连接&#247;&#247;</strong><br><a href="http://www.lupaworld.com/26540"><font color=#0066cc>陈莉君老师的博客</font></a> <br><span class=GramE><span style="FONT-SIZE: 10.5pt"><a href="http://www.xiyoulinux.cn/" target=blank><font color=#0066cc>西邮Linux兴趣小组主页</font></a></span> <br><span style="FONT-SIZE: 10.5pt"><a href="http://blog.chinaunix.net/group/group_1488.html/" target=blank><font color=#0066cc>西邮嵌入式Linux博客圈</font></a></span></span> <br><a href="http://www.chineselinuxuniversity.net/" target=blank><span style="FONT-SIZE: 10.5pt"><font color=#0066cc>中国Linux大学</font></span></a> <br><a href="http://zh-kernel.org/" target=blank><font color=#0066cc>内核开发中文社区</font></a> <br><a href="http://zhwen.org/" target=blank><font color=#0066cc>七度黑光</font></a> </span><br><br><strong>&#247;&#247;推荐站点&#247;&#247;</strong> <br><a href="http://www.kerneltraffic.org/" target=_blank><font color=#0066cc>http://www.kerneltraffic.org/ </font></a>是个难得的好站点 <a href="http://www.lwn.net/" target=_blank><font color=#0066cc>http://www.lwn.net/</font></a><br>一定要看<br><a href="http://www.linuxjournal.com/" target=_blank><font color=#0066cc>http://www.linuxjournal.com/</font></a><br>太多的精彩文章了<br><a href="http://www.kernel.org/" target=_blank><font color=#0066cc>http://www.kernel.org</font></a> <br>Linux Kernel官方网站<br><a href="http://lxr.linux.no/source/" target=_blank><font color=#0066cc>http://lxr.linux.no/source/</font></a><br>Linux内核在线浏览<br><a href="http://www.kernelnewbies.org/" target=_blank><font color=#0066cc>http://www.kernelnewbies.org/ </font></a>鼓舞和帮助新内核黑客<br><a href="http://www.linuxdevices.com/" target=_blank><font color=#0066cc>http://www.linuxdevices.com/ </font></a>嵌入 Linux 的乐园<br><a href="http://www.linuxforum.net/" target=_blank><font color=#0066cc>http://www.linuxforum.net/ </font></a>中国人气第一 </div>
<img src ="http://www.cppblog.com/iniwf/aggbug/79211.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-07 22:01 <a href="http://www.cppblog.com/iniwf/archive/2009/04/07/79211.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入分析Linux内核源码</title><link>http://www.cppblog.com/iniwf/archive/2009/04/06/79123.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 06 Apr 2009 14:43:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/06/79123.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79123.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/06/79123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79123.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79123.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自http://www.kerneltravel.net/kernel-book/深入分析Linux内核源码.htmlhttp://www.lupaworld.com/bbs/thread-30907-1-5.html深入分析Linux内核源码前言&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一章 走进linux1.1 GNU...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2009/04/06/79123.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/79123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-06 22:43 <a href="http://www.cppblog.com/iniwf/archive/2009/04/06/79123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ArmLinux BOOTLOADER全程详解</title><link>http://www.cppblog.com/iniwf/archive/2009/04/06/79121.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Mon, 06 Apr 2009 14:10:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/04/06/79121.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/79121.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/04/06/79121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/79121.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/79121.html</trackback:ping><description><![CDATA[转自<a href="http://www.dz863.com/Embedded-Systems-Design/ARM-Microcontroller/ArmLinux-BOOTLOADER.htm">http://www.dz863.com/Embedded-Systems-Design/ARM-Microcontroller/ArmLinux-BOOTLOADER.htm</a><br><br>网上关于Linux的BOOTLOADER文章不少了,但是大都是vivi,blob等比较庞大的程序,读起来不太方便,编译出的文件也比较大,而且更多的是面向开发用的引导代码,做成产品时还要裁减,这一定程度影响了开发速度,对初学者学习开销也比较大,在此分析一种简单的BOOTLOADER,是在三星公司提供的2410 BOOTLOADER上稍微修改后的结果,编译出来的文件大小不超过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放到指定地址,然后由BOOTLOADER把这个地址通过启动参数的方式ATAG_INITRD2传递给KERNEL.具体看代码分析. <br><br>启动参数(摘自IBM developer) <br>在调用内核之前，应该作一步准备工作，即：设置 Linux 内核的启动参数。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。启动参数标记列表以标记 ATAG_CORE 开始，以标记 ATAG_NONE 结束。每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。数据结构 tag 和 tag_header 定义在 Linux 内核源码的include/asm/setup.h 头文件中. <br>在嵌入式 Linux 系统中，通常需要由 BOOTLOADER 设置的常见启动参数有：ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。 <br>(注)参数也可以用COMMANDLINE来设定,在我的BOOTLOADER里,我两种都用了. <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以内的BOOTLOADER 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 Linux,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>第三步,执行BOOTLOADER代码,从串口得到调试数据,引导armlinux <br><br><strong>4.代码分析</strong> <br>讲了那么多执行的步骤,是想让大家对启动有个大概印象,接着就是BOOTLOADER内部的代码分析了,BOOTLOADER文章内容网上很多,我这里精简了下,删除了不必要的功能. <br>BOOTLOADER一般分为2部分,汇编部分和c语言部分,汇编部分执行简单的硬件初始化,C部分负责复制数据,设置启动参数,串口通信等功能. <br>BOOTLOADER的生命周期: <br>1. 初始化硬件,比如设置UART(至少设置一个),检测存储器= =. <br>2. 设置启动参数,这是为了告诉内核硬件的信息,比如用哪个启动界面,波特率 = =. <br>3. 跳转到Linux 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("ntLinux S3C2410 Nor BOOTLOADERn"); <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>ISA</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>ISA</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>ISA</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的水平了. 
<img src ="http://www.cppblog.com/iniwf/aggbug/79121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-04-06 22:10 <a href="http://www.cppblog.com/iniwf/archive/2009/04/06/79121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下基于jrtplib库的实时传送实现</title><link>http://www.cppblog.com/iniwf/archive/2009/03/17/76941.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Tue, 17 Mar 2009 15:06:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/03/17/76941.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/76941.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/03/17/76941.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/76941.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/76941.html</trackback:ping><description><![CDATA[转自<a href="http://www.doserver.net/read.php/1027.htm">http://www.doserver.net/read.php/1027.htm</a><br><br>linux 下基于jrtplib库的实时传送实现<br><span style="COLOR: #ff0000">一、RTP 是进行实时流媒体传输的标准协议和关键技术</span><br>实时传输协议（Real-time Transport Protocol，PRT）是在 Internet 上处理多媒体数据流的一种网络协议，利用它能够在一对一（unicast，单播）或者一对多（multicast，多播）的网络环境中实现传流媒体数据的实时传输。RTP 通常使用 <a class=mykeyword title=http://www.server-development.cn/go.php/tags/udp/ href="http://www.server-development.cn/go.php/tags/udp/" target=_blank><font color=#666666>UDP</font></a> 来进行多媒体数据的传输，但如果需要的话可以使用 TCP 或者 ATM 等其它协议。<br><a name=entrymore></a><br>协议分析 ：每一个RTP数据报都由头部（Header）和负载（Payload）两个部分组成，其中头部前 12 个字节的含义是固定的，而负载则可以是音频或者视频数据。<br><br>&nbsp; &nbsp; &nbsp;RTP 是目前解决流媒体实时传输问题的最好办法，要在 Linux 平台上进行实时传送编程，可以考虑使用一些开放源代码的 RTP 库，如 LIBRTP、JRTPLIB 等。JRTPLIB 是一个面向对象的 RTP 库，它完全遵循 RFC 1889 设计，在很多场合下是一个非常不错的选择。JRTPLIB 是一个用 C++ 语言实现的 RTP 库，这个库使用socket 机制实现网络通讯 因此可以运行在 Windows、Linux、FreeBSD、Solaris、Unix和VxWorks 等多种操作系统上。<br><span style="COLOR: #ff0000">二、JRTPLIB 库的使用方法及程序实现</span><br>(1)JRTPLIB &nbsp;函数 的使用<br>a、在使用 JRTPLIB 进行实时流媒体数据传输之前，首先应该生成 RTPSession 类的一个实例来表示此次 RTP 会话，然后调用 Create() 方法来对其进行初始化操作。RTPSession 类的 Create() 方法只有一个参数，用来指明此次 RTP 会话所采用的端口号。<br>RTPSession sess; &nbsp;sess.Create(5000); <br><br>b、设置恰当的时戳单元，是 RTP 会话初始化过程所要进行的另外一项重要工作，这是通过调用 RTPSession 类的 SetTimestampUnit() 方法来实现的，该方法同样也只有一个参数，表示的是以秒为单元的时戳单元。<br>sess.SetTimestampUnit(1.0/8000.0);<br><br>c、当 RTP 会话成功建立起来之后，接下去就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发送的目标地址，RTP 协议允许同一会话存在多个目标地址，这可以通过调用 RTPSession 类的 AddDestination()、DeleteDestination() 和 ClearDestinations() 方法来完成。例如，下面的语句表示的是让 RTP 会话将数据发送到本地主机的 6000 端口： <br><br>unsigned long addr = ntohl(inet_addr("127.0.0.1")); <br>sess.AddDestination(addr, 6000);<br><br>d、目标地址全部指定之后，接着就可以调用 RTPSession 类的 SendPacket() 方法，向所有的目标地址发送流媒体数据。SendPacket() 是 RTPSession 类提供的一个重载函数<br>对于同一个 RTP 会话来讲，负载类型、标识和时戳增量通常来讲都是相同的，JRTPLIB 允许将它们设置为会话的默认参数，这是通过调用 RTPSession 类的 SetDefaultPayloadType()、SetDefaultMark() 和 SetDefaultTimeStampIncrement() 方法来完成的。为 RTP 会话设置这些默认参数的好处是可以简化数据的发送，例如，如果为 RTP 会话设置了默认参数： <br><br>sess.SetDefaultPayloadType(0);<br>&nbsp;sess.SetDefaultMark(false); &nbsp;<br>sess.SetDefaultTimeStampIncrement(10);<br><br><br><br>之后在进行数据发送时只需指明要发送的数据及其长度就可以了： <br><br>sess.SendPacket(buffer, 5); <br><br><br>e、对于流媒体数据的接收端，首先需要调用 RTPSession 类的 PollData() 方法来接收发送过来的 RTP 或者 RTCP 数据报。由于同一个 RTP 会话中允许有多个参与者（源），你既可以通过调用 RTPSession 类的 GotoFirstSource() 和 GotoNextSource() 方法来遍历所有的源，也可以通过调用 RTPSession 类的 GotoFirstSourceWithData() 和 GotoNextSourceWithData() 方法来遍历那些携带有数据的源。在从 RTP 会话中检测出有效的数据源之后，接下去就可以调用 RTPSession 类的 GetNextPacket() 方法从中抽取 RTP 数据报，当接收到的 RTP 数据报处理完之后，一定要记得及时释放。<br><br>JRTPLIB 为 RTP 数据报定义了三种接收模式，其中每种接收模式都具体规定了哪些到达的 RTP 数据报将会被接受，而哪些到达的 RTP 数据报将会被拒绝。通过调用 RTPSession 类的 SetReceiveMode() 方法可以设置下列这些接收模式： <br>? RECEIVEMODE_ALL　　缺省的接收模式，所有到达的 RTP 数据报都将被接受； <br>? RECEIVEMODE_IGNORESOME　　除了某些特定的发送者之外，所有到达的 RTP 数据报都将被接受，而被拒绝的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() 和 ClearIgnoreList() 方法来进行设置； <br>? RECEIVEMODE_ACCEPTSOME　　除了某些特定的发送者之外，所有到达的 RTP 数据报都将被拒绝，而被接受的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList 和 ClearAcceptList () 方法来进行设置。 下面是采用第三种接收模式的程序示例。<br>if (sess.GotoFirstSourceWithData()) { &nbsp; <br>&nbsp;do { &nbsp; <br>&nbsp; sess.AddToAcceptList(remoteIP, allports,portbase);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);<br><br>&nbsp; &nbsp;RTPPacket *pack; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp;pack = sess.GetNextPacket(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 处理接收到的数据 &nbsp; &nbsp;<br>&nbsp; &nbsp;delete pack; &nbsp; } <br>&nbsp;while (sess.GotoNextSourceWithData()); <br>&nbsp;}<br><br><br>&nbsp;（2）程序流程图<br>发送：获得接收端的 IP 地址和端口号 &nbsp; &nbsp; &nbsp; &nbsp;创建 RTP 会话 &nbsp; &nbsp; &nbsp; &nbsp;指定 RTP 数据接收端 设置 RTP 会话默认参数 &nbsp; 发送流媒体数据<br>接收：获得用户指定的端口号 &nbsp;创建RTP会话 &nbsp;设置接收模式 &nbsp;接受RTP数据 &nbsp;检索RTP数据源 &nbsp;获取RTP数据报 &nbsp;删除RTP数据报<br><br><br><span style="COLOR: #ff0000">三、环境搭建及编译方法</span><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content>（1）Toolchain的安装<br>首先找到xscale-arm-toolchain.tgz文件，假设该文件包放在/tmp/下<br>#cd /<br>#tar -zxvf /tmp/xscale-arm-toolchain.tgz<br>再设置环境变量<br>#export PATH=/usr/local/arm-linux/bin:$PATH<br>最后检查一下交叉编译工具是否安装成功<br>#arm-linux-g++ --version<br>看是否显示arm-linux-g++的版本，如有则安装成功。<br>（2）JRTPLIB 库的交叉编译及安装<br>首先从 JRTPLIB 的网站（http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmll） 下载最新的源码包，此处使用的是jrtplib-2.8.tar，假设下载后的源码包放在/tmp下，执 行下面的命令对其解压缩：<br>#cd /tmp<br>#tar -zxvf jrtplib-2.8.tar<br>然后要对jrtplib进行配置和编译<br>#cd jrtplib-2.8<br>#./configure CC=arm-linux-g++ cross-compile=yes<br>修改Makefile文件<br>将链接命令ld 和ar改为arm-linux-ld和 arm-linux-ar<br>#make<br>最后再执行如下命令就可以完成 JRTPLIB 的安装：<br>#make install<br>(3)程序编译<br>a、配置编译环境<br>可以用export来配置，也可以用编写Makefile的方法。这里采用Makefile。<br>编写Makefile&amp;:<br>INCL = -I/usr/local/include<br>CFLAGS = -pipe -O2 -fno-strength-reduce<br>LFLAGS = /usr/local/lib/libjrtp.a -L/usr/X11R6/lib<br>LIBS = -LX11 -LXext /usr/local/lib/libjrtp.a<br>CC = arm-linux-g++<br><br>main:main.o<br>$(CC) $(LFLAGS) $(INCL) -o main main.o $(LIBS)<br>main.o:main.cpp<br><br>clean:<br>rm -f main<br>rm -f *.o<br><br>.SUFFIXES:.cpp<br>.cpp.o:<br>$(CC) -c $(CFLAGS) $(INCL) -o $@ $&lt; &nbsp; &nbsp; &nbsp; &nbsp; /* &nbsp;$@表示目标的完整名字 &nbsp; &nbsp; &nbsp;*/<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* $&lt;表示第一个依赖文件的名字 */<br>b、编译<br>假设发送和接收程序分别放在/tmp/send和/tmp/receive目录下<br>#cd /tmp/send<br>#make<br>#cd /tmp/receive<br>#make</div>
</div>
<br><span style="COLOR: #ff0000">四、易出错误及注意问题</span><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content>1、找不到一些标准的最 基本的一些头文件。<br>&nbsp;主要是因为Toolchain路径没安装对，要 严格按照步骤安装。<br>2、找不到使用的jrtplib库中的一些头文件。<br>&nbsp;在 jrtplib的安装目录下，include路径下不能再有别的目录。<br>3、recieve函数接收数据包不能正确提出所要数据。<br>&nbsp;由于每一个RTP数据报都由头部（Header）和负载（Payload）两个部分组成，若使用getrawdata()是返回整个数据包的数据，包含传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息。getpayload()函数是返回所发送的数据。两者一定要分清。<br>4、设置RECEIVEMODE_ACCEPTSOME　　接收模式后，运行程序接收端不能接包。<br>&nbsp;IP地址格式出了问题。iner_addr()与ntohl()函数要用对，否则参数传不进去，接受列表中无值，当然接收不了数据包。<br>5、编译通过，但测试时接收端不能接收到数据。<br>&nbsp;可能是接收机防火墙未关闭。运行：<br>&nbsp;#iptables -F<br>&nbsp;也可能是IP地址没有设置好。运行：<br>&nbsp;#ifocnfig eth0 &nbsp;*.*.*.* &nbsp;netmask *.*.*.*<br>6、使用jrtolib库时，在程序中include 后最好加上库所在的路径。</div>
</div>
五、程序<br><br><span style="COLOR: #ff0000">send:</span><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;stdio.h&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;string.h&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;"rtpsession.h" </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=comment><font color=#ee0000>//&nbsp;错误处理函数 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;checkerror(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;err) &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>*&nbsp;errstr&nbsp;=&nbsp;RTPGetErrorString(err); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Error:%s\\n"</font></span><span>,&nbsp;errstr); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;exit(-1); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=alt><span>} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;main(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;argc,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>**&nbsp;argv) &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;RTPSession&nbsp;sess; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>long</font></strong></span><span>&nbsp;destip; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;destport; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;portbase&nbsp;=&nbsp;6000; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;status,&nbsp;index; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;buffer[128]; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(argc&nbsp;!=&nbsp;3)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Usage:&nbsp;./sender&nbsp;destip&nbsp;destport\\n"</font></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;-1; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;获得接收端的IP地址和端口号 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;destip&nbsp;=&nbsp;inet_addr(argv[1]); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(destip&nbsp;==&nbsp;INADDR_NONE)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Bad&nbsp;IP&nbsp;address&nbsp;specified.\\n"</font></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;-1; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;destip&nbsp;=&nbsp;ntohl(destip); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;destport&nbsp;=&nbsp;atoi(argv[2]); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;创建RTP会话 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;status&nbsp;=&nbsp;sess.Create(portbase); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;checkerror(status); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;指定RTP数据接收端 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;status&nbsp;=&nbsp;sess.AddDestination(destip,&nbsp;destport); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;checkerror(status); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;设置RTP会话默认参数 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;sess.SetDefaultPayloadType(0); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;sess.SetDefaultMark(</span><span class=keyword><strong><font color=#5697d9>false</font></strong></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;sess.SetDefaultTimeStampIncrement(10); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;发送流媒体数据 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;index&nbsp;=&nbsp;1; &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>do</font></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;sprintf(buffer,&nbsp;</span><span class=string><font color=#ff0000>"%d:&nbsp;RTP&nbsp;packet"</font></span><span>,&nbsp;index&nbsp;++); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;sess.SendPacket(buffer,&nbsp;strlen(buffer)); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Send&nbsp;packet&nbsp;!\\n"</font></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;}&nbsp;</span><span class=keyword><strong><font color=#5697d9>while</font></strong></span><span>(1); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;0; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>} &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include "rtpsession.h"
// 错误处理函数
void checkerror(int err)
{
&nbsp;if (err &lt; 0) {
&nbsp; &nbsp;char* errstr = RTPGetErrorString(err);
&nbsp; &nbsp;printf("Error:%s\\n", errstr);
&nbsp; &nbsp;exit(-1);
&nbsp;}
}
int main(int argc, char** argv)
{
&nbsp;RTPSession sess;
&nbsp;unsigned long destip;
&nbsp;int destport;
&nbsp;int portbase = 6000;
&nbsp;int status, index;
&nbsp;char buffer[128];
&nbsp;if (argc != 3) {
&nbsp; &nbsp;printf("Usage: ./sender destip destport\\n");
&nbsp; &nbsp;return -1;
&nbsp;}
&nbsp;// 获得接收端的IP地址和端口号
&nbsp;destip = inet_addr(argv[1]);
&nbsp;if (destip == INADDR_NONE) {
&nbsp; &nbsp;printf("Bad IP address specified.\\n");
&nbsp; &nbsp;return -1;
&nbsp;}
&nbsp;destip = ntohl(destip);
&nbsp;destport = atoi(argv[2]);
&nbsp;// 创建RTP会话
&nbsp;status = sess.Create(portbase);
&nbsp;checkerror(status);
&nbsp;// 指定RTP数据接收端
&nbsp;status = sess.AddDestination(destip, destport);
&nbsp;checkerror(status);
&nbsp;// 设置RTP会话默认参数
&nbsp;sess.SetDefaultPayloadType(0);
&nbsp;sess.SetDefaultMark(false);
&nbsp;sess.SetDefaultTimeStampIncrement(10);
&nbsp;// 发送流媒体数据
&nbsp;index = 1;
&nbsp;do {
&nbsp; &nbsp;sprintf(buffer, "%d: RTP packet", index ++);
&nbsp; &nbsp;sess.SendPacket(buffer, strlen(buffer));
&nbsp; &nbsp;printf("Send packet !\\n");
&nbsp;} while(1);
&nbsp;return 0;
}
</textarea><br><br><br><br><br><span style="COLOR: #ff0000">receive:</span><br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;&lt;stdio.h&gt; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;"rtpsession.h" </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;"rtppacket.h" </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=comment><font color=#ee0000>//&nbsp;错误处理函数 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;checkerror(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;err) &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>{ &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(err&nbsp;&lt;&nbsp;0)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>*&nbsp;errstr&nbsp;=&nbsp;RTPGetErrorString(err); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Error:%s\\n"</font></span><span>,&nbsp;errstr); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;exit(-1); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=alt><span>} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;main(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;argc,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>**&nbsp;argv) &nbsp;&nbsp;</span></span></li>
    <li class=""><span>{ &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;RTPSession&nbsp;sess; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;localport,portbase; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;status; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>long</font></strong></span><span>&nbsp;remoteIP; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(argc&nbsp;!=&nbsp;4)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Usage:&nbsp;./sender&nbsp;localport\\n"</font></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;-1; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;获得用户指定的端口号 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;remoteIP&nbsp;=&nbsp;inet_addr(argv[1]); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;localport&nbsp;=&nbsp;atoi(argv[2]); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;portbase&nbsp;=&nbsp;atoi(argv[3]); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;创建RTP会话 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;status&nbsp;=&nbsp;sess.Create(localport); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;checkerror(status); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//RTPHeader&nbsp;*rtphdr; </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>long</font></strong></span><span>&nbsp;timestamp1; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*&nbsp;RawData; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;temp[30]; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;lengh&nbsp;,i; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>bool</font></strong></span><span>&nbsp;allports&nbsp;=&nbsp;1; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;sess.AddToAcceptList(remoteIP,&nbsp;allports,portbase); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>do</font></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;</span><span class=comment><font color=#ee0000>//设置接收模式 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;sess.AddToAcceptList(remoteIP,&nbsp;allports,portbase); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;接受RTP数据 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;status&nbsp;=&nbsp;sess.PollData(); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;检索RTP数据源 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(sess.GotoFirstSourceWithData())&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>do</font></strong></span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RTPPacket*&nbsp;packet; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;获取RTP数据报 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>while</font></strong></span><span>&nbsp;((packet&nbsp;=&nbsp;sess.GetNextPacket())&nbsp;!=&nbsp;NULL)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"Got&nbsp;packet&nbsp;!\n"</font></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;timestamp1&nbsp;=&nbsp;packet-&gt;GetTimeStamp(); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;lengh=packet-&gt;GetPayloadLength(); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;RawData=packet-&gt;GetPayload(); &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>for</font></strong></span><span>(i=0;i&lt;lengh;i++){ &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;temp[i]=RawData[i]; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"%c"</font></span><span>,temp[i]); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;temp[i]=</span><span class=string><font color=#ff0000>'\0'</font></span><span>; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;printf(</span><span class=string><font color=#ff0000>"&nbsp;&nbsp;timestamp:&nbsp;%d&nbsp;lengh=%d&nbsp;data:%s\n"</font></span><span>,timestamp1,lengh,&amp;temp); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;删除RTP数据报 </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>delete</font></strong></span><span>&nbsp;packet; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class=keyword><strong><font color=#5697d9>while</font></strong></span><span>&nbsp;(sess.GotoNextSourceWithData()); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;}&nbsp;</span><span class=keyword><strong><font color=#5697d9>while</font></strong></span><span>(1); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;0; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>} &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>#include &lt;stdio.h&gt;
#include "rtpsession.h"
#include "rtppacket.h"
// 错误处理函数
void checkerror(int err)
{
&nbsp;if (err &lt; 0) {
&nbsp; &nbsp;char* errstr = RTPGetErrorString(err);
&nbsp; &nbsp;printf("Error:%s\\n", errstr);
&nbsp; &nbsp;exit(-1);
&nbsp;}
}
int main(int argc, char** argv)
{
&nbsp;RTPSession sess;
&nbsp;int localport,portbase;
&nbsp;int status;
&nbsp;unsigned long remoteIP;
&nbsp;if (argc != 4) {
&nbsp; &nbsp;printf("Usage: ./sender localport\\n");
&nbsp; &nbsp;return -1;
&nbsp;}
&nbsp; // 获得用户指定的端口号
&nbsp;
&nbsp;remoteIP = inet_addr(argv[1]);
&nbsp;localport = atoi(argv[2]);
&nbsp;portbase = atoi(argv[3]);
&nbsp;// 创建RTP会话
&nbsp;status = sess.Create(localport);
&nbsp;checkerror(status);
&nbsp;
&nbsp;//RTPHeader *rtphdr;
&nbsp;unsigned long timestamp1;
&nbsp;unsigned char * RawData;
&nbsp;unsigned char temp[30];
&nbsp;int lengh ,i;
&nbsp;bool allports = 1;
&nbsp;
&nbsp;sess.AddToAcceptList(remoteIP, allports,portbase);
&nbsp;
&nbsp; &nbsp; do {
//设置接收模式
&nbsp; &nbsp; &nbsp; &nbsp;sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);
&nbsp; sess.AddToAcceptList(remoteIP, allports,portbase);
&nbsp; &nbsp;// 接受RTP数据
&nbsp; &nbsp;status = sess.PollData();
&nbsp; &nbsp;
// 检索RTP数据源
&nbsp; &nbsp;if (sess.GotoFirstSourceWithData()) {
&nbsp; &nbsp; &nbsp;do {
&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp;RTPPacket* packet;
&nbsp; &nbsp; &nbsp; &nbsp;// 获取RTP数据报
&nbsp; &nbsp; &nbsp; &nbsp;while ((packet = sess.GetNextPacket()) != NULL) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf("Got packet !\n");
&nbsp; timestamp1 = packet-&gt;GetTimeStamp();
&nbsp; lengh=packet-&gt;GetPayloadLength();
&nbsp; RawData=packet-&gt;GetPayload();
&nbsp;
&nbsp; for(i=0;i&lt;lengh;i++){
&nbsp; &nbsp; &nbsp;temp[i]=RawData[i];
&nbsp;printf("%c",temp[i]);
&nbsp; }
&nbsp; temp[i]='\0';
&nbsp; printf(" &nbsp;timestamp: %d lengh=%d data:%s\n",timestamp1,lengh,&amp;temp);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 删除RTP数据报
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;delete packet;
&nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;} while (sess.GotoNextSourceWithData());
&nbsp; &nbsp;}
&nbsp;} while(1);
&nbsp;return 0;
}
</textarea><!-- Added by RelatedTopic, plugin for Bo-Blog 2.0.0 --> 
<img src ="http://www.cppblog.com/iniwf/aggbug/76941.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-03-17 23:06 <a href="http://www.cppblog.com/iniwf/archive/2009/03/17/76941.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下的实时流媒体编程/jrtplib介绍</title><link>http://www.cppblog.com/iniwf/archive/2009/03/17/76939.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Tue, 17 Mar 2009 14:57:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/03/17/76939.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/76939.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/03/17/76939.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/76939.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/76939.html</trackback:ping><description><![CDATA[<span style="COLOR: #ff0000">转自<a href="http://www.doserver.net/read.php/1028.htm">http://www.doserver.net/read.php/1028.htm</a><br><br>一、流媒体简介</span><br>随着Internet 的日益普及，在网络上传输的数据已经不再局限于文字和图形，而是逐渐向声音和视频等多媒体格式过渡。目前在网络上传输音频/视频（Audio/Video，简称A/V）等多媒体文件时，基本上只有下载和流式传输两种选择。通常说来，A/V文件占据的存储空间都比较大，在带宽受限的网络环境中下载可能要耗费数分钟甚至数小时，所以这种处理方法的延迟很大。如果换用流式传输的话，声音、影像、动画等多媒体文件将由专门的流媒体<a class=mykeyword title=http://www.server-development.cn/go.php/tags/服务器/ href="http://www.server-development.cn/go.php/tags/服务器/" target=_blank><font color=#666666>服务器</font></a>负责向用户连续、实时地发送，这样用户可以不必等到整个文件全部下载完毕，而只需要经过几秒钟的启动延时就可以了，当这些多媒体数据在客户机上播放时，文件的剩余部分将继续从流媒体服务器下载。<br><a name=entrymore></a><br>流（Streaming）是近年在Internet上出现的新概念，其定义非常广泛，主要是指通过网络传输多媒体数据的技术总称。流媒体包含广义和狭义两种内涵：广义上的流媒体指的是使音频和视频形成稳定和连续的传输流和回放流的一系列技术、方法和协议的总称，即流媒体技术；狭义上的流媒体是相对于传统的下载-回放方式而言的，指的是一种从Internet上获取音频和视频等多媒体数据的新方法，它能够支持多媒体数据流的实时传输和实时播放。通过运用流媒体技术，服务器能够向客户机发送稳定和连续的多媒体数据流，客户机在接收数据的同时以一个稳定的速率回放，而不用等数据全部下载完之后再进行回放。<br><br>由于受网络带宽、计算机处理能力和协议规范等方面的限制，要想从Internet上下载大量的音频和视频数据，无论从下载时间和存储空间上来讲都是不太现实的，而流媒体技术的出现则很好地解决了这一难题。目前实现流媒体传输主要有两种方法：顺序流（progressive streaming）传输和实时流（realtime streaming）传输，它们分别适合于不同的应用场合。<br><br><span style="COLOR: #ff0000">顺序流传输</span><br><br>顺序流传输采用顺序下载的方式进行传输，在下载的同时用户可以在线回放多媒体数据，但给定时刻只能观看已经下载的部分，不能跳到尚未下载的部分，也不能在传输期间根据网络状况对下载速度进行调整。由于标准的HTTP服务器就可以发送这种形式的流媒体，而不需要其他特殊协议的支持，因此也常常被称作 HTTP流式传输。顺序流式传输比较适合于高质量的多媒体片段，如片头、片尾或者广告等。<br><br><span style="COLOR: #ff0000">实时流传输</span><br><br>实时流式传输保证媒体信号带宽能够与当前网络状况相匹配，从而使得流媒体数据总是被实时地传送，因此特别适合于现场事件。实时流传输支持随机访问，即用户可以通过快进或者后退操作来观看前面或者后面的内容。从理论上讲，实时流媒体一经播放就不会停顿，但事实上仍有可能发生周期性的暂停现象，尤其是在网络状况恶化时更是如此。与顺序流传输不同的是，实时流传输需要用到特定的流媒体服务器，而且还需要特定网络协议的支持。<br><br><span style="COLOR: #ff0000">二、流媒体协议</span><br><br>实时传输协议（Real-time Transport Protocol，PRT）是在Internet上处理多媒体数据流的一种网络协议，利用它能够在一对一（unicast，单播）或者一对多（multicast，多播）的网络环境中实现传流媒体数据的实时传输。RTP通常使用<a class=mykeyword title=http://www.server-development.cn/go.php/tags/udp/ href="http://www.server-development.cn/go.php/tags/udp/" target=_blank><font color=#666666>UDP</font></a>来进行多媒体数据的传输，但如果需要的话可以使用TCP或者 ATM等其它协议，整个RTP协议由两个密切相关的部分组成：RTP数据协议和RTP控制协议。实时流协议（Real Time Streaming Protocol，RTSP）最早由Real Networks和Netscape公司共同提出，它位于RTP和RTCP之上，其目的是希望通过IP网络有效地传输多媒体数据。<br><br><span style="COLOR: #ff0000">2.1 RTP数据协议</span><br><br>RTP数据协议负责对流媒体数据进行封包并实现媒体流的实时传输，每一个RTP数据报都由头部（Header）和负载（Payload）两个部分组成，其中头部前12个字节的含义是固定的，而负载则可以是音频或者视频数据。RTP数据报的头部格式如图1所示：<br><br><a href="http://www.doserver.net/attachment/1174664576_0.bmp" target=_blank><img class=insertimage title=点击在新窗口中浏览此图片 alt=点击在新窗口中浏览此图片 src="http://www.doserver.net/attachment/1174664576_0.bmp" onload="if(this.width>500) {this.resized=true; this.width=500;}" border=0></a><br>图1 RTP头部格式<br><br>其中比较重要的几个域及其意义如下：<br><br><br><span style="COLOR: #ff0000">CSRC记数（CC）</span>　　表示CSRC标识的数目。CSRC标识紧跟在RTP固定头部之后，用来表示RTP数据报的来源，RTP协议允许在同一个会话中存在多个数据源，它们可以通过RTP混合器合并为一个数据源。例如，可以产生一个CSRC列表来表示一个电话会议，该会议通过一个 RTP混合器将所有讲话者的语音数据组合为一个RTP数据源。 <br><span style="COLOR: #ff0000">负载类型（PT）</span>　　标明RTP负载的格式，包括所采用的编码算法、采样频率、承载通道等。例如，类型2表明该RTP数据包中承载的是用ITU G.721算法编码的语音数据，采样频率为8000Hz，并且采用单声道。 <br>序列号　　用来为接收方提供探测数据丢失的方法，但如何处理丢失的数据则是应用程序自己的事情，RTP协议本身并不负责数据的重传。 <br>时间戳　　记录了负载中第一个字节的采样时间，接收方能够时间戳能够确定数据的到达是否受到了延迟抖动的影响，但具体如何来补偿延迟抖动则是应用程序自己的事情。 <br>从RTP 数据报的格式不难看出，它包含了传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息，这些都为实时的流媒体传输提供了相应的基础。RTP协议的目的是提供实时数据（如交互式的音频和视频）的端到端传输服务，因此在RTP中没有连接的概念，它可以建立在底层的面向连接或面向非连接的传输协议之上；RTP也不依赖于特别的网络地址格式，而仅仅只需要底层传输协议支持组帧（Framing）和分段（Segmentation）就足够了；另外RTP 本身还不提供任何可靠性机制，这些都要由传输协议或者应用程序自己来保证。在典型的应用场合下，RTP一般是在传输协议之上作为应用程序的一部分加以实现的，如图2所示：<br><a href="http://www.doserver.net/attachment/1174664576_1.bmp" target=_blank><img class=insertimage title=点击在新窗口中浏览此图片 alt=点击在新窗口中浏览此图片 src="http://www.doserver.net/attachment/1174664576_1.bmp" onload="if(this.width>500) {this.resized=true; this.width=500;}" border=0></a><br><br>图2 RTP与各种网络协议的关系<br><br><span style="COLOR: #ff0000">2.2 RTCP控制协议</span><br><br>RTCP 控制协议需要与RTP数据协议一起配合使用，当应用程序启动一个RTP会话时将同时占用两个端口，分别供RTP和RTCP使用。RTP本身并不能为按序传输数据包提供可靠的保证，也不提供流量控制和拥塞控制，这些都由RTCP来负责完成。通常RTCP会采用与RTP相同的分发机制，向会话中的所有成员周期性地发送控制信息，应用程序通过接收这些数据，从中获取会话参与者的相关资料，以及网络状况、分组丢失概率等反馈信息，从而能够对服务质量进行控制或者对网络状况进行诊断。<br><br>RTCP协议的功能是通过不同的RTCP数据报来实现的，主要有如下几种类型：<br><br><br>SR　　发送端报告，所谓发送端是指发出RTP数据报的应用程序或者终端，发送端同时也可以是接收端。 <br>RR　　接收端报告，所谓接收端是指仅接收但不发送RTP数据报的应用程序或者终端。 <br>SDES　　源描述，主要功能是作为会话成员有关标识信息的载体，如用户名、邮件地址、电话号码等，此外还具有向会话成员传达会话控制信息的功能。 <br>BYE　　通知离开，主要功能是指示某一个或者几个源不再有效，即通知会话中的其他成员自己将退出会话。 <br>APP　　由应用程序自己定义，解决了RTCP的扩展性问题，并且为协议的实现者提供了很大的灵活性。 <br>RTCP数据报携带有服务质量监控的必要信息，能够对服务质量进行动态的调整，并能够对网络拥塞进行有效的控制。由于RTCP数据报采用的是多播方式，因此会话中的所有成员都可以通过RTCP数据报返回的控制信息，来了解其他参与者的当前情况。<br><br>在一个典型的应用场合下，发送媒体流的应用程序将周期性地产生发送端报告SR，该RTCP数据报含有不同媒体流间的同步信息，以及已经发送的数据报和字节的计数，接收端根据这些信息可以估计出实际的数据传输速率。另一方面，接收端会向所有已知的发送端发送接收端报告RR，该RTCP数据报含有已接收数据报的最大序列号、丢失的数据报数目、延时抖动和时间戳等重要信息，发送端应用根据这些信息可以估计出往返时延，并且可以根据数据报丢失概率和时延抖动情况动态调整发送速率，以改善网络拥塞状况，或者根据网络状况平滑地调整应用程序的服务质量。<br><br><span style="COLOR: #ff0000">2.3 RTSP实时流协议</span><br><br>作为一个应用层协议，RTSP提供了一个可供扩展的框架，它的意义在于使得实时流媒体数据的受控和点播变得可能。总的说来，RTSP是一个流媒体表示协议，主要用来控制具有实时特性的数据发送，但它本身并不传输数据，而是必须依赖于下层传输协议所提供的某些服务。RTSP可以对流媒体提供诸如播放、暂停、快进等操作，它负责定义具体的控制消息、操作方法、状态码等，此外还描述了与RTP间的交互操作。<br><br>RTSP在制定时较多地参考了 HTTP/1.1协议，甚至许多描述与HTTP/1.1完全相同。RTSP之所以特意使用与HTTP/1.1类似的语法和操作，在很大程度上是为了兼容现有的Web基础结构，正因如此，HTTP/1.1的扩展机制大都可以直接引入到RTSP中。<br><br>由RTSP控制的媒体流集合可以用表示描述（Presentation Description）来定义，所谓表示是指流媒体服务器提供给客户机的一个或者多个媒体流的集合，而表示描述则包含了一个表示中各个媒体流的相关信息，如数据编码/解码算法、网络地址、媒体流的内容等。<br><br>虽然RTSP服务器同样也使用标识符来区别每一流连接会话（Session），但 RTSP连接并没有被绑定到传输层连接（如TCP等），也就是说在整个RTSP连接期间，RTSP用户可打开或者关闭多个对RTSP服务器的可靠传输连接以发出RTSP 请求。此外，RTSP连接也可以基于面向无连接的传输协议（如UDP等）。<br><br>RTSP协议目前支持以下操作：<br><br><br>检索媒体　　允许用户通过HTTP或者其它方法向媒体服务器提交一个表示描述。如表示是组播的，则表示描述就包含用于该媒体流的组播地址和端口号；如果表示是单播的，为了安全在表示描述中应该只提供目的地址。 <br>邀请加入　　媒体服务器可以被邀请参加正在进行的会议，或者在表示中回放媒体，或者在表示中录制全部媒体或其子集，非常适合于分布式教学。 <br>添加媒体　　通知用户新加入的可利用媒体流，这对现场讲座来讲显得尤其有用。与HTTP/1.1类似，RTSP请求也可以交由代理、通道或者缓存来进行处理。 <br><span style="COLOR: #ff0000">三、流媒体编程</span><br><br>RTP 是目前解决流媒体实时传输问题的最好办法，如果需要在Linux平台上进行实时流媒体编程，可以考虑使用一些开放源代码的RTP库，如LIBRTP、 JRTPLIB等。 JRTPLIB是一个面向对象的RTP库，它完全遵循RFC 1889设计，在很多场合下是一个非常不错的选择，下面就以JRTPLIB为例，讲述如何在Linux平台上运用RTP协议进行实时流媒体编程。<br><br><span style="COLOR: #ff0000">3.1 环境搭建</span><br><br>JRTPLIB 是一个用C++语言实现的RTP库，目前已经可以运行在Windows、Linux、FreeBSD、Solaris、Unix和 VxWorks等多种操作系统上。要为Linux 系统安装JRTPLIB，首先从JRTPLIB的网站（http: //lumumba.luc.ac.be/jori/jrtplib/jrtplib.html）下载最新的源码包，此处使用的是jrtplib- 2.7b.tar.bz2。假设下载后的源码包保存在/usr/local/src目录下，执行下面的命令可以对其进行解压缩：<br><br>
<div class=quote>
<div class=quote-title>引用</div>
<div class=quote-content>[root@linuxgam src]# bp2 -dc jrtplib-2.7b.tar.bz2 | tar xvf -<br><br><br>接下去需要对JRTPLIB进行配置和编译：<br><br>[root@linuxgam src]# cd jrtplib-2.7[root@linuxgam jrtplib-2.7b]# ./configure [root@linuxgam jrtplib-2.7b]# make<br><br><br>最后再执行如下命令就可以完成JRTPLIB的安装：<br><br>[root@linuxgam jrtplib-2.7b]# make install</div>
</div>
<br><br><br><span style="COLOR: #ff0000">3.2 初始化</span><br><br>在使用JRTPLIB进行实时流媒体数据传输之前，首先应该生成RTPSession类的一个实例来表示此次RTP会话，然后调用Create() 方法来对其进行初始化操作。RTPSession类的Create()方法只有一个参数，用来指明此次RTP会话所采用的端口号。清单1给出了一个最简单的初始化框架，它只是完成了RTP会话的初始化工作，还不具备任何实际的功能。<br><br>代码清单1：initial.cpp<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;"rtpsession.h"&nbsp;&nbsp;&nbsp;&nbsp; </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;main(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>){&nbsp;&nbsp;RTPSession&nbsp;sess;&nbsp;&nbsp;sess.Create(5000);&nbsp;&nbsp;</span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;0;} &nbsp;&nbsp;</span></span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>#include "rtpsession.h"&nbsp;&nbsp;&nbsp;&nbsp;
int main(void){&nbsp;&nbsp;RTPSession sess;&nbsp;&nbsp;sess.Create(5000);&nbsp;&nbsp;return 0;}
</textarea><br>如果RTP会话创建过程失败，Create()方法将会返回一个负数，通过它虽然可以很容易地判断出函数调用究竟是成功的还是失败的，但却很难明白出错的原因到底什么。JRTPLIB采用了统一的错误处理机制，它提供的所有函数如果返回负数就表明出现了某种形式的错误，而具体的出错信息则可以通过调用 RTPGetErrorString()函数得到。RTPGetErrorString()函数将错误代码作为参数传入，然后返回该错误代码所对应的错误信息。清单2给出了一个更加完整的初始化框架，它可以对RTP会话初始化过程中所产生的错误进行更好的处理：<br><br>代码清单2：framework.cpp<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=preprocessor><strong><font color=#cd00cd>#include&nbsp;"rtpsession.h" </font></strong></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;main(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>){&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>RTPSession&nbsp;sess;&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;status;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>*&nbsp;msg;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>sess.Create(6000);&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=alt><span>msg&nbsp;=&nbsp;RTPGetErrorString(status);&nbsp;&nbsp; &nbsp;&nbsp;</span></li>
    <li class=""><span>printf(</span><span class=string><font color=#ff0000>"Error&nbsp;String:&nbsp;%s\\n"</font></span><span>,&nbsp;msg);&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=keyword><strong><font color=#5697d9>return</font></strong></span><span>&nbsp;0; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>} &nbsp;&nbsp;</span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>#include "rtpsession.h"
int main(void){ &nbsp;
RTPSession sess; &nbsp;
int status; &nbsp;
char* msg; &nbsp;
sess.Create(6000); &nbsp;
msg = RTPGetErrorString(status); &nbsp;
printf("Error String: %s\\n", msg); &nbsp;
return 0;
}
</textarea><br><br>设置恰当的时戳单元，是RTP会话初始化过程所要进行的另外一项重要工作，这是通过调用RTPSession类的SetTimestampUnit ()方法来实现的，该方法同样也只有一个参数，表示的是以秒为单元的时戳单元。例如，当使用RTP会话传输8000Hz采样的音频数据时，由于时戳每秒钟将递增8000，所以时戳单元相应地应该被设置成1/8000：<br><br>sess.SetTimestampUnit(1.0/8000.0);<br><br><br><span style="COLOR: #ff0000">3.3 数据发送</span><br><br>当RTP 会话成功建立起来之后，接下去就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发送的目标地址，RTP协议允许同一会话存在多个目标地址，这可以通过调用RTPSession类的AddDestination()、DeleteDestination()和 ClearDestinations()方法来完成。例如，下面的语句表示的是让RTP会话将数据发送到本地主机的6000端口：<br><br>unsigned long addr = ntohl(inet_addr("127.0.0.1"));sess.AddDestination(addr, 6000);<br><br><br>目标地址全部指定之后，接着就可以调用RTPSession类的SendPacket()方法，向所有的目标地址发送流媒体数据。SendPacket()是RTPSession类提供的一个重载函数，它具有下列多种形式：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;SendPacket(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*data,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;len) &nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;SendPacket(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*data,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;len,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;pt,</span><span class=datatypes><strong><font color=#2e8b57>bool</font></strong></span><span>&nbsp;mark,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>long</font></strong></span><span>&nbsp;timestampinc) &nbsp;&nbsp;</span></span></li>
    <li class=alt><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;SendPacket(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*data,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;len,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>short</font></strong></span><span>&nbsp;hdrextID,</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*hdrextdata,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;numhdrextwords) &nbsp;&nbsp;</span></span></li>
    <li class=""><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;SendPacket(</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*data,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;len,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;pt,</span><span class=datatypes><strong><font color=#2e8b57>bool</font></strong></span><span>&nbsp;mark,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>long</font></strong></span><span>&nbsp;timestampinc,unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>short</font></strong></span><span>&nbsp;hdrextID,</span><span class=keyword><strong><font color=#5697d9>void</font></strong></span><span>&nbsp;*hdrextdata,</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;numhdrextwords) &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>int SendPacket(void *data,int len)
int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc)
int SendPacket(void *data,int len,unsigned short hdrextID,void *hdrextdata,int numhdrextwords)
int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc,unsigned short hdrextID,void *hdrextdata,int numhdrextwords)
</textarea><br><br>SendPacket()最典型的用法是类似于下面的语句，其中第一个参数是要被发送的数据，而第二个参数则指明将要发送数据的长度，再往后依次是RTP负载类型、标识和时戳增量。<br><br>sess.SendPacket(buffer, 5, 0, false, 10);<br><br><br>对于同一个RTP会话来讲，负载类型、标识和时戳增量通常来讲都是相同的，JRTPLIB允许将它们设置为会话的默认参数，这是通过调用 RTPSession类的SetDefaultPayloadType()、SetDefaultMark()和 SetDefaultTimeStampIncrement()方法来完成的。为RTP会话设置这些默认参数的好处是可以简化数据的发送，例如，如果为 RTP会话设置了默认参数：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span>sess.SetDefaultPayloadType(0); &nbsp;&nbsp;</span></span></li>
    <li class=""><span>sess.SetDefaultMark(</span><span class=keyword><strong><font color=#5697d9>false</font></strong></span><span>); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>sess.SetDefaultTimeStampIncrement(10); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);
</textarea><br><br>之后在进行数据发送时只需指明要发送的数据及其长度就可以了：<br><br>sess.SendPacket(buffer, 5);<br><br><br><span style="COLOR: #ff0000">3.4 数据接收</span><br><br>对于流媒体数据的接收端，首先需要调用RTPSession类的PollData()方法来接收发送过来的RTP或者RTCP数据报。由于同一个 RTP会话中允许有多个参与者（源），你既可以通过调用RTPSession类的GotoFirstSource()和GotoNextSource() 方法来遍历所有的源，也可以通过调用RTPSession类的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法来遍历那些携带有数据的源。在从RTP会话中检测出有效的数据源之后，接下去就可以调用 RTPSession类的GetNextPacket()方法从中抽取RTP数据报，当接收到的RTP数据报处理完之后，一定要记得及时释放。下面的代码示范了该如何对接收到的RTP数据报进行处理：<br>
<div class=dp-highlighter>
<div class=bar>
<div class=tools><a onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>view plain</font></a><a onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" href="http://www.doserver.net/#"><font color=#808080>copy to clipboard</font></a><a onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" href="http://www.doserver.net/#"><font color=#808080>print</font></a><a onclick="dp.sh.Toolbar.Command('About',this);return false;" href="http://www.doserver.net/#"><font color=#808080>?</font></a></div>
</div>
<ol class=dp-cpp>
    <li class=alt><span><span class=keyword><strong><font color=#5697d9>if</font></strong></span><span>&nbsp;(sess.GotoFirstSourceWithData())&nbsp;{&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;</span><span class=keyword><strong><font color=#5697d9>do</font></strong></span><span>&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;RTPPacket&nbsp;*pack;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>pack&nbsp;=&nbsp;sess.GetNextPacket();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#ee0000>//&nbsp;处理接收到的数据&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;pack;&nbsp; </font></span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;}&nbsp;</span><span class=keyword><strong><font color=#5697d9>while</font></strong></span><span>&nbsp;(sess.GotoNextSourceWithData()); &nbsp;&nbsp;</span></span></li>
    <li class=alt><span>} &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp; &nbsp;&nbsp;</span></li>
</ol>
</div>
<textarea class=c style="DISPLAY: none" name=code rows=15 cols=100>if (sess.GotoFirstSourceWithData()) {
do { &nbsp; &nbsp;RTPPacket *pack;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;
pack = sess.GetNextPacket();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;// 处理接收到的数据 &nbsp; &nbsp;delete pack;
} while (sess.GotoNextSourceWithData());
}
</textarea><br>JRTPLIB为RTP数据报定义了三种接收模式，其中每种接收模式都具体规定了哪些到达的RTP数据报将会被接受，而哪些到达的RTP数据报将会被拒绝。通过调用RTPSession类的SetReceiveMode()方法可以设置下列这些接收模式：<br><br>RECEIVEMODE_ALL　　缺省的接收模式，所有到达的RTP数据报都将被接受； <br>RECEIVEMODE_IGNORESOME　　除了某些特定的发送者之外，所有到达的RTP数据报都将被接受，而被拒绝的发送者列表可以通过调用AddToIgnoreList()、DeleteFromIgnoreList()和ClearIgnoreList()方法来进行设置； <br>RECEIVEMODE_ACCEPTSOME　　除了某些特定的发送者之外，所有到达的RTP数据报都将被拒绝，而被接受的发送者列表可以通过调用AddToAcceptList ()、DeleteFromAcceptList和ClearAcceptList ()方法来进行设置。 <br><span style="COLOR: #ff0000">3.5 控制信息</span><br><br>JRTPLIB 是一个高度封装后的RTP库，程序员在使用它时很多时候并不用关心RTCP数据报是如何被发送和接收的，因为这些都可以由 JRTPLIB自己来完成。只要PollData()或者SendPacket()方法被成功调用，JRTPLIB就能够自动对到达的RTCP数据报进行处理，并且还会在需要的时候发送RTCP数据报，从而能够确保整个RTP会话过程的正确性。<br><br>而另一方面，通过调用RTPSession类提供的SetLocalName()、SetLocalEMail()、 SetLocalLocation()、SetLocalPhone()、SetLocalTool()和SetLocalNote()方法， JRTPLIB又允许程序员对RTP会话的控制信息进行设置。所有这些方法在调用时都带有两个参数，其中第一个参数是一个char型的指针，指向将要被设置的数据；而第二个参数则是一个int型的数值，表明该数据中的前面多少个字符将会被使用。例如下面的语句可以被用来设置控制信息中的电子邮件地址：<br><br>sess.SetLocalEMail("xiaowp@linuxgam.com",19);<br><br><br>在RTP 会话过程中，不是所有的控制信息都需要被发送，通过调用RTPSession类提供的EnableSendName()、 EnableSendEMail()、EnableSendLocation()、EnableSendPhone()、EnableSendTool ()和EnableSendNote()方法，可以为当前RTP会话选择将被发送的控制信息。<br><br><span style="COLOR: #ff0000">3.6 实际应用</span><br><br>最后通过一个简单的流媒体发送-接收实例，介绍如何利用JRTPLIB来进行实时流媒体的编程。清单3给出了数据发送端的完整代码，它负责向用户指定的IP地址和端口，不断地发送RTP数据包：<br><br><br><span style="COLOR: #ff0000">四、小结</span><br><br>随着多媒体数据在 Internet上所承担的作用变得越来越重要，需要实时传输音频和视频等多媒体数据的场合也将变得越来越多，如IP电话、视频点播、在线会议等。RTP 是用来在Internet上进行实时流媒体传输的一种协议，目前已经被广泛地应用在各种场合，JRTPLIB是一个面向对象的RTP封装库，利用它可以很方便地完成Linux平台上的实时流媒体编程。<br><br><span style="COLOR: #ff0000">五、参考资源</span><br><br>1. 在JRTPLIB的网站http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.html上，可以下载到JRTPLIB最新的源码包，并且还能找到一些与RTP相关的资源。<br><br>2. 顾淑珍等编著，宽带增值服务开发实例，北京：机械工业出版社，2002<br><br>3. 黄永峰等编著，IP网络多媒体通信技术，北京：人民邮电出版社，2003
<img src ="http://www.cppblog.com/iniwf/aggbug/76939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-03-17 22:57 <a href="http://www.cppblog.com/iniwf/archive/2009/03/17/76939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spcaview.spcaserver源码详解</title><link>http://www.cppblog.com/iniwf/archive/2009/03/17/76933.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Tue, 17 Mar 2009 14:31:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/03/17/76933.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/76933.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/03/17/76933.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/76933.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/76933.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自http://blog.csdn.net/neohuo/archive/2006/04/01/646507.aspx首先看看对视频设备自定义的数据结构，源码在spcav4l中：struct&nbsp;vdIn&nbsp;{&nbsp;int&nbsp;fd;&nbsp;char&nbsp;*videodevice&nbsp;;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n...&nbsp;&nbsp;<a href='http://www.cppblog.com/iniwf/archive/2009/03/17/76933.html'>阅读全文</a><img src ="http://www.cppblog.com/iniwf/aggbug/76933.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-03-17 22:31 <a href="http://www.cppblog.com/iniwf/archive/2009/03/17/76933.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>摄像头（WebCam）在Linux中采用Spcaserv 架设网络视频服务器 (v0.1b) </title><link>http://www.cppblog.com/iniwf/archive/2009/03/17/76931.html</link><dc:creator>iniwf</dc:creator><author>iniwf</author><pubDate>Tue, 17 Mar 2009 14:26:00 GMT</pubDate><guid>http://www.cppblog.com/iniwf/archive/2009/03/17/76931.html</guid><wfw:comment>http://www.cppblog.com/iniwf/comments/76931.html</wfw:comment><comments>http://www.cppblog.com/iniwf/archive/2009/03/17/76931.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/iniwf/comments/commentRss/76931.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/iniwf/services/trackbacks/76931.html</trackback:ping><description><![CDATA[转自<a href="http://www.chinalinuxpub.com/read.php?wid=1227">http://www.chinalinuxpub.com/read.php?wid=1227</a><br><br><strong><font size=4>1、网络监控服务器的说明；<br></font></strong>
<p>如何用摄像头做一个时时监控的系统呢？也就是说有一台网络专用临控服务器，其它客户机通过网络影像监控软件或浏览器来进行时时监控摄像头所在位置，所发生的一切。我没有弄过专业类的视频监控，也不知道他们用的是什么专业的设备，也不知道他们有什么功能。我所理解的网络监控服务器应用如下内容：</p>
<pre>＊ 时时监控功能：时时监控摄像头所监视的范围；
＊ 数据流采集功能：时时采集监控数据流，以备将来查找相关影像资料做准备；
＊ 照片抓取功能：比如每隔5分钟抓取一张照片；
＊  时间戳功能；
＊ 数据备份功能（附加）；
</pre>
<p><font id=2 size=4><strong><br>2、Linux 用摄像头（WebCam）做网络监视服务器所需条件和要达到的目的；<br></strong></font></p>
<p>我们首先定义一下我们用摄像头（WebCam）来做网络时时临控系统所需的条件和要达到的目的；</p>
<p><font id=2.1 size=3><strong><br>2.1 所需要的软件环境和硬件设备；<br></strong></font></p>
<p><strong>首先：</strong>得有一个摄像头（WebCam），并且是 能被 Linux 所支持的摄像头，<strong>请参考：</strong> <a href="http://www.linuxsir.org/main/?q=node/219"><font color=#4444cc>《摄像头（WebCam）在Linux操作系统中的驱动方法 》</font></a></p>
<p><strong>其次：</strong>软件环境，我们用 Spcaserv来架设；</p>
<p><strong>第三：</strong>网络环境，我们得有一个网络环境，比如我来测试我所做的网络监视系统是否成功，至少在局域网内有两台机器吧，虽然一台也能做，但至于网络的其它计算机是否能看得到，如果机器太少，我们无从得知；</p>
<p><font id=2.2 size=3><strong><br>2.2 所要达到的目的；<br></strong></font></p>
<pre>＊ 时间监控：能跨平台，所有的系统都无障碍查看监视；
＊ 数据采集：可在服务器端进行，但要运行在桌面环境下；在Linux 客户端中进行；由于软件限制，只能这样说了；
</pre>
<p><font id=3 size=4><strong><br>3、Linux 用摄像头（WebCam）的驱动方法；<br></strong></font></p>
<p><strong>请参考：</strong> <a href="http://www.linuxsir.org/main/?q=node/219"><font color=#4444cc>《摄像头（WebCam）在Linux操作系统中的驱动方法 》</font></a></p>
<p><font id=4 size=4><strong><br>4、在 Linux 中用 Spcaserv架设网络监控服务器；<br></strong></font></p>
<p><font id=4.1 size=3><strong><br>4.1 下载软件 Spcaview；<br></strong></font></p>
<p><strong>下载地址：</strong> <a href="http://mxhaard.free.fr/spca50x/Download"><a href="http://mxhaard.free.fr/spca50x/Download>" target=_blank><font color=#333333>http://mxhaard.free.fr/spca50x/Download&gt;</font></a></a> ，我下载的是目前这个版本： <a href="http://mxhaard.free.fr/spca50x/Download/spcaview-20051212.tar.gz"><font color=#4444cc>spcaview-20051212.tar.gz</font></a></p>
<p><strong>spcaview-20051212.tar.gz软件包包含一组工具；</strong></p>
<p>＊ Spcaview 工具是用来纪录数据流，也能用来播放数据；也能做为网络监视客户端用；<br>＊ Spcaserv 是流媒体服务器，我们就是用这个工具来做监控服务器；<br>＊ Spcacat 简单图片的抓取工具，不能用于网络监视客户端 ；</p>
<p><font id=4.2 size=3><strong><br>4.2 Spcaview 软件包的安装；<br></strong></font></p>
<p><strong>依赖关系；</strong></p>
<p>此软件依赖 libsdl，要先安装它才行，下载地址：<a href="http://www.libsdl.org/" target=_blank><font color=#333333>http://www.libsdl.org</font></a> ，我下载的是：SDL-1.2.10.tar.gz</p>
<p><a href="http://www.libsdl.org/download-1.2.php" target=_blank><font color=#333333>http://www.libsdl.org/download-1.2.php</font></a></p>
<p><font color=#333333></font>
<div class=codeblock><code><font face=新宋体>[root@localhost ~]# tar zxvf SDL-1.2.10.tar.gz<br>[root@localhost ~]# cd SDL-1.2.10<br>[root@localhost SDL-1.2.10]# ./configure ; make ;make install</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>安装SpcaView 软件包：</strong></p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost ~]# tar zxvf spcaview-20051212.tar.gz<br>[root@localhost ~]# cd spcaview-20051212<br>[root@localhost spcaview-20051212]# make ; make install</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>配置可执行程序的路径：</strong></p>
<p>可执行的工具被安装到 /usr/local/bin目录中，所以我们还要配置一下用户的环境变量PATH 。配置命令执行路径，在当前用户家目录下的.bashrc文件中加入下面的一行； </p>
<p>
<div class=codeblock><code><font face=新宋体>export PATH=".:/bin:/sbin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/X11R6/bin"</font></code></div>
<p><font face=新宋体></font></p>
<p>然后运行如下命令；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost spcaview-20051212]# source .bashrc</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>关于路径的配置，请参考：</strong><a href="http://www.linuxsir.org/main/?q=node/26"><font color=#4444cc>《Linux 命令及可执行程序路径的设置》</font></a> ；<a href="http://www.linuxsir.org/main/?q=node/193"><font color=#4444cc>《关于Linux 文件系统中路径的理解》</font></a></p>
<p><font id=4.3 size=3><strong><br>4.3 Spcaserv 服务器的配置和运行；<br></strong></font></p>
<p><font id=4.31 size=2><strong><br>4.31 Spcaserv 服务器的运行；<br></strong></font></p>
<p><strong>Spcaserv 运行方法如下；</strong></p>
<p>
<div class=codeblock><code><font face=新宋体>spcaserv [-h -d -g -f -s] [-w Port]</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>参数说明：</strong></p>
<pre>-h      查看帮助；
-d      /dev/videoX       指定摄像头设备，比如 /dev/video0
-g      use read method for grab instead mmap
-f &nbsp; &nbsp; &nbsp; &nbsp;   影像格式，默认为YUV420P，fourcc I420
jpg JPEG                fourcc MJPG
yuv YUV420P             fourcc I420
r16 RGB565 16bits       fourcc RGB2
r24 RGB 24bits          fourcc RGB3
r32 RGB 32bits          fourcc RGB4
-v RAW data             fourcc RAWD
-s      设置分辨率，宽x高  ，比如  320x240，或 640x480，或160x120;
-w      指定服务器的网络地址或端口，比如 192.168.1.3:8888
</pre>
<p><font id=4.32 size=2><strong><br>4.32 Spcaserv 运行示例；<br></strong></font></p>
<p>我在 192.168.1.3 这台机器做为网络监控服务器，并且指定服务器运行在 8888端口上；并且客户端监视时所显示的画面是 320x240的分辨率；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost spcaview-20051212]# spcaserv -s 320x240 -w 192.168.1.3:8888&amp;</font></code></div>
<p><font face=新宋体></font></p>
<p><font id=4.33 size=2><strong><br>4.33 配置HTTPD服务器，以便客户端能在浏览器下查看；<br></strong></font></p>
<p><strong>首先：</strong>安装和配置HTTPD服务器；</p>
<p>至于HTTPD服务器在Linux常用的就是Apache服务器，您可以通过您所用的发行版提供的软件包来安装，也可以自行编译安装，在 LinuxSir.Org 的网络讨论区的置顶帖子中有很多这样的帖子；安装好HTTPD服务器后，要把服务器启动起来。</p>
<p>在 Fedora 或Redhat 中，如果是用其所提供的 RPM包安装的，要用如下方法启动；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost spcaview-20051212]# /etc/init.d/httpd start</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>其次：</strong>复制SpcaView 软件包解压目录下的 http-java-applet 目录到WEB服务器的家目录下；</p>
<p>比如Fedora的HTTPD服务器的默认家目录是 /var/www/html，我以我的机器来说明了，所以我要做如下的工作；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost spcaview-20051212]# cp -R http-java-applet /var/www/html/webcam/<br>[root@localhost spcaview-20051212]# chown -R 755 /var/www/html/webcam</font></code></div>
<p><font face=新宋体></font></p>
<p><strong>第三：</strong> 进入/var/www/html/webcam 目录制作视频首页；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost spcaview-20051212]# cd /var/www/html/webcam<br>[root@localhost webcam]# cp index-sample.html index.htm</font></code></div>
<p><font face=新宋体></font></p>
<p>您可以用查看index.htm文件内容，可以自己改一改，也就是把洋文改成中文；根据自己的情况自己看着办吧；</p>
<p><font id=5 size=4><strong><br>5、 网络客户端监控工具介绍；<br></strong></font></p>
<p>对于客户端网络监控，能跨平台的客户端只有通过浏览器了，这是最方便的方法；另外在Linux中有 Spcaview工具可用；现一一介绍；</p>
<p><font id=5.1 size=3><strong><br>5.1 通过浏览器监控；<br></strong></font></p>
<p><font id=5.11 size=2><strong><br>5.11 安装JRE，以让浏览器支持JAVA程序；<br></strong></font></p>
<p>我们可以通过浏览器来监控，利用浏览器监控可以跨平台监视，这样无论Linux、Windows、还是MacOS，或者是BSD及其它UNIX系统，只要有图形界面的浏览器，在桌面环境下就可以对摄像头（WebCam） 所&#8220;控制&#8221;的范围进行监控。</p>
<p>由于服务器端的WEB页面包括JAVA控件，所以您得安装 JAVA环境。要让浏览器支持JAVA插件。</p>
<p>在 Linux 中，您可以参考：<a href="http://www.linuxsir.org/main/?q=node/59"><font color=#4444cc>《JRE 安装和配置，以适合JAVA程序运行所具备的环境》</font></a></p>
<p>在Windows中，点鼠标就可以完成操作，不再介绍了；</p>
<p><font id=5.12 size=2><strong><br>5.12 浏览器监控示例（跨平台）；<br></strong></fpnt></p>
<p>当您安装好JRE后，在浏览器的地址栏上输入Spcaserv监控服务器地址，看能不能显示出来？比如我的服务器地址是：</p>
<pre><a href="http://192.168.1.3/webcam/index.htm" target=_blank><font color=#333333>http://192.168.1.3/webcam/index.htm</font></a>
</pre>
<p>如图所示，我们可以看到在局域网的计算机都可以通过浏览器进行监视了；</p>
<p><a href="http://www.linuxsir.org/main/files/spcaserv0000linuxsir.JPG"><img onmousewheel="return bbimg(this)" onmouseover="this.style.cursor='hand'" style="CURSOR: hand" onclick="{window.open('http://www.linuxsir.org/main/files/spcaserv0000linuxsir.JPG');}" height=200 src="http://www.linuxsir.org/main/files/spcaserv0000linuxsir.JPG" onload="if(this.width>500) {this.resized=true; this.width=500;}" weight="200"></a> <a href="http://www.linuxsir.org/main/files/spcaserv0001linuxsir.JPG"><img onmousewheel="return bbimg(this)" onmouseover="this.style.cursor='hand'" style="CURSOR: hand; ZOOM: 70%" onclick="{window.open('http://www.linuxsir.org/main/files/spcaserv0001linuxsir.JPG');}" height=200 src="http://www.linuxsir.org/main/files/spcaserv0001linuxsir.JPG" onload="if(this.width>500) {this.resized=true; this.width=500;}" weight="200"></a></p>
<p><font id=5.2 size=3><strong><br>5.2 Linux 客户端通过Spcaview；<br></strong></font></p>
<p><font id=5.21 size=2><strong><br>5.21 Spcaview 进行网络时时监控；<br></strong></font></p>
<p>Spcaview 是 Linux网络客户端监控工具，当然也能用于本地测试摄像头用，如果Spcaserv占用了摄像着，只能作为网络监控工具来用，通过-w参数来指定Spcaserv 服务器地址；</p>
<p>Spcaserv 时时监控很简单，您可以通过 spcaserv -h 来获得帮助；比如我们获取 Spcaserv服务器上的监视数据流。就可以用下面的办法；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost ~]# spcaview -s 320x240 -w 192.168.1.3</font></code></div>
<p><font face=新宋体></font></p>
<p>当然您可以在服务器端运行上面的命令也可，如果在服务器端运行，得有桌面环境；</p>
<p><font id=5.22 size=2><strong><br>5.22 通过Spcaview 进行数据流采集；<br></strong></font></p>
<p>对于数据流的采集，可以在服务器端，也可以在客户端。当然服务器端和客户端都得有桌面环境； Spcaview 工具是通过 -o 参数把数据流传到一个文件中。您也可以采用压缩数据流的办法，要用到-z参数；</p>
<p>
<div class=codeblock><code><font face=新宋体>[root@localhost ~]# spcaview -h 注：查看帮助；</font></code></div>
<p><font face=新宋体></font></p>
<p>关于通过Spcaview通过指定 Spcaserv服务器地址的办法来采集数据，我测试并成功，表现在不能写入到输出文件中，我先学习学习再说。如果您成功了，请在本文后面留言 ，谢谢。</p>
<p>SORRY 。。。</p>
<p><font id=6 size=4><strong><br>6、其它网络监控服务器；<br></strong></font></p>
<p><font id=7 size=4><strong><br>7、关于本文；<br></strong></font></p>
<p><font id=8 size=4><strong><br>8、更新日志；<br></strong></font></p>
<p>2006.06.10 v0.1b 正文完成，进入修订阶段；</p>
<p><font id=9 size=4><strong><br>9、参考文档；<br></strong></font></p>
<p>spcaview -h<br>spcaserv -h<br>Spcaview 软件包中的README；</p>
</font>
<img src ="http://www.cppblog.com/iniwf/aggbug/76931.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/iniwf/" target="_blank">iniwf</a> 2009-03-17 22:26 <a href="http://www.cppblog.com/iniwf/archive/2009/03/17/76931.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>