2010年6月11日星期五

递归调用背后的玄机

在函数调用前,首先要将被调用函数所需要的参数放入堆栈。然后执行汇编指令CALL进行函数调用,与此同时CALL下面一条指令的地址也压入到了堆栈中。当函数返回时,调用RET指令。RET指令会从堆栈中取出一个地址(CALL压入的地址),之后程序从这个地址继续向下执行。这样调用完函数还可以按照既定顺序去执行。

函数调用是A调用B,递归调用则是A调用A。递归调用和函数调用在堆栈的处理方式上是有区别的,只是我们平时不会用汇编去玩递归,很多堆栈操作都被Compiler代工。

下面是一段使用递归调用的C函数代码:
0001: void
0002: merge_sort(int array[], int p, int r)
0003: {
0004: int q;
0005:
0006: if (r > p) {
0007: q = (p + r) / 2;
0008: merge_sort(array, p, q);
0009: merge_sort(array, q + 1, r);
000a: merge(array, p, q, r);
000b: }
000c: }

反汇编(objdump -d)后函数代码如下:

0001 <merge_sort>:
0001: push %ebp
0002: mov %esp,%ebp
0003: sub $0x28,%esp
0004: mov 0x10(%ebp),%eax
0005: cmp 0xc(%ebp),%eax
0006: jle 8048691 <merge_sort+0x7f>
0007: mov 0x10(%ebp),%eax
0008: mov 0xc(%ebp),%edx
0009: lea (%edx,%eax,1),%eax
000a: mov %eax,%edx
000b: shr $0x1f,%edx
000c: lea (%edx,%eax,1),%eax
000d: sar %eax
000e: mov %eax,-0xc(%ebp)
000f: mov -0xc(%ebp),%eax
0010: mov %eax,-0x10(%ebp)
0011: mov -0xc(%ebp),%eax
0012: mov %eax,0x8(%esp)
0013: mov 0xc(%ebp),%eax
0014: mov %eax,0x4(%esp)
0015: mov 0x8(%ebp),%eax
0016: mov %eax,(%esp)
0017: call 8048612 <merge_sort>
0018: mov -0xc(%ebp),%eax
0019: lea 0x1(%eax),%edx
001a: mov 0x10(%ebp),%eax
001b: mov %eax,0x8(%esp)
001c: mov %edx,0x4(%esp)
001d: mov 0x8(%ebp),%eax
001e: mov %eax,(%esp)
001f: call 8048612 <merge_sort>
0020: mov 0x10(%ebp),%eax
0021: mov %eax,0xc(%esp)
0022: mov -0xc(%ebp),%eax
0023: mov %eax,0x8(%esp)
0024: mov 0xc(%ebp),%eax
0025: mov %eax,0x4(%esp)
0026: mov 0x8(%ebp),%eax
0027: mov %eax,(%esp)
0028: call 8048414 <merge>
0029: leave
002a: ret


分析:
1 C[0008]对应A[0017],可以看出A[000e~0016]是把参数array,p,q
压入堆栈,这和普通的函数调用相同。所以很显然,在 r > p 的情况下, C[0008]的持续执行,会导致堆栈中有很多组 array, p,
q,r变量。
2 C[0009]对应A[001f],C[0009]什么时候调用?答:C[0008]引起的调用违反条件(r >
p)后,C[0009]会被执行。A[0018~001e]在做什么?嗯,这就是递归调用的特殊一面,如果是正常函数调用,是不需要这段代码的。那到底A[0018~001e]在做什么?就是把堆栈里面之前保存的一组参数提取出来,然后再讲这组参数压入堆栈,为C[0009]调用做准备。你可能会想拿出来在放进去这不是瞎折腾吗?非也!我们从C代码角度看,提取步骤让局部变量(array,p,q,r)的值已经变成之前堆栈保存的那组数据了,这是重点。之后再把局部变量放入堆栈这是为了后续函数调用做准备。
3 C[000a]对应A[0028],C[000a]什么时候调用?答:道理同上。A[0020~0027]在做什么?道理同上,哈哈。
4 对A程序的疑惑,为什么上来只有push,没有看到pop?其实leave就完成了这个动作,请看注释2。

总结:
递归调用需要在汇编语言级别做特殊处理。对于特殊处理,直白点讲,就是把最后吃进去的给我吐出来。吐到哪里?吐到局部变量里。这样变量就来了一次超级大变身!!!

