Patrick He on January 9th, 2010

升级到 Ubuntu 9.10 后,在显示方面发生了好几次问题,之前解决了无法进入 XWindow 环境的问题。在更新过一次驱动程序后又遭遇到窗口丢失标题栏的问题。具体表现是窗口标题栏、边框都消失,也不相应 alt+f4、alt+tab 之类的视窗环境快捷键。开始时没有找到根本原因,只发现在 System->Preferences->Appearances 中 Visual Effects 变为 None,需要选择 Norma 或者 Extra 来激活 compiz 才能将恢复正常。每次启动 XWindow 之后都要如此操作,不但麻烦,而且还要重新选择 compiz 中自己需要的 plugin。

在网上搜索发现这个问题很早就出现,ubuntuforums.org 上有大把2007年的帖子在讨论这个问题。但是找到的各种方法都没有能够将我的系统恢复正常。不过这些方法应该也是一些必需的步骤。

最基础的是要安装驱动程序,我的笔记本是 nVidia 的显示卡,因此需要安装 nvidia-glx 相应的驱动程序。Ubuntu repository 中最新的驱动程序是 nvidia-glx-185,如果需要更新的驱动程序,可以添加相应的 apt repository:

sudo add-apt-repository ppa:nvidia-vdpau/ppa && sudo apt-get update

更新 apt 之后可以安装 nvidia-glx-190/195 版本的驱动程序。当然驱动程序不是版本越高越好,稳定性是首要考虑的因素,可以按照自己电脑的实际情况选择相应的版本。

安装驱动程序之后,还可能需要修改 /etc/X11/xorg.conf 文件,找到 Device section,例如:

Section "Device"
Identifier "nVidia Corporation NVIDIA Default Card"
VendorName "NVIDIA Corporation"
BoardName "Quadro NVS 140M"
Option "RenderAccel" "true"
Option "AddARGBGLXVisuals" "True"
Driver "nvidia"
Option "NoLogo" "True"
EndSection

重点是标红的那行 Option。在网上还看到有方法这样添加:

sudo nvidia-xconfig --add-argb-glx-visuals -d 24

nvidia-xconfig 会把 option 添加到 Screen section,这种做法并不是很提倡,option 应该和相关的设备尽量放在一起比较好。

到以上步骤都是网上比较常见的解决方案,但是我经过以上的设置还是没有恢复出标题栏来。后来在 System->Preferences->Startup Applications 发现有一项 Compiz Tray Icon 启动名为 compiz-tray-icon 的程序,在 terminal 中尝试手动执行这条命领,发现系统中不存在 compiz-tray-icon!然后在进入 XWindow 后标题栏消失的情况下执行

pgrep -fl compiz

发现系统中根本不存在包含 compiz 名字的进程,因此确定是 compiz 未能正常启动。而 Startup Applications 中的 Compiz Tray Icon 可能是以前版本遗留下来的配置,在 9.10 中 compiz-tray-icon 却消失了。再调查一番,发现有一个替代的程序,叫做 fusion-icon,执行

apt-cache show fusion-icon

得到的软件描述是:

Description: tray icon to launch and manage Compiz Fusion
The OpenCompositing Project brings 3D desktop visual effects that
improve the usability and eye candy of the X Window System and provide
increased productivity.
.
This package contains a tray icon that allows you to easily enable, disable and
restart Compiz, and change the currently used window manager and/or window
decorator.

于是安装这个软件包

sudo apt-get install fusion-icon

并且在 Startup Applications 删除无效的 Compiz Tray Icon,添加新的一项 Fusion Icon

Add Fusion Icon into Startup Applications

另为了使用 Emerald,在 CompizConfig Setting Manager 中启用 Windows Decoration,将 General->Command 设置为

emerald --replace

重新启动 XWindow,大功告成。

Tags: , , , ,

