Archive for the ‘笔记’ Category

GCC内联汇编的笔记

Posted on 六月 10th, 2010 in 笔记 | 2 Comments »

起比VC风格的内联汇编,GCC的确实要别扭些,一开始要不看手册肯定一头雾水。

int foo = 10, bar = 15;
asm volatile("addl  %%ebx,%%eax"
            :"=a"(foo)                //output constraint
            :"a"(foo), "b"(bar)      //input constraint
            :                            //clobbered registers(ignored)
);

指令大家都明白,不过:”=a”(foo)这样的语法就古怪了。:后面的东西好像叫做约束,指明了输出和输入中用到的变量和寄存器。第一个的”=a”(foo)是输出的约束,就表示汇编执行完毕后foo=a。后面的”a”(foo)是输入的约束,表示汇编执行前的a=foo。这一来C和汇编就可以在约束下边交换数据了。

刚才这个a就是表示分配eax寄存器。

各种约束还挺多的…

a,b,c,d 对应eax,ebx,ecx,edx
S,D 对应esi,edi
I 常数
q eax,ebx,ecx,edx中静态分配一个
r eax,ebx,ecx,edx,esi,edi中静态分配一个
m 内存定位
A 同时分配eax和ebx,形成一64位的寄存器
i 一个编译时确定的立即数。好像ljmp指令的第一个参数就必须得是立即数,比如ljmp $0×80, $label。如果ljmp ax, $label就绘出现一个“Error: suffix or operands invalid for ‘ljmp’的错误”

为什么要这么难看的语法呢…我猜这东西最早应该是给编译器而不是给人类设计的吧,比起VC风格的内联汇编,它可以得到更多关于变量和寄存器的信息,编译器分配起寄存器来可以心里有数,不用怕自作聪明的人类把事情都搞乱掉。

随记

Posted on 五月 18th, 2010 in 笔记 | 2 Comments »

无论何时,只要单个资源需要在多个用户间共享,就必须处理一致性的问题。
回头想下“函数式语言无副作用适合写并发程序”之类的说法,只是在计算上的并发,对存储上的并发照样得上锁上钥匙。

win32的窗体使用handle而不是指针
一个窗体可能会被多个进程处理,也可以隐藏窗体对象的内部实现。

真空管和三极管的共性
都可以将信号放大,这一来就可以用电信号来控制开关了。就有了逻辑电路…

lua从堆栈机转到寄存器机
因为lua实现里表示值的那个结构体Tvalue是个tagged union,个头太大了(两个指针这么长)。在堆栈上push/pop一来一回吃资源,搞成寄存器机可以省些搬运。

为什么要有链接器
可以把程序分成模块。

C中结构体不可比较
因为结构体里数据对齐的空隙里面的东西可能随机的。

C++为什么不用printf
类型不安全。像cout好像是对每个内置类型都重载了一个<<操作符

x86实模式的逻辑地址
x86实模式的逻辑地址是段寄存器(如cs)里存一个基地址(16位),左移四位再加上一个16位的偏移。
为什么要这么蛋疼,因为当时的主线有20位(可寻址1mb),但是intel的寄存器只有16位。

自动机
就是根据输入自动切换状态的计算模型。不过状态有限,识别的语言(语言就是字符串的集合)是最少的。自动机可以识别的语言就叫正则语言。
给自动机加一个堆栈就是下推自动机,这样状态就可以无限了,可以识别的语言就叫上下文无关语言。
给自动机加一个随机存储,就是图灵机了。可以识别的语言就叫可判定语言。

动态作用域

Posted on 四月 15th, 2010 in 笔记 | 18 Comments »

好像刚弄明白动态作用域是怎么回事,拿个例子:

var a=0; //global
def f1():
    a=1
def f2():
    var a; //local
    f1()
 
f2()
puts a

若是静态作用域,f2调用f1,f1修改全局变量a的值为1,输出1。这个多自然…
可是动态作用域就…输出0,全局变量a的值没有变化。

f2不是调用了f1么,f1不是改变a的值么…是啊,f2调用的那个f1改变的是f2那个局部变量a的值。也就是说,同一个函数在不同的环境下调用会有不同的行为。

好吧我正在纳闷为什么会有这种东西存在过…囧

各种垃圾收集的随便笔记

Posted on 四月 11th, 2010 in 笔记 | 4 Comments »

GC多好多好就不多说了。没银弹嘛,什么好处什么坏处都了解下就好 TvT

引用计数

