2009年3月27日星期五

Ifconfig 如何获得流量统计信息

本文旨在讲解 Ifconfig 流量信息的获得。将通过 Ifconfig 代码阅读来跟踪流量统计的来 源,这将贯通网络模块,proc文件系统,到硬件设备驱动来探寻这些信息的来源和流向。

Ifconfig

Ifconfig是 net-tools的一个组件。 net-tools 为 GNU/Linux 提供控制网络子系统的很多 重要工具(arp, ifconfig, netstat...),他几乎成为所有发行版必备的软件。

来看他的输出情况。包含了基本的网卡信息,内核网络配置。除此之外,还包括了流量统计 信息。笔者开始就抱有非常疑惑的信息,Ifconfig是如何做到这一点的?这样一个高层的应 用程序难道和网卡紧密相连么?正是由于这样的疑问,我才开始了阅读代码的过程。

wick@ ~: sudo ifconfig eth0
eth0 Link encap:以太网 硬件地址 00:e0:4c:43:d4:8d
inet 地址:192.168.16.91 广播:192.168.16.255 掩码:255.255.255.0
inet6 地址: fe80::2e0:4cff:fe43:d48d/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:20825 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:35057 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:2225064 (2.2 MB) 发送字节:50827282 (50.8 MB)
中断:23 基本地址:0xb800

本篇实例讲解使用到了 —- net-tools-1.60 源码,可以在官方网站找到

linux-2.6.29 代码

net-tools/ifconfig.c

NET_LIB = $(NET_LIB_PATH)/lib$(NET_LIB_NAME).a


ifconfig: $(NET_LIB) ifconfig.o
$(CC) $(LDFLAGS) -o ifconfig ifconfig.o $(NLIB) $(RESLIB)
来自 net-tools 的Makefile告诉我们,ifconfig工具的链接很简单。除了一个库文件以外, 只需要ifconfig.o。默认情况下,没有指明 obj 文件生成方式的,都只是需要单个同名的C 源码文件 ifconfig.c。

尽快找寻main文件中的参数选项,我们只需要找到列举 eth0 信息的选项分支,其他赶快跳 过。在ifconfig.c:288后以下段落,是我们关注的参数解析过程。

    /* Create a channel to the NET kernel. */
if ((skfd = sockets_open(0)) < 0) {
perror("socket");
exit(1);
}

/* Do we have to show the current setup? */
if (argc == 0) {
int err = if_print((char *) NULL);
(void) close(skfd);
exit(err < 0);
}
/* No. Fetch the interface name. */
spp = argv;
safe_strncpy(ifr.ifr_name, *spp++, IFNAMSIZ);
if (*spp == (char *) NULL) {
int err = if_print(ifr.ifr_name);
(void) close(skfd);
exit(err < 0);
}
首先,调用NET_LIB中的 sockets_open 函数创建 socket 链接;然后在无参数的情况下, if_print 打印所有网络接口信息;如果有参数,则将参数作为接口名称,将会打印对应接 口信息。后者将是我们 `ifconfig eth0' 的参数情况,而 if_print 是唯一调用函数。

同一个文件(ifconfig.c)就可以看到if_print函数,看起来很简洁。

static int if_print(char *ifname)
{
int res;

if (ife_short)
printf(_("Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg\n"));

if (!ifname) {
res = for_all_interfaces(do_if_print, &opt_a);
} else {
struct interface *ife;

ife = lookup_interface(ifname);
res = do_if_fetch(ife);
if (res >= 0)
ife_print(ife);
}
return res;
}
首先,如果ife_short有效,则打印表头。这是网络接口信息的简洁模式,使用`ifconfig -s'的效果正式如此,`-s'将设置 ife_short = 1,这在main函数解析参数时候已经完成。 然后,分析参数,我们这里的ifname就是"eth0",因此进入else分支。

下面深入到lookup_interfaces (lib/interfaces.c)

struct interface *lookup_interface(char *name)
{
struct interface *ife = NULL;

if (if_readlist_proc(name) < 0)
return NULL;
ife = add_interface(name);
return ife;
}