十月底 Ubuntu 9.10 正式发布,用 Alternative CD 从 9.04 升级,一切顺利。但是重启后,发现 Grub 中最新的 2.6.31 版本内核无法启动,系统在启动后进入命令行模式,屏幕不停闪烁,而且键盘输入也呈现问题,完全无法输入密码登录系统。幸好还保留了一个老版本的内核 2.6.28 可以正常启动,但是在安装了 nvidia-glx-185 显卡驱动之后,连 2.6.28 也无法启动了,故障的状况和 2.6.31 类似。上网搜索了一下,在云风的 blog 上看到他也遇到过类似的问题,但是我在查看 /etc/X11/xconf 文件之后,并没有发现分辨率的设置有异。不过因此得到了提示,手动执行 startx,控制台上输出报错信息显示因为某些出现 error 导致 nvidia 驱动无法正常加载,只是报错信息比较含糊,没有办法定位问题之所在,而模糊的信息,也无法提供良好的搜索关键字。

在浪费了若干时间之后,想到可以在 Ubuntu Forum 上或许可以寻找到一些有用的信息。果然,在 Installation & Upgrades 版块就有一篇 stick 在顶部的 thread《ATI & Nvidia: no X, just blinking on 2.6.31 (Karmic 9.10) 》,文中的问题描述和我遇到的情况别无二致。该 thread 中提供了好几种解决方案,我采用的方法(by Sunflower1970)是

  1. 备份并修改 X 的配置文件:
    sudo cp /etc/X11/xconf /etc/X11/xconf.bak && sudo vi /etc/X11/xconf
  2. 找到类似显示设备配置的段落,类似:
    Section "Device"
    Identifier "Default Device"
    Driver "the driver here"
    Option "NoLogo" "True"
    EndSection
  3. Driver 一项修改成为 vesa,之后保存配置文件,重启系统即可。

导致该问题的原因是系统自动安装 nvidia 显卡驱动失败,但是依旧修改了 /etc/X11/xconf 的配置,于是 X 在启动时根据配置去加载没有安装成功的驱动程序,导致出现故障。可以在成功启动系统之后,再尝试自行安装专用的显卡驱动程序。我是按照 chessmani 提供的方法利用 envyng 来安装 nvidia 显卡驱动的。步骤如下:

  1. 增加 PPA 并且更新 apt 源:
    sudo add-apt-repository ppa:nvidia-vdpau/ppa && sudo apt-get update
  2. 安装 envyng:
    sudo apt-get install envyng-core
  3. 安装 nvidia-190-modaliases 以便 envyng-core 检测到需要安装的驱动:
    sudo apt-get install nvidia-190-modaliases
  4. 执行 envyng 开始安装驱动。注意首先卸载旧版本的驱动。
    sudo envyng -t
    按照出现的提示操作即可。

Tags: ,

Patrick He on October 11th, 2009

在编程中,有些操作可能在执行过程中失败,比如说连接某个远程服务的过程中出现超时,而我们不希望因为一次偶然的超时导致整个操作流程被废弃;或者在访问缓存时发现数据不存在于缓存中,因此触发异步过程去数据库中获取数据放入缓存,并且期望在短时间内远程访问数据库的操作能够完成以便能够及时地拿到数据返回给客户端。在条件允许的情况下,我们可以对失败的操作重新尝试一次或着若干次。

我们可以将实现重试操作的代码嵌入到相关的类中,但是更好的方法是应用 Command pattern 将重试的职责分离出来,成为可以复用代码。

最简单的方法,是使用 Callable 接口表示需要重试的操作。编写一个 RetryService 的服务类用于调用相应的 Callable 对象。

public class RetryService {

	private final ExecutorService executorService;

	private RetryService(ExecutorService executorService) {
		this.executorService = executorService;
	}

	/**
	 * Factory method
	 *
	 * @param executorService
	 * @return
	 */
	public static RetryService createRetryService(
			ExecutorService executorService) {
		return new RetryService(executorService);
	}

