Ubuntu下使用串口配置H3C流量转发

2015-04-02 · qhsong

Ubuntu下ntopng配置小记

$ sudo ntpdate time.buptnet.edu.cn #对时 $ svn co https://svn.ntop.org/svn/ntop/trunk/ntopng/ $ cd ntopng $ rm .svn/ -r $ sudo apt-get install autoconf automake libtool libcurl4-openssl-dev libsqlite3-dev libpcap-dev libcairo2-dev libpango1.0-dev libxml2-dev libnuma-dev $ ./autogen.sh $ make $ sudo make install 貌似对PF_RING有依赖,还在研究中。。重新make install PF $ cd kernel $ sudo make && make install $ cd ../userland/lib $ sudo make && make install 需要修改ntopng的makefile文件 在LIBS变量后面加入 -lpfring -lnuma //TODO:需要安装redis server $ sudo apt-get install redis-server 实际使用中发现CPU占用率过高,CPU占用率在65%左右,考虑可能使用的是libpcap库是原生的,并不是PF_RING重写的库,所以需要重新编译这个程序,使用PF_RING中的库。 重新编译PF_RING中的libpcap之后,效率提升10倍!CPU稳定在10%以内,以前是60%~70%。 重新编译PF_RING方法:...

2015-03-31 · qhsong

关于博客新系统的说明

从暑假到现在,拖拖踏踏的把这个博客系统架起来,起因是因为听了内核恐慌:静态网站生成器讲的,作为一个个人博客,Wordpress太大而且笨重,需要太多的资源。所以最后决定把这一套给抛弃了。另外一个个人的原因是因为自己开始喜欢上了Markdown这样的语言来撰写博客,Wordpress 的插件对Markdown支持并不是很好,所以就想找一个Markdown的编辑器。就这样使用上了Hugo作为我的博客生成。现在做笔记都是用Markdown语言,使用马克飞象和evernote进行同步,还挺喜欢这个软件的,有喜欢的大家可以试试。 hugo是一个用go语言做的静态网站生成器,生成速度快,由于使用的是go语言,所以有go语言一切的优点。我最喜欢的是没有依赖,整个安装程序就只有一个可执行文件,就再也没有别的东西了。 在使用hugo时,最困难的就是进行新旧博客的转换。由于以前的网站没有采用静态URL,所以所有的文件都要自己重新复制粘贴,所以会比较累一些。好在花了几个晚上的时间把这个问题解决了。 现在我采用的是Ngnix做反响代理,代理到hugo的端口,代码托管在github上,每次同步可以通过git进行同步。hugo的启动使用的命令是hugo server -w -b http://sqh.me --appendPort=false。这是一件非常有意思的事情。 总之,换掉了冗杂的wordpress,拥抱go语言和静态网站。

2015-03-16 · qhsong

理解IPv6(1):介绍

