Btrfs 测试结果简述

2009/12/09 | 22:32 | 分类:Linux与开源 | 标签: | 634次阅读

  Btrfs 凭借着其优良的可伸缩性和诸多有用的特性,有望成为 Linux 下一代文件系统。目前它已被纳入主流内核支持,接受用户的实验性(Experimental)使用。
  今年 2 月 hutuworm 给出了一篇《Ext4 ReiserFS Btrfs 等七种文件系统性能比拼》,他是在 2.6.29-rc3 内核上使用 IOZone 3.318 做的测试。Btrfs 官方 Wiki 也给出了一些第三方测试数据,基于的是 4、5 月份的版本。官方 Wiki 同时指出他们在不断解决这些测试中所发现的问题和瓶颈。这几天我也对 Btrfs 进行了一些测试,并针对某些新特性进行了研究。我的测试基于 2.6.32 内核,使用的工具是 IOZone 3.327,对比对象为 ext3 和 ext4,机器配置为 2 * 4Cores Xeon 2.0G / 8G Memory。从测试结果看,性能方面,Btrfs 在 CPU Cache 区和 Buffer Cahce 区较好,在 Disk I/O 区不如 ext3/ext4(所谓三个区的划分参考 IOZone 文档);可靠性方面,Btrfs 仍有待加强。下面简述我的几条观察(详细数据省略)。

Btrfs 测试结果简述
./iozone -Rab output.xls -g 16G

Btrfs 测试结果简述
./iozone -Rab output.xls -s 16G -r 2M

Btrfs 测试结果简述
tar cf *.tar *; tar jcf *.tar.bz2 *

  性能方面:
  1.CPU Cache 区和 Buffer Cahce 区的 read 吞吐率,Btrfs、ext3、ext4 相差不大。
  2.CPU Cache 区和 Buffer Cahce 区的 write 吞吐率,明显地 Btrfs 优于 ext4 优于 ext3(tar 测试检验了这个结果)。
  3.Disk I/O 区的 read 吞吐率 ext3 占优,write 吞吐率 ext4 占优,而 Btrfs 两方面均劣于 ext3、ext4(与 hutuworm 的结论类似)。
  4.三个文件系统对于小于 CPU Cache 区的小文件,都有“文件或记录过小时读写性能变差”的问题。其负面影响程度 BtrFS 大于 ext4 大于 ext3。
  而可靠性方面,常规的读写校验并没有出现问题,但在测试 Copy on write(COW)特性时,先后遇到两个错误:
  1.最初使用 2.6.31-15 内核测试,出现了克隆文件与原始文件计算 MD5 有可能不一致的问题。开发人员回复说这个 bug 已经在 2.6.32-rc 以后的内核中修复。升级内核后此问题得以解决。
  2.目前使用 2.6.32 内核测试,发现同时读写原始文件和克隆文件,有可能致使 syslogd 出错、文件系统没有响应。现在我还没有得到可靠的答复,但相关回复者基本怀疑是竞态问题。
  有关 Btrfs 其它的高级特性我还没有详细测试。正在使用或打算使用 Btrfs 的朋友,也欢迎与我交流。

在 Dell PowerEdge 1950 上安装 Linux 2.6.32-rc8 内核的问题与解决