	/**
	 * 执行可能需要重试的方法,最多若干次,如果执行成功立即返回,否则将重试指定的次数。
	 *
	 * @param task
	 *            operation to retry
	 * @param maxRetryCount
	 *            the maximum number of times this task will be executed
	 * @throws ExecutionException
	 *             if the computation threw an exception
	 * @throws InterruptedException
	 *             if the current thread was interrupted while waiting
	 * @throws TimeoutException
	 *             if the wait timed out
	 * @throws NullPointerException
	 *             if task is null
	 */
	public <T> T submit(Callable<T> task, int maxRetryCount, long timeout,
			TimeUnit unit) throws InterruptedException, ExecutionException,
			TimeoutException {
		if (task == null) {
			throw new NullPointerException();
		}

		for (int i = 0; i < maxRetryCount; i++) {
			Future<T> future = executorService.submit(task);
			try {
				return future.get(timeout, unit);
			} catch (TimeoutException e) {
				if (i <= maxRetryCount - 1) {
					throw new TimeoutException(e.getMessage());
				} else {
					continue;
				}
			}
		}
		// failed to get result
		return null;
	}

}

Tags: ,

Patrick He on October 8th, 2009

最近有朋友问如何在文件中选择不包含诸如1900、1901、……1999字符串的行,目标文件中所有数据都是由数字字符组成。
如果使用 grep 这样的工具,只用写很一个非常简单的正则表达式就能解决问题:

grep -v '19\d\d' target_file

grep 的 -v 选项表示选择 target_file 中不匹配目标正则表达式的行。
对于诸如 Java 或者 Ruby 这样的程序设计语言,则没有提供类似的参数。我们需要直接使用正则表达式来匹配不包含以上字符串的行。解决这个问题的正则表达式如下:

^((?!19\d\d)\d)+$

其中 (?!19\d\d) 的作用是,一旦正则表达式引擎发现行内包含有符合 19\d\d 模式的数据,即判断该行不匹配此模式;如果一直没有发现符合 19\d\d 模式的数据,而且所有数据都是数字字符,则该行数据匹配此模式。这样就能选取到目标文件中所有不包含以上字符串的行。

Tags:

Patrick He on October 8th, 2009