v4局限 IP地址耗尽、路由表较大、简洁配置(DHCP)、IP级别安全、QoS NAT转换、NAT编辑器(对IP数据包进行改动,如FTP中的IP地址转换) v6特性 新的协议头格式(简化协议头) 巨大地址空间 有效的、分级的寻址和路由结构 有状态(DHCP)、无状态(自动配置链路地址)的地址配置 更好的QoS(对流进行处理) 新协议处理邻节点交互(NDP 取代ARP、ICMPv4) 可拓展性 v6术语 节点 路由器:转发不以它为目标的数据包节点 主机:不转发以它为目标的数据包节点 上层协议:传输层协议TCP/UDP + Internet层协议ICMPv6 局域网段:链路的一部分、单一介质、以网桥或者二层交换为边界 链路:以路由器为边界的一个或多个局域网段 子网:使用相同的64位IPv6地址前缀的一个或多个链路、可以被内部路由器分为几个部分 网络:路由器连接起来的两个或者多个子网 邻节点:同一链路上的节点 接口:一个链路上的物理或者逻辑节点 地址:IP地址 数据包:IPv6的PDU(协议数据单元) 链路MTU:一个链路上发送的MTU 路径MTU:源节点到目标节点路径上,不执行主机拆分的情况下可发送最大长度IPv6包 解决问题 IP地址耗尽、国际地址分配 重建端到端通讯(没有NAT) IPv6区别地址选择(可以根据不同作用域进行地址选择) 更好的转发(转发过程中字段处理较少) 内置移动性和安全性 win Server 2003种的IPv6 支持特性 基本协议栈:单播、多播、泛播寻址;ICMPv6、ND、MLD协议;无状态的地址自动配置;支持移动IPv6中的通信节点 6to4: ISATAP协议:站点内自动隧道寻址协议(纯v4体系的节点与v6其他节点进行通讯) 6over4:IPv4多播隧道(把IPv4网络体系作为一个支持多播的逻辑链路) 端口代理协议(PortProxy):不能使用通常IP层协议进行连接的节点和应用程序能够相互通讯 临时地址:随机获得接口标识、过段时间会改变 DNS支持:AAAA、IPv6网络中发送DNS通讯流(默认FEC0:0:0:FFFF::1/FEC0:0:0:FFFF::2/FEC0:0:0:FFFF::3)、v4或v6种将IPv6主机AAAA记录动态注册到DNS中 IPSec 支持: 静态路由器支持:转发IPv6包、发送路由器公告(有路由表发布时) 地址选择:netsh interface ipv6 show prefixpolicy(查看默认前缀策略表) 路由器公告中的站点前缀:已发布链路上配置一个站点前缀长度 应用程序支持 balabala SNMP MIB(RFC draft) API socket RPC IP助手 winInet(windows Internet扩展) .Net扩展 IPv6工具 ipconfig Route Ping tracert pathping(路径网络延迟和丢包信息,比较慢) Netstat:同linux netstat netsh interface ipv6 show interface:接口列表 show address:地址列表 show routes:ipv6路由 show neighbors:邻节点缓存 show destinationcache:目标缓存内容 6to4 isatap teredo (本书尚未涉及)

2015-01-28 · qhsong

使用BP神经网络算法进行手写数字识别

@这个是coursea上机器学习作业的一个变形,把它转移过来,供大家参考。 样本来源 样本源自THE MNIST DATABASE of handwritten digits,原始的数据可以从这里下载matlab文件,这个样本是NIST的一个子库,在NIST的基础之上进行了居中和大小的统一。其中,里面包含有6万个训练样本和1万个测试样本。数字的具体样子如下图所示。 具体的word文档参考这里 里面详细介绍了BP神经算法和实现步骤。

2015-01-25 · qhsong

基于Libevent和Nanomsg的网络爬虫实现(1)