注释1:C[000a] 代表 C语言000a行;A[000a] 代表汇编语言000a行;
注释2:LEAVE : Set SP to BP, then pop BP.

2010年6月9日星期三

How to become a world-class programmer

If you want to be a good programmer, you just program ever day for two
years, you will be an excellent programmer. If you want to be a
world-class programmer, you can program every day for ten years, or
you can program every day for two years and take an algorithms class.

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/

2010年4月16日星期五

OpenWrt岁月(一)

由于鄙人不想只停留在交换机的开发路上而忘记摘采盛开的路由器野花,面对OpenWrt的诱惑,女朋友走后我和她亲密接触并放纵了几个夜晚(昨天从晚上8点奋战到了今天凌晨1点)。本想透过svn
co svn://svn.openwrt.org/openwrt/trunk/去抓取ING的她,不过却始终无法成功,不知道是否北G*.F$#W所为。无奈选择了http://downloads.openwrt.org/kamikaze/8.09.2/kamikaze_8.09.2_source.tar.bz2

#tar xvfj kamikaze_8.09.2_source.tar.bz2 ./*
#make menuconfig (Target System选择BCM947XX/953XX [2.6])
这个配置方案以下简称"她"
#make

基本上就是上面这几步骤,就可以把方案需要的bin档案生成了,档案放在./bin目录下。插一句:为了更了解她,我加入了一个她的QQ群,进去后我就出来了,里面很像华强北的商家,天天讨论的话题都是刷机刷机刷JJJJ,我当然要的不是刷JJ这么简单,我要了解她的灵魂。

在与她亲密接触的途中,我带着疑惑考察了下面几个要点。

一 Flash分区
嵌入式系统的Flash就像PC的硬盘。硬盘有分区,Flash也有分区。她给Flash分了4个区块,分别是bootloader(cfe),kernel(linux),root
file system(rootfs),configuration(nvram),详细见下面结构:

