Wednesday, April 29, 2009

系统程序员成长计划-序

转载时请注明出处和作者联系方式

文章出处:http://www.limodev.cn/blog
作者联系方式:李先静

写作背景

在经历过几个大型的,失败的项目之后,我终于明白没有什么比高素质的程序员更能决定项目的成功了,无论什么过程,什么编程语言和开发工具,离开了高 素质的程序员,什么都是白费力气。毫无疑问,人是软件开发中最重要的因素,但不是每个人都重要,不是什么样的人都重要,只有那些高素质的程序员和那些对项 目有突出贡献的人才是重要的。

不过高素质的程序员并不多见,所以从我开始带人之际,就在思考团队成员培养的问题。我做过很多尝试,从小组内学习到整个部门一起上大课,最后又回到 对个人单独的辅导;从通过codreview做现场教育到制定过一个宏伟的培训计划,最后又回到一个朴素的培训过程。其中遇到了很多问题,开始是培训不够 系统,效果不甚理想,后来又因为计划过于”宏伟”而无法实施,直到最后行成一个朴素的,切实可行的培训方案,中间经过了好几年时间,直到去年,整个计划才 趋于完善。我把这个培训计划称为系统程序员成长计划,这就是我在这个系列中要写的。

培训内容不是来源于某本书,毕业八年来,我坚持不断的看书,家中放了7大储物箱,有300多本不同类型的书籍,其中囊括了大部分经典的IT图书。当 然也不是全部内容都来源书本,这几年我在开源软件吸取了大量的营养,一些思想和经验在broncho项目中也有充分的发挥,可以说是理论,经验和实践的结 合。但我不尝试阐述什么高深的道理,相反我是针对应届毕业生和业余爱好者写的,目的是要让初学者进阶为一个专业的程序员。

为什么叫 系统程序员成长计划?程序员的范围太广了, 虽然软件开发有很多相似之处,但是隔行如隔山,比如对于目前炙手可热的WEB开发,我完全是外行。想什么都讲一点,结果是什么都没有讲清楚,所以我得把培 训计划限定在我熟悉的范围之内。先定义一下系统程序员:从事操作系统内核、DBMS、GUI系统,基础函数库,应用程序框架,编译器和虚拟机等基础软件开 发的程序员。这些培训同样适用于桌面软件和智能手机软件开发,我想对其它软件开发也会有一些启发作用。

草莓酱定律与果酱定律。第一次在咨询的奥秘中看到草莓酱定律时,我觉得非常有意思。当然这个系列也无法脱离草莓酱定律的魔法,利用这个系列中的内 容,我手把手的教了十多个同事,收到了良好的效果。当有数百个读者读这些文章时,我不敢期望有同样的效果。不过在果酱定律的鼓励下,我相信这个系列中至少 有部分内容的价值不会因为读者群的增大而消失,所以最终决定写出来。

中心思想

软件开发的困难在哪里?对于这个问题,不同的人有不同的答案,同一个人在不同职业阶段也会有不同的答案。作为一个系统程序员来说,我认为软件开发有两大难点:

一是控制软件的复杂度。软件的复杂度越来越高,而人类的智力基本保持不变,如何以有限的智力去控制无限膨胀的复杂度?我经历过几个大型项目,也分析 过不少现有的开源软件,我得出一个结论:没有单个难题和技术细节是我们无法搞定的,而所有这些问题出现在一个项目中时,其呈指数增长的复杂度往往让我们束 手无策。

二是隔离变化。用户需求在变化,应用环境在变化,新技术不断涌现,所有这些都要求软件开发能够射中移动的目标。即使是开发基础平台软件,在超过几年 时间的开发周期之后,需求的变化也是相当惊人的。需求变化并不可怕,关键在于变化对系统的影响,如果牵一发而动全身,一点小小的变化可能对系统造成致命的 影响。

为了解决这两个问题,方法学家们几十年来不断努力,他们发明或改进软件的开发过程和设计方法。系统程序员面对的基础软件通常都是大型的复杂的软件,其通用性也要求能容纳更多变化,解决这两个问题也是系统程序员成长计划的主要目标。