2009/12/03 | 19:26 | 分类:Linux与开源 | 标签: | 1,429次阅读

  出于实验和使用 Linux 内核某些新特性的需要,我要在 Dell PowerEdge 1950 服务器上安装最新的内核,而且必须是 2.6.32-rc 以后的版本。由于服务器硬件的特殊性,这一过程费了一番周折,最终在 @Sisyphusliu 师兄的帮助下搞定,记录如下。
  服务器上已安装的操作系统是 CentOS 5.3,软件栈的需求使之不能用别的发行版替代。保守的 RHEL/CentOS 5 系列的软件源上最新的内核是 2.6.18-164,因此只能从 Linux Kernel 官方获得我们所需的内核,自行编译安装。
  在 Kernel.org 下载当前最新的 2.6.32-rc8 内核,将原有内核的 config 文件复制过来,重新 make menuconfig,加入我们需要测试的新特性,然后编译、安装,这一过程顺利。但在系统重启之后,initrd 中的 init 脚本却报出“mount: could not find filesystem '/dev/root'”的错误。看来新内核没有成功挂载本地根文件系统,所以我们仍改用旧内核启动,开始排查原因。
  PowerEdge 1950 服务器安装了 RAID 卡,即使只使用一块硬盘,它也会走 RAID 的 Virtual Drive。因此 RAID 卡成了最大的嫌疑。检查新内核配置,与 RAID 相关的选项都已经选中,编译出来的 modules 目录中也存在诸如 raid456.ko 之类的模块,那么是什么地方有所缺失呢?对比旧内核与新内核的 initrd 中的 init 脚本,发现 raid456.ko 之类并没有在启动时加载;新旧内核不同的地方在于:旧内核加载了 dm-raid45.ko 模块,而新内核则没有。对比两个内核的配置文件,发现 dm-raid45 相关的内容只在旧内核中出现,新内核没有相应的选项,也没有对应的源代码。
  于是我们需要核查 dm-raid45 的身份。google 发现 dm- 系列模块不是 Linux Kernel 官方组件,而是 RedHat 开发的 Device-mapper 模块,是以 patch 方式添加到内核中的。CentOS 5 软件源提供的修改版内核中,已经附带了包含 dm-raid45 在内的 3000 多个 patch,这些在我们下载的标准内核中当然没有。在 dm-raid45 的作者 Heinz Mauelshagen(傻根?)的主页上,提供了 dm-raid45 patch 下载,但均针对的是较旧版本的内核,打在新内核上编译屡屡出错。进一步的 google 使我们惊喜地发现,Mauelshagen 一周之前在 Kernel Patchworkβ 上发布了针对 2.6.32-rc8 的 dm-raid45 patch——尽管他这个 patch 的主要目的是提供一个 xor 算法,但这个版本恰好能为我们实验所使用。下载并运行这个 patch 之后,内核编译通过并生成了 dm-raid45.ko 模块,init 脚本中也自动增加了相应的 insmod 语句。
  然而重新启动系统后,除了多了几句 insmod 的输出,mount 语句照样出错。难道我们找错了原因?这次不猜了,直接深入 initrd 内部分析吧。我们将 initrd 解包,在 /bin 目录中放一个静态编译的 busybox,并在 init 脚本的 mkrootdev 语句(这一语句创建了 /dev/root 节点)之前插入 busybox sh 命令。将这个 initrd 重新打包后重启系统,即可对 initrd 阶段的执行进行调试。调试时,我们发现 initrd 的 /sys/devices 目录下已经出现了一系列 PCI 设备,用 find 查找,其中有一个正是包含了 sda、sda1 等磁盘和分区的节点。看来硬盘是正常工作的。而在 /dev/ 目录下,却没有出现 /dev/sda、/dev/sda1 等设备节点。看来是 mkrootdev 之前的 mkblkdevs 语句没有生效。我们进入 nash,再次执行 mkblkdevs,发现仍然没有效果。于是试图采用 mknod 命令手工建立设备节点。mknod 所需的主、从设备号可以在类似于“/sys/devices/pci0000:00/.../block/sda/sda1/dev”的文件中找到,其语法也可以参考 init 脚本中的其它几条 mknod 语句。在 nash 中手工 mknod 之后,运行 mkrootdev、mount 仍然没能将本地根文件系统挂载。但改用 busybox 中的 mount,挂载则是成功的,可以访问磁盘文件。
  于是我们推测,可能是 CentOS 5 中较旧版本的 mkinitrd 及其包含的 nash 与新版本的内核出现了不兼容。因为使用新版内核的 Fedora 中的 mkinitrd 和 nash 早已进化到了 6.0.X 版,而 CentOS 5 中的还是 5.1.19.6 版。CentOS 5 的软件源上并没有更新版本的 mkinitrd,在分析了 mkinitrd 包的依赖关系之后,我们认为 6.0.X 有太多的、较为关键的依赖项(而 5.1.19.6 版的 nash 是静态编译的单个文件)。贸然使用 Fedora 源更新 mkinitrd 有可能造成较大范围的影响,不利于我们软件栈的稳定移植。我们需要的只是 initrd 中的 nash,因此可以从更容易的途径获得。我们从一台安装了 Fedora 10 的机器中复制出 initrd,将 nash 6.0.71 及其依赖的库覆盖到 CentOS 5 的 initrd 中。使用这个 initrd 启动,调试 mkblkdevs、mkrootdev 和 mount 顺利通过,最终 Linux 成功启动,文件系统访问正常。
  不过仔细观察启动时输出的信息,我们还是发现了两个小问题。一是 init 脚本中 stabilized 语句报错:“Could not detect stabilization, waiting 10 seconds”,经查这是 Fedora 10 上 mkinitrd 的一个 bug,在 switchroot 之后并不影响根文件系统的使用,可以忽略。再者是在执行 /etc/rc.d 下的脚本时,udevd-event 报错:“udevd-event[XXXX]: wait_for_sysfs: waiting for '/sys/devices/pci0000:00/.../ioerr_cnt' failed”。估计这也是旧版工具与新版内核不兼容的问题,udevd 主要用于监听设备的热插拨等事件,在我们的实验环境中一般不会涉及。毕竟这只是一个使用 rc 版内核的实验环境,这个问题暂不予理睬。我们最终需要测试的程序在新版内核上运行通过,效果良好。