/build_dir/linux-brcm47xx/linux-2.6.25.20/drivers/mtd/maps/bcm47xx-flash.c
static struct mtd_partition bcm47xx_parts[] = {
{ name: "cfe", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
{ name: "linux", offset: 0, size: 0, },
{ name: "rootfs", offset: 0, size: 0, },
{ name: "nvram", offset: 0, size: 0, },
{ name: NULL, },
};

看到上面是否觉得奇怪,为何offset和size都为0,my god~
我迷糊了,到底每个区块从哪里开始,多大尺寸啊!不过又仔细看了看bcm47xx-flash.c,原来里面有动态分析Flash的代码,然后填充了bcm47xx_parts[]
的 size 和 offset 变量。那么问题又来了:用什么方法分析的呢?原来她的Firmware文件采用TRX文件格式,动态解析TRX
Head就可以分析出个个区块的offset和size了,具体TRX结构的详细信息请参阅
https://forum.openwrt.org/viewtopic.php?id=6938

二 Root File System
在Kernel目录下的.config文件中找到了CONFIG_CMDLINE的定义:
CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=squashfs,jffs2
init=/etc/preinit noinitrd console=ttyS0,115200"
所以她采用squashfs或jffs2来作为Root File
System,squashfs是read-only的文件系统,jffs2是r/w都OK的系统,如果你想在Router
Shell下用ipkg(轻量级包管理工具)来动态安装Package,那么推荐使用jffs2作为Root
FS,不过忧着点,Flash不是硬盘,没多少空间,动态df看看有多少结余,别纵欲过度。

三 启动流程
由于CONFIG_CMDLINE把init配置成了/etc/preinit,所以内核启动后会执行/etc/preinit,
preinit会做根文件系统的加载动作,然后执行/sbin/init。

/build_dir/mipsel/root-brcm47xx/etc/inittab
::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K stop
tts/0::askfirst:/bin/ash --login
ttyS0::askfirst:/bin/ash --login
tty1::askfirst:/bin/ash --login

/build_dir/mipsel/root-brcm47xx/etc/init.d/rcS
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org

run_scripts() {
for i in /etc/rc.d/$1*; do
[ -x $i ] && $i $2 2>&1
done | $LOGGER
}

LOGGER="cat"
[ -x /usr/bin/logger ] && LOGGER="logger -s -p 6 -t sysinit"

if [ "$1" = "S" ]; then
run_scripts "$1" "$2" &
else
run_scripts "$1" "$2"
fi

看了inittab和rcS就很清楚了,接下来就是执行/etc/rc.d/S*,基本上就是初始化各种各样应用程序的脚本。

四 让我苦闷一晚的Web
在/etc/rd.d/下有一个文件叫S50httpd,是负责开启http server的脚本,她采用busybox内潜的httpd做http server
anders@ubuntu:~/share/openwrt/8.09.2/build_dir/mipsel/root-brcm47xx/usr/sbin$
ls -l
lrwxrwxrwx 1 anders anders 17 2010-04-16 14:21 httpd -> ../../bin/busybox
httpd是用www作为web目录的,于是我进入了www目录,发现里面竟然空空如野,WHY?她没有Web?那还玩个P :-(
不过,这么有名气的她,怎么可能没Web呢?上google搜索来搜索去都有web啊,为何我没有?(先讲讲五再回头说这个)

五 IPKG的包管理方式
In an OpenWrt firmware, almost everything is an .ipk, a software
package which can be added to the firmware to provide new features or
removed to save space. Note that packages are also maintained outside
of the main trunk and can be obtained from subversion using the
package feeds system:
$ ./scripts/feeds update
Those packages can be used to extend the functionality of the build
system and need to be symlinked into the main trunk. Once you do that,
the packages will show up in the menu for configuration. From kamikaze
you would do something like this:
$ ./scripts/feeds search nmap
Search results in feed 'packages':
nmap Network exploration and/or security auditing utility
$ ./scripts/feeds install nmap
To include all packages, issue the following command:
$ make package/symlinks

原来她有几个仓库,仓库里面有N多软件,web只是仓库的软件之一了,于是我用下面的方法安装了她的web:
anders@ubuntu:~/share/openwrt/8.09.2$./scripts/feeds update packages luci
anders@ubuntu:~/share/openwrt/8.09.2$./scripts/feeds install -a -p luci
anders@ubuntu:~/share/openwrt/8.09.2$make menuconfig
Administration
- LuCI Components
- luci-admin-full: M
anders@ubuntu:~/share/openwrt/8.09.2$make
anders@ubuntu:~/share/openwrt/8.09.2/build_dir/mipsel/root-brcm47xx/www$ ls
cgi-bin index.html luci-static resources


(To be continued..)

2010年3月16日星期二

likely & unlikey

GCC的likely和unlikely功能就是让很可能被执行的分支代码紧跟着je或jne这样的条件跳转指令后面。为何非要跟在条件跳转指令屁股后?主要是更好利用CPU指令Cache。想想看:如果要执行的语句都在Cache里,省去从Memory抓数据的时间直接去执行,速度岂不是快了?所以likely和unlikely这种功能主要是为了提速,去掉不会影响功能。

http://kerneltrap.org/node/4705

2010年3月12日星期五

Comparison of Hard Link and Symbolic Link

Comparison of Hard Link and Symbolic Link

ItemHard LinkSymbolic Link
Name resolution Faster. A hard link contains a direct reference to the object.Slower. A symbolic link contains a path name to the object, which must be resolved to find the object.
Object existenceRequired. An object must exist in order to create a hard link to it. Optional. A symbolic link can be created when the object it refers to does not exist.
Object deletion Restricted. All hard links to an object must be unlinked (removed) to delete the object.Unrestricted. An object can be deleted even if there are symbolic links referring to it.
Dynamic objects (attributes change)Slower. Many of the attributes of an object are stored in each hard link. Changes to a dynamic object, therefore, are slower as the number of hard links to the object increases. Faster. Changes to a dynamic object are not affected by symbolic links.
Static objects (attributes do not change) Faster. For a static object, name resolution is the primary performance concern. Name resolution is faster when hard links are used. Slower. Name resolution is slower when symbolic links are used.
ScopeRestricted. Hard links cannot cross file systems. Unrestricted. Symbolic links can cross file systems.

2010年3月7日星期日

LockMeter

最近在系统学习LDD3(以前都是临时抱佛脚),在第五章看到了关于LockMeter(http://oss.sgi.com/projects/lockmeter/)。LockMeter可以分析出不同Linux版本在SMP支持上的性能的差异,分析Lock
Hold/Wait Time。我很想在我的Kernel里面玩玩,但是看到LockMeter网站上支持的内核不够新(2.6.16
Latest),我的Kernel是2.6.32.3,或许Kernel已经加入了这个功能?于是我开始在我的2.6.32.3内核中找寻是否有相关的东西。在Document目录中发现了lockstat.txt,很像LockMeter,OK!menuconfig->Kernel
Hacking->Lock usage statistics,make bzImage/Update
grub.confg,一切妥当,boot新的内核,意外发生了:

Give up waiting for root device. Common problem:
- Boot args (cat /proc/cmdline)
- Check rootdelay = (did the system wait log enough?)
- Check root = (did the system wait for the right device)
- Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/dist/by-uuid/xxxxx doest not exist. Dropping to a shell!


奇怪,怎么就找不到硬盘了呢?我把rootdelay设置到了200都不OK!忙活了几个小时,不知道为何,思路有点乱,改天再研究!

2010年3月1日星期一

招聘的瞬间

今天Interveiw了一个人。未见人之前我首先拿到的是他的笔试卷子(C Language),做的比较差,成绩不超过20分。

我首先对这20分准备了几个问题:
1. 这次是才出来找工作吗?(如果是刚出来找,没有准备笔试的话,有时候会出现惨不忍睹的场面)
2 你平时用什么语言开发?(如果平时不用C开发,可能会做不太好)

见到人后,显示出来的是比较好的亲和力,眼神中透露出了期待和自信。第一印象还是不错的,于是开始了项目细节的盘问。

[团队协作问题]
Anders> 你当负责人时,下面几个人?(简历中有看到他当过一段时间项目负责人)
He> 最多的时候是3个。
Anders> 你平时如何和下面人协调工作?
He> 手下,也不能说手下,都是一起工作,我主要是做好环境和驱动,然后其他几个人做App(应用)。

[项目问题]
Anders> 你现在做什么产品?
He> 车载播放器, XXXXXX(一些产品介绍)
Anders> 你在项目中主要做哪些工作?
He> 构建WinCE环境和驱动
Anders> 你们项目都有哪些驱动?用什么总线?
He> 主要用IIC和UARD (他接下来说了很多CAN总线的东西,我具体问,他说目前没用到CAN)
Anders> 你能给我讲一下IIC这个总线吗?
He> 从历史说了一下,然后说了一下细节,提及到IIC 地址的部分
Anders> 能说下IIC里面地址是怎么确定的吗?
He> 由IC确定, IIC可以挂多个设备

[OS问题]
Anders> 你除了WinCE还用过其他OS吗?比如Linux
He> 稍微了解一下Ubuntu
Anders> 能具体说说Ubuntu吗?
He> 了解很少

[网络问题]
Anders> 你做过网络驱动吗?
He> 没做过,车载播放器目前还没加网络功能
Anders> 你对TCP/IP熟悉吗?
He> 不是很熟悉,就是大学学那些东西

[之前埋伏笔试的问题]
Anders> 这次是才出来找工作吗?
He> 陆陆续续有一个月了
Anders> 你平时工作用什么语言?
He> C++

[结束]
Anders> 我们大概工作是做网卡驱动,当前有一个任务是WinCE的,不过还有很多不同OS的驱动项目,所以我们需要一个比较全面的人
He> 他听到这里,应该知道他没戏了
Anders> 你有什么问题要问我吗?
He> 没有了
Anders> 感觉气氛不太轻松,我就感叹了一下福田那边的房租和房价缓和气氛(知道他从福田区过来)
He> 你指租金还是房价?
Anders> 租金,你的房租多少钱?
He> 我买房了 (这一瞬间,他的脸上出现了一丝得意,不过也难以掩饰他被淘汰的郁闷,估计初期他认为他胜算很大)

[总结]
基础太差(笔试成绩差,而且是出来一个月拼杀过后的成绩)
接触面窄 (只会WinCE,对其他OS没有任何接触。也不是很深入,说东西前爱说很多无用的东西,不直接切入主题,切入主题后说得很少,给人感觉夸夸其谈)
网络知识为0 (这点很不符合工作需要)
说他买房了那一瞬间的表情让我看到他缺乏沉稳,有些浮躁。