文章特色

以引导读者思考为主。培训可以制造合格的程序员,却无法造就一流的高手。培训是一个被动的过程,我们都知道在大学里听课的效果,我不希望本系列文章 的成为单纯的培训教材,相反我们要变被动为主动,最大限度的提高学习的效果。大多数情况下,我先提出问题让读者去思考,让读者自己尝试去解决,能不能解决 这个问题不重要,重要的是在思考中提升自己。如果读者在一定时间无法找到解决问题的方法,后面会有专业程序员的参考做法(或许不是最优的)。

以简单的例子讲述复杂的设计方法。我曾经制定一个宏伟的培训计划,结果失败了,原因是我忘记了我在走路之前也艰难的爬行过。这次我吸取了教训,用简 单的示例讲述复杂的设计方法,而且不对读者的背景太多假设。里面不会出现复杂的数据结构和算法,也不会引入大型软件来唬人。即有足够的挑战,不会让读者感 到乏味;又一切尽在掌握之中,不会因为挫折而打击积极性。

技术能力与工作态度并重。古人说德才兼备者才是君子。同样,做一流的程序员,也要德才兼修才行。当我手把手的教别人的时候,我不但希望他能学会我讲 的知识点,更希望他能学习我的工作态度和作为程序员的道德素养。当然有些东西只可意会不可言传,未必能用文字表达出来,不管怎样这是我的初衷之一。

读者群

这个系列完全是针对初学者写的,初学者包括在校学生、应届毕业生和其他业余爱好者。我面试过很多应届毕业生,他们大多数都不具备编程能力,唯一的优 势就是对基本理论有一知半解的了解。本系列文章就是为他们量身定制的,经历十几个人的实践,取得令人满意的效果,大多数人在开始一行代码都写不出,到培训 结束时一般都能独立开发/维护一些几千行的小模块。本系列文章并不是金钥匙,最终学习的效果与个人的悟性和努力密切相关,但不管怎样,只要读完这些文章, 你都会有不小收获的。

如何使用

温伯格说过,医生的药方包括药物和服药的方法,两者缺一不可。同样的教材,不同的学习方法,效果也有很大差别。对于本系列文章的服用方法,我的建议 是,对于文中提出的问题,先自己想办法去解决,可以查资料,至少经过两三个小时的思考之后,再继续阅读,最后再按学到的方法独立写一遍,学习编程一定要多 写多练,否则效果会大打折扣。

Enjoy it!

Wednesday, March 18, 2009

系统程序员成长计划-背景知识


转载时请注明出处和作者联系方式

文章出处:http://www.limodev.cn/blog
作者联系方式:李先静

对于是否写这样一章,我犹豫了很久,最后考虑到这个系列是针对新手而写的,不应该对读者做过多假设,这些基础知识是必须掌握的,不能不介绍一下。如果你已经了解它们,可以放心的跳过本章。如果你是新手,请认真学习本章提到的内容。

基础知识

C语言。千万不要认为C语言过时了,它始终是开源社区,特别是系统软件和嵌入式系统中的王者,在可以预见的未来,C语言将持续焕 发出生命力。有些外行认为C语言不适合开发大型软件,这是大错特错了,操作系统内核,虚拟机,数据库管理系统,图形引擎和WEB服务器等大型软件几乎都是 用C语言开发的。相反C语言不适合开发小程序,这时候脚本语言更能显出威力。C语言能经久不衰,自有它的道理:

C语言是最简单的语言之一,大部分编程语言在出现时都以其简单而获得好评,几乎全部都随着时间的推移变得越来越复杂,C语言经过数十年的发展,却始 终保持其简洁和优美。初学者认为C语言难学,其实主要是对计算机本身不理解,花点时间去学习一下计算机组成原理和操作系统原理,再来学习C语言就很简单 了。一旦掌握了它,你会发现C语言的每项特性都是必须,常用的,根本不需要记忆任何不必要的东西,它的特性真是减无可减了。