SimpleSpider 一、功能分析 这个使用C语言实现了一个基本的网络爬虫,目前(2015年1月24日)为止,主要设计的内容有链接分析、网页抓取。程序使用libevent(2.0.21-stable)来进行socket连接处理,使用自动机进行链接分析(详情见下文)。 本程序要求: 从给定网址作为入口,爬取对应入口网页并分析其中的URL并爬取对应的网页,直到没有更多页面爬取时完成 爬取时只爬取本地服务器的网页,不怕取外站资源 采用多线程结构设计,输出每一个文件的文件大小 使用方法: make ./crawler entrance_url out_put_file 二、架构设计 本系统整体架构如下所示,由主线程、抓取线程、分析分配线程和分析线程五类线程组成。线程间的通信主要使用一个轻量级消息队列Nanomsg来进行。主线程主要负责抓取线程和分析分配线程的创建。抓取线程主要负责网页的Socket的抓取,并将得到的网页内容放到队列中。分析分配线>程主要负责从消息队列中提取得到的网页内容,并将其分配到分析进程的进程池中对应的线程进行计算。分析线程主要负责从得到网页内容中解>析出地址并放到消息队列中。 三、核心线程的设计 1、网页抓取线程 网页抓取线程主要是负责的是网页的socket抓取,并且将抓取的内容放到nanomsg中。网页抓取线程采用的是Libevent库来进行网络的连接和抓取,Libevent库是一个基于Reactor模型的一个网络库,支持异步的调用函数。只需在初始化的时候在相应时间上一个函数,程序就会在该事件发生时调用对应的函数库。 1.Bufferevents和Evbuffer Libevent在event事件调用的基础上,又在之上封装了一个Bufferevents,主要用于面向流的通讯,其类似Java的输入输出流,通过指定IP地址和端口,就能直接进行读写。在Bufferevents中封装了两个Evbuffer类型的指针,一个是input,另外一个是output。Evbuffer类型主要用于存储字符串内容,是一个能够用在网络编程的缓冲区,类似C++中的String类型。只要一次定义,可以无限的进行读取和写入而不用担心缓冲区溢出的问题。但是在bufferevent中,每次读取的字节数只能是4096个字节。对于大量的数据,这个缓冲区大小是比较小的,这个可以通过调整源码进行修改。 2.HTTP的keep-alive HTTP包头的keep-alive能够保证在每一次传输完成后保持连接可以继续下一次的数据传递,这样可以大大节省每次打开和关闭TCP连接的时间开销。但是keep-alive并不是无限次的,不同的HTTP服务器默认对keep-alive的请求次数和超时时间的设置是不一样的。Apache默认的keep-alive请>求次数是100次,超时时间是5s。Nginx默认的请求次数也是100次,但是超时时间是75s。 在请求HTTP包的时候,其请求顺序是发送一个HTTP请求包,HTTP服务器就会返回相应的请求页面,待当前页面发送完成后,再接收下一个HTTP请>求包。而不是能够一直接受HTTP请求包。 3.HTTP包头分析 在处理HTTP连接时,需要进行HTTP包头的分析。HTTP包头的处理也是一个状态机,其状态机的转换如下所示。在分析HTTP包头时,对于目前的爬>虫,仅仅需要得到HTTP状态和content-length的值。所以目前的HTTP包头分析比较简单。 2、分析线程 分析线程将接收到的HTML语言,使用状态机的方法分析HTML中出现的链接,将外站的连接去除,只留下本地链接,并将本地链接中含有相对路径>的部分转换成为绝对地址链接。 ####1、HTML链接提取 HTML链接中的URL主要放在 <a> 标签中的href属性中,我们采用的状态机的方式提取出标签中的URL。分析时也需要去除href=”#”、href=”javascript::void(0)”等这样的无效链接,对于href=”a.html#a1”这样的锚点链接,我们要将后面的锚点去掉,只留下a.html。这样在去重的时候,能够保证不将a.html#a1和a.html#a2视为两个链接。本程序使用的状态机如下所示。 使用状态机的好处是简单易行,相比起解析HTML DOM模型能够更加高效的解析出URL地址,但是由于这个解析器不够完备,只能解析出<a>标签>中href属性的地址。还有一些包含在<iframe>、<table> 标签backgroud属性等的网页链接地址、还有Ajax中的页面,是不能够被这个状态机分析到的。这里需要引入一个模拟浏览器来进行分析。 2、URL转换 HTTP包的请求地址中是不能含有相对链接的,而且使用相对链接对于去重也是比较复杂的,所以我们需要将我们抓到的URL链接转换成为绝对链接,同时也要去除外站地址,避免我们的程序爬取到外站的链接导致程序无法收敛。 在转换相对链接是,需要转换诸如 ../../a.html、a.html、/a.html这样的链接,转换成为对应的相对地址。 3、URL地址的去重 为了避免重复的抓取相同的URL地址,在本程序中URL去重。在本次实验中,使用的是实验一大规模字符串查找中的TRIE树来进行查重。在使用TRIE树的时候,需要注意进行线程的同步。由于上一个实验的代码是单线程的程序,所以TRIE树的插入、查找的操作也是单线程的。所以在使用时需要用pthread_mutex_t来进行线程间的同步与互斥操作。 3、消息队列 消息队列采用的是Nanomsg库,这是一个使用C语言编写的一个消息队列,可以轻松完成跨线程、跨进程、跨机器的通讯。Nanomsg的通讯模式有NN_PAIR、NN_REPREQ、NN_PIPELINE等通讯模型。本程序采用的通讯模型主要是NN_PAIR的通讯模型,进行一对一的通讯。考虑到程序是在同一个进>程内运行,所以我们主要以传递指针为主。下图所示的就是两个进程通讯之间传递的数据类型: Nanomsg中可以实现“零拷贝”,Send端采用: void *msg = nn_allocmsg(3, 0); strncpy(msg, "ABC", 3); nbytes = nn_send (s, &msg, NN_MSG, 0); assert (nbytes == 3); 发送端采用 void *buf = NULL; nbytes = nn_recv (s, &buf, NN_MSG, 0); if (nbytes < 0) { /* handle error */ ....

2015-01-24 · qhsong

大规模字符串查找---Trie树、压缩Trie树