“穷人的垃圾收集”。实现简单,可以在对象失去引用时立即收集,不过对付不了循环引用。“立即收集”容易给人一种“高效”的印象,其实正相反,引用计数中的每个mov指令后面都必须跟着几条额外指令,引用的局部性还不好。跟各种GC算法比起来,引用计数的效率是最低的 :(

CPython是引用计数加备用的mark-sweep,(好像如果一开始就使用GC的话就没必要引入那套复杂的内存池机制了?

mark-sweep

从根(寄存器、局部变量和全局变量等)开始,遍历所有可访问的对象并标记之;再遍历所有变量,将所有没mark的对象收集。

C\C++中的GC实现大约是扫描bss段和堆栈什么的,找到全局变量和局部变量。C\C++中你不知道一块数据是指针还是一个纯洁的整数,所以只能用个is point to heap来猜它是不是指针,如果这个整数恰好是一块内存的地址,就只能“保守地”当它是指针mark掉了。保守式的GC中比较有名的就是Boehm的GC(貌似很靠谱!)。

停止-复制

这个需要额外一倍的内存空间。先将一切活动停止,遍历堆中所有可以访问的对象并将其拷贝到另个堆中,并将将原先的指针统统指向新的堆中对应的地址,完毕后继续程序的执行。

停止复制的同时也紧缩了内存,因而不存在内存碎片的问题(malloc/free在整理堆中碎片时候的各种搬运不容小觑),性能上好像要优于mark-sweep。缺点就是对空间浪费比较大(一倍啊一倍~)。

分代收集

遍历所有对象时候的停滞会比较厉害。想办法把停滞缩短到人类察觉不到吧…就有了分代收集和增量收集 TvT

分代收集基于这样的假设:

1.对象越新,生存期越短;
2.对象越老,生存期越长;
3.少有指向新对象的老对象

就把堆分成n块,对应n代的对象(三代就差不多吧)。再加个记忆表什么的记下指向新对象的老对象,以记忆表中的老对象以及活跃的变量等做根,重点照顾最新的这代就行了。

.net那CLR好像是分代的…缺点是经过多次收集,较老的代会积攒比较多的垃圾。

增量收集

增量收集好像是记住上次遍历到了什么地方,然后每次遍历一点,最后集中收集。

有个三色标记:
白色:未被访问的对象;
灰色:已经被访问过,子对象还未访问;
黑色:已经被访问过,子对象也已被访问;

然后就是性质:
当没有灰色对象时,所有的白色对象都是垃圾;
不会有黑色对象指向白色对象;
每个灰色对象都位于收集器的队列中(作为下次收集的根);

State Monad笔记二

Posted on 三月 5th, 2010 in 笔记 | 8 Comments »

老师:Monad想到什么呢?
同学:毛茸茸的猫猫!

好同学们,请默念“Warm and Fuzzy”100遍~

。。
。。
。。










。。
。。

100遍了没。。。好,我们先看看State猫猫的类型吧 ^_^

newtype State s a = State { runState :: s -> (a, s) }

里面有个难看的record syntax,咱们无视之,改成:

data State g v = State (g -> (v, g))

就是一个lambda,外加一二元组:v表示猫猫正经计算中的值,g就表示猫猫的状态啦(就一个全局变量嘛)~那个lambda的参数g也一样。相应的猫猫Instance也就是如下:

instance Monad (State g) where
    return v = 
        State $ \g -> (v, g)
 
    (>>=) (State m) f = 
        State $ \g -> 
                let (v, g') = m g
                    (State m') = f v
                in  m' g'

还是一环套一环,函数编程没有变量,那就向下传递的时候改变它的值就行了。再加两个小喽骡:

get_g = State $ \g -> (g, g)
set_g v = State $ \_ -> (v, v)

全局变量的值不也在那个lambda的参数里么,get_g它设成计算中的值,这样在do-notation中就可以g <- get_g了。set_g则是把它的参数设到g里,而原先的g无视扔掉即可。

加个函数试验下:

run (State m) i = m i 
 
add1 :: State Int Int
add1 = do {
    g <- get_g;
    set_g $ g + 1;
}
 
add3 = do {
    add1;
    add1;
    add1;
}

进ghci输入run add3 1可得(4,4),可见那个“全局变量”就这么变化了。

回头再想想它的类型,一个二元组不就够了嘛,为什么要套那层lambda呢?试下就知道

data State g v = State (g, v) 
 
instance Monad (State g) where
    (>>=) (State (g, v)) f = (g, f v)

看着不错,接着来:

    return v = ...

发现问题了没,return我们实现不了…搞不到原先g的值,链子就断在了这一环。解决方法就是把它推后,不是没有g么,于是放到参数里等着别人(>>=)给,这算不算惰性呢? :)

ps:明白了State是怎么回事,附一山寨parsec ^_^ http://github.com/Fleurer/FParser