紧跟着是 if_readlist_proc 和 add_interface,一个个来。

static int if_readlist_proc(char *target)
{
static int proc_read;
FILE *fh;
char buf[512];
struct interface *ife;
int err;

if (proc_read)
return 0;
if (!target)
proc_read = 1;

fh = fopen(_PATH_PROCNET_DEV, "r");
if (!fh) {
fprintf(stderr, _("Warning: cannot open %s (%s). Limited output.\n"),
_PATH_PROCNET_DEV, strerror(errno));
return if_readconf();
}
fgets(buf, sizeof buf, fh); /* eat line */
fgets(buf, sizeof buf, fh);

#if 0 /* pretty, but can't cope with missing fields */
fmt = proc_gen_fmt(_PATH_PROCNET_DEV, 1, fh,
"face", "", /* parsed separately */
"bytes", "%lu",
"packets", "%lu",
"errs", "%lu",
"drop", "%lu",
"fifo", "%lu",
"frame", "%lu",
"compressed", "%lu",
"multicast", "%lu",
"bytes", "%lu",
"packets", "%lu",
"errs", "%lu",
"drop", "%lu",
"fifo", "%lu",
"colls", "%lu",
"carrier", "%lu",
"compressed", "%lu",
NULL);
if (!fmt)
return -1;
#else
procnetdev_vsn = procnetdev_version(buf);
#endif

err = 0;
while (fgets(buf, sizeof buf, fh)) {
char *s, name[IFNAMSIZ];
s = get_name(name, buf);
ife = add_interface(name);
get_dev_fields(s, ife);
ife->statistics_valid = 1;
if (target && !strcmp(target,name))
break;
}
if (ferror(fh)) {
perror(_PATH_PROCNET_DEV);
err = -1;
proc_read = 0;
}

#if 0
free(fmt);
#endif
fclose(fh);
return err;
}
一大长串,这该是我们现在遇到的最长的函数了,但是这个方法远远比想象的要简单的多, 文件I/O就占据了老长。这个方法就是读取网络接口的列表,从那儿读取呢?我们一眼就看 到了 ————

fh = fopen(_PATH_PROCNET_DEV, "r");

在 lib/pathnames.h 中看到其芳踪

#define _PATH_PROCNET_DEV "/proc/net/dev"

首先,探测是否已经检测过 网络接口列表,咱们不作浪费时间的无用功。 然后读取 /proc/net/dev,跳过文件的前两行。为啥?自己瞅瞅就知道了。

wick@ ~: cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
eth0: 3342410 31393 0 0 0 0 0 0 76194593 52347 0 0 0 0 0 0
eth1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

接着解析参数,将得到的所有接口加入到列表中去(add_interface),然后依据格式填充 ife结构体(get_dev_fields)。这个结构体是自定义的,可以看到这个结构体是一个链表结 构。get_dev_fields方法填充的是其中一个成员结构体,他在人群中是那么的耀眼:

struct user_net_device_stats stats;

他包含的就是统计信息数据,这些数据的来源就是/proc/net/dev。我们作的仅仅是读取这 个文件信息而已……

你要问,还有其他的信息呢?诸如IP地址,MAC地址,ifconfig如何知道我的网卡是无线有 线的呢?你就需要去看 if_fetch 函数了。从中你可以看到大量的ioctl调用,一切豁然开 朗。

linux/net/core/dev.c

立刻来到内核的领地。

以上总结到统计数据信息的位置坐落于 /proc/net/dev,他是被谁写入的呢?要理解这个问 题,你得先理解 PROC 文件系统。介于时间和能力原因,我只能大概解释, PROC 是内核和 用户空间通讯的又一个接口,PROC fs是一个由软件创建的文件系统,我们需要追根溯源到 创建 /proc/net/dev 的代码文件。

如果要问我是如何找到创建软件的代码?我不知道。我只知道最蠢的方法,google 和 grep。

好吧我是直接grep的……于是找到了 /net/core/dev.c。

