2006年09月29日

    最近在玩Python,觉得它是一个简练、但高效,而且很有意思的一门语言,据说在google是使用频率最高的三大语言之一了。今晚看的是<Dive into Python>的正则表达式部分,正则表达式是搜索、替换和解析复杂字符模式的一种强大而标准的方法。

    字符串也有很多方法,可以进行搜索(index, find, 和 count), 替换(replace)和解析 (split), 但他们仅限于处理最简单的情况。搜索方法查找单个和固定编码的子串,并且他们总是大小写敏感的。对一个字符串s, 如果要进行大小写不敏感的搜索,则你必须调用 s.lower() 或 s.upper() 将s转换成全小写或者全大写,然后确保搜索串有着相匹配的大小写。replace 和 split方法有着类似的限制。

   如果你要解决的问题利用字符串函数能够完成,你应该使用他们。他们快速、简单且容易阅读,而对于快速、简单、可读性强的代码等方面有很多内容。但是,如果你发现你用了许多不同的字符串函数和 if语句来处理一个特殊情况,或者你组合使用了 split 、join 等函数而导致用一种奇怪的甚至读不下去的方式理解列表,此时,你也许需要转到正则表达式了。

    其中有个电话号码匹配正则表达语句巨牛,一条语句完成了可能c++或JAVA 几十条语句完成的功能,而且如果考虑时效开销,Python的正则表达式的优势是十分明显的。以下是通过正则表达式匹配电话号码的语句:

   >>> phonePattern = re.compile(r’^\D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$’)
   >>> phonePattern.search(‘(800)5551212 ext. 1234′).groups()                
   (‘800′, ‘555′, ‘1212′, ‘1234′)
   >>> phonePattern.search(‘800-555-1212′).groups()                          
   (‘800′, ‘555′, ‘1212′, ”)
   >>> phonePattern.search(‘work 1-(800) 555.1212 #1234′)                    
 >>>
 然而改进后的表达式更牛,摆到明是无论如何都要找到你丫电话号码了,哈哈

    >>> phonePattern = re.compile(r’(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$’)
   >>> phonePattern.search(‘work 1-(800) 555.1212 #1234′).groups()       
   (‘800′, ‘555′, ‘1212′, ‘1234′)
   >>> phonePattern.search(‘800-555-1212′)                               
   (‘800′, ‘555′, ‘1212′, ”)
   >>> phonePattern.search(‘80055512121234′)                             
   (‘800′, ‘555′, ‘1212′, ‘1234′)
     让我想起了c++或java语句来实现他的N多个if , for,iptr++。。。。。,所以说Python在某些方面还是很有优势的。咔咔,下面贴写Python的小结:

^ 匹配字符串的开始。
$ 匹配字符串的结尾。
\b 匹配一个单词的边界。
\d 匹配任意数字。
\D 匹配任意非数字字符。
x? 匹配一个可选的x字符(换句话说,它匹配1次或者0次x 字符)。
x* 匹配0次或者多次x字符。
x+匹配1次或者多次x字符。
x{n,m} 匹配x字符,至少n次,至多m次。
(a|b|c)要么匹配a,要么匹配b,要么匹配c。
(x) 一般情况下表示一个记忆组(remembered group). 你可以利用re.search函数返回对象的groups()函数获取它的值。

2006年09月27日

嵌入式解决方案
在做ucLinux+xgui+net+dns+2.4.20-8的解决方案的过程中
1:编译Busybox
 其中包括/bin /sbin 尤其是/sbin/init等,这个很熟,就不累书了
2:文件系统,用uclibc做为库文件
   为节约空间,/lib下的库直接做指向/usr/uclinu-i386-uclibc的符号连接
3:启动配置
这个是最难,极易出错的地方。
其中/etc/inittab中配置如下
==================
::sysinit:/etc/init.d/rcS
::respawn:-/bin/login
tty2::respawn:-/bin/login
tty3::respawn:-/bin/login
tty4::respawn:-/bin/login
::restart:/sbin/init
::ctrlaltdel:/etc/init.d/rc.reboot
::shutdown:/etc/init.d/rc.reboot
===================
   
Init进程一进来就执行inittab,这个也很熟,不说什么了。
做完以上,其实已经可以跑起来一个最基础的babylinux了,ok,接下来添加系统模块。
4:添加设备文件
   必须添加的设备为:
   null console zero tty0 tty1 fb fb0
   如果没有console的话在嵌入式系统启动时候会报:
   Unable to open an initial console的错误,do be silly!
   而其中null和zero是用来接受系统的输出,默认情况null和zero会忽略他们,不会显示也不会保持。
   也可以通过:
   mknod –m 660 console c 5 1
   mknod –m 660 null c 1 3
5:添加网络模块
   网络模块是系统内核支持,所以其首先要求系统引导的内核支持网络模块
   如果内核支持网络模块,那么系统需要和内核版本匹配的内核驱动模块
   例如:kernel是2.4.20-8,那么网络驱动模块也要相应该硬件的匹配2.4.20-8的版本
   网络模块的地址是:
   /lib/modules/[kernel vesion]/kernel/drivers/net
   注:kernel version是内核版本号,我的版本号是2.4.20-8
   而我的网卡是8139系列的,所以我的内核驱动是:
   /lib/modules/2.4.20-8/kernel/drivers/net/8139too.o
   但在加载这个模块前,可以通过查看
   /lib/modules/2.4.20-8/kernel/modules.dep
   查看到8139模块依赖于mii模块
   所以我们也需要添加mii模块,该模块和8139too.o要放在一个目录。
   所以要添加网络模块的命令可以写入/etc/profile,让系统每次启动自动加载。
   其中/etc/profile的配置如下
modprobe /lib/modules/2.4.20-8/kernel/drivers/net/mii.o
modprobe /lib/modules/2.4.20-8/kernel/drivers/net/8139too.o
ifconfig eth0 202.38.215.121
ifconfig eth0 up
route add default gw 202.38.215.254
6:配置网络
   配置网络静态ip是ifconfig,这个非常熟悉,
其中要说明的是route添加网关,too naive,犯了一个非常非常低级低级的错误,把网关和dns搞混了,一直把202.38.193.33来作为网关往上面加,于是出现了:
SIOCADDRT :Network is unreachable
搞得都快崩溃了,无意中巧了254才ping通,基础还是不扎实啊,怎么基础的东西,艾。。。。讲出来自己都觉得好笑。不过回来看其实人家的错误提示已经不错了,网络不可达,也就是你的数据链路和193.33不通,但是你可以拼通你内网215以内的ip,所以要想的办法是如何ping到外面去,所以要的是254作为网关帮你转发。
注:其实这个问题核心还是没有搞懂网关和dns的概念,混淆了。网络差不多忘光了。
7:配置DNS
   这个本来应该是比较复杂的,记得当初陪牛哥在外面公司加班加点就是配DNS,但是也不知道为什么居然让我不是花很大功夫就搞定了,看来最近rp不错啊,^_^。
   Uclinux的dns依赖与以下几个文件:hosts , hosts.conf , nsswitch.conf, resolv.conf
   # /etc/host.conf
   order hosts,bind
 
   # /etc/resolv.conf
nameserver 202.38.193.33

   # /etc/hosts
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1 localhost.localdomain localhost
202.38.193.33    dnsserver

# /etc/nsswictch.conf
host:filesdns

其中各个文件的功能和作用不想写了,网上有很多的,一查uclinux dns或者busylinux d ns就很多。(baidu在中文搜索还是比google好一些)
   
8: 网络自解析
在hosts里面加上
211.94.144.100 www.baidu.com
64.233.189.104 www.google.com
202.112.17.188 www.scut.edu.cn
可以将一些常用的网址的ip地址写入hosts里面,以后访问以上网址的,系统不用dns服务,直接转入该ip连接。
9:难点、突破点
   难点在于内核模块加载的部分
modprobe /lib/modules/2.4.20-8/kernel/drivers/net/mii.o
modprobe /lib/modules/2.4.20-8/kernel/drivers/net/8139too.o
   开发人员如何知道加那些模块、怎么加了?这个问题困惑了我很久,从一年前到现在,总算搞清楚了。Modprobe is the key!
   在大系统modprobe可以看到系统加载的模块
   其中有8139 和mii,
   再执行: modprobe –l |grep ‘8139’
   可以知道8139模块指向的具体模块位置:
/lib/modules/2.4.20-8/kernel/drivers/net/8139too.o
   可通过执行:depmod 8139 调用module.conf
得知其依赖于mii模块。
然后用insmod加载就ok了,不知道为什么modprobe的效果好像不佳。
10:牢骚、期望
虽然写下来不多,但是其实在做的过程中还是很多挑战,要对linux系统的运行机制非常了解,所以做的过程中还是比较伤脑筋的,但是也正是有调战,才显的很有意思。
整体就看到我在不停的reboot,在大系统和嵌入式系统中转换,多希望能分多一台机 器给我,让我可以在两个系统中自由切换,可极大极大极大的提高工作效率。
艾。。。又做梦了。

       这两天每天早上8到实验室,坐到晚上8点、10点,就是中午、下午出去打了个盒饭回来,总算搞定了uclinux和linux的嵌入式解决方案。一年前的这个时候,我对嵌入式IPTV解决方案如痴如醉,现在又回过头来做这个,想想其实蛮郁闷的。但是没有办法啊,mary逼的。。。。。为了国庆、为了假期必须要努力提前把任务都做完,让我没有顾虑的,开心痛苦的玩一场。
        毕竟是过了一年,刚开始的时候手很生,很多东西都忘了,最开始有点慢,但是也毕竟是又多学了一年,上手非常快,而且思绪比较敏捷,找问题和找解决方法的能力强了很多,所以两天之内能把2-3个星期的工作量做完,哈哈哈哈,接下来可以自己好好利用时间国庆玩玩放松一下,然后抓紧时间看书,写代码。
       哦,哦。。。还有锻炼身体,多运动

2006年09月24日

昨晚小上海从上海来广州,我们借机在广州和广州附近的同学趁机在校门口北雪湘园聚餐,大家见见面。

本来我预定了三桌,后面来了24个人,于是大家挤挤座了两桌。

有很多同学是毕业一年都没见过的了

丰丰:在鲁班听说混的teamleader了,还是那么白白净净,还是原来的老样子,没怎么变,还是那么瘦,不过就是沉默寡言了不少,据他自己说是:对得电脑太多,好少讲野。。。

志哥:据说在欧core楼上上班,看上去还是那么腼腆的。。哈哈^_^

老大:佛山未来的“明星市长”,上次元旦见过面了,感觉还是没有什么变,帅帅的,笑很多,一看就知道对现在的佛山现在的公务员“蛀虫”生活很满意,咔咔,佛山人们有难了。。。

欧core:汇丰的core team干将,可能压力大,经常加班,欧core居然现在焕发第二春,满脸的青春豆。哈,欧core就是欧core, core啊。。

昆昆:腾讯仔,在腾讯的日子应该过的很充实的,据说经常加班,压力很大,昨晚最活跃的就是这小子了

小白:帅帅的小白,电发男,据说为了见岳母大人特地去电了发,哇,真搞,见未来的岳母不是应该给人一个老实本分的印象嘛?看来我要upload我的价值观

主角:小上海,上海无良地产商,据说胖了20斤,确实胖了很多,他说他每天做的就是“一三五断电,二四六断水”,逼这些租户交房租。

凌若天:发了IEEE论文了,呵呵,现在在申请去香港读博士,还是那么牛

刘佳妍:hp工作肯定很不错,愈发靓丽了不少,还是一副“大姐大”的模样,哈哈

女人英:跳到IBM买机器了,哈哈,那下子嘴巴总是停不下来,这份工作看来很适合。。

还有其他一干人等,大家聚在一起说说笑笑还是很有意思的,下一次聚餐又是什么时候了?

 

2006年09月22日

做一个嵌入式Linux系统究竟要做哪些工作
做一个嵌入式Linux系统究竟需要做哪些工作?也就是本文究竟要讲述哪些内容?我先介绍一个脉络,可以做为我们后面工作的一个总的提纲:
第一步、建立交叉编译环境
没有交叉开发经验的读者,可能一时很难接受这个概念。首先,要明白两个概念:一般我们工作的机器,称为开发机、主机;我们制作好的系统将要放到某台机器,如手机或另一台PC机,这台机我们称为目标主机。
我们一般开发机上已经有一套开发工具,我们称之为原生开发套件,我们一般就是用它们来写程序,那么,那什么又是交叉编译环境呢?其实一点也不神秘,也就是在开发机上再安装一套开发工具,这套开发工具编译出来的程序,如内核、系统工作或者我们自己的程序,是放在目标主机上运行的。
那么或许有初学者会问,直接用原生开发工具为目标主机编译程序不就完了?至少我当初是这么想的。一般来说,我们的开发机都是X86平台,原生开发套件开发的工具,也针对X86平台,而我们的目标主机可能是PowerPC、IXP、MIPS……所以,我们的交叉编译环境是针对某一类具体平台的。
一般来讲,交叉开发环境需要二进制工具程序、编译器、C链接库,嵌入式开发常用的这三类软件是:
Binutils
Gcc
uClibc
当然,GNU包含的工具套件不仅于此,你还要以根据实际需要,进行选择

第二步、编译内核
开发工具是针对某一类硬件平台,内核同样也是。这一步,我们需要用第一步中建立的工具,对内核进行编译,对于有内核编译经验的人来说,这是非常简单的;

第三步、建立根文件系统
也就是建立我们平常看到的bin、dev、proc……这一大堆目录,以及一些必备的文件;另外,我们还需要为我们的目标系统安装一些常用的工具软件,如ls、ifconfig……当然,一个办法是找到这些工具的源代码,用第一步建立的交叉编译工具来编译,但是这些软件一是数量多,二是某些体积较大,不适合嵌入式系统,这一步,我们一般都是用busybox来完成的,包括系统引导软件init;
最后,我们为系统还需要建立初始化的引导文件,如inittab……

第四步、启动系统
在这一步,我们把建立好的目标、文件、程序、内核及模块全部拷贝到目标机存储器上,如硬盘。然后为系统安装bootloader,对于嵌入式系统,有许多引导程序可供我们使用。不过它们许多都有硬件平台的限制。当然,如果你是工作在X86,可以直接用lilo来引导,事实上,本文就是采用的lilo。
做到这一步,将目标存储设备挂上目标机,如果顺利,就可以启动系统了。
当然,针对某些特别的平台,不能像硬盘这样拷贝了,需要读卡器、烧录……但是基本的方法是相通的!

第五步、优化和个性化系统
通过前四步,我们已经得到了一个可以正常工作的系统。在这一步里,就是发挥你想像的时候了……

本文的工作环境
项目根目录/home/kendo/project  ——>;我将它指定至PATH:$PRJROOT
子目录及说明
目录 内容
bootloader 目标板的引导加载程序,如lilo等
build-tools 建立交叉编译平台的工具源码
debug 调试工具及所有相关包
doc 项目中用到的所有文档
images 编译好的内核映像,以及根文件系统
kernel 各个版本的Linux内核源码
rootfs 制作好的根文件系统
sysapps 目标板将要用到的系统应用系统,比如thttpd,udhcpd等
tmp 存放临时文件
tools 编译好的跨平台开发工具链以及C链接库

工作的脚本
#!/usr/bin

export PROJECT=skynet
export PRJROOT=/home/${PROJECT}
export TARGET=i386-linux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PREFIX}/bin:/bin:/sbin:/usr/bin:/usr/sbin

cd $PRJROOT

第二章 建立交叉编译环境
在CU中发表的另一篇同名的贴子里,我讲述了一个全手工创建交叉编译环境的方法。目前,创建交叉编译环境,包括建立根文件,一般来讲,有两种方法:
手功创建
可以得到最大程序的个性化定制,缺点是过程繁杂,特别是极易出错,注意这个“极”字,包括有经验的开发人员;
自动创建
无它,方便而。

因为前一篇文章中,已经讲述了全手工创建交叉编译环境的一般性方法,本文就不打算再重复这个步骤了,感兴趣的朋友,可以再去搜索那篇贴子,提醒一点的就是,在准备工具链的时候,要注意各个工具版本之间的搭配、每个工具需要哪些补丁,我建议你在google上针对这两项搜索一下,准备一个清单,否则……
本章要讲述的是自动创建交叉编译环境的方法。目标,针对商业硬件平台,厂家都会为你提供一个开发包,我用过XX厂家的IXP425和MIPS的,非常地方便,记得我第一次接触嵌入式开发,拿着这个开发包自动化创建交叉编译环境、编译内核、建立根文件系统、创建Ram Disk,我反复做了三四次,结果还不知道自己究竟做了些什么,呵呵,够傻吧……
所以,建议没有这方面经验的读者,还是首先尝试一下手工创建的方法吧,而本章接下来的内容,是送给曾经被它深深伤害而不想再次去亲历这项工作而又想提高交率而又在通用平台上工作没有商业开发包的朋友。

建立交叉开发工具链
准备工具:
buildroot-0.9.27.tar.tar
只需要一个软件?对,其它的不用准备了,buildroot事实上是一个脚本与补丁的集合,其它需要用到的软件,如gcc、uClibc,你只需在buildroot中指明相应的版本,它会自动去给你下载。
事实上,buildroot到网上去下载所需的所有工作是需要时间的,除非你的带宽足够,否则下载软件时间或许会占去80%,而我在做这项工作之间,所需的工作链全部都在我本地硬盘上,我解压开buildroot后,新建dl文件夹,将所有工具源码的压缩包拷贝进去,呵呵,buildroot就不用去网上下载了。

我的软件清单:
Linux-libc-headers-2.4.27.tar.bz2
Gcc-3.3.4.tar.bz2
binutils 2.15.91.0.2.tar.bz2
uClibc 0.9.27.tar.bz2
genext2fs_1.3.orig.tar.gz
ccache-2.3.tar.gz

将它拷贝到${PRJROOT}/build-tools下,解压
[root@skynet build-tools]# tar jxvf buildroot-0.9.27.tar.tar
[root@skynet build-tools]#cd buildroot
配置它:
[root@skynet build-tools]#make menuconfig
Target Architecture (i386)  —>; 选择硬件平台,我的是i386 
Build options  —>; 编译选项
这个选项下重要的是(${PRJROOT}/tools) Toolchain and header file location?编译好的工具链放在哪儿?
如果你像我一样,所有工具包都在本地,不需它到网上自动下载,可以把wget command选项清空;
Toolchain Options  —>; 工具链选项
— Kernel Header Options 头文件它会自动去下载,不过应该保证与你将要用的内核是同一个版本;
[] Use the daily snapshot of uClibc?   使用最近的uClibc的snapshot
Binutils Version (binutils 2.15.91.0.2)  —>; Binutils的版本
GCC compiler Version (gcc 3.4.2)  —>; gcc 版本
[*] Build/install c++ compiler and libstdc++? 
[ ]   Build/install java compiler and libgcj? 支持的语言,我没有选择java
[ ] Enable ccache support? 启用ccache的支持,它用于编译时头文件的缓存处理,用它来编译程序,第一次会有点慢,但是以后的速度可就很理想了,呵呵……
— Gdb Options 根据你的需要,选择gdb的支持

Package Selection for the target  —>;
这一项我没有选择任意一项,因为我打算根文件系统及busybox  等工具链创建成工,手工来做。
Target Options  —>;  文件系统类型,根据实际需要选,我用的ext2;

配置完成后,编译它:
[root@skynet build-tools]#make

这一项工作是非常花时间的,我的工具包全部在本地,也花去我一小时十三分的时间,如果全要下载,我估计网速正常也要多花一两个钟头。

经过漫长的等待(事实上并不漫长,去打了几把游戏,很快过去了):
……
make[1]: Leaving directory `/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3′
touch -c /home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs
#-@find /home/skynet/build-tools/buildroot/build_i386/root/lib -type f -name \*.so\* | xargs /home/skynet/tools/bin/i386-linux-uclibc-strip –remove-section=.comment –remove-section=.note –strip-unneeded 2>;/dev/null || true;
/home/skynet/build-tools/buildroot/build_i386/genext2fs-1.3/genext2fs -i 503 -b 1056 \
        -d /home/skynet/build-tools/buildroot/build_i386/root -q -D target/default/device_table.txt /home/skynet/build-tools/buildroot/root_fs_i386.ext2

大功告成!!!

清点战利品
让我来看看它究竟做了哪些事情吧:
[root@skynet skynet]# cd tools
[root@skynet tools]# ls
bin  bin-ccache  i386-linux  i386-linux-uclibc  include  info  lib  libexec  man  usr

bin:所有的编译工具,如gcc,都在这儿了,只是加了些指定的前缀;
bin-ccache:如果在Toolchain optaion中没有选择对ccache的支持,就没有这一项了;
i386-linux:链接文件;实际指向include
i386-linux-uclibc:uclibc的相关工具;
include:供交叉开发工具使用的头文件;
info:gcc 的info文件;
lib:供交叉开发工具使用的链接库文件;
……

现在可以把编译工具所在目录XXX/bin添加至PATH了

测试工具链
如果你现在写一个程序,用i386-linux-gcc来编译,运行的程序会告诉你:
./test: linked against GNU libc
因为程序运行库会寻到默认的/lib:/usr/lib上面去,而我们目前的uclibc的库并不在那里(虽然对于目标机来讲,这是没有错的),所以,也只能暂时静态编译,试试它能否工作了。当然,你也可以在建好根文件系统后,试试用chroot……

第三章 编译内核
本章的工作,是为目标机建立一个合适的内核,对于建立内核,我想有两点值得考虑的:
1、功能上的选择,应该能够满足需要的情况下,尽量地小;
2、小不是最终目的,稳定才是;

所以,最好编译内核前有一份目标机硬件平台清单以及所需功能清单,这样,才能更合理地裁减内核。

准备工具
Linux内核源码,我选用的是Linux-2.4.27.tar.bz2

编译内核
将Linux-2.4.27.tar.bz2拷贝至${PRJROOT}/kernel,解压
#cd linux-2.4.27
//配置
# make ARCH=i386 CROSS_COMPILE=i386-linux- menuconfig
//建立源码的依存关系
# make ARCH=i386 CROSS_COMPILE=i386-linux- clean dep
//建立内核映像
# make ARCH=i386 CROSS_COMPILE=i386-linux- bzImage
ARCH指明了硬件平台,CROSS_COMPILE指明了这是交叉编译,且编译器的名称为i386-linux-XXX,这里没有为编译器指明路径,是因为我前面已将其加入至环境变量PATH。

又是一个漫长的等待……
OK,编译完成,673K,稍微大了点,要移到其它平台,或许得想办法做到512以下才好,回头来想办法做这个工作。

安装内核
内核编译好后,将内核及配置文件拷贝至${PRJROOT}/images下。
# cp arch/i386/boot/bzImage ${PRJROOT}/images/bzImage-2.4.27-rmk5
# cp vmlinux ${PRJROOT}/images/vmlinux-2.4.27-rmk5
# cp System.map ${PRJROOT}/images/System-2.4.27-rmk5
# cp .config ${PRJROOT}/images/2.4.27-rmk5

我采用了后缀名的方式重命名,以便管理多个不同版本的内核,当然,你也可以不用这样,单独为每个版本的内核在images下新建对应文件夹也是可行的。

安装内核模块
完整内核的编译后,剩下的工作就是建立及安装模块了,因为我的内核并没有选择模块的支持(这样扩展性差了一点,但是对于我的系统来说,功能基本上定死了,这样影响也不太大),所以,剩下的步骤也省去了,如果你还需要模块的支持,应该:
//建立模块
#make ARCH=i386 CROSS_COMPILE=i386-linux- modules
//安装内核模块至${PRJROOT}/images
#make ARCH=i386 CROSS_COMPILE= i386-linux- \
>;INSTALL_MOD_PATH=${PRJROOT}/images/modules-2.4.18-rmk5 \
>;modules_install

最后一步是为模块建立依存关系,不能使用原生的depmod来建立,而需要使用交叉编译工具。需要用到busybox中的depmod.pl脚本,很可惜,我在busybox1.0.0中,并没有找到这个脚本,所以,还是借用了busybox0.63中scripts中的depmod.pl。
将depmod.pl拷贝至${PREFIX}/bin目录中,也就是交叉编译工具链的bin目录。
#depmod.pl \
>;-k ./vmlinux –F ./System.map \
>;-b ${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules >; \
>;${PRJROOT}/images/modules-2.4.27-rmk5/lib/modules/2.4.27-rmk5/modules.dep

注:后面讨论移植内核和模块内容时,我只会提到内核的拷贝,因为我的系统并没有模块的支持。如果你需要使用模块,只需按相同方法将其拷贝至相应目录即可。

附,内核编译清单
附,内核选择:
内核编译记录:
Code maturity level options 不选
Loadable module support 不选 
Processor type and features 根据实际,选择处理器类型
General setup  —>;
[*] Networking support
[*] PCI support
(Any)   PCI access mode
[*] PCI device name database
[*] System V IPC
[*] Sysctl support
(ELF) Kernel core (/proc/kcore) format
[*] Kernel support for ELF binaries
[*] Power Management support

Memory Technology Devices (MTD)  —>; MTD设备,我用CF卡,不选

Parallel port support  —>; 不选
Plug and Play configuration  —>; 我的系统用不着即插即用,不选

Block devices  —>;
[*] Loopback device support  
[*] RAM disk support 
(4096)   Default RAM disk size (NEW)
[*]   Initial RAM disk (initrd) support  

Multi-device support (RAID and LVM)  —>;  不选

Networking options  —>;  基本上都选了

ATA/IDE/MFM/RLL support  —>;   用了默认的

Telephony Support  —>; 不选

SCSI support  —>;  不选

Fusion MPT device support  —>;  不选 

I2O device support  —>;    不选

Network device support  —>;   根据实际情况选择

Amateur Radio support  —>;  不选   

IrDA (infrared) support  —>; 不选 

ISDN subsystem  —>; 不选

Old CD-ROM drivers (not SCSI, not IDE)  —>; 不选

Input core support  —>; 不选

Character devices  —>;  
[*] Virtual terminal 
[*]   Support for console on virtual terminal 
[*] Standard/generic (8250/16550 and compatible UARTs) serial support 
[*]   Support for console on serial port

Multimedia devices  —>;  不选

File systems  —>; 
 [*] Kernel automounter version 4 support (also supports v3) 
 [*] Virtual memory file system support (former shm fs)
 [*] /proc file system support
 [*] Second extended fs support

Console drivers  —>; 
 [*] VGA text console   调试时接显示器用

剩下三个都不要
Sound  —>;
USB support  —>;  
Kernel hacking  —>;  

第四章 建立根文件系统
1、建立目录
构建工作空间时,rootfs文件夹用来存放根文件系统,
#cd rootfs
根据根文件系统的基本结构,建立各个对应的目录:
# mkdir bin dev etc lib proc sbin tmp usr var root home
# chmod 1777 tmp
# mkdir usr/bin usr/lib usr/sbin
# ls
dev  etc  lib  proc  sbin  tmp  usr  var
# mkdir var/lib var/lock var/log var/run var/tmp
# chmod 1777 var/tmp

对于单用户系统来说,root和home并不是必须的。
准备好根文件系统的骨架后,把前面建立的文件安装到对应的目录中去。
2、拷贝链接库
把uclibc的库文件拷贝到刚才建立的lib文件夹中:
# cd ${PREFIX}/lib
[root@skynet lib]# cp *-*.so ${PRJROOT}/rootfs/lib
[root@skynet lib]# cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib

3、 拷贝内核映像和内核模块
因为没有模块,所以拷贝模块就省了,
新建boot目录,把刚才建立好的内核拷贝过来
# cd /home/kendo/control-project/daq-module/rootfs/
# mkdir boot
# cd ${PRJROOT}/images
# cp bzImages-2.4.18-rmk5 /home/kendo/control-project/daq-module/rootfs/boot

4、 建立/dev下边的设备文件
在linux中,所有的的设备文件都存放在/dev中,使用mknod命令创建基本的设备文件。
mknod命令需要root权限,不过偶本身就是用的root用户,本来是新建了一个用户专门用于嵌入式制作的,不过后来忘记用了……
# mknod -m 600 mem c 1 1    
# mknod -m 666 null c 1 3
# mknod -m 666 zero c 1 5
# mknod -m 644 random c 1 8
# mknod -m 600 tty0 c 4 0
# mknod -m 600 tty1 c 4 1
# mknod -m 600 ttyS0 c 4 64 
# mknod -m 666 tty c 5 0
# mknod -m 600 console c 5 1
基本的设备文件建立好后,再创建必要的符号链接:
# ln -s /proc/self/fd fd
# ln -s fd/0 stdin
# ln -s fd/1 stdout
# ln -s fd/2 stderr
# ls
console  fd  mem  null  random  stderr  stdin  stdout  tty  tty0  tty1  ttyS0  zero

设备文件也可以不用手动创建,听说RedHat /dev下的脚本MAKEDEV 可以实现这一功能,不过没有试过……

基本上差不多了,不过打算用硬盘/CF卡来做存储设备,还需要为它们建立相关文件,因为我的CF在目标机器上是CF-to-IDE,可以把它们等同来对待,先看看Redhat 下边had的相关属性:
# ls -l /dev/hda
brw-rw—-    1 root     disk       3,   0 Jan 30  2003 /dev/hda
# ls -l /dev/hda1
brw-rw—-    1 root     disk       3,   1 Jan 30  2003 /dev/hda1
对比一下,可以看出,had类型是b,即块设备,主编号为3,次编号从0递增,根限位是
rw-rw—-,即660,所以:
# mknod -m 660 hda b 3 0
# mknod -m 660 hda1 b 3 1
# mknod -m 660 hda2 b 3 2
# mknod -m 660 hda3 b 3 3

5、添加基本的应用程序
未来系统的应用程序,基本上可以分为三类:
&#61557; 基本系统工具,如ls、ifconfig这些……
&#61557; 一些服务程序,管理工具,如WEB、Telnet……
&#61557; 自己开发的应用程序

这里先添加基本的系统工具,有想过把这些工具的代码下载下来交叉编译,不过实在是麻烦,用BusyBox,又精简又好用……
将busybox-1.00.tar.gz下载至sysapps目录下,解压:
#tar zxvf busybox-1.00.tar.gz
#cd busybox-1.00
//进入配置菜单
#make TARGET_ARCH=i386 CROSS=i386-linux- PREFIX=${PRJROOT}/rootfs menuconfig
//建立依存关系
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs dep
//编译
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs
//安装
#make TARGET_ARCH=i386 CROSS= i386-linux- PREFIX=${PRJROOT}/rootfs install

# cd ${PRJROOT}/rootfs/bin
# ls
addgroup  busybox  chown  delgroup  echo      kill   ls     mv       ping  rm     sleep
adduser   chgrp    cp     deluser   grep      ln     mkdir  netstat  ps    rmdir  umount
ash       chmod    date   dmesg     hostname  login  mount  pidof    pwd   sh     vi
一下子多了这么多命令……
配置busybox的说明:
A、如果编译时选择了:
Runtime SUID/SGID configuration via /etc/busybox.conf
系统每次运行命令时,都会出现“Using fallback suid method ”
可以将它去掉,不过我还是在/etc为其建了一个文件busybox.conf搞定;
B、[*] Do you want to build BusyBox with a Cross Compiler?                                      (i386-linux-gcc) Cross Compiler prefix
这个指明交叉编译器名称(其实在编译时的命令行已指定过了……)
C、安装选项下的(${PRJROOT}/rootfs) BusyBox installation prefix,这个指明了编译好后的工具的安装目录。
D、静态编译好还是动态编译好?即是否选择
[ ] Build BusyBox as a static binary (no shared libs) 
动态编译的最大好处是节省了宝贵空间,一般来说都是用动态编译,不过我以前动态编译出过问题(其实是库的问题,不关busybox的事),出于惯性,我选择了静态编译,为此多付出了107KB的空间。
E、其它命令,根据需要,自行权衡。

6、系统初始化文件
内核启动时,最后一个初始化动作就是启动init程序,当然,大多数发行套件的Linux都使用了与System V init相仿的init,可以在网上下载System V init套件,下载下来交叉编译。另外,我也找到一篇写得非常不错的讲解如何编写初始化文件的文件,bsd-init,回头附在后面。不过,对于嵌入式系统来讲,BusyBox init可能更为合适,在第6步中选择命令的时候,应该把init编译进去。
#cd ${PRJROOT}/rootfs/etc
#vi inittab
我的inittal文件如下:
#指定初始化文件
::sysinit:/etc/init.d/rcS
#打开一个串口,波特率为9600
::respawn:/sbin/getty 9600 ttyS0
#启动时执行的shell
::respawn:/bin/sh
#重启时动作
::restart:/sbin/init
#关机时动作,卸载所有文件系统
::shutdown:/bin/umount -a –r

保存退出;

再来编写rcS脚本:
#mkdir  ${PRJROOT}/rootfs/etc/init.d
#cd ${PRJROOT}/rootfs/etc/init.d
#vi rcS
我的脚本如下:
#!/bin/sh

#Set Path
PATH=/sbin:/bin
export PATH

syslogd -m 60
klogd

#install /proc
mount -n -t proc none /proc

#reinstall root file system by read/write mode(need: /etc/fstab)
mount -n -o remount,rw /

#reinstall /proc
mount -n -o remount,rw -t proc none /proc

#set lo ip address
ifconfig lo 127.0.0.1

#set eth0 ip address
#当然,这样子做只是权宜之计,最后做的应该是在这一步引导网络启动脚本,像RedHat
#那样,自动读取所有指定的配置文件来启动
ifconfig eth0 192.168.0.68 netmask 255.255.255.0

#set route
#同样的,最终这里应该是运行启动路由的脚本,读取路由配置文件
route add default gw 192.168.0.1

#还差一个运行服务程序的脚本,哪位有现成的么?
#网卡/路由/服务这三步,事实上可以合在一步,在rcS这一步中,做一个循环,运行指定启动目录下的所有脚,先将就着这么做吧,确保系统能够正常启动了,再来写这个脚本。

#set hostname
hostname MyLinux

保存退出。

编写fstab文件
#vi fstab
我的fstab很简单:
/dev/hda1 / ext2 defaults 1 1
none /proc proc defaults 0 0

第五章 让MyLinux能够启动
前一章,我们把编译好的内核、应用程序、配置文件都拷贝至rootfs目录对应的子目录中去了,这一步,就是把这些文件移植至目标机的存储器。这里,我是先另外拿一块硬盘,挂在我的开发机上做的测试,因为我的本本用来写文档,PC机用来做开发机,已经没有另外的机器了……但是本章只是讲述一个一般性的过程,并不影响你直接在目标主机上的工作。
因为以后目标机识别硬盘序号都是hda,而我现在直接挂上去,则会是hdb、hdc……这样,安装lilo时有点麻烦(虽然也可以实现)。所以我想了另一个办法:
&#61557; 把新硬盘挂在IDE0的primary上,进入linux后,会被认为是had;
&#61557; 原来主机的装Redhat的硬盘,我将它从IDE0的primary上变到了IDE1 的primary,因为它的lilo早已装好,基本上不影响系统的使用;

分区和格式化
BIOS中改为从第二个硬盘启动;也就是从我原来开发机启动,新的硬盘被识别成了had。
#fdisk /dev/hda
用d参数删除已存在的所有分区
用n参数新建一个分区,也是就/dev/hda1
格式化
#mkfs.ext2 /dev/hda1

安装bootloader
因为我是X86平台,所以直接用了lilo,如果你是其这平台,当然,有许多优秀的bootloader供你选择,你只需查看其相应的说明就可以了。
编译lilo配置文件,我的配置文件名为target.lilo.conf,置于${PRJROOT}/rootfs/etc目录。内容如下所示:
boot=/dev/hda
disk=/dev/hda
bios=0×80
image=/boot/bzImage-2.4.18-rmk5
label=Linux
root=/dev/hda1
append="root=/dev/hda1"
read-only

//新建文件夹,为mount做新准备
#mkdir /mnt/cf
//把目标硬盘mount上来
#mount –t ext2 /dev/hdc1 /mnt/cf
回到rootfs
#cd ${PRJROOT}/rootfs
拷贝所有文件至目标硬盘
#cp –r * /mnt/cf

这样,我们所有的文件都被安装至目标硬盘了,当然,它还不能引导,因为没有bootloader。使用如下命令:
# lilo -r /mnt/cf -C etc/target.lilo.conf
Warning: LBA32 addressing assumed
Added Linux *
-r :改变根目标为/mnt/cf ,这样配置文件其实就是/mnt/cf/etc/target.lilo.conf,也就是我们先前建立的文件。
当然,完成这一步,需要lilo22.3及以后版本,如果你的版本太旧,比如Redhat9.0自带的,就会出现下面的信息:
#lilo –r /mnt/cf –C etc/target.lilo.conf
Fatal: open /boot/boot.b: No such file or directory
这时,你需要升级你的lilo,或者重新安装一个。

启动系统
#umount /mnt/cf
#reboot

将BIOS改为从IDE0启动,也就是目标硬盘。如果一切顺利,你将顺利进入一个属于你的系统。

回头再来看看我们的工作空间吧
[root@skynet lib]# df /dev/hda1
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/hda1              3953036      1628   3750600   1% /mnt/cf

总共花去了我1628KB的空间,看来是没有办法放到软盘里边去了^o^,不过一味求小,并不是我的目标。

[root@skynet skynet]# ls ${PRJROOT}
bootloader build-tools  debug  doc  images  kernel  rootfs  sysapps  tmp  tools
这几个目录中的文件,呵呵,与本文一开头规划的一样

[root@skynet skynet]# ls build-tools/
buildroot  buildroot-0.9.27.tar.tar
包含了buildroot源码及压缩包,事实上buildroot下边还包括了GNU其它工具的源码、编译文件等诸多内容,是我们最重要的一个文件夹,不过到现在它已经没有多大用处了,如果你喜欢,可以将它删除掉(不建议)。

[root@skynet skynet]# ls images
2.4.18-rmk5  bzImage-2.4.18-rmk5  System-2.4.18-rmk5  vmlinux-2.4.18-rmk5
内核映像及配置文件等,如果你有模块,因为还有相应的目录

[root@skynet skynet]# ls kernel/
linux-2.4.27  linux-2.4.27.tar.bz2
内核源码及压缩包

[root@skynet skynet]# ls rootfs/       
bin  boot  dev  etc  home  lib  linuxrc  proc  root  sbin  tmp  usr  var
制作好的根文件系统,重中之重,注意备份……

[root@skynet skynet]# ls sysapps/
busybox-1.00  busybox-1.00.tar.gz
busybox-1.00源码包,或许你还要继续添加/删除一些命令……

[root@skynet skynet]# ls tools
bin  i386-linux  i386-linux-uclibc  include  info  lib  man
这个也很重要,我们制作好的交叉开发工具链。如果你要继续开发程序,这个目录重要性就很高了。

其它目录暂时是空的。

第六章 完善MyLinux
关于进一步的调试,你可以在开发机上使用chroot /mnt/cf /bin/sh这样的命令,以使我们在目标根文件系统上工作。

支持多用户
因为我在编译busybox时,已经将它的多用户那一大堆命令编译了进来。现在关键是的要为其建立相应的文件;
进入原来的开发机,进入rootfs目录,切换根目录
#chroot rootfs/ /bin/sh
A、 建立/etc/passwd文件,我的文件内容如下:
root:x:0:0:root:/root:/bin/bash
B、 建立/etc/group文件,我的文件内容如下:
root:x:0:
bin:x:1:
sys:x:2:
kmem:x:3:
tty:x:4:
tape:x:5:
daemon:x:6:
disk:x:7:
C、 为root建立密码
#passwd root

试试用addgroup/addusr……这堆命令。然后重启,从目标硬盘上启动;从console口,9600登陆试试(因为我在inittab中启用了ttyS0,我未来的目标机,是没有显卡的,需要从console口或SSH进去管理)
MyLinux login: root
Password:

BusyBox v1.00 (2004.10.10-04:43+0000) Built-in shell (ash)
Enter ’help’ for a list of built-in commands.

~ #
成功了……

增加WEB Server
Busybox里边有httpd选项,不过我编译时并没有选择,所以还是自己来安装。我使用的软件是thttpd-2.25b.tar.gz,将它移至sysapps目录下。
[root@skynet sysapps]# tar zxvf thttpd-2.25b.tar.gz
[root@skynet sysapps]# cd thttpd-2.25b
//配置
[root@skynet thttpd-2.25b]# CC=i386-linux-gcc ./configure –host=$TARGET
……
i386-linux-gcc  -static htpasswd.o -o htpasswd -lcrypt 
make[1]: warning:  Clock skew detected.  Your build may be incomplete.
make[1]: Leaving directory `/home/skynet/sysapps/thttpd-2.25b/extras’
//拷贝至根文件目录
[root@skynet thttpd-2.25b]# cp thttpd ${PRJROOT}/rootfs/usr/sbin
//trip处理
[root@skynet thttpd-2.25b]# i386-linux-strip ${PRJROOT}/rootfs/usr/sbin/thttpd

2006年09月21日

标准Linux系统的进程、线程

    进程:进程是一个运行程序并为其提供执行环境的实体,它包括一个地址空间和至少一个控制点,进程在这个地址空间上执行单一指令序列。进程地址空间包括可以访问或引用的内存单元的集合,进程控制点通过一个一般称为程序计数器(program counter,PC)的硬件寄存器控制和跟踪进程指令序列。

    fork:由于进程为执行程序的环境,因此在执行程序前必须先建立这个能"跑"程序的环境。Linux系统提供系统调用拷贝现行进程的内容,以产生新的进程,调用fork的进程称为父进程;而所产生的新进程则称为子进程。子进程会承袭父进程的一切特性,但是它有自己的数据段,也就是说,尽管子进程改变了所属的变量,却不会影响到父进程的变量值。

    父进程和子进程共享一个程序段,但是各自拥有自己的堆栈、数据段、用户空间以及进程控制块。换言之,两个进程执行的程序代码是一样的,但是各有各的程序计数器与自己的私人数据。

    当内核收到fork请求时,它会先查核三件事:首先检查存储器是不是足够;其次是进程表是否仍有空缺;最后则是看看用户是否建立了太多的子进程。如果上述说三个条件满足,那么操作系统会给子进程一个进程识别码,并且设定cpu时间,接着设定与父进程共享的段,同时将父进程的inode拷贝一份给子进程运用,最终子进程会返回数值0以表示它是子进程,至于父进程,它可能等待子进程的执行结束,或与子进程各做个的。

    exec系统调用:该系统调用提供一个进程去执行另一个进程的能力,exec系统调用是采用覆盖旧有进程存储器内容的方式,所以原来程序的堆栈、数据段与程序段都会被修改,只有用户区维持不变。

    vfork系统调用:由于在使用fork时,内核会将父进程拷贝一份给子进程,但是这样的做法相当浪费时间,因为大多数的情形都是程序在调用fork后就立即调用exec,这样刚拷贝来的进程区域又立即被新的数据覆盖掉。因此Linux系统提供一个系统调用vfork,vfork假定系统在调用完成vfork后会马上执行exec,因此vfork不拷贝父进程的页面,只是初始化私有的数据结构与准备足够的分页表。这样实际在vfork调用完成后父子进程事实上共享同一块存储器(在子进程调用exec或是exit之前),因此子进程可以更改父进程的数据及堆栈信息,因此vfork系统调用完成后,父进程进入睡眠,直到子进程执行exec。当子进程执行exec时,由于exec要使用被执行程序的数据,代码覆盖子进程的存储区域,这样将产生写保护错误(do_wp_page)(这个时候子进程写的实际上是父进程的存储区域),这个错误导致内核为子进程重新分配存储空间。当子进程正确开始执行后,将唤醒父进程,使得父进程继续往后执行。

uClinux的多进程处理

    uClinux没有mmu管理存储器,在实现多个进程时(fork调用生成子进程)需要实现数据保护。

    uClinux的fork和vfork:uClinux的fork等于vfork。实际上uClinux的多进程管理通过vfork来实现。这意味着uClinux系统fork调用完程后,要么子进程代替父进程执行(此时父进程已经sleep)直到子进程调用exit退出,要么调用exec执行一个新的进程,这个时候将产生可执行文件的加载,即使这个进程只是父进程的拷贝,这个过程也不能避免。当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,父进程继续往下执行。

     uClinux的这种多进程实现机制同它的内存管理紧密相关。uClinux针对nommu处理器开发,所以被迫使用一种flat方式的内存管理模式,启动新的应用程序时系统必须为应用程序分配存储空间,并立即把应用程序加载到内存。缺少了MMU的内存重映射机制,uClinux必须在可执行文件加载阶段对可执行文件reloc处理,使得程序执行时能够直接使用物理内存。

上午下班,从实验室回来,去饭堂打饭,然后回来的路上,看到一张张稚气又陌生的师弟师妹的面孔,呵呵,我都研二了,在华工呆了5年了,真的过的很快

上午google的时候无意中发现了一个耒阳老乡的blog,也是在清华读研,牛人啊,是个基础扎实、技术超强的牛淫,同时耒阳老乡,同是计算机的研究生,人家可以做的那么好,而我只有高山仰止的份。。。

感觉自己还是比较浮,不扎实,但要怎么改了?好像很多想法,但又无从下手,迷茫

昨晚俱乐部邀请了IBM 公司加拿大分部的DB2部门高级技术工程师ralm来到大学城做报告,报告完以后我们一起乘大学城的末班车回广州市区。

ralm是100%的华人血统,但他是他的爷爷从广东去了美国,然后他的父亲也是在美国出身,而他也是在密鲁长大然后去加拿大多伦多大学求学,最后进入了加拿大IBM公司,在IBM公司工作九年了。虽然他是华人,但是他一句中文也不会讲,连听也不会,只会说几句极为不标准的广东话:你食饭未?^_^

因为他不会中文,所以和他沟通必须 要用英语。呵呵,我这种典型的中国应试形的英语学生用着中国式的英语很艰难的和他沟通,很多意思难以表达,基本都是直接用中文的意思直接直译为英语,我怀疑我可能真的是一个个单词直接翻译过去,哈哈,没有时态、没有句式、没有语法。。。。^_^

很庆幸,我的三角猫英语还能和他进行沟通,甚至有点“相谈甚欢”了,我们从昨晚的讲座谈到了对广州的印象、对中国的印象、对中国人太多的恐怖场面(一个bus塞满了人,踮脚的地方都没有),谈到了对中国自然环境的看法,谈到了计划生育,谈到了西藏,谈到了中国经济、谈到了两极分化,谈到了移民加拿大(我哪有那本事啊)。。。。

最后谈到了婚姻,我问他:have u married?? 他说:not yet? 然后我问:why?how old r u? 他说:three sixy!我说:what??36?i think u‘d better find some girl get married as soon as possible!  他说:u know i have lots of work to finish in IBM DB2 lab, i didn’t have time to share the love with some girl 。。。。。

难道这就是IT 技术人员的宿命?整晚感慨良多,有点失眠,思绪象一只脱缰的野马,在脑海里转个不停。。。

2006年09月20日

有几句说的非常好的话,用来自省:

只有永远看到别人优点的人,才是进步最快的人;

经常议论别人的缺点,你就是一个道德水准低下者;经常议论人类的缺点,你就是一个思想家。 

2006年09月19日

两个星期瘦了六斤。。。

哈哈哈哈哈哈

今晚心情好,可以睡个好觉