完成了计算机应用编程的第一个实验,有必要进行一些总结。一直没抽空出来把第一个实验的报告进行总结,今天假期,正好把这个问题进行一下总结。 大规模字符串的查找问题定义的是在一个有千万级别的字符串中查找相应的字符串存在不存在,若存在输出yes,否则输出no。这项技术被广泛用在一些场合,如进行EMail地址过滤,要求在ms级别的时间内进行查找,但是相反对于程序启动的时间却没有要求,只要求查找速度尽量快。所以提出了进行建树。本文主要整理了Trie树、Trie树的压缩的相关算法,作为参考。 本次作业以查找Email为例,代码详见:EmailSearch。 1. Trie树 Trie树是一颗多叉树,实际上是一个DFA,每一个节点的孩子表示这个字符是否存在,若存在指向下一个节点,若不存在,则指向NULL。所以在搜索时,只用从头节点开始,顺序搜索即可。具体详见wiki 为了实现这样一个Trie树,我们采用的数据结构是这样的: struct trieTree{ int isEmail; struct trieTree *next[63]; };//saved in trie.h 使用这个作为树的节点,Email中含有字符串0-9,A-Z,a-z,’@’,’.’,还有一些非标准字符串,总共63个字符。显而易见,这样的Trie树节点存储效率很低。我们的输入数据有1kW行Email地址。实际测试中,64bit的操作系统中,我们使用了60G的内存,再加上一些Swap,也只建树了640W左右个Email地址,真是浪费内存。最主要的是在叶子节点上,只用了一个isEmail字段,但是仍然要分配63个指针。在64bit的系统下,每个指针占用内存为8字节,在加上一个整型数,光存储叶子节点就需要1kw648B=5G。 所以,我们就把Trie树后面的静态数组,变为链表。虽然增加了查找的时间,但是减少了内存的消耗,尤其是叶子节点的内存占用。其数据结构如下: typedef struct sNodeList NODELIST; typedef struct trieTree TRIE; struct sNodeList { char c; //存储字符 TRIE *tnext; //下一个子节点位置 struct sNodeList *next; //下一个字符的位置 }; struct trieTree{ int isEmail; //是否为字符串 struct sNodeList *list; //标记节点 };//saved in listtrie.h 采用链表的方式来保存后续的节点,这样就能够很好的节省内存。其插入过程如下图: 使用这样的一个数据结构,能够比较好的减少内存开支,大概使用了15G左右的内存,就能完成整个查询。其中,主要是叶子节点占用的内存较多 2.压缩Trie树 在Trie树中,我们存储的是字符,很多情况下,我们会有如下图的情况。 为了解决Trie树中有这样孤立的一串字符节点,我们采用了压缩trie树的方法。这是一个典型的压缩trie树的插入过程: 采用上面的数据结构,我们只需要将上面的char c换为char *str。这种方法的关键是比较两个字符串有多少个字母相同,具体的数据结构,详见clisttrie.h 在使用这样的压缩trie之后,我们的程序只需3.31G就能够完成了整个程序,而且时间比之前的程序还少了。 虽然这已经减少了内存的占用,但是我们还是浪费了一些内存。所以我们将这棵树转变为二叉树,用二叉树的方式来处理这个节点,问题就回到了二叉树上来。 定义的代码如下: typedef struct trieTree TRIE; struct trieTree{ short int isEmail; char *str; TRIE *bro; TRIE *son; };//in clisttrie2....

2014-11-06 · qhsong

类型系统(type system)

