让Ruby On Rails走进嵌入式开发
lmxbitihero
2009-02-04
在嵌入式开发领域C和Linux是一对黄金搭档,几乎占据国内的绝大部分市场,成为嵌入式开发的主流模式。当前,对于一些设备做配置的界面,很多都是通过网页来进行,比如adsl modem。可是要用C来写Web服务和网页,那工作量将是巨大的。不知道各位是怎么来实现的呢?本人不是嵌入式开发的专家,在这方面了解的不是很多。但是我前段时间尝试把Ruby On Rails平台移植到龙芯的嵌入式开发板上,将我在Windows平台开发的Web应用一行不改,顺利移植到了板子上,这期间经历了不少挫折,虽然最终也不算是一个完美的胜利,但我有必要把这个经验说出来,让大家提提建议,也希望大家帮我把这个工作做到完美。
开发环境介绍: 编译平台:Red Hat Enterprise 4, mipsel-linux交叉编译器(龙芯开发板公司帮助配置好,重庆) 运行环境:龙芯开发板,uboot,剪裁型linux(体积2M),内存64M,CPU主频300多M,存储空间1G采用外接设备 Ruby版本:Ruby 1.8.7, Rails 1.2.2, sqlite3.6.8, Mongrel1.1.5 Rails应用介绍:我们公司从事安防领域,做视频编解码设备。我做的这个系统是一个管理系统,进行各项信息的管理配置,通过socket与多重第三方设备或者客户端软件通讯。需要增删改查的信息并不是太多,但是socket通讯的分量比较重。 整个工作持续了近3周,中间经历了不少挫折,可谓工作量巨大。之前我曾用ruby1.8.5和ruby1.8.6去编译,可最终发现问题较多(主要在GC垃圾回收上),最后采用ruby1.8.7编译后发现运行更加完美。所以这里介绍ruby1.8.7编译的方式。 1:编译方式 自然跟一般的嵌入式编译是一样的,找到Ruby1.8.7的源码,进入源码目录./configure --host=mipsel-linux, 然后make。编译出miniruby后下面的代码无法编译了。没关系,miniruby就是我们需要的。 2:底层库依赖问题 Ruby主程序运行依赖c,dl, encrypt库,为了避免裁剪型linux底层库版本不一致问题,必须将Ruby进行静态编译,也就是修改makefile文件,在gcc后加一个 -static选项。 3:Ruby扩展库问题 众所周知,Ruby有许多的用C实现的扩展库,最典型的比如socket.so。扩展库被Ruby动态加载后提供功能。那么在嵌入式上,Ruby主程序和扩展库怎么组织?最初我的想法是Ruby主程序做静态编译,各个扩展库也做静态编译,象一般的Ruby平台一样处理Ruby与扩展库的关系。但是后来发现,扩展库编译成so库,无论如何也不能做静态链接。也就是说我要把socket.so库编译成一个动态链接库,怎么样也无法摆脱对libc的依赖。而开发板上的libc无法提供red hat上libc的全功能,导致socket.so无法加载。 无奈之下,只好自己动手修改Ruby代码了。找到扩展库的源码,把.c和.h文件都复制到ruby源码根目录,修改MakeFile,将需要编译的.c文件加入进去。然后修改inits.c文件,这里面的功能会在Ruby初始化的时候执行。按如下方式修改: /********************************************************************** inits.c - $Author: knu $ $Date: 2008-04-09 20:13:04 +0900 (Wed, 09 Apr 2008) $ created at: Tue Dec 28 16:01:58 JST 1993 Copyright (C) 1993-2003 Yukihiro Matsumoto **********************************************************************/ #include "ruby.h" void Init_Array _((void)); void Init_Bignum _((void)); void Init_Binding _((void)); void Init_Comparable _((void)); void Init_Dir _((void)); void Init_Enumerable _((void)); void Init_Enumerator _((void)); void Init_Exception _((void)); void Init_syserr _((void)); void Init_eval _((void)); void Init_load _((void)); void Init_Proc _((void)); void Init_Thread _((void)); void Init_File _((void)); void Init_GC _((void)); void Init_Hash _((void)); void Init_IO _((void)); void Init_Math _((void)); void Init_marshal _((void)); void Init_Numeric _((void)); void Init_Object _((void)); void Init_pack _((void)); void Init_Precision _((void)); void Init_sym _((void)); void Init_process _((void)); void Init_Random _((void)); void Init_Range _((void)); void Init_Regexp _((void)); void Init_signal _((void)); void Init_String _((void)); void Init_Struct _((void)); void Init_Time _((void)); void Init_var_tables _((void)); void Init_version _((void)); //以下是我添加的 void Init_socket _((void)); void Init_sha2 _((void)); void Init_sha1 _((void)); void Init_digest _((void)); void Init_thread _((void)); void Init_stringio _((void)); void Init_syck _((void)); void Init_zlib _((void)); void Init_md5 _((void)); void Init_sqlite3_api _((void)); void Init_bigdecimal _((void)); void Init_strscan _((void)); void Init_fcntl _((void)); void Init_syslog _((void)); void Init_nkf _((void)); void Init_iconv _((void)); void Init_etc _((void)); void Init_http11 _((void)); void rb_call_inits() { Init_sym(); Init_var_tables(); Init_Object(); Init_Comparable(); Init_Enumerable(); Init_Precision(); Init_eval(); Init_String(); Init_Exception(); Init_Thread(); Init_Numeric(); Init_Bignum(); Init_syserr(); Init_Array(); Init_Hash(); Init_Struct(); Init_Regexp(); Init_pack(); Init_Range(); Init_IO(); Init_Dir(); Init_Time(); Init_Random(); Init_signal(); Init_process(); Init_load(); Init_Proc(); Init_Binding(); Init_Math(); Init_GC(); Init_Enumerator(); Init_marshal(); Init_version(); Init_socket(); Init_digest(); Init_sha2(); Init_sha1(); Init_thread(); Init_stringio(); Init_syck(); Init_zlib(); Init_md5(); Init_sqlite3_api(); Init_bigdecimal(); Init_strscan(); Init_syslog(); Init_fcntl(); Init_nkf(); Init_iconv(); Init_etc(); Init_http11(); } 这样编译后将各个扩展库都内置到Ruby体内了,所以你写"sock = UDPSocket.new"的时候不必调用require 'socket',但是为了保证与现有代码的无缝连接,我们必须在ruby的lib目录下生成一些空文件,比如socket.rb,这样调用require 'socket'才不至于出现异常。 4:数据库问题 在嵌入式上数据库除了sqlite似乎没有其他选择了。在red hat上编译sqlite3.6.8,注意编译选项要指定单线程,因为开发板上对多线程支持可能不好。编译完成后将sqlite.h文件、编译出的lib文件复制到Ruby目录下,从rubyforge上下载sqlite-ruby-2.2.3,这是sqlite和rails结合的一个驱动文件,按照3的方法编译。ok,数据库搞定了。在red hat上调用rake db:migrate,先初始化好数据库。然后复制到板子上 5:Gem问题 按照上述方法将,miniruby编译出来以后,改名为ruby,把ruby需要的lib目录复制到板子里,配置好环境变量,ruby就可以运行了。为了运行rails还得先安装Gem,从RubyForge上下载Gem安装包,非常顺利的就可以安装上。调用"gem list",如果不出错,就说明Gem安装成功了。安装Gem后可能出现各种小问题,不要着急,需要自己看情况手动修改代码。Gem运行成功后就可以安装Rails了,"gem install rails -v=1.2.2",ok。 5:Web服务的问题 期初我打算用Ruby自身做Web服务,本身Rails就自带这个功能。但是运行后发现非常不稳定,Ruby容易崩溃,后来选用Mongrel,发现稳定性还不错。但Mongrel不能使用Gem编译,而是从RubyForge手动下载,将ext目录下的http11.c和http11_parser.c复制到Ruby源码目录中,编译到Ruby体内。 当上面的工作都顺利完成后,把Rails应用通过NFS复制到板子里。在系统中配置一下Ruby的环境变量,激动人心的时刻来到了,"mongrel_rails start"应用起来了。通过ps观察一下,耗用35M作用的内存。打开IE访问网址,网页弹出来了,输入用户名密码,看到了熟悉的界面。内存时涨时销,垃圾回收起作用了。 工作做到这个程度,按理说就算是成功了,可是事情偏偏没有那么顺利。调用"mongrel_rails start"的时候经常会发生Ruby崩溃,导致Rails应用起不来。Rails起来后频繁访问网页,一段时间以后Ruby也会崩溃。而且这种崩溃似乎没有什么规律性,也没有什么提示。在板子上如何调试程序,这都超出了我的能力。 希望大家看到这篇文章的时候,能多提意见,如何能够调试Ruby,捕捉错误所在。解决了这个问题,Ruby On Rails算是真正的移植到嵌入式了。 |
|
JCheung
2009-07-10
恩 不错 主要是移植Ruby Rails Sqlite3 Mongrel到龙芯上
|
相关讨论
相关资源推荐
- arm linux路由器配置,arm-linux配置pppoe
- ppp lcp协商报文有哪些_课后分享PPP协议第十三周
- PPP协议详解
- PPP协议
- 路由器重温——串行链路链路层协议积累
- pppoe linux 配置文件,arm-linux配置pppoe
- 解决出现的警告:Warning - secret file /etc/ppp/pap-secrets has world and/or group access Warning - secret fi
- 终于有人把P2P、P2C、O2O、B2C、B2B、C2C的区别讲透了!
- 分析Padavan的代码一
- linux系统修改etc,Linux系统中修改/etc/profile文件的方法