在 Linux 服务器上,有众多命令行小工具可以用于监控服务器性能和状态。这些工具对于快速判断服务器运行情况,非常有帮助。

  • top
    top 是最常用的命令之一,用来持续显示系统资源的最新使用状况。
    [patrick@ubuntu:~]$ top
    top - 23:39:07 up  5:55,  3 users,  load average: 0.16, 0.22, 0.18
    Tasks: 141 total,   2 running, 138 sleeping,   0 stopped,   1 zombie
    Cpu(s):  3.1%us,  1.1%sy,  0.0%ni, 95.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
    Mem:   2041888k total,  1314284k used,   727604k free,    57688k buffers
    Swap:  1951856k total,        0k used,  1951856k free,   728052k cached
    PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    6043 root      20   0  855m  50m  13m R    3  2.5  12:11.37 Xorg
    2477 root      15  -5     0    0    0 S    1  0.0   0:02.28 scsi_eh_3
    7008 patrick   20   0 15648 4936 3948 S    1  0.2   0:10.46 gnome-screensav

    按 M 将会按照占用内存的大小排列进程,按 P 则会按照 CPU 占用率进行排列。
    VIRT 表示进程使用的内存总大小,包括进程实际使用的内存、映射过的文件、与其它进程共享的内存等。RES 表示实际使用中的内存大小,SHR 表示与其它进程共享的内存大小。
  • uptime
    uptime 最常用的作用是快速查看当时的系统负载情况。
    [patrick@ubuntu:~]$ uptime
    23:48:26 up  6:04,  3 users,  load average: 0.76, 0.38, 0.24

    通常来说,负载值/CPU数目 <= 1 的情况下,表示系统拥有足够的剩余处理能力,当负载值/CPU数目 > 1 的时候,就表示有进程需要等待 CPU 了。
  • vmstat
    vmstat 会提供有关内存、交换分区、进程、IO、CPU 的报告。
    [patrick@ubuntu:~]$ vmstat -S M
    procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
    r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
    0  0      0    697     57    712    0    0    28    17  161  552  8  2 89  1
  • iostat
    iostat 提供有关 CPU 的平均使用情况,以及 IO 信息。
    [patrick@ubuntu:~]$ iostat -x
    Linux 2.6.24-19-generic (ubuntu)     06/28/2008
    avg-cpu:  %user   %nice %system %iowait  %steal   %idle
    8.46    0.01    1.91    1.00    0.00   88.62
    Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
    sda               0.70     2.88    1.48    1.97    56.64    38.80    27.66     0.07   21.16   3.31   1.14
    sdb               0.09     0.00    0.06    0.00     6.44     0.00   113.77     0.00   17.13   5.41   0.03
  • mpstat
    mpstat 提供全局与单个 CPU 使用情况的统计报告。
    [patrick@ubuntu:~]$ mpstat -P ALL
    Linux 2.6.24-19-generic (ubuntu)     06/28/2008
    11:57:36 PM  CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s
    11:57:36 PM  all    8.48    0.01    1.69    0.99    0.05    0.17    0.00   88.61    183.99
    11:57:36 PM    0    8.63    0.02    1.66    1.08    0.08    0.28    0.00   88.25    152.05
    11:57:36 PM    1    8.33    0.00    1.73    0.90    0.02    0.07    0.00   88.95     31.93
    11:57:36 PM    2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00      0.00
  • netstat
    netstat 可以用来显示活动的 TCP 连接、计算机侦听的端口、以太网统计信息、IP 路由表、IPv4 统计信息(对于 IP、ICMP、TCP 和 UDP 协议)以及 IPv6 统计信息(对于 IPv6、ICMPv6、通过 IPv6 的 TCP 以及通过 IPv6 的 UDP 协议)。
    [patrick@ubuntu:~]$ netstat -a
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State
    tcp        0      0 localhost:mysql         *:*                     LISTEN
    tcp        0      0 localhost:8118          *:*                     LISTEN
    tcp        0      0 localhost:ipp           *:*                     LISTEN
    tcp        0      0 localhost:9050          *:*                     LISTEN
    tcp        0      0 192.168.1.3:59421       by2msg1204111.gate:msnp ESTABLISHED
  • ps
    ps 可以提供一个当前运行的进程的列表。
    [patrick@ubuntu:~]$ ps aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0   2844  1688 ?        Ss   Jun28   0:01 /sbin/init
    root         2  0.0  0.0      0     0 ?        S<   Jun28   0:00 [kthreadd]
    root         3  0.0  0.0      0     0 ?        S<   Jun28   0:00 [migration/0]
    root         4  0.0  0.0      0     0 ?        S<   Jun28   0:00 [ksoftirqd/0]
    root         5  0.0  0.0      0     0 ?        S<   Jun28   0:00 [watchdog/0]
    root         9  0.0  0.0      0     0 ?        S<   Jun28   0:00 [events/0]
  • pstree
    pstree 会显示出当前运行中的进程之间的树状结构。
    [patrick@ubuntu:~]$ pstree
    init─┬─/usr/bin/revela
    ├─NetworkManager───{NetworkManager}
    ├─NetworkManagerD
    ├─gconfd-2
    ├─gdm───gdm─┬─Xorg
    │           └─x-session-manag─┬─bluetooth-apple
    │                             ├─compiz───compiz.real───compiz-decorato───gtk-window-deco
    │                             ├─gnome-panel
    │                             ├─gnome-settings-─┬─pulseaudio─┬─gconf-helper
    │                             │                 │            └─2*[{pulseaudio}]
    │                             │                 └─{gnome-settings-}
    │                             ├─nautilus───{nautilus}
    │                             ├─nm-applet
    │                             ├─python
    │                             ├─scim
    │                             ├─seahorse-agent
    │                             ├─update-notifier
    │                             └─{x-session-manag}
  • pgrep
    pgrep 会根据提供的正则表达式查找进程。
    [patrick@ubuntu:~]$ pgrep -l 'scim'
    6966 scim
    7006 scim-launcher
    7046 scim-helper-man
    7047 scim-panel-gtk
    7049 scim-launcher
    7213 scim-bridge
  • lsof
    lsof 用于列出进程所打开的文件列表。
    [patrick@ubuntu:~]$ lsof -p 12894
    COMMAND   PID    USER   FD   TYPE     DEVICE     SIZE    NODE NAME
    firefox 12894 patrick  cwd    DIR        8,7     4096 5423105 /home/patrick
    firefox 12894 patrick  rtd    DIR        8,5     4096       2 /
    firefox 12894 patrick  txt    REG        8,5    26924  557548 /usr/lib/firefox-3.0/firefox
    firefox 12894 patrick  mem    REG        8,5    17884  900100 /lib/tls/i686/cmov/libnss_dns-2.7.so
    firefox 12894 patrick  mem    REG        8,5     7552  899934 /lib/libnss_mdns4_minimal.so.2

    因为在 Linux 中,端口也是被视作文件的,所以也可以使用 lsof -i 显示计算机上所有打开的因特网地址。
    [patrick@ubuntu:~]$ lsof -i
    COMMAND   PID    USER   FD   TYPE DEVICE SIZE NODE NAME
    pidgin  11114 patrick   14u  IPv4  58594       UDP 192.168.1.3:54865->reverse.gdsz.cncnet.net:8000
    pidgin  11114 patrick   15u  IPv4  57930       TCP 192.168.1.3:59421->by2msg1204111.phx.gbl:msnp (ESTABLISHED)