C语言是运行时效率最高的编程语言之一。同样的算法,C语言通常比其它语言更高效,这也它作为系统软件主流编程语言的原因之一。有些动态语言号称比较C语言更快,那都是骗人的,拿一个特定算法作为例子不足为证。选择是高效的算法是根本,但C语言更能把高效发挥到极致。

C语言是最直观的语言之一。C语言能够直观的表达程序员的想法,不像其它一些语言,一行简单的代码,你不清楚里面到底做了什么,不清楚它将花多少时 间执行。C语言的直观性很好的满足了程序员好奇心,使用C语言你更能感觉编程是一种艺术。一切尽在掌握之中,更能满足你的成就感。

在系统程序员炼成计划中,前面部分都是使用C语言作为示例,读者应该找本C语言入门书籍看看,可以先通读一遍,不求甚解都可以,随着后面的课程而深入的学习。

数据结构与算法。不管使用什么设计方法和开发过程,数据结构与算法都是软件开发的基石。打好基础在以后的工作中会事半功倍。后继 课程也都是这些基本数据结构和算法为中心,讲述如何用这些基本的材料构建大型系统。读者暂时无需精通数据结构和算法,先找本书看看,了解一下像双向链表、 动态数组、队列、堆、栈、hash表、排序和查找的基本原理就行了,后面我们会以这些数据结构题材反复的练习。

开发环境

本系列文章重点讲解软件开发的基础知识,这些知识不依赖于特定的平台和开发环境,读者可以根据自己喜好来选择,我们推荐读者使用下列开发环境:

操作系统使Linux。Linux是最适合程序员使用的操作系统,它是开源的,有多种不同的发行版可以免费使用,这些发行版默认安装就带了开发工具。学习Linux本身就需要一本书,如果你从来没接触过Linux,也不用惊慌,花几个小时学会十来个常用的命令就够了,其它的以后慢慢再学。

编辑器使用VIM。编辑器的功能是创建源文件,也就是把我们编写的代码输入到电脑中。vim和emacs是Linux下最流行的 代码编辑器,vim入门更简单,功能也很强大。它支持查找剪切替换等基本编辑功能,也支持符号跳转和代码补全等高级编辑特性。vimtutor是最好的入 门教材,初学者跟着这个tutor学习一遍就可以用它来编程了,等用得比较熟练之后,再去掌握那些高级功能。你掌握得越熟练,你就能更高效的工作,这个投 资是值得的。

编译器使用gcc。编译器的功能是把源代码翻译成计算机可以“读懂”的机器语言。在Linux下可用的C编译器有好几个,gcc 是其中最流行的,大多数发行版都默认安装了gcc。gcc的参数很多,看起来很复杂,我们只掌握最简单的用法就好了,大概像这样的:gcc -g test.c -o test。

调试器使用gdb。调试器的功能是帮助程序员定位错误,这是最后一招,也是最不期望的一招,使用调试器越多通常说明你的水平越 差,不过对初学者来说,掌握这个工具必不要可少的。gdb的功能强大,推荐读者使用命令行的gdb,它更灵活更方便。读者先掌握如何设置断点、显示变量和 继续执行等基本操作就行了。

工程管理使用make。make是Linux下最流行的工程管理工具,Makefile是make的输入文件,它本身就相当于一 种编程语言,执行make相当于调用其中的函数。编写Makefile是一件繁琐无趣的工作,幸好我们不用学习它,后面我们会讲解make的改进版 automake,现在你能写出下面这种简单的Makefile就行了:
all:
gcc -g test.c -o test
clean:
rm -f test

在这里,你可以把all看作一个函数名,gcc -g test.c -o test是函数体(前面加tab),它的功能是编译test.c成test,在命令行运行make all就相当于调用这个函数。clean是另外一个函数,它的功能是删除test。如果你有时间学习一下Makefile当然更好,如果没有时间,了解这 么多也够了。

我在培训初学者时,如果他从来没用过Linux,没有用C语言写过程序,我会给两到四周时间学习上述内容。如果读者处于类似的水平,也不急着看后面的课程,好好学习一下这里提到的内容。

系统程序员成长计划-序

系统程序员成长计划-序