最近在看go语言的一些东西,很多书都讲到了这样一个东西。百度了一些资料,学习学习。 类型系统wiki:http://zh.wikipedia.org/wiki/类型系统 类型系统(type system)是一门编程语言最核心也是最基础的部分。无论该语言基于何种编程范式,都必须在开天辟地之初首先对类型系统作出明确的定义。这是因为,编程语言虽然五花八门,千奇百怪,但是归根结底,编程语言最终的目标,本质上无非是回答两个问题: 1、如何表示信息 2、如何处理信息 无论是面向过程的编程语言、面向对象的编程语言、函数式编程语言、并行编程语言或者其他任何千奇百怪的编程语言,其根本性的终极目标,就是回答以上两个问题。各种编程语言之所以差异颇大,其实就是对这两个问题给出的答案不同导致的。 在如何表示信息这一问题上,编程语言通常需要定义一些“基本存储单元”,作为整个语言世界的基本构成要素。这种思想很类似于我们对物理世界的认识——宇宙虽然鬼斧神工,丰富多彩,但是在微观上,整个世界仅仅是由少数寥寥几种基本粒子构成的(物理细节不必深究,这里只是打个比方)。但是奇怪的是,基本粒子就只有几种,为何却能构成地球、水、人、树、风这些看似截然不同的东西呢?答案在于,基本粒子虽然不多,但是自然界确立了一套简单而精妙的组合规则,使得基本粒子能够以许多种不同的方式组合在一起,由于组合方式的不同(结构差异),组合规模的不同(数量差异),导致了最终宏观表现的不同。 与现实物理世界类似,一门编程语言就确立了一个独特的“世界”,这个世界可能丰富多彩,千奇百怪。但是就如我们现实世界一样,繁杂的外表之下,骨子里都是由一些“基本粒子”,按照一定的组合方式构成的。那么究竟有哪些基本粒子,又允许进行何种组合,对编程语言所确立的世界最终的宏观结果影响非常巨大。甚至可以说是根本性的。 有一定编程经验的程序员,往往对类型系统不太关心。他们更感兴趣的是语言的其他特性,例如并行计算能力,编程风格,类库等等。这些特性当然非常重要,就生产环境的应用来说,语言特性甚至是处于次要地位的。类库被许多程序员认为比语言本身更重要。然而,坚实的应用是以对语言深刻的理解为基础的,花费一些时间对语言的本质进行研究,会对深入理解语言背后的设计考虑有很大的帮助。也能让我们避免陷入语言的陷阱,或者陷入与别人的口水战之中。 回到对类型系统的考虑,那么究竟什么是类型系统呢? 一门语言定义了一套基本类型的“集合”,这个集合就作为一个整体被称为类型系统。这一称谓中,涉及到两个关键词——“类型”和“系统”。 什么是“类型”? 计算机存储是以二进制方式进行的,并以连续的八个二进制位为一个基本单元——“字节”。从这一点来看,计算机存储是通用的,存储人类的文字或者存储图像、声音或其他别的媒介都没有内在的本质差异。但是奇怪的是,在编程语言概念上,却总是会引入一系列的大相径庭的“基本类型”。例如,int和double,一个存储整数,一个存储浮点数。如果我们考虑一下,就会发现其实int和double本身并没有什么差别,都只不过是若干个字节构成的存储单元而已。那么对他们进行区分意义何在?其实就“存储”概念而言,我们用二进制方式,以字节为单位来实现信息的存储,已经给出了信息表示的“终极答案”了。但是完成存储,只是整个计算机系统功能的一部分。如果仅仅是把信息存储,而不进行计算,那么计算机就不叫计算机,改叫存储机了。可见,问题的实质在于,我们要存储,更要计算。 int和double都是几个字节构成的,但是其运算规则截然不同,差异巨大。同样的,string和int或double也不相同。我们在string上进行拼接、删减、搜索、替换。但是在int上进行加减乘除。这些计算需求和内部实现上的差异,迫使我们的语言层次上进行明显的区分。 上面举的类型,都是来自C家族语言中的概念。但是我们也知道,在一些语言中,不需要我们对类型进行显式的说明。但是不说明不代表不存在。只不过一个是程序员显式声明,一个是编译器(或解释器)自主推导;一个是把责任推给程序员,一个是把责任推给编译器作者。类型系统总是内在的存在的。永远没有被去除。 什么是“系统”? 坦白讲,这是一个非常模糊的概念。我们会说操作系统、消化系统、生态系统……各种各样的系统,然而对于系统本身是什么,在不同的科学领域有截然不同的定义。通常我们所说的系统中,存在一些基本要素(软件模块、细胞、物种等等),然后存在一定的相互作用关系(函数调用、细胞连接、捕食与被捕食等等),在此基础上实现一定的功能(完成金融计算、排解人体毒素、完成有机物的自然循环等等)。那么我们就把这些基本元素,以及其构成方式,统称为一个系统。 之所以说“系统”是个模糊的概念,原因在于,这一概念本身并非原子概念,一个系统,也可能再分解为一系列的子系统,例如操作系统就可以分为输入输出子系统,绘图子系统等等,人体内的消化系统也能够分解为一系列的子系统。而子系统又可在更小的级别进行分解。系统的划分是相对的,系统的构成也是相对的,因此其本身常常是模糊的。 这么说来,如果要追究系统一词的内涵,会很困难。但是在我们讨论的编程语言这一领域内,当提到“类型系统”时,系统其实就是指: 1、一组基本类型构成的“基本类型集合” 2、“基本类型集合”上定义的一系列组合、运算、转换方法 这两点合起来,就成为了我们的“类型系统”。 只要做到这两点,就已经非常强大了。这其中,“基本类型集合”是一个非常小的有限集合,也就寥寥几个元素,而“组合、运算、转换”等规则,也是一个较小的有限集合。但是通过选择不同的元素进行组合,这两个有限的集合之上,却衍生出了一个无限集合——“类型空间”。 理解这一点非常关键。因为这恰好符合了我们对自然界构成的认识——有限的若干种基本粒子,有限的若干种基本规则,结果却是无限可能性的巨大世界。 这一简单优雅而惊人的世界构成观,贯穿了人类现实世界和计算机编程语言所定义的虚拟世界。或许语言的设计者也没有料想到,但是最终的结果确实是有限的设计导出了无限的可能性。 所以,当认识到这一点之后。就再也不会轻视类型系统,再也不会把类型系统看得简单,自以为十分了解了。而类型系统设计上的细微差异,最终也会导致截然不同的类型空间,导致对信息表达方式的巨大差异。 那么还有什么理由不认真的研究这一基本要素的呢?还有什么理由让我们逃开这一许多程序员认为“简单”的主题呢?