更新于 2009-12-06:
  在我们刚刚取得基本成功的时刻,恰逢 Linux 2.6.32 正式版内核发布,要不在正式版内核上也实验一下吧。既然上面是因为升级 nash 而最终成功的,那么 dm-raid45 有可能不是核心问题所在。因此在编译正式版内核时,我们没有加入 dm-raid45,而只替换了 nash 版本。结果一次就启动成功!看来这个问题真是 nash 引起的。至于 dm-raid45,这个机器使用的是硬件 RAID 卡,对操作系统透明,一般不需要内核支持;而 dm-raid45 是软 RAID 模块(感谢网友指正),对我们的系统来说安装不安装无所谓。

更新于 2009-12-07:
  今天 google 到了另一种解决方案:编译内核前,在 .config 中设置“CONFIG_SYSFS_DEPRECATED_V2=y”即可。经我们实验,果然如此。究其原因,google 到的答案[1][2]大约是说旧版的 mkinitrd 及其 nash 在内核没有 CONFIG_SYSFS_DEPRECATED_V2 参数时默认使用旧版 sysfs 路径格式,从而在新内核下无法正确访问 /sys 内的硬盘信息节点。

让 Dropbear 更好地支持 PAM

2009/10/22 | 14:45 | 分类:Linux与开源 | 标签: | 690次阅读

  Dropbear 是一套来自澳大利亚的 *nix SSH 工具集,以体积微小著称,因此在嵌入式环境被广泛中使用。Dropbear 支持 Linux PAM,但直到目前最新的 0.52 版本,它对 PAM 的支持仍极其有限,只能做基本的身份认证(auth、account),不支持会话与密码管理(session、password)。在使用时,我发现即使是 Dropbear 已实现的 PAM 特性也存在一些不大不小的问题,需要自己动手解决。不过作者在 configure 的帮助中已经说明了现在只是“Try to include PAM support”,并且在与 PAM 相关的源代码注释中多次告知用户可以在什么地方 add、edit、hack 一些代码来满足自己特定的需求。我在编译部署 Dropbear 时,为了正常使用 PAM 认证,做了如下工作(基于 0.52 版本):
  1.开启 PAM 支持
  不要以为 configure 时加入 --enable-pam 参数就开启了 PAM 支持,仔细看 configure 输出的最后一句:Now edit options.h to choose features。我们需要自行修改 options.h,定义 ENABLE_SVR_PAM_AUTH 宏,同时将与其冲突的 ENABLE_SVR_PASSWORD_AUTH 宏注释掉,这样 PAM 相关的代码才会被启用。
  2.修正每次 PAM 会话(conversation)之后立即销毁密码的 bug
  svr-authpam.c 中的 pamConvFunc 函数实现了将客户端消息传递给服务端 PAM 的会话。为安全起见,第 119 行使用 m_burn 函数将用完的消息中的密码销毁(使用 0x66 字符覆盖)。但我们知道,在一次 pam_authenticate 调用中有可能配置了多个 PAM(pam_unix.so、pam_ldap.so……)陆续调用应用程序提供的会话函数来取用户名、密码,如果在第一次会话之后立即销毁密码,后续的会话就无法正常工作了。因此,需要等待所有 PAM 会话结束之后再销毁密码。我们可以注释掉 119 行;而 pam_authenticate 之后销毁密码的工作在 svr_auth_pam 函数中已经实现,不用新增。
  3.去除 PAM 会话函数只识别“password:”提示符为密码消息的限制
  svr-authpam.c 的第 101 行限制了密码消息的提示符为“password:”,但一些 PAM 有可能使用其它自定义的提示符,这使得 Dropbear 拒绝传送密码。事实上像 OpenSSH 等软件只是通过消息的类型为 PAM_PROMPT_ECHO_OFF 来认定其内容为密码,而不在意提示符是什么。因此,我们可以去除此处对提示符的判断,让使用自定义提示符的 PAM 也能与之兼容。
  4.解决 PAM 和基于 NSS 的用户映射无法同时工作的问题
  有的应用系统结合使用 PAM 以及 GNU C Library 提供的 NSS 特性实现应用级的用户到本地系统用户的映射。在这类系统中,用户提供给 PAM 的应用级用户名在 NSS 中被映射为本地系统级用户名,用户只持有应用级的密码而不用知道本地系统级的密码。login、OpenSSH、vsftp 等程序对此种需求的支持良好,但 Dropbear 的实现却是首先调用 getpwnam 获得本地系统级用户名(已经由 NSS 映射过的),再用这个用户名去调用 PAM 认证,这就使得 PAM 得到的是“本地系统级用户名+应用级密码”的组件,自然验证失败。我们可以修改 Dropbear 的实现,在 svr-authpam.c 的 recv_msg_userauth_request 函数中保存客户端输入的用户名,在 svr-authpam.c 的 svr_auth_pam 函数中读取前面保存的用户名,而不要使用 NSS 输出在 ses.authstate.pw_name 的本地用户名。
  所有修改如下,如果你也需要在 Dropbear 中支持 PAM 认证,可以直接打上这些 patch。我有空向 Dropbear 的作者反馈一下吧。

  1. dropbear-0.52$ cat options.h.diff
  2. 152c152
  3. < #define ENABLE_SVR_PASSWORD_AUTH
  4. ---
  5. > /*#define ENABLE_SVR_PASSWORD_AUTH*/
  6. 154c154
  7. < /*#define ENABLE_SVR_PAM_AUTH*/
  8. ---
  9. > #define ENABLE_SVR_PAM_AUTH
  1. dropbear-0.52$ cat svr-auth.c.diff
  2. 36a37,38
  3. > char client_login_username[256];
  4. >
  5. 142a145,146
  6. >     strncpy(client_login_username, username, 256);
  7. >
  1. dropbear-0.52$ cat svr-authpam.c.diff
  2. 41a42,43
  3. > extern char client_login_username[256];
  4. >
  5. 101c103
  6. <             if (!(strcmp(compare_message, "password:") == 0)) {
  7. ---
  8. >             if (/*!(strcmp(compare_message, "password:") == 0)*/ 0) {
  9. 119c121
  10. <             m_burn(userDatap->passwd, strlen(userDatap->passwd));
  11. ---
  12. >             /*m_burn(userDatap->passwd, strlen(userDatap->passwd));*/
  13. 198c200
  14. <     userData.user = ses.authstate.pw_name;
  15. ---
  16. >     userData.user = m_strdup(client_login_username);
  17. 247a250,252
  18. >     if (userData.user != NULL) {
  19. >         m_free(userData.user);
  20. >     }

vCard Assistant:将 vCard 导入 Nokia S60v2 手机的辅助工具

2009/09/02 | 00:17 | 分类:Web与移动平台 | 标签: | 1,283次阅读

  晚上为 Nokia S60v2 老机型用户写了一个 PC 端的小工具——vCard Assistant,用于将含有中文姓名的 vCard 文件(可以是 GB、UTF-8 或 UTF-16LE 编码的)转换成 ASCII 字节编码 UTF-8 形式;并可将 vCard 3.0 格式转为 2.1 格式,便于在某些对 vCard 中文姓名或 3.0 标签支持不好的手机中导入联系人。效果如下:
  输入:

BEGIN:VCARD
VERSION:3.0
N:;张三
FN:张三
TEL;TYPE=CELL;TYPE=VOICE:13811112222
EMAIL;TYPE=PREF;TYPE=INTERNET:foo@bit.edu.cn
REV:20090901T035836Z
END:VCARD
BEGIN:VCARD
VERSION:3.0
N:;李四
FN:李四
EMAIL;TYPE=PREF;TYPE=INTERNET:bar@gmail.com
REV:20090901T035835Z
END:VCARD
...

  输出:

BEGIN:VCARD
VERSION:2.1
N;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:;=E5=BC=A0=E4=B8=89
FN;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=E5=BC=A0=E4=B8=89
TEL;CELL;VOICE:13811112222
EMAIL;PREF;INTERNET:foo@bit.edu.cn
REV:20090901T035836Z
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:;=E6=9D=8E=E5=9B=9B
FN;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=E6=9D=8E=E5=9B=9B
EMAIL;PREF;INTERNET:bar@gmail.com
REV:20090901T035835Z
END:VCARD
...

  这种格式可以被 Best vCard for S60 等软件读取,导入手机通信录。
  vCard Assistant 下载地址(包含源代码):
  http://files.linjian.org/dotNet/VCardAssistant.zip
  http://www.linjian.cn/files/dotNet/VCardAssistant.zip
  (需要 .Net Framework 3.5 平台支持)


  我为什么要写这个东西呢?主要还是自己的需求。我使用的 Nokia 6600 的 PC Suite 只支持从 IBM Lotus Notes、Microsoft Outlook 这类庞大的程序或 Windows Address Book 这种已被放弃的格式中导入联系人。我没有使用这两个商业软件,也不愿使用已经失去官方支持的 WAB 格式,而选择使用跨平台的、广为各类软件支持的 vCard 格式管理联系人,以便在多个系统、软件中直接共享。以前同步联系人时,先将 vCard 导出为 CSV,再使用虚拟机中的 Windows XP 通信录将 CSV 转换成 WAB 格式,然后通过 PC Suite 同步,比较折腾。现在只需要将 vCard 转换一次,通过蓝牙或 Email 发到手机上,用手机端的软件做同步,简便不少。
  事实上我以前也尝试过用 Google Sync 服务在线同步联系人(Gmail 也支持 vCard 导入导出),但同步过程总是异常停止。换用其它一些 SyncML 同步服务问题依旧。最终我在 mobical 网站发现一句话:“Nokia 6600: This device has a known issue where the synchronization may stop working at any time. As far as we can tell this is a bug in the Nokia software.”原来这是 Nokia 6600 的 bug……我也没有找到其它免费的 SyncML for S60v2 客户端,只能作罢。

编译器与标准——严格还是智能?

2009/03/21 | 14:21 | 分类:计算机科学与编程 | 标签: | 758次阅读

  最近做的C/C++工程需要在多种平台下编译(下面所说的编译泛指编译、连接等过程)并运行通过,多种不同版本的编译器考验着代码正确性和严格性。经测试,我们的历史代码中存在着这样几类问题,使得它们在VS2008和低版本gcc下编译通过,但在高版本的gcc下编译就会或多或少地报错:
  1、使用了外部的函数,但没有在本文件中声明或#include对应的头文件。VS2008和gcc3.2在很多情况下可以智能地从一同编译的其它文件或标准库中找到对应的函数并连接,而gcc4.3要求必须声明。
  2、使用模板时,T::name表示T::name类型还是T类的name成员?即使不使用typename关键字,VS2008和gcc3.2在很多情况下可以智能推断T::name a;中的T::name表示表示一个类型。但在gcc4.1/4.3下必须使用typename T::name来表示T::name类型。
  3、隐式类型转换。对于函数Func(string&),在VS2008中可以采用Func(string("new"));来调用,但gcc不能隐式地转换构造函数的返回,需要使用string str("new"); Func(str);来调用。
  4、一些copy-paste粗心小问题,比如声明类的成员函数时不应该加“类名::”限定。如果加了,VS2008会忽略,而gcc4.1/4.3会报错。
  5、非标准的库函数,比如VS2008的itoa(_itoa)。如果说上述几条是“严格性”的问题,这个应该算是“正确性”的问题了———个编译器扩展的内容在另一个编译器看来自然是不知所云。当然,为方便起见,使用编译器提供的非标准扩展也是可以的(比如在不完全支持C99的gcc和VC版本下,分别使用atoq和_atoi64来代替C99的atoll),但这时候别忘了使用条件编译,针对不同的编译器提供不同的代码。
  等等。
  总体来看,我们用到的几个编译器的严格程度如下。注意仅仅是从一些小的侧面反映的结果,不完全准确,说明大致情况而已。

  ─────── 更严格 ──────→
   VS2008  gcc3.2  gcc4.1  gcc4.3
  ←────── 更智能 ───────

  编译器的严格性也许是一把双刃剑。但我个人认为严格一些还是利大于弊的。拿上面的问题1来说,我就遇到过gcc3.2“智能”连接错误的问题,而如果使用gcc4.3,这个bug就可以轻易地被定位,节约大量的调试时间。
  智能的底线是正确性。如果代码中出现了不规范的写法,编译器给出智能判断并忽略问题的同时,应该给程序员一定的警告信息,让他们核实代码并判断是否需要向标准的方向修改。不过可悲的是编译器的警告往往被程序员们一略而过了。也许正是因为这一点,gcc才变得越发严格,试图和程序员划清责任域,减少扯皮现象?
  当然,仅仅写出语法上严格的代码,解决了不同编译器下编译通过的问题,我们还不能保证程序运行时语义的正确性。例如程序中存在未初始化的变量,一种编译器的实现是以0填充,恰好与程序员的原意相符;而切换到另一编译器下,未初始化的变量成了随机值,程序运行就会出问题。所以,对C/C++这样高度自由的语言来说,严格的编译器也不是万能的,人的因素始终是第一位的。
  说了这么些严格和智能,评判它们的依据是什么?自然是语言的标准了(C99、ISO C++之类)。有关编译器对语言(包含库)标准的支持,我们大约可以按以下维度划分:

      标准要求实现的
         ↑
         │
  标准未定义的 │ 标准定义的
     ←───┼───→
         │
         │
         ↓
      编译器扩展的

  标准定义的语法在任何编译器下都应该有相同、无歧义的语义。尽管其内部实现可能不同,或是依赖于具体的机器架构,但在标准中总有明确的说明。而标准中也存在大量未定义的语义,这通常是为了让编译器作者放开限制,做更高效、更简单的实现。这类语法在不同的编译器或不同的运行环境下可能产生不同的结果,经典的例子就是n=m+++m++这种副作用语句的叠加,再如L'abcd'表示什么的问题。
  工程中通常应该严格禁用标准未定义的语法,因为机器架构、编译器种类和版本、优化选项、运行时环境等的变化都有可能对程序的运行结果产生不可预知的影响。但有些时候标准未定义的的语法也是有用的,比如为了学习理解编译器的原理和优化算法。另外在一些技巧性游戏,如Quine,以及IOCCCTime Limit Exceeded这样的hack竞赛中,使用未定义语法的捷径也是未尝不可的。
  而编译器扩展的语法和库则是一类相对有用的工具。不同于标准未定义的内容,编译器所做的扩展也是有严格定义的,不会产生不可预知的结果。在对代码的迁移性要求不高的场合直接使用并无大碍。如果对代码的迁移性有要求,那就如上文所述使用条件编译来限制其作用范围。毕竟编译器所做的扩展是为了弥补标准库的不足,方便开发者。如果对代码将来的使用范围有明显的预知,就需要权衡是否使用编译器的扩展特性。不过就连Linux内核的代码都在使用gcc的扩展语法
  在C/C++语言教学中,强调标准是十分必要的。然而市面上的C/C++教材鱼目混珠:那天在师兄的桌上看到的一本国内某知名高校出版的C++教材上,Hello World程序竟有3处明显的问题,不用试验就知道在VS2008或gcc下编译有两处是Error,一处至少是Warning(据说在VC6下可以编译通过)。一些高校计算机教学中普遍使用历史遗留的、非标准的编译器也成为滋生不良编程习惯、阻碍标准推广的源泉。究其原因,某些教师的惰性是一方面,而相关资格考试与现行标准、业界规范的严重脱节也成为应试教育体制下技术标准难以为师生重视的重要因素。正面的例子又如何呢?北理工计算机学院有一门程序设计实践与方法的本科课程,这门课的重心本是算法与编程技巧的训练,但由于它使用了基于gcc的Online Judge机制和像ACM-ICPC一样严格、众多的测试用例,使得学生对代码的标准与严格、算法的正确与高效、条件的完全覆盖必须做仔细的考量。我自己上过这门课,也做过这门课的助教,两种角色的体验让我深知师生各自的抱怨与苦衷。错误究竟在谁?我想不在于这门课的教师和学生,而在于这门课之外的计算机教育氛围。改变这个氛围并不可以一蹴而就,但至少有两点是可以尽己所能的:主观方面,建立标准意识,培养良好的编程习惯;客观方面,使用实现了符合现行标准的编译器。尽管这对计算机教学的整体环境影响甚微,但起码对未来从事计算机方面的工作来说是有益的——实际工程对标准的要求非同儿戏。

页面存档: 上页 1 2 下页