转载时请注明出处和作者联系方式
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静

写作背景

在经历过几个大型的,失败的项目之后,我终于明白没有什么比高素质的程序员更能决定项目的成功了,无论什么过程,什么编程语言和开发工具,离开了高素质的程序员,什么都是白费力气。毫无疑问,人是软件开发中最重要的因素,但不是每个人都重要,不是什么样的人都重要,只有那些高素质的程序员和那些对项目有突出贡献的人才是重要的。

不过高素质的程序员并不多见,所以从我开始带人之际,就在思考团队成员培养的问题。我做过很多尝试,从小组内学习到整个部门一起上大课,最后又回到对个人单独的辅导;从通过codreview做现场教育到制定过一个宏伟的培训计划,最后又回到一个朴素的培训过程。其中遇到了很多问题,开始是培训不够系统,效果不甚理想,后来又因为计划过于”宏伟”而无法实施,直到最后行成一个朴素的,切实可行的培训方案,中间经过了好几年时间,直到去年,整个计划才趋于完善。我把这个培训计划称为系统程序员成长计划,这就是我在这个系列中要写的。

培训内容不是来源于某本书,毕业八年来,我坚持不断的看书,家中放了7大储物箱,有300多本不同类型的书籍,其中囊括了大部分经典的IT图书。当然也不是全部内容都来源书本,这几年我在开源软件吸取了大量的营养,一些思想和经验在broncho项目中也有充分的发挥,可以说是理论,经验和实践的结合。但我不尝试阐述什么高深的道理,相反我是针对应届毕业生和业余爱好者写的,目的是要让初学者进阶为一个专业的程序员。

为什么叫 系统程序员成长计划?程序员的范围太广了, 虽然软件开发有很多相似之处,但是隔行如隔山,比如对于目前炙手可热的WEB开发,我完全是外行。想什么都讲一点,结果是什么都没有讲清楚,所以我得把培训计划限定在我熟悉的范围之内。先定义一下系统程序员:从事操作系统内核、DBMS、GUI系统,基础函数库,应用程序框架,编译器和虚拟机等基础软件开发的程序员。这些培训同样适用于桌面软件和智能手机软件开发,我想对其它软件开发也会有一些启发作用。

草莓酱定律与果酱定律。第一次在咨询的奥秘中看到草莓酱定律时,我觉得非常有意思。当然这个系列也无法脱离草莓酱定律的魔法,利用这个系列中的内容,我手把手的教了十多个同事,收到了良好的效果。当有数百个读者读这些文章时,我不敢期望有同样的效果。不过在果酱定律的鼓励下,我相信这个系列中至少有部分内容的价值不会因为读者群的增大而消失,所以最终决定写出来。

中心思想

软件开发的困难在哪里?对于这个问题,不同的人有不同的答案,同一个人在不同职业阶段也会有不同的答案。作为一个系统程序员来说,我认为软件开发有两大难点:

一是控制软件的复杂度。软件的复杂度越来越高,而人类的智力基本保持不变,如何以有限的智力去控制无限膨胀的复杂度?我经历过几个大型项目,也分析过不少现有的开源软件,我得出一个结论:没有单个难题和技术细节是我们无法搞定的,而所有这些问题出现在一个项目中时,其呈指数增长的复杂度往往让我们束手无策。

二是隔离变化。用户需求在变化,应用环境在变化,新技术不断涌现,所有这些都要求软件开发能够射中移动的目标。即使是开发基础平台软件,在超过几年时间的开发周期之后,需求的变化也是相当惊人的。需求变化并不可怕,关键在于变化对系统的影响,如果牵一发而动全身,一点小小的变化可能对系统造成致命的影响。

为了解决这两个问题,方法学家们几十年来不断努力,他们发明或改进软件的开发过程和设计方法。系统程序员面对的基础软件通常都是大型的复杂的软件,其通用性也要求能容纳更多变化,解决这两个问题也是系统程序员成长计划的主要目标。

文章特色