/*
* Called from the PROCfs module. This now uses the new arbitrary sized
* /proc/net interface to create /proc/net/dev
*/
static int dev_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "Inter-| Receive "
" | Transmit\n"
" face |bytes packets errs drop fifo frame "
"compressed multicast|bytes packets errs "
"drop fifo colls carrier compressed\n");
else
dev_seq_printf_stats(seq, v);
return 0;
}
我们直接看到这段,注释就包含了 /proc/net/dev 信息,由此可见,友好详细的注释是多 么多么的重要啊…… 直接看方法,dev_seq_show 是文件中定义的struct seq_operations dev_seq_ops的成员, 这就提到了 proc提供的seq接口,他使用简单的迭代器方法,使用 seq_open -> read_proc 就可以直接输出 proc文件的接口信息。如果你不熟悉 PROCfs 和 seq_file 接口,这一部 分会有点凹口。

简单来说,当你使用open等系统调用打开 /proc/net/dev 的时候,系统将调用 seq_open 函数;相似的,当你使用read时,内核会使用 seq_show 来将实际数据传递到用户空间。我 们的 `cat /proc/net/dev' 输出,就是这里的 dev_seq_show 打印的。

接着,是 dev_seq_show 中调用的 dev_seq_printf_stats 方法,他仅仅使用到了一个函数。 不用多说,继续深入到dev_get_stats,他将是core模块此行的终点。

/**
* dev_get_stats - get network device statistics
*
@dev: device to get statistics from
*
* Get network statistics from device. The device driver may provide
* its own method by setting dev->netdev_ops->get_stats; otherwise
* the internal statistics structure is used.
*/

const struct net_device_stats *dev_get_stats(struct net_device *dev)
{
const struct net_device_ops *ops = dev->netdev_ops;

if (ops->ndo_get_stats)
return ops->ndo_get_stats(dev);
else
return &dev->stats;
}
EXPORT_SYMBOL(dev_get_stats);
多么有爱的注释啊……如果定义了设备驱动的 get_stats 方法,就调用 get_stats 调用获 得信息,如果没有,直接返回设备写好的数据结构。

在这里真的需要强调注释的好处,简短的两句话,可以让代码的可读性激增啊!

感动之后,我们进入驱动,我现在使用的网卡是8139,相关的驱动是 8139too.c。

linux/drivers/net/8139too.c

8139too 驱动直接包含在内核里了,我们立刻去关怀他的 get_stats 函数。

static const struct net_device_ops rtl8139_netdev_ops = {
.ndo_open = rtl8139_open,
.ndo_stop = rtl8139_close,
.ndo_get_stats = rtl8139_get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_start_xmit = rtl8139_start_xmit,
.ndo_set_multicast_list = rtl8139_set_rx_mode,
.ndo_do_ioctl = netdev_ioctl,
.ndo_tx_timeout = rtl8139_tx_timeout,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = rtl8139_poll_controller,
#endif
};

哦,简单直接,rtl8139_get_stats

static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
{
struct rtl8139_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
unsigned long flags;

if (netif_running(dev)) {
spin_lock_irqsave (&tp->lock, flags);
dev->stats.rx_missed_errors += RTL_R32 (RxMissed);
RTL_W32 (RxMissed, 0);
spin_unlock_irqrestore (&tp->lock, flags);
}

return &dev->stats;
}
这个函数实体只是重写了 dev->stats.rx_missed_errors,其他的信息我们直接 return。 网卡状态信息 struct net_device_stats 已经在网卡驱动的其他操作方法中即时更新了。

总结

Ifconfig 使用了简单的文件I/O控制,读取 /proc/net/dev 文件来打印流量统计信息。此 文件是 PROCfs 的一个组成,他的操作方法在代码 net/core/dev.c 中,其中 seq 接口的 show 方法可能调用了网卡设备驱动的 get_status 驱动方法来获得网卡信息。这些信息在 网卡操作中会即时更新。

2009年3月17日星期二

SCSI和SATA

SCSI

概念

Small Computer System Interface, 它是一组在电脑和外围设备间实现物理连接和数据传 输的标准集合。SCSI标准定义了命令、协议、电子和光学接口,它可以连接大量的外围设备。

  • SCSI 隐藏了物理设备接口的复杂性,算是一种智能设备
  • SCSI 是一个并行设备,可以将 8-16个SCSI设备连接到一根单独的总线
  • SCSI 是带缓冲的接口,使用了设备间的握手信号,支持出错检查
  • SCSI 是一个点对点设备,协议已经定义了三组标准:主机对主机,主机对外围设备,外 围设备对外围设备
两个SCSI连接器,via wikipedia

SCSI历史

  1. SCSI SPI,SCSI是个很老的协议(1981),至今经历了三代协议变换,也定义了三套标 准SCSI1, 2, 3,SPI是对并行SCSI接口的统称。到了2008年,SPI开始被SAS所取代
  2. SSA,现行的串行SCSI标准有三个,SSA,FC-AL,SAS,他们从SPI中独立出来,使用串 口传输。
  3. SAS, Serial Attached SCSI 是串行标准中最具活力的一个
  4. iSCSI,在此不做讨论

ATA/ATAPI

概念

AT Attachment / AT Attachment Packet Interface (ATA/ATAPI) 是一套专门连接大容量 存储介质的标准集合,这些设备包括有硬盘、固态硬盘、光驱等等。

ATA/ATAPI历史

  1. IDE(ATA-1), 1994年,ANSI统一了IDE接口,诞生了ATA-1
  2. EIDE(ATA-2), 西部电子公布了EIDE标准,催生出1996的ATA-2标准
  3. ATAPI 是ATA标准的里程碑,它定义了一系列拓展命令,大大加强了ATA接口。他更可回 应SCSI命令,被称之为"会说话的SCSI"。这个协议也叫"ATA/ATAPI-4"
  4. Parell ATA, 2003年,SATA诞生了,由于他的出现,我们把以前的ATA称之为PATA。 ATA标准要求电缆长度最大为46cm,这限制了计算机存储技术的发挥。
  5. Serial ATA, 2007年度,SATA已经全面取代PATA,它是现行ATA接口市场上的主流。 注意,此时的Serial ATA的规范中就包含了SCSI SAS部分
7孔SATA缆线和主板SATA接口,via Wikipedia

SCSI和SATA

SCSI和SATA相比,速率要快上一些,但是其总线结构很复杂,自然成本也较高。SCSI总线支 持多个驱动器连接(多个通道,一个通道连接7-15个设备)。而SATA只支持一对一,除非使 用端口倍加器。

性能上看,SATA 3Gb/s 提供了 300MB/s 的传输速率,而SCSI可以达到 320MB/s,而且SCSI 驱动器可以提供更高的持久式吞吐量;兼容性上看,SATA 一般保留了向 SAS 的兼容性,而 SCSI 是不能直连到SATA总线上的。

总体上看,SATA总线更简单,保留了向SAS的兼容性,而且仍旧保持较高速率。SCSI的功能 更强,拓展性更高,但其成本也较高。

索引

2009年3月10日星期二

翻阅 C99 标准

自从C89之后,C语言就没有经过大的改变。相比之下,C++的特性却是一天一个样儿,作为C 语言的超集,C++至今拥有了大量的特性,和多家库支持。为使C一样与时俱进,ISO 在99年 制定了ISO/IEC 9899:1999,被后来称之为C99。

C99的重要更新有:

  1. inline 方法
  2. 不一定需要在代码块开始位置定义变量
  3. 更多的类型支持:比如 long long int等扩展整形,复数类型等
  4. 支持变长数组
  5. 使用"//"但行注释
  6. 新的库函数,比如snprintf
  7. 新的头文件,比如stdbool.h
  8. 引入通用类型的数学方法,(tgmath.h, 包含math.h和complex.h,两者通吃)
  9. 改进的IEEE浮点支持
  10. 制定的初始化方法
  11. 混合迭代器
  12. variadic 宏支持(不清楚)
  13. restrict 支持,允许更深的代码优化

尽管C99也已经过去了10年,在我第一次摸的时候还是感觉很凹口和生疏,一方面自己的基 础还是很薄弱,一方面标准性的文档往往追求经准而晦涩难懂。相信随着以后的学习,那些 诸如multibyte char之类的字眼还会出现在我面前。

WIKIPEDIA:

http://en.wikipedia.org/wiki/c99

Open-Std 标准文档:

http://www.open-std.org/JTC1/SC22/WG14/www/standards.html#9899

GCC C99 Status:

http://gcc.gnu.org/c99status.html

2009年2月20日星期五

EP93xx GPIO interface

名词

AMBA是EP93xx采用的内部总线结构,他包含一个高速总线(AHB)和一个低速总线(APB)。

AHB (Advanced High-Performance Bus),包含一个高速内部总线时钟,用来同步协处理 器、MMU、cache、DMA控制器和其他内存模块。

APB (Advanced Peripheral Bus),此总线就来连接一些低速并行设备,比如UART和GPIO。 AHB 和 APB 使用 AMBA 架构中的 AHB/APB 总线连接,

GPIO (General Purpose I/O),GPIO在EP93xx中是APB的一个子模块,他提供简单的LED控制, 可实现非常基础的调试功能(相当于printk?)。

GPIO

GPIO 包含两种类型和8个接口。

  • 标准GPIO (port C, D, E, G, H)。每个端口包含两组寄存器
    1. data reg x1
    2. data direction reg x1
  • 增强型的GPIO(EGPIO, port A, B, F),包含三种寄存器
    1. data reg x1
    2. data direction reg x1
    3. intr reg and status regs x?

标准GPIO的每个端口都包含一个8位数据寄存器(data regs)和另外的8位数据方向寄存器 (datadirection regs)。而EGPIO还会多包含一组8位中断控制寄存器。

ABF端口还包括了七个多余的寄存器。

  1. GPIO Interrupt Enable registers (GPIOAIntEn, GPIOBIntEn, GPIOFIntEn)
  2. GPIO Interrupt Type 1 registers (GPIOAIntType1, GPIOBIntType1, GPIOFIntType1)
  3. GPIO Interrupt Type 2 registers (GPIOAIntType2, GPIOBIntType2, GPIOFIntType2)
  4. GPIO End-Of-Interrupt registers (GPIOAEOI, GPIOBEOI, GPIOFEOI)
  5. GPIO Debounce registers (GPIOADB, GPIOBDB, GPIOFDB)
  6. Interrupt Status registers (IntStsA, IntStsB, IntStsF)
  7. Raw Interrupt Status registers (RawIntStsA, RawIntStsB, RawIntStsF)

GPIO 初始化

系统复位的时候,GPIO 会执行初始化。

  • 所有端口的 data reg 和 data direction reg 都将清零,设置为 input 方式
  • Port E[1:0] 用来表示 RDLED 和 GRLED,他们将会被置高,也就是 0x11
  • Port G[3:2] 用来表示 SLA[1:0] 输出,他们被置低。
  • Port G[1:0] 用来表示 EEDAT 和 EECLK,设置为 input 方式
  • 所有的中断控制寄存器和祛抖动寄存器都被清零

GPIO端口和针脚映射

EP93xx的阵脚情况不一样,后继版本的板子总是多了点,下面只是列出EP9301/EP9302的针 脚映射:

主板针脚名称 位于GPIO模块上的端口
EGPIO[7:0] Port A
EGPIO[15:8] Port B
GRLED1 Port E0
RDLED2 Port E1
EECLK3 Port G0
EEDAT4 Port G1

GPIO 寄存器映射

鉴于GPIO寄存器很多,我也就不列出来了。

只是说几个要点。

  1. PADR ― PHDR,数据寄存器
    • 读取这些寄存器将会返回GPIO模块输入端口的值(由PXDDR控制),默认情况是缺省端 口映射下的端口状态信息。
  2. PEDR,他的bit[1:0]提供了LED 驱动方法,默认是0x11
  3. RAWSTATUSx,共三个寄存器。显示了无关针脚的读取状态

寄存器索引

引用

  1. EP9302 User's Guide

2009年2月17日星期二

新章: 试用LXDE Desktop Environment

闲聊

好了,很长时间没写字,我都不知道如何动笔了。

其实不是不想写,也非过年过皮实了,而是这些天都在忙活。从过年来,天天加班到九点, 回到家里还要折腾自己的小窝,面对邪恶的加菲,我在家里几乎无所事事。但是我也没闲着, 天天折腾到两三点,至于我干什么?我在折腾这两年累计的东西呢。

就说今天干了什么吧。

新手接触 LXDE

我今天测试了一下新的桌面环境LXDE,这是来自彼岸的台湾同胞洪任谕开发的(他同时也是 PC Man文件管理器的作者)。LXDE是非常小巧的羽量级桌面环境,他的桌面组建加载的很少, 这样本身的代码量就比较小(加起来总共只有9M左右),这样运行起来就很快很快。

LXDE采用了openbox作为窗口管理器,这个东西也是非常的迅速。使用的文件浏览器是看家 的 pcmanfm,一些官方维护的组件如下:

  • PCManFM,文件管理器,类似于Nautilus
  • LXLauncher,一个快速启动面板
  • LXPanel,简洁的桌面面板,类似于 gnome-panel
  • LXSession,会话管理器
  • LXAppearance,主题管理器
  • LXSessionEdit,进化的任务管理器
  • Leafpad,默认的记事本
  • Xarchiver,内嵌的归档管理器
  • GPicView
  • LXTerminal
  • LXTask
  • LXNM
  • Openbox
  • LXRandr,屏幕管理器,可以管理多个屏幕的分辨率,刷新频率等等,大赞!
  • LXShortCut,用来快捷方式
  • LXMusic
  • LXDE Common
  • GtkNetCat

希望

目前的LXDE还是太少,跟树大根深的GNOME自然无法比,但是LXDE有着很多的先天优势。对 于我来说,LXDE还需要改进的是:一个快捷键管理工具,虽然你可以手动编辑 ~/.config/openbox/lxde-rc.xml;一个很好的桌面字体渲染引擎;采用更多的轻量级应用 软件,比如Office,画图等等;增强PCMan文件管理器功能,当然这要建立在不牺牲速度和 性能的基础之上,毕竟这是LXDE的设计哲学。

安装 LXDE 请看:

追踪 LXDE 社区线索:

截图


2009年1月18日星期日

游历深圳博物馆 (Shenzhen Museum)

深圳博物馆位于福田区市民中心的东部,新馆于12月25日开馆,由此免费向公众开放。

博物馆主题重要的有三个,古代深圳、近代深圳和深圳民俗文化,以此分别设置三个主题展
厅。这三个展厅相当相当的长,里面的玩意儿非常之多,很对得起2.4亿的造价啊。。。

我和大黄俊丽三人浩浩荡荡的来到深圳博物馆,开始长游,因为展品繁多,地形复杂。我只
说说几个非常重要的 ————


中国国家典藏精品展览
先是一楼,中国国家典藏精品展览。你可以看到很多在历史书里翻看到的东西,包括四羊方
尊、战神之鸟、铜面具、青瓷莲花尊等等等等。未能想到在这里就见到我朝思暮想的宝贝啊~

四羊方尊,在历史书籍里的出境率非常之高。


战神之鸟,基本看不出来是只鸟。


编钟,1952年中国首次使用编钟演奏了《东方红》。


兵马俑,让我想起了The Mummy!


金缕玉衣,大黄坏坏的给了这样一个特写。


重点的重点啊!青瓷莲花尊的做工非常精美呐,介绍词给的非常庄严——涅磐之舟!


民俗文化厅
然后是二楼,民俗文化展厅,里面展示深圳古色古香的民俗风情。里面大多来自清朝和近代
的一些民间情景,其中各个情景中的泥偶人物非常逼真。第一次看到的时候吓我一跳:(


官员展示农耕的场景。






古代展厅
这个似乎在说深圳的悠长历史,可是我印象中这是深圳的薄弱之处。抱歉,我这里没有好的
素材。


近代展厅
此展厅讲述深圳从鸦片战争以来的发展历史。

民国时期的成绩单,看看那时候的科目:国语,算术,历史,自然,地理,图画,美术等等
……


旧式缝纫机。我真的不觉得很旧,因为老妈还有一个缝纫机,形状神似。


罗湖火车站的旧景。


抗日战争中使用的武器,我总以为解放军最先进的是驳壳枪……


现代展厅
深圳所具备的魅力,毫无疑问被定格在了现代。作为改革开放的试点城市,深圳引进外资,
对外开放,成就了神话般的成长速度。这个地方看到的东西就非常新奇了。

恨死这家伙,把我照片拍的血肉模糊……


模拟的深圳特区全景,包括了关内的四个区。


超级乐高机器人!太太太酷了,我还在szlug里贴给他们看呐,只可惜我不能碰它:P


展示深圳金融系统推出的各种卡:银穗卡,一卡通,信用卡。教条内容:2000年深圳率先建
立以银行、证券、保险为主体,其他类型金融机构并存的现代金融体系。


深圳湾码头,被大堆的集装箱所淹没。


这一切的始作俑者,邓公。


谢谢欣赏。

2009年1月9日星期五

初学sed

sed, awk等等UNIX下的工具初看显得晦涩又难用,就些命令行搞那么复杂干哈子呀。

不过一旦掌握以后就能发现给日常操作带来很大好处,那些简洁高效的命令行可都有着悠久 的历史了……

因此在学习他们之前,告诫自己:Don't get confuSed~

这里的例子全部来自ibm developerworks。
http://www.ibm.com/developerworks/linux/library/l-sed1.html

# 基本篇
sed -e 'd' /etc/services #删除所有行,因此没有输出
sed -e '1d' /etc/services | more #删除第一行
sed -e '1,10d' /etc/services | more #删除1-10行

# 正则篇
sed -e '/^#/d' /etc/services | more #删除注释
sed -n -e '/regexp/p' /path/to/my/test/file | more #打印匹配regexp的行
#-n 标示只打印regexp匹配字符,-e针对文本,-f将指定sed 脚本

#标准的正则符号有
# ^: matches all begin with...
# $: matches the end of the line...
# .: matches any single char
# *: will match zero or more occurences of the previous char
# []: matches all the characters inside the []

sed -n -e '/BEGIN/,/END/p' /my/test/file | more
#打印一个范围,首行包含BEGIN, 尾行包含END

sed -ne '/main[[:space:]]*(/,/^)/p' tmp.c
#打印main函数

# 替换篇
sed -e 's/foo/bar/' myfile.txt #找到第一个foo,替换为bar
sed -e 's/foo/bar/g' myfile.txt #全局替换
sed -e '1,10s/foo/bar/g' myfile.txt
sed -e '/^$/,/^END/s/foo/bar/g' myfile.txt
#以最前的空行为开始,END字符结束

sed -e 's:/usr/local:/usr:g' myfile.txt
#改变操作服为`:'

sed -e 's/<[^>]*>//g' myfile.html
#原文本: This is what I want
#改变后: This is what I want.

sed -e 's/.*/ralph said: &/' myfile.html
#在所有首行添加"ralph said: "

sed -e 's/\(.*\) \(.*\) \(.*\)'/Victor \1-\2 Von \3/' myfile.txt
#原文本:
# foo bar oni
# eeny meeny miny
#改变后:
# Victor foo-bar Von oni
# Victor eeny-meeny Von miny


sed一般用来 (via builder.com)—

  • Double/triple-space a file
  • 转化DOS/UNIX 的新行(newline)
  • 删除前后的空格
  • 在所有/全部行上进行取代操作
  • 删除连续的空行
  • 删除文件开头和结尾的空行