2014-08-10 · qhsong

ubuntu编译安装muduo网络库undefined reference to 问题

安装muduo网络库时,一开始用默认配置,string类型是__gun_cxx::__sso_string,和google protocol buffer的网络类型不兼容。然后就重新编译了一下muduo库,结果导致了如下的错误: ../../../lib/libquery_proto.a(query.pb.cc.o): In function `google::protobuf::GoogleOnceInit(int*, void (*)())': /usr/local/include/google/protobuf/stubs/once.h:127: undefined reference to `google::protobuf::GoogleOnceInitImpl(int*, google::protobuf::Closure*)' collect2: ld 返回 1 make[2]: *** [bin/protobuf_client] 错误 1 make[1]: *** [examples/protobuf/codec/CMakeFiles/protobuf_client.dir/all] 错误 2 make: *** [all] 错误 2 通过跟踪CMakeList的参数,发现的问题。默认的google protocol buffer动态安装库在/usr/local/lib,而不在编译器的输出路径当中。需要自己增加链接库文件。由于自己对CMake不是很熟悉。所以我直接把CMakeList.txt文件中的CXX_FLAGS变量,把pkg-config --cflags --libs protobuf的输出结果加进去,再次运行build.sh。 安装muduo网络库即可。 ========2014年11月6日============== 出现这个问题大部分的原因是protobuf的动态链接库的位置没有指定。So,请在参数中指定你的动态链接库。

2014-06-03 · qhsong

使用Vmware vCenter Converter进行P2V转换Ubuntu主机

最近小本性能太差,考虑将我的小本中的Ubuntu移植到小Y的虚拟机中。由于Vmware并没有提供直接能将物理机器转换成Workstation能够使用的虚拟机工具,但是他提供了通过vCenter Converter转换到Vmware EXSi,再从EXSi下载ovf模板下载到本地进行部署。 1、你要找一台Windows主机,能够访问到你的Ubuntu电脑和Vmware EXSi。在这台Windows主机上进行安装vCenter Converter。vCenter Converter是免费的,直接去Vmware官网注册后即可下载。 2、安装完成后,参照http://vaemon.com/article/1117.html的方式进行安装。安装要点主要有: 3、在虚拟机中挂载一张Ubuntu桌面版(服务器版也行) 的光盘,使用blkid命令查看新分区硬盘的uuid(需要加上sudo)。分别替换/etc/fatab和grub.cfg文件里面原来硬盘的uuid。(一定要替换的是原来硬盘的文件,所以要将原系统的分区挂载上来) 4、修复引导。具体参见我的 另外一篇文章,安装grub,修复引导,即可启动成功。实现Ubuntu 的完美移植。

2014-05-13 · qhsong