`
CharlesCui
  • 浏览: 427808 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

从main.c开始走进Ruby-有形亦无形的数据

阅读更多

 

上一篇文章我们找到了如何调试Ruby的入口,只要走进去,我们就有可能揭开Ruby的奥秘.但如果我说我要从每个分支都走一遍,每个函数都解读一遍,这可是impossible mission,我肯定没那么强大的理解力,要知道,在没有充分理解一个Ruby对像的实现之前就去阅读它的源码,那大部分的理解都是靠猜测,成功的几率不大,看你的运气以及能得到多少资料.
用过Ruby的人都该知道,Ruby里面没有数据类型的概念:Type是模糊的,但Value是绝对的;123可以是Fixnum,也可以马上变成Bignum,但123就是123,它的值是不变的.
所以当我们阅读R uby源码的时候,我们看到满眼的声名为VALUE类型的变量,这其实就是Ruby的对象,它是一个指针,指向一块内存,内存里面的数据是值,是结构体,如果你认为它是Fixnum类型(T_FIXNUM),那么你就用FIXNUM_P这个宏去检测一下,如果返回Qtrue,那么恭喜,你的运气很好,一次就碰对了,同时,你马上可以用FIX2INT() or FIX2LONG()这两个宏把它的值在gdb里面打印出来(print或者p);如果返回的不是Qtrue,那么很可惜,你必须继续尝试,在R uby中处理未知数据类型时,通常用如下代码来完成检测过程,而且随着判断类型的增多,代码会更加冗余:
  switch (TYPE(obj)) {
    case T_FIXNUM:
      /* process Fixnum */
      break;
    case T_STRING:
      /* process String */
      break;
    case T_ARRAY:
      /* process Array */
      break;
    default:
      /* raise exception */
      rb_raise(rb_eTypeError, "not valid value");
      break;
  }
 上面这个例子说明了些什么呢?可以说明能量守恒的定理么?我觉得是的,动态语言灵活了,你可以不需要指定类型的去使用,写起代码来一大堆的黑魔法让人看得眼花缭乱.但实际上它的实现过程中却为了作了判断类型的操作,同时由于类型的不确定性,我们还不得不为不管多精简的代码花费这些时间,这就是代价,有得亦有失,这不就是能量守恒么?
由于类型的不确定,由于无论是字符串还是数字还是指针,看起来都是VALUE类型,让我们难以猜测,所以我们这次Ruby调试之旅会很艰苦,因为当我们进入一个断点,watch一个变量的时候,我们将无从下手,我们不知道是该以什么类型去print这个VALUE指针.假如我们得到了一个VALUE变量,我们想拿到它的值,比如String的字符,Fixnum的数值,或者Hash的Key,我们都必须去用(类型_P)这个宏去探测,如果你有阅读过并且读懂了源码,那么恭喜你,你可以根据上下文进行猜测,这样命中的几率大的很多.但在没有找到更好的调试方法之前,让我们多干点好事吧(积德).
ps:rb_type这个函数可以帮我们做一些简单判断:
static inline int
rb_type(VALUE obj)
{
    if (IMMEDIATE_P(obj)) {
	if (FIXNUM_P(obj)) return T_FIXNUM;
	if (obj == Qtrue) return T_TRUE;
	if (SYMBOL_P(obj)) return T_SYMBOL;
	if (obj == Qundef) return T_UNDEF;
    }
    else if (!RTEST(obj)) {
	if (obj == Qnil) return T_NIL;
	if (obj == Qfalse) return T_FALSE;
    }
    return BUILTIN_TYPE(obj);
}
 
我不该说这么多调试的痛苦,但这些废话还是有点价值的,比如就引出的Ruby在C中的数据结构.如果专门说这个数据结构,我想这一篇肯定说不完了,但我这一篇是想介绍Ruby在main方法中几个初始化过程的,所以我简单的说一下R uby的几个重要的数据结构.
Ruby的数据都是以VALUE类型存放的,VALUE的定义如下:
typedef unsigned long VALUE;
 VALUE类型如此简单,这就是一个指针么.也就是说,在Ruby中,数据都是以指针方式来访问的(除了Fixnum),当我访问的时候,我通过类型转换将这个指针变成我期望的类型,比如我拿到了一个指针h并且我知道它的类型是Hash,那么h所指向的那片内存区域肯定存放了一个Hash对象,我现在要操作这个对象该怎么办?通过如下的宏来实现:
RHASH(h)->hash_method
 
#define RHASH(obj)   (R_CAST(RHash)(obj))
R_CAST,顾名思义,将obj转换为RHash类型,它的实现如下:
#define R_CAST(st)   (struct st*)
 呵呵,很容易就能看懂吧,Ruby的实现中使用了大量的宏来简化操作,同时又能给出平易近人的命名方式,这使得我们阅读的时候顺畅了不少,感谢Core Team,不过也别忘了上一篇我为什么要强调编译Ruby时的那几个参数了,如果不加上,我们是无法在gdb的时候去通过
GDB 写道
info macro RHASH

来看到RHASH的实现的.

 

上面是以一个Hash对象的操作为例,讲解R uby对象的存在方式(VALUE指针),以及习惯的操作方法(R_CAST转换).不过刚才也提到过Fixnum不是指针,为啥?如果一个VALUE的类型你能确定是Fixnum的话,就不需要去通过*取值了,我们可以直接拿到Fixnum的指,因为VALUE这个unsign long已经足够大了,它的长度足够存储一个Fixnum,所以Fixnum是以值而不是指针的形式存在的.

这也就说道Ruby中哪些类型是传值的,哪些类型是传址的.如果死记硬背那些教条,我估计肯定没有自己去发现Ruby的底层实现给你的印象更深刻.

 

下面这几个类型也很重要,尤其是True和False.

    RUBY_Qfalse = 0,
    RUBY_Qtrue  = 2,
    RUBY_Qnil   = 4,
    RUBY_Qundef = 6,
 上面是这几个类型的值,千万不要小看这几个值阿,他们可不是随便写写的,他们的用处可巧妙了,比如给你看一个宏的实现,
#define RTEST(v) (((VALUE)(v) & ~Qnil) != 0)
 这个宏是用作判断一个VALUE对象是否为True或者为False和Nil的,这个判断就用到了上面的几个值的特点,为了让Ansi C的 if表达式在False和Nil的时候都走失败的分支,而只有True的时候走成功的分支.至于怎么实现,请用位运算计算一下,看看是不是取值设计的巧妙.
#define Qfalse ((VALUE)RUBY_Qfalse)
#define Qtrue  ((VALUE)RUBY_Qtrue)
#define Qnil   ((VALUE)RUBY_Qnil)
#define Qundef ((VALUE)RUBY_Qundef)	/* undefined value for placeholder */
Ruby的数据类型远远没有讲清楚呢,那些各种Ruby层对象所对应的C层的结构体,那些Hash存储策略的设计及Hash算法的选择,RString结构变长存储的设计思路和成员aux的作用等,这些我几乎都没有提到过呢.但这不会影响我们继续探索下去,相信我,知道上面那些就足够我们迈步向前了,起码我自己还什么都不懂呢,所以你们别担心.......
好晚了,写着写着,不知不觉就半夜了,明天还要早起,我发现我离原定要写的内容还有好大一截,反倒是想给大家普及的一些基本知识说了好多,剩下本来要讲Ruby解释器初始化的部分,都准备好了一些代码,但我困了,擦,再不睡觉明天要误事了.我就放到下一章吧.好像一口气读完R uby的实现,但那样不会理解它的精髓,我们要取其精华去其糟粕,慢慢来啊.

============================================================
下面是准备好了的第三篇的部分东西,先放到这里,大家预习下.
/**********************************************************************

  main.c -

  $Author: akr $
  created at: Fri Aug 19 13:19:58 JST 1994

  Copyright (C) 1993-2007 Yukihiro Matsumoto

**********************************************************************/

#undef RUBY_EXPORT
#include "ruby.h"
#include "debug.h"
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

RUBY_GLOBAL_SETUP

int
main(int argc, char **argv)
{
#ifdef RUBY_DEBUG_ENV
    ruby_set_debug_option(getenv("RUBY_DEBUG"));
#endif
#ifdef HAVE_LOCALE_H
    setlocale(LC_CTYPE, "");
#endif

    ruby_sysinit(&argc, &argv);
    {
	RUBY_INIT_STACK;
	ruby_init();
	return ruby_run_node(ruby_options(argc, argv));
    }
}
 
#define CALL(n) {void Init_##n(void); Init_##n();}

void
rb_call_inits(void)
{
    CALL(RandomSeed);
    CALL(sym);
    CALL(var_tables);
    CALL(Object);
    CALL(top_self);
    CALL(Encoding);
    CALL(Comparable);
    CALL(Enumerable);
    CALL(String);
    /*..........*/
}
 
README.EXT 写道
4. Example - Creating dbm extension

......
(3) write C code.
......
Ruby will execute the initializing function named ``Init_LIBRARY'' in
the library. For example, ``Init_dbm()'' will be executed when loading
the library.

Here's the example of an initializing function.

--
void
Init_dbm(void)
{
省略
}
--
分享到:
评论
1 楼 night_stalker 2010-08-23  
就算是静态类型语言,往往也有运行期类型检查 ……

VALUE 是一种 tagged pointer,tagged pointer 的应用广泛的 …… 连 C 语言的指针都可以看作一种 tagged pointer,其 0(NULL) 值就不是指针量。

Ruby 里面调用方法之前只对是否为直接量作检查(IMMEDIATE_P;见 ruby.h 里面 rb_class_of 的定义),这个基本不算速度的瓶颈 ……

相关推荐

    嵌入式Linux应用开发完全手册

    | | |-- main.c | | |-- Makefile | | |-- nand.c | | |-- s3c24xx.h | | |-- serial.c | | `-- serial.h | |-- hello | | `-- hello.c | |-- i2c | | |-- head.S | | |-- i2c.c | | |-- i2c.h | | |-- i2c.lds | | ...

    DELPHI皮肤控件BusinessSkinForm以及137套精美皮肤

    2004-07-16 13:16 24036 2415 BusinessSkinForm\Ampix\main.bmp 2004-07-09 21:54 3422 91 BusinessSkinForm\Ampix\mask.bmp 2004-07-16 13:17 8912 1183 BusinessSkinForm\Ampix\menuitems.bmp 2004-07-16 13:17 ...

    编译原理 C语言编译器(包括词法/语法/语义分析器等)

    编译原理 C语言编译器(包括词法/语法/语义分析器等) 项目结构如下 -source --lexAnalysis 词法分析器(原创) ---analyse.c 词法分析器 ---text.c 测试用例(被分析的C代码) --lexSynAnalysis 语法分析器(转存...

    gvim常用插件及其配置文件配置(下载解压即可使用)

    main.c Makefile.multi-target.template print_int_array.c.noindent .vim/c-support/doc: ChangeLog c-hotkeys.pdf c-hotkeys.tex .vim/c-support/rc: customization.ctags customization.gvimrc customization....

    ruby-main.zip

    通过这个“Ruby学习指南”,你可以从基础到高级,全面掌握Ruby编程,包括类的设计、面向对象编程的精髓、元编程的技巧,以及如何利用Ruby的Gem生态进行高效开发。通过实践其中的示例和项目,你将能够深入理解并熟练...

    uCOS-II源代码下载

    The main directory where all μC/OS-II files are located. \SOFTWARE\uCOS-II\EX1_x86L This directory contains the source code for EXAMPLE #1 (see section 1.07, Example #1) which is intended to run ...

    Ruby-一个Ruby的例子

    Ruby是一种面向对象的、动态类型的编程语言,以其简洁、优雅的语法和强大的元编程能力而闻名。在这个"Ruby-一个Ruby的例子"中,我们将探讨Ruby的基础知识,以及如何通过具体的代码示例来理解其核心特性。 首先,...

    C语言跑酷小游戏-NJU-CPL-期末项目.zip,捣蛋猫跑酷,捣蛋猫收集金币,避开障碍,含源码+导出exe+试玩视频等等

    gcc common.c main.c menu.c guide.c game.c -o RUN -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf 2. 使用Clion编译 请注意文件夹中的```CMakeLists.txt``` 在编译时,请点开**编辑配置**窗口,将**工作目录改为本...

    heroku-buildpack-ruby:Heroku的Ruby Buildpack

    用法Ruby用法示例: $ lsGemfile Gemfile.lock$ heroku create --buildpack heroku/ruby$ git push heroku main...-----&gt; Heroku receiving push-----&gt; Fetching custom buildpack-----&gt; Ruby app detected...

    c语言程序,调用cmd运行当前目录下名为main.pyw或main.py的文件

    c语言程序,调用cmd运行当前目录下名为main.pyw或main.py的文件

    C语言复习题C语言复习题包含选择填空应用题100多道含解析

    - C程序从main()函数开始执行,直到main()函数结束。 4. C语言语句: - 选项D(p&&=q)不正确,因为C语言中没有逻辑与赋值运算符。 5. C语言关键字: - 选项A中,'define', 'getc', 'include'不是C语言的关键字...

    main.c 哈夫曼编码实现_c语言 求WPL -----递归求解

    哈夫曼编码实现_c语言 (最小堆) 求WPL -----递归求解

    Ruby-Main一个类工厂和DSL用于快速生成命令行程序

    Ruby是一种强大的动态编程语言,常用于Web开发、脚本编写以及构建命令行工具。在创建命令行接口(CLI)时,通常需要编写大量的代码来处理命令解析、参数验证和业务逻辑。为了简化这一过程,开发者们创造了各种工具,...

    第4章习题参考答案1

    这是因为`m1`中的`main`函数实际上从`0x5589`地址开始执行,这是通常的栈帧布局的一部分,其中`%ebp`寄存器的初始值。 理解这些概念对于编写和调试多文件C程序至关重要,特别是在处理全局变量和函数时,以及在模块...

    .archivetemp第1-2章 程序设计&C语言&算法.pdf

    - `main()`函数是C程序的入口点,程序从这里开始执行。 - C语言标准没有强制规定`main()`函数的名称必须为`main`,但实际上几乎所有C程序都使用这个名字。 - **算法的概念**: - **算法**定义了一组解决问题的...

    混沌时间序列分析与预测工具箱 开源版本.zip

    改进的CC方法 - \C-C Method Improved\Main_CC_Method_Improved.m (5)求关联维(correlation dimension) GP算法 - \CorrelationDimension_GP\Main_CorrelationDimension_GP.m (6)求K熵(Kolmogorov Entropy) GP算法 ...

    commons-compress.jar包

    例如,从1.0到1.8的升级可能增加了对更多压缩格式的支持,提高了处理速度,或者解决了之前版本中存在的一些兼容性和稳定性问题。 在Java编程中,`commons-compress.jar` 可以通过以下步骤使用: 1. **添加依赖**:...

    logstash-codec-json-lines-main.tar.gz

    Logstash 是一个强大的数据收集、处理和转发工具,广泛用于日志管理和实时数据分析。这个压缩包 "logstash-codec-json-lines-main.tar.gz" 提供的是 Logstash 的一个插件,名为 "json_lines" 编解码器。这个编解码器...

    深入理解C语言中编译相关的常见错误

    1. /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crt1.o: In function `_start’:(.text+0x18): undefined reference to `main’collect2: ld 返回 1Reason: no main function in source file2....

    custom_buildpack

    用于Ruby的Heroku Buildpack 这是用于Ruby,Rack和Rails应用程序的 。 它使用进行依赖管理。 该buildpack需要64位Linux。 用法 Ruby 用法示例: $ ls Gemfile Gemfile.lock $ heroku create --buildpack heroku/...

Global site tag (gtag.js) - Google Analytics