以引导读者思考为主。培训可以制造合格的程序员,却无法造就一流的高手。培训是一个被动的过程,我们都知道在大学里听课的效果,我不希望本系列文章的成为单纯的培训教材,相反我们要变被动为主动,最大限度的提高学习的效果。大多数情况下,我先提出问题让读者去思考,让读者自己尝试去解决,能不能解决这个问题不重要,重要的是在思考中提升自己。如果读者在一定时间无法找到解决问题的方法,后面会有专业程序员的参考做法(或许不是最优的)。

以简单的例子讲述复杂的设计方法。我曾经制定一个宏伟的培训计划,结果失败了,原因是我忘记了我在走路之前也艰难的爬行过。这次我吸取了教训,用简单的示例讲述复杂的设计方法,而且不对读者的背景太多假设。里面不会出现复杂的数据结构和算法,也不会引入大型软件来唬人。即有足够的挑战,不会让读者感到乏味;又一切尽在掌握之中,不会因为挫折而打击积极性。

技术能力与工作态度并重。古人说德才兼备者才是君子。同样,做一流的程序员,也要德才兼修才行。当我手把手的教别人的时候,我不但希望他能学会我讲的知识点,更希望他能学习我的工作态度和作为程序员的道德素养。当然有些东西只可意会不可言传,未必能用文字表达出来,不管怎样这是我的初衷之一。

读者群

这个系列完全是针对初学者写的,初学者包括在校学生、应届毕业生和其他业余爱好者。我面试过很多应届毕业生,他们大多数都不具备编程能力,唯一的优势就是对基本理论有一知半解的了解。本系列文章就是为他们量身定制的,经历十几个人的实践,取得令人满意的效果,大多数人在开始一行代码都写不出,到培训结束时一般都能独立开发/维护一些几千行的小模块。本系列文章并不是金钥匙,最终学习的效果与个人的悟性和努力密切相关,但不管怎样,只要读完这些文章,你都会有不小收获的。

如何使用

温伯格说过,医生的药方包括药物和服药的方法,两者缺一不可。同样的教材,不同的学习方法,效果也有很大差别。对于本系列文章的服用方法,我的建议是,对于文中提出的问题,先自己想办法去解决,可以查资料,至少经过两三个小时的思考之后,再继续阅读,最后再按学到的方法独立写一遍,学习编程一定要多写多练,否则效果会大打折扣。

Enjoy it!
(欢迎批语,鼓励和建议)

Sunday, February 17, 2008

Let arm-linux support setlocale

Author: XianJim lee

Most of the ARM-linux BSP(Board support package) provided by the CPU manufacture don't support setlocate. Marvell PXA3xx linux BSP is an example, I ask FAE for help, but they told me that it is beyond their support scope. So, I have to do it myself.

After reading the source code of setlocale in glibc-2.5, I knew setlocale need the data file /usr/lib/locale/locale-archive, but it doesn't exist in my board. I should put one at here, but where can I get the file? From the PC? Oh, it is about 60M, too large for mobile device.

Does the toolchain provide a tiny one? I checked the toolchain, no, there is no such file in the toolchain directory. I studied the crosstool and glibc-2.5, I found it is easy to make a customized one. The following text shows the process step by step:

1. Unpack the glibc tarball in the sources directory.

  # tar zxvf glibc-2.5.tar.gz


2.Edit glibc-2.5/localedata/SUPPORTED to customize your own locale-archive.
 # vim glibc-2.5/localedata/SUPPORTED
(Delete all the unused locales.)


3.Edit glibc-2.5/localedata/Makefile to support cross-compile.
 # vim glibc-2.5/localedata/Makefile
(Redefine the macro LOCALEDEF, use host localedef instead.)


4.Edit crosstool.sh to build and install locale-archive.
 # vim crosstool.sh
(Uncomment the line make localedata/install-locales install_root=${SYSROOT},
and change it to make localedata/install-locales install root=${TARGET},
or your system locale-archive will be overwritten.)


5. Tar the modified file back the tarball.
 # tar czvf glibc-2.5.tar.gz glibc-2.5.)


6.Build it by run build.sh. Wait until it is done,
copy locale-archive to your board.
 NOTE: You should copy the files in lib/gconv too, 