Tags: ,

Patrick He on October 7th, 2009

通常来说,一个函数所要返回的值,要么是一个数据(例如一个员工),要么是一组数据(例如所有女性员工)。
1. 对于第一种情况而言,函数的返回值类型应当是值(value)类型或者实体(entity)类型。例如,需要根据员工编号得到一个员工对象,我们可以编写一个这样的函数:

public Employee getEmployee(int id);

如果能找到具有该编号的员工,该函数返回一个 Employee 对象。否则返回 null 值,表示未找到指定 id 对应的员工;或者抛出一个 checked exception,函数的识别标志因此需要修改为:

public Employee getEmployee(int id) throws EmployeeNotFoundException;

由函数的调用者来捕获该异常,并根据需要进行处理。
2. 对于第二种情况而言,函数的返回值类型应当是 Collection 类型(包括 Map 类型)。例如,需要根据查询某月某部门迟到员工的列表,我们可以编写一个这样的函数:

public Set<Employee> getLateEmployees(Month month, Department department);

如果查询结果是,在八月份技术部没有一位同学迟到,太好了,这个函数只需要简单地返回一个空 Set 对象即可。

对于第一种情况,假设我们选择的是让函数返回 null 值,如果在程序中有相当多的地方需要调用该函数,那么我们有可能会在代码中发现很多像

if (employee == null) {
...
}

这样的代码,这些代码通常并不是用来处理业务逻辑,编写它们的最主要目的可能仅仅只是为了防止 NullPointerException 出现 。倘若这种情况降低了程序的可读性或者可维护性,我们可以采用 Null Object 模式来消除这些遍处滋生的防御代码。

Tags: ,

在 Java 中,继承一词的意义是有限制的。一个子类只能继承其父类的可访问的成员,并且该子类没有覆盖或者说隐藏父类中的那些可访问成员。所以,一个类的成员就是指在这个类中所声明的属性和方法,再加上从其父类继承而来的属性和方法。也就是说,子类是不能继承父类的私有成员的。

虽然子类不继承父类中的私有成员,但是在父类中生命的这些私有成员仍然是子类类型对象的一部分。因为在实例化对象的时候,只初始化在当前类中所声明的属性明显是不足够的,还需要初始化其父类中所有声明的属性。在实例化的过程中,JVM 需要为对象的类及其父类中所有定义的属性分配空间,包括父类中声明的私有成员。

所以,我们可以说:子类不能从父类继承私有成员,但是子类的对象是包括子类所不能从父类中继承的私有成员的。

Tags: ,