or the function iconv will still fail..)


Friday, February 15, 2008

Debug shared library with gdbserver

Author: XianJim lee

It is hard to debug in embedded system, especially the bug can't be reproduced in simulator. Don't be surprise, if it takes several days to find out why the system failed--just because there is no suitable debugger!

You are lucky, if you develop programs in Linux embedded environment. GDB server is powerful tool for you, actually, it helps me solved many difficult problems. It is a pity that it doesn't support shared library, you can't insert break points in the shared library code.

There is a command named add-shared-symbol-files, but it doesn't work. The mechanism of inserting break points is very simple: generally, the debugger insert a piece of special instruction at the address, when the CPU execute that instruction, an exception will be throw, then the debugger take over the control of execution.

Why doesn't it work? The most possible answer is that the symbol does not match the according address. After reading the help information of the command add-symbol-file, I knew that I should specify an address for it. But what address should I specify? We should know where the code of the shared library locates in the memory. You will say, that is simple, we can consult the /proc/$PID/maps. Yes, you are right, but not enough. The following example shows a complete demonstration of debugging shared library.

1. Let's create a shared library.


foo.c
int foo(int a, int b)
{
int s = a + b;
printf("%d\n", s);
return s;
}

2. Then create an executable file that calls the shared library.

main.c
#include
extern int foo(int a, int b);
int main(int argc, char* argv[])
{
int s = foo(10, 20);
return s;
}

3. Of course we need a Makefile.

Makefile
all: so main
so:
gcc -g foo.c -shared -o libfoo.so
main:
gcc -g main.c -L./ -lfoo -o test
clean:
rm -f test *.so

4. Make and prepare for running.
# make
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./

5. Run the gdbserver
# gdbserver localhost:2000 ./test

6. Connect to gdbserver and run to function main.
#gdb
(gdb) symbol-file test
(gdb) target remote localhost:2000
(gdb) b main
(gdb) c

7. Now, It is time to check where library is loaded.
# ps -efgrep ./test
(You can get the PID from the output, here is 7186)
# cat /proc/ 7186/maps
It will output something like:

007b1000-007cc000 r-xp 00000000 08:02 2737838 /lib/ld-2.6.so
007cc000-007cd000 r--p 0001a000 08:02 2737838 /lib/ld-2.6.so
007cd000-007ce000 rw-p 0001b000 08:02 2737838 /lib/ld-2.6.so
08048000-08049000 r-xp 00000000 08:02 1759415 /root/writting/gdbserver/test
08049000-0804a000 rw-p 00000000 08:02 1759415 /root/writting/gdbserver/test
4d940000-4da8e000 r-xp 00000000 08:02 2738392 /lib/libc-2.6.so
4da8e000-4da90000 r--p 0014e000 08:02 2738392 /lib/libc-2.6.so
4da90000-4da91000 rw-p 00150000 08:02 2738392 /lib/libc-2.6.so
4da91000-4da94000 rw-p 4da91000 00:00 0
b7efc000-b7efd000 rw-p b7efc000 00:00 0
b7f11000-b7f12000 r-xp 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f12000-b7f13000 rw-p 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f13000-b7f14000 rw-p b7f13000 00:00 0
bff04000-bff19000 rw-p bffeb000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]

This means the code segment of libfoo.so is loaded at 0xb7f11000.

8. With the help of objdump, we can get the offset.
# objdump -h libfoo.so grep text
It will output something like:

.text 00000154 000002f0 000002f0 000002f0 2**4

So, the offset is 0x000002f0

9. Add the loaded address and offset, we can get the real address.
ADDR=0xb7f11000+0x000002f0=0xb7f112f0

10. Now, we can load the symbol file into gdb.
(gdb) add-symbol-file libfoo.so 0xb7f112f0
add symbol table from file "libfoo.so" at
.text_addr = 0xb7f112f0
(y or n) y
Reading symbols from /root/writting/gdbserver/libfoo.so...done.

11. Done, debug it as normal case.

Well, it works, but it is still complex. If you get better solution, let me know please, thank you in advance.