Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

发表在 Uncategorized | 一条评论

Popcount Problem: 求二进制数中1的个数 (上)

一、问题描述:求一个N位整数x的二进制表示中1的个数,越快越好。

据说这是道很常见的面试题。原题是说如何在常量时间内算出32位整数的二进制表示中1的个数。实际上这么问是有漏洞的,因为按照最笨的方法,一个一个数,也不过32次,当然是常量时间。但如果你这么告诉面试官,十有八九会被骂白痴。因为大家一般都会认为32次实际上是O(N)的做法。所以这么问可能会更好一些,求一个N位整数x的二进制表示中1的个数,越快越好。

实际上这个问题叫做Hamming weight[1],或者叫做population count以及pop count。看到Hamming大家基本上可以恍然大悟一下了,这玩意可以用来计算海明距离。另外在信息论、编码学等也有很多应用。

对于这个问题的解法,Google一下popcount 或者count bits 1会有一大把。基本上无非是三大类,直观的挨个数O(N),分治法O(lgN),查表法O(1)。但实际上对于这种问题,单单考虑时间复杂度意义不大。尤其是在问题规模较小的情况下,比如说32位整数或者64位整数。比方说,对于查表法,如果查找表太大导致程序运行时不得不访存,这个开销可能要比前两种方法都要大得多。下面分别总结一下,以64位整数为例,源程序和解析都给出来了。这个题目虽然简单,但很多解法,尤其是HAKMEM解法非常精彩,让人看完以后不禁拍案叫绝又扼腕叹息,为什么我就想不到这种方法呢 T.T。

二、解法分析:

1) naive solution
:依次移位,数1的个数,时间复杂度为O(N)。这里的N为整数的位宽。

typedef unsigned __int64 uint64
int popcount_naive(uint64 x)
{
int count = 0;
for(; x; x>>1)
++count;
return count;
}

2) less-naive solution:  对方法1)进行优化,x&(x-1)可以将最右端的(rightmost)1转化为0

int pop_count_lnaive(uint64 x)
{
int count = 0;
for (; x; ++count)
x&=x-1;
return count;
}

时间复杂度为O(n),n为1的个数。这样在1比较少的时候比第一种方法快很多。
同理,x | (x+1) 可以消除最右端的一个0(转化为1)。因此在0比较多的时候,可以先计算0的个数。下面是wikipedia上给出的版本。跟上面的代码类似,只是把循环展开了。

const uint64 hff = 0xffffffffffffffff;
#
define f(y) if ((x &= x-1) == 0) return y;
int
popcount_5(uint64 x) {
    if
(x == 0) return 0;
    f( 1) f( 2) f( 3) f( 4) f( 5) f( 6) f( 7) f( 8)
    f( 9) f(10) f(11) f(12) f(13) f(14) f(15) f(16)
    f(17) f(18) f(19) f(20) f(21) f(22) f(23) f(24)
    f(25) f(26) f(27) f(28) f(29) f(30) f(31) f(32)
    f(33) f(34) f(35) f(36) f(37) f(38) f(39) f(40)
    f(41) f(42) f(43) f(44) f(45) f(46) f(47) f(48)
    f(49) f(50) f(51) f(52) f(53) f(54) f(55) f(56)
    f(57) f(58) f(59) f(60) f(61) f(62) f(63)
    return
64;
}

//Use this instead if most bits in x are 1 instead of 0

#
define f(y) if ((x |= x+1) == hff) return 64-y;

3) shift_and_add 时间复杂度O(logN)
这个方法主要利用两点,一是根据最直观的做法,把每一位相加后就可以得到结果,这个过程我们可以利用分治+并行加法来优化;二是对于n位整数,最多有n个1,而n必定能由n位二进制数来表示,因此我们在求出某k位中1的个数后,可以将结果直接存储在这k位中,不需要额外的空间。
以4位整数abcd为例,最终结果是a+b+c+d,循环的话需要4步加法。
1′ 每一位一组[a][b][c][d],相邻两组相加得到[a+b][c+d],结合成两位一组。所有的加法可以像这样并行进行:

[0] [b] [0] [d]
[0] [a] [0] [c]
—————
[e   f] [g   h]

其中ef=a+b, gh=c+d。而0b0d = (abcd) & 0101, 0a0c = (abcd)>>1 & 0101
2′ 两位一组: [ef][gh],相邻两组相加,结合成四位一组。

[0 0] [g h]
      [e f]
———–
  [i j k l]

其中ijkl = ef + gh,00gh = (efgh) & 0011, 00ef = (abcd)>>2 && 0011。这样得出的ijkl就是最终结果。这样只需要log(N)步。
按照这个方法类推,我们很容易可以得出计算uint64的方法。

typedef unsigned __int64 uint64;  //assume this gives 64-bits
const uint64 m1 = 0x5555555555555555; //binary: 0101...
const uint64 m2 = 0x3333333333333333; //binary: 00110011..
const uint64 m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ...
const uint64 m8 = 0x00ff00ff00ff00ff; //binary: 8 zeros, 8 ones ...
const uint64 m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
const uint64 m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones

int popcount_1(uint64 x) {
x = (x & m1 ) + ((x >> 1) & m1 );
x = (x & m2 ) + ((x >> 2) & m2 );
x = (x & m4 ) + ((x >> 4) & m4 );
x = (x & m8 ) + ((x >> 8) & m8 );
x = (x & m16) + ((x >> 16) & m16);
x = (x & m32) + ((x >> 32) & m32);
return x;
}

总体需要 6次shift, 12次and, 6次add 共24次算术运算。

4) 对shift_and_add进行优化

因为具体的分析实在是有点啰嗦,所以先看程序,再来分析

int popcount_2(uint64 x) {
x -= (x >> 1) & m1;
x = (x & m2) + ((x >> 2) & m2);
x = (x + (x >> 4)) & m4;
x += x >> 8;
x += x >> 16;
x += x >> 32;
return x & 0x7f;
}

共需要 6次shift, 5次and, 5次add, 1次sub,共17次算数运算。下面具体来看这7个与运算是如何省掉的。

1′ 第一步要做的是将2bits的整数ab (=2*a+b)变成 a+b。不难看出,(2*a+b) – a = a+b。也就是说: x – (x>>1) & m1就是我们要的结果。这样省掉了1步与运算

2′ 先与后加=>先加后与,再省掉1步与运算。

之所以先与后加,是为了去掉不必要的干扰。比如说对于上面所讲的例子:

[a] [b] [c] [d]
[0] [a] [b] [c]
—————
[e   f] [g   h]

我们所关心的结果只是奇数组(d+c)和(b+a)的结果,这里我们称之为有效组; 红色标注的部分(c+b)和(a+0)的结果是没有用的(上例中直接置为0),我们称之为无效组。每一步的操作,实际上是将有效组相加,结果存放在有效组和无效组连接所组成的新组中

如果不通过and操作将abcd=>0b0d, 0abc=>0a0c,那么在做加法的时候无效组会出来捣乱。第一组的结果(d+c)如果有进位,就会被第二组的结果(c+b)所干扰。同理,第二组(c+b)的进位也可能会干扰到第三组的结果(b+a)。这样的无效组我们称之为干扰组,因此在加法操作进行之前有必要利用掩码将干扰组屏蔽

通过上面的分析我们可以知道,干扰组的出现是由于两个k位分组的最大和(2k)超出了k位二进制数能表示的范围,从而不可避免地产生进位,干扰相邻组的结果那么如果不会产生进位呢,就没有必要在加法操作之前利用掩码将干扰位过滤掉了,过滤操作可以延迟到加法操作之后进行。也就是说,我们可以先加后与,省掉一个与操作。(就为了省掉一个与运算,还真是折腾。。。)

不难看出,从4位分组开始,两个分组的最大和8就可以仅用原来的4位表示了。因此m4, m8, m16, m32都可以延迟到加法操作之后。

3′ 只加不与,再省掉1步与运算

之所以加法操作之后还是要进行与运算,是为了防止将脏数据带入下一步计算中。比如说:

[a1 a2 a3 a4] [b1 b2 b3 b4] [c1 c2 c3 c4] [d1 d2 d3 d4]
              [a1 a2 a3 a4] [b1 b2 b3 b4] [c1 c2 c3 c4]
——————————————————-
[e1 e2 e3 e4] [f1 f2 f3 f4] [g1 g2 g3 g4] [h1 h2 h3 h4]

很明显,红色标注的部分的无效组并不会产生进位,从而不需要在加法操作之前过滤。那么如果加法操作之后还是不过滤呢?如果继续将这部分脏数据带到下一步运算中呢?

[e1 e2 e3 e4 f1 f2 f3 f4] [g1 g2 g3 g4 h1 h2 h3 h4]
                         [e1 e2 e3 e4 f1 f2 f3 f4]
—————————————————-

[h1 h2 h3 h4] + [f1 f2 f3 f4]最大是16, 很显然4位已经存不下了,但上一步操作中的脏数据。也就是说,无效组即便在当前步骤中不会捣乱,也可能在下一步中出来捣乱,我们可以称之为延迟干扰。因此及时斩草除根免除后患是必要的。

那么如果无效组在今后所有的步骤中都不会形成延迟干扰呢?也就是说,之后每一步的中间结果都只用k位就能存下,不再需要进位。这样一来,每次计算只需要关心分组中后k位的结果就可以了,无效位即使不清除,也不会造成干扰,因此加法操作后的与运算也可以省掉

不难看出,64位整数最多有64个1,而64只需要7位就可以存下。也就是说,从8位一组两两相加开始,我们只需要关心每组中后7位的运算结果,而且这后7位的结果不会受到任何干扰。因此m8, m16, m32全部都可以不要,只需要在最后return x& 0x7f就可以了。

To be continued……

P.S: 唉,live space居然提示我字数太多必须分篇…我很无奈…难道是因为经济危机?

发表在 Uncategorized | 一条评论

Popcount Problem: 求二进制数中1的个数 (下)

5) 传说中的MIT HAKMEM169方法

HAKMEM 是1972年由MIT人工智能实验室的一帮牛人(当然也包括RMS)发布的一本备忘录,里面都是些很无敌的算法,主要的目标是更快更好地进行数学运算。其中的第169个算法,就跟popcount有关,原始的代码[3]是用汇编写的,翻译成C代码如下:

typedef unsigned __int32 uint32;  
const uint32 m1=033333333333;
const uint32 m2=011111111111;
const uint32 m3=030707070707;

int popcount_hakmem(uint32 x) {
x -= (x>>1 & m1 + x>>2 & m2);
x += x>>3 & m3;
return x % 63;
}

总共需要3次shift, 3次and, 2次add, 1次mul, 1次mod 共10次算数运算。这是32位整数的版本,改成适用于64位整数的版本也很简单。主要思想也是分治以及并行加法。只是与原始的shift_and_add有两点不同:

一是第一步的时候是每位一组,相邻三组相加。比如说对于x=(abc)2=4*a+2*b+c,x>>1&m1= 2*a+c,x>>2&m2=a,于是 x – x>>1&m1 – x>>2&m2 = a+b+c。
二是在第二步做完之后,这时是每6位自成一组,以12位整数为例,x=(ab)64 = a*64 + b。很明显, a*64+b
≡ a+b mod 63。也就是说x与a+b关于模63同余。同理,对于64位整数我们也可以这么处理。

6) 再次优化shift_and_add方法,用乘法操作代替加法操作

利用CPU中的基本运算部件乘法器,我们可以继续优化shift_and_add方法。还是先看代码:

const uint64 h01 = 0x0101010101010101;
int popcount_3(uint64 x) {
x -= (x >> 1) & m1;
x = (x & m2) + ((x >> 2) & m2);
x = (x + (x >> 4)) & m4;
return (x * h01)>>56;
}

总共需要 4次shift, 4次and, 2次加法,1次减法,1次乘法共12次算数运算。

可以看出,第三步以后的操作全部都被省略了。这是因为从这步开始,我们需要的操作是8位一组,8个组的数依次相加,就可以得到最终结果。这恰恰可以通过通过乘法器来做到。以32位整数为例,共有四组:
            0a 0b 0c 0d
        *   01 01 01 01
           ————-
            0a 0b 0c 0d
         0a 0b 0c 0d
      0a 0b 0c 0d
  0a 0b 0c 0d
           ————–
            ef gh ij kl
红色标注部分为溢出位,舍掉。橙色标注部分是我们需要的结果右移24位就可以得到。同理对于64位整数,乘法操作后的结果右移56位就可以得到结果了

注意如果CPU执行乘法操作指令比较慢的话,这样优化可能会适得其反。但一般来说不会这样,比如说AMD在两个时钟周期里就可以完成乘法运算。

7) 最笨最快的查表法:
很多情况下我们都可以以牺牲空间的代价来得到时间上的最优解。比如说对于这道题目,我们枚举8位整数的二进制中1的个数,组成一个查找表,看代码[4]:

const int lut[256] =
{
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};

static inline int popcount_lut(uint64 x) {
int i, ret = 0;

for (i = 0; i < 64; i += 8)
ret +=lut[(x >> i) & 0xff];
return ret;
}

之前几种方法中的所有操作都可以在寄存器中完成,而查找表法由于无法将查找表放在寄存器中,不可避免地要出现访问cache或内存的操作。但由于我们采用一个循环频繁访问查找表,在所有的实现中查找表都应该被锁定在cache中,访问开销很小[4]。因此从后面的评测我们可以看到,这种方法的效率最高。

三、性能评测

实际上我在总结这几种方法的时候,基本上是按照性能逐渐提升的顺序来的。也就是说对于时间开销:

naive > less_naive > shift_and_add > shift_add_add_opt > HAKMEM169 > shift_and_add_mul > lut

对于很酷的HAKMEM169,由于用到了耗时的mod操作,在大多数平台上都不如shift_and_add的乘法版本速度快,当然就更比不上我们最笨最好的查找表大法了。KISS准则再次被证明是有效的,哦也。具体的评测数据,可以参考Bart Massey的结果[5]以及感言,或者看这个更加全面详尽啰嗦的评测[6]。

实际上,Intel已经将这个数1操作封装成一个指令popcnt,在Itanium中就已提供。既然CPU提供原生支持,那想必应该是最快的了(据说1个时钟周期就可以[10])。

参考文献:

[1] Hamming Weight: http://en.wikipedia.org/wiki/Hamming_weight
[2] Everything上的讨论: http://everything2.com/index.pl?node=Counting%201%20bits
[3] HAKMEM ITEM 169: http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item169
[4] Set-bit Population Counts: http://www.technovelty.org/code/arch/popcount.html
[5] Popcount: http://wiki.cs.pdx.edu/forge/popcount.html
[6] HAKMEM 169 and other popcount implementations: http://www.dalkescientific.com/writings/diary/archive/2008/07/03/hakmem_and_other_popcounts.html
[7] Population count of a 32-bit register: http://www.df.lth.se/~john_e/gems/gem002d.html
[8] 《编程之美》读书笔记——“求二进制数中1的个数: http://bvcat007.javaeye.com/blog/203577
[9] Henry S. Warren, Jr. Chapter 10. The Quest for an Accelerated Population Count. Beautiful Code.
[10]GMP: http://gmplib.org/

发表在 Uncategorized | 留下评论

AIR及twhirl试用手记

一直没个好用的Twitter客户端。

叽歪倒是可以直接输出到Twitter,但看别人的信息还是不方便。
找了个Pidgin的扩展Microblog-purple,纯文字的界面,看上去很不爽。
其实用起来也不怎么样,只是登录之初弹一下你follow的人的信息。
twhirl倒是不错…偏偏是基于AIR的,主页上挂着大大的Windows,MAC的图标,就是没有Linux…
而且"INSTALL"下面清清楚楚地码着一行字"Adobe AIR is not available for your system."
!!!!!!

下午刚去听了Adobe的宣讲会(又赚了件T恤…还是长袖的…),心说Adobe没这么BC吧
口口声声号称连入Internet的机器98%都装了flash浏览器
难道到了推AIR的时候,就把linux这块放了? (后来一想….linux好像还真的是不到1%)
于是敢于尝试新鲜事物,不畏艰难,勇于探索,奋力拼搏的我
跑到Adobe的网站上去看了看,赫然发现:

哈哈看来还是有的嘛,twhirl太不厚道了。点download,接着…
又是一个页面!!!

使劲臭屁了一把AIR,不管那么多,接着点"Adobe AIR for Linux"

哈哈用的是Ubuntu的桌面截图啊。点大大的"TRY IT"…居然又被链接到另外一个纯文本页面…
再次臭屁AIR,然后是Terms Of Use,最后才是下载链接….
!!!!!搞什么啊,Adobe难道需要这样来增加PV??真是搞不懂。。。

好了,最终的下载页面终于找到了,在这里

下载完以后是个adobeair_linux_b1_091508.BIN文件,看来目前最新的版本是9月15号释放出来的。
安装还算比较友好,chmod +x一下,然后sudo ./adobe*.bin 就好了。

又是一大堆许可协议,直接"I Agree"。

好像今年流行黑色调的主题?

装完啦!!哈哈哈。点那个大大的Finish。

接下来从twhirl上下载.air安装包,从这里安装(当然双击安装也可以,我就是显摆下有这俩菜单项了 ^^)

接下来输入用户名,密码登录就好了。哇哈哈哈终于用上了。

哈哈哈终于可以用了。

P.S: 写个blog真费劲…重新安装,截图,传到picasa,粘链接到ScribeFire里,码图,写字。。。天…我以后再也不干这种傻事了。

发表在 Uncategorized | 留下评论

Ubuntu Is *NOT* Hard Drive Killer

Ubuntu 中的Load/Unload Cycle Count问题及解决方案

本文已经发布在LinuxToy, 感谢Toy

说明:本文所描述的问题只在笔记本硬盘中才会出现。

1. 问题描述

几周前收到soldiers童鞋的短信说,Ubuntu伤硬盘?我说没事,好多人都用呢。过了一周,soldiers童鞋又问,Ubuntu伤硬盘?我说我查查看….

# 安装smart参数查看工具,由此可以查看硬盘的smart信息
$ sudo apt-get install smartmontools

# 查看/dev/sda这块硬盘的smart参数, 你可能需要把/dev/sda这部分修改成你的硬盘设备地址
# grep 193是只查看 Load Cycle Count这项
$ sudo smartctl -a /dev/sda | grep 193
193 Load_Cycle_Count        0x0012   090   090   000    Old_age   Always       –       109989

这样就可以看到Load/Unload Cycle Count数目了。用Windows的童鞋可以借助Everest工具,查看存储器->SMART信息,也可以找到相应项的数据。

不看不知道,一看吓一跳,我的是华丽的11W!!! T.T 据说到了60W,就离挂掉不远了。我才用了4个月,算下来照这个速度用下去的话,只能用4*60/10/12=2年…. 同寝的Acrest童鞋的也过了10W大关哈哈哈。

$ while true; do sudo smartctl -a /dev/sda | grep 193; sleep 300; done;

这样可以每隔5分钟查询一下LCC,一般来说每小时增长在15上下应该是正常的。这样的话即便你每天24小时开着本子,硬盘也可以坚持4年(当然是从理论上来讲)。

1.1 这个Load/Unload Cycle Count到底是什么?

Load/Unload Cycle Count(以下简称为LCC)就是Load/Unload的次数,那么什么叫做Load/Unload呢,下面是一段非常罗嗦的解释,建议不感兴趣的同学出门右转,直接看下一节吧。

大家都知道,硬盘的数据传输是通过磁头读写磁盘上的数据来完成的。在工作过程中,磁头并不与磁盘的盘面直接接触,两者之间有一层很薄的空气薄膜,这层空气薄膜是由于磁盘的高速旋转产生的。如果磁盘停止旋转,空气薄膜消失,磁头则会直接接触到盘片,更详细一点说,会接触到盘片的landing zone,或者叫做start/stop zone,这无疑对盘片的寿命以及对存储在这块区域的数据造成不好的影响。因此在早期阶段,硬盘制造商一般会在对盘片的表面或landing zone部分做特殊的处理,并尽量避免在landing zone存储数据。

但是随着人们对于硬盘传输速度和硬盘容量需求的不断增加,制造商需要不断提高硬盘的面密度,同时要求盘片表面尽可能地平滑,这无疑与之前采用的技术产生了冲突,再加上其他的一些因素,硬盘制造商迫切地需要一种新的方式来替代之前采用的磁头直接接触盘面的行为。这时IBM的工程师们提出了一种叫做Load/Unload的技术。简单来说,Load/Unload技术有点像老式的点唱机,当盘片转速降低无法再产生空气薄膜的时候,就将磁臂以及磁头旋转一下,停靠到磁盘旁边的一个小斜坡上。这样就完全避免了磁头与盘片的直接接触。

总体来说,Load/Unload技术是有利的,比如可以提高硬盘的可靠性:硬盘遭到撞击的时候磁头不会划伤盘面;可以提高硬盘的面密度:不再需要对盘片表面做特殊的处理,可以提供平滑的盘面;以及可以有效地降低功耗:低功耗的程序可以通过多次请求Load/Unload来减少盘片的旋转时间,或者设置旋转超时时间(spin down timeout)来让磁头定期的做Load/Unload等等。

1.2 这个参数值高了有啥危害?

虽然Load/Unload技术有很多优点,但毫无疑问频繁的Load/Unload操作会造成磁头的磨损,严重的话会造成数据读写失效,也就说,硬盘挂了。

那么到底Load/Unload多少次会挂呢?最流行的说法是到60W次,西部数据的一份产品规格说明书上也明确标示出了这一数字。

但也有人指出SMART参数根本就是扯淡,好多坏掉的硬盘SMART值很低好的硬盘SMART值超标,因此根本不能成为评判标准以至于现在好多新机器都直接屏掉。但无论如何,频繁地卸载/挂载总不是什么好事。尤其是当你已经了解到Load/Unload次数过多有可能造成硬盘挂掉的时候,我想无论再有人辟谣,你也不会高枕无忧了。毕竟相对于硬盘本身来说,上面的数据可是要重要的多。

1.3 LCC为啥会那么高?

简单来说,可能有下面几个原因:

    1) 硬盘厂商在固件中制定的节能策略过于苛刻,以至于为了节能,硬盘频繁地Load/Unload
    2) 操作系统的电源策略过于苛刻。

1.4 其他的发行版有没有这个问题?Windows呢,MAC OS呢?

各大linux发行版好像就Ubuntu被报告有这个问题,但这实际上并不是Ubuntu的电源策略太变态,恰恰相反,默认情况下Ubuntu会直接沿用硬件固件里面的设定。其他的发行版中SUSE也有类似的电源管理的BUG,初次之外的发行版似乎默认会忽略硬盘的这个节能功能,所以不会有类似的问题。

至于Windows,也会出现类似的现象,比如说我宿舍的Acrest童鞋,但我的没有。
MAC OS也有报告出现类似的问题。

总体来说这个并不是个别现象,也并不应该算是操作系统的问题。感觉由于Windows下硬盘几乎会一直不停地运作,所以硬件厂商不太重视硬盘固件中的初始设定,比如说我的日立硬盘,电源管理级别被设置为128,结果由于Linux并不像Windows那样频繁读盘,磁头为了节能会频繁地做Load/Unload操作。

2. 如何修复这个问题?

2.1 硬件修改法 (***推荐使用***)

正如上面所说,如果你的硬盘在Ubuntu下有这个问题,那么有可能是硬件本身的节能策略太激进了。最简单也是最根本的方法,就是用厂商提供的固件修改工具对出厂的默认设置进行修改,比如说日立的Feature Tools。

在Feature Tools中,有一项"Change Advanced Power Mode",默认是128,可以选择从1到254不同的数值。

简单来说,数字越小越节能,数字越大性能越好。Feature Tools中将1-254分成三段并分别做了简单的说明,一般来说,设置到192-254则表示不允许Load/Unload操作,而255则表示禁用APM(Advanced Power Management)。这个数字也就是后文提到的APM级别。

2.2 软件修改法

修改硬盘固件是最根本的解决方案,除此之外,关于在Ubuntu中修改相关策略,网上有很多种不同的解法,有兴趣的童鞋可以看关于这个Bug的讨论, Ubuntu Wiki上关于这个Bug的介绍起因分析以及解决方案的总结等。基本上流传的方法有这么两种:

2.2.1 启用laptop-mode,通过修改laptop-mode.conf中的相关设置达到控制Load/Unload的目的

2.2.2 直接在/etc/acpi/start.d, resume.d等目录下放置脚本,通过hdparm命令修改APM级别和spin down time.

具体内容见文后附注。归根结底,这两种方法都是利用hdparm工具,通过-B参数修改高级电源管理(APM)级别,通过-S参数修改旋转超时时间(Spin Down Timeout),从而控制硬盘的Load/Unload次数。所谓APM级别就是我们上面介绍过的1~255,而Spin-down Timeout就是指硬盘空闲(或者旋转?这个拿不准)多久后才会Spin Down,也就是停转,做Unload操作。相对应的,有一个Spin up Time,这是指硬盘重新启动到正常运转所需要的时间。

Windows下也有一款HDDScan软件可以很方便地做到这一点。这样的软件改法确实有效,但由于hdparm不会将设置写入固件,因此在关机、休眠以及待机之后,由于硬盘掉电,这些通过软件的设置会失效,需要重新启用一次。目前这两种方法在我的机器上的测试结果是待机唤醒之后参数不会重新启用。实际上,laptop-mode只会在开机的时候才会应用我们设定的参数,而acpi的resume.d目录下放置的脚本并不会被执行,不知道这是不是个别现象。 所以如果大家非要用软件的修改方法时,推荐下面这一种。

2.2.3 pm.utils大法 (推荐使用)

除了这两种修改方法之外,还有另外一种通过pm.utils来调用hdparm的方法。这实际上是Suse的一个解决方案。pm.utils全称是Power Management Utilities,与acpi类似,它可以通过加入Hook脚本的方法在待机、休眠和唤醒的时候修复一些待机/休眠方面的Bug或者实现某些特定的功能。pm.utils很有可能会在8.10中就取代acpi,所以从这个意义上来讲这个方案也会有更长的效用。具体步骤如下:

1) 首先做一些配置,主要就是设置省电模式开启和关闭的模式下hdparm的参数,具体的内容脚本中有注释。

你可能需要将“/dev/sda"修改成你的硬件设备,比如你有两个硬盘,可以修改为"/dev/sda  /dev/sdb"。

$ sudo vi /etc/pm/config.d/disk

# Configure disk power management settings to ensure both
# long disk life and good power management.
#
# Space delimited list of disk devices this affects.
#
DEVICES_DISK_PM_NAMES="/dev/sda"
#
#
# Power management modes
#
# Powersave mode off
#  Set APM as 192
#  Set spin-down for 30 minutes
#
DEVICES_DISK_PM_POWERSAVE_OFF="hdparm -q -B 192 -q -S 241 -q -M 128"
#
# Powersave mode on
# Enable APM to conservative 192 and set spin-down for 21 minutes
#
DEVICES_DISK_PM_POWERSAVE_ON="hdparm -q -B 192 -q -S 252 -q -M 128"

2) 在power.d中加入Hook脚本,作用是在使用电池和AC电源的时候可以自动切换省电模式。

$ cd /etc/pm/power.d
$ sudo vi disk

#!/bin/bash
. /usr/lib/pm-utils/functions
. /etc/pm/config.d/disk

if test -z "${DEVICES_DISK_PM_NAMES}"; then
    exit 1
fi

case "$1" in
    true)
        echo "**enabled pm for harddisk"
        for DISK_NAME in `echo ${DEVICES_DISK_PM_NAMES}`; do
            ${DEVICES_DISK_PM_POWERSAVE_ON} ${DISK_NAME}
        done ;;
    false)
        echo "**disabled pm for harddisk"
        for DISK_NAME in `echo ${DEVICES_DISK_PM_NAMES}`; do
            ${DEVICES_DISK_PM_POWERSAVE_OFF} ${DISK_NAME}
        done ;;
esac

$ sudo chmod +x disk

3) 在sleep.d中加入脚本,目的是在休眠/待机之后唤醒的时候重新设定hdparm的参数:

$ cd /etc/pm/sleep.d/
$ sudo vi disk

#!/bin/bash
. /usr/lib/pm-utils/functions
. /etc/pm/config.d/disk

if test -z ${DEVICES_DISK_PM_NAMES}; then
    exit 1
fi

case "$1" in
    thaw|resume)
        /usr/bin/on_ac_power;
        if [ "$?" -eq 0 ]; then
            echo "**disabled PM for harddisk"
            for DISK_NAME in `echo ${DEVICES_DISK_PM_NAMES}`; do
                ${DEVICES_DISK_PM_POWERSAVE_OFF} ${DISK_NAME}
            done
        elif [ "$?" -eq 1 ]; then
            echo "**enabled PM for harddisk"
            for DISK_NAME in `echo ${DEVICES_DISK_PM_NAMES}`; do
                ${DEVICES_DISK_PM_POWERSAVE_ON} ${DISK_NAME}
            done               
        fi
        ;;
esac

$ sudo chmod +x disk

***注意最后一定要为disk脚本添加执行权限。否则pm.tuils不会自动执行这段脚本

4) 如果你没有启用laptop mode (默认是不启用的),可以跳过这部分了。

由于Ubuntu中acpi和pm.utils是共存的,所以如果你启用了laptop mode,那么在改变电源状态(指电池->AC电源或者反之)的时候,acpi会在启用/停用laptop mode的同时设置hdparm参数,会覆盖掉pm-utils所做的设置。

所以如果你启用了laptop mode的话,需要做如下修改:

1′ $ sudo vi /etc/default/acpi-support

将最后的

SPINDOWN_TIME=12

修改为

SPINDOWN_TIME=241

2′ $ sudo vi /etc/acpi/power.sh

将function laptop_mode_enable部分的

$HDPARM -B 1 /dev/$drive 2>/dev/null

修改成

$HDPARM -B 192 /dev/$drive 2>/dev/null

上述的解决方案在Dell Inspiron 700m + Ubuntu 8.04.1上测试通过。在待机唤醒之后参数会重新被设置,但是由于我的机器上休眠有问题,所以没有办法测试休眠。但理论上来也是可以的。

3. 我想定期检测Load_Cycle_Count,怎么办?

好办,这里是一个脚本,具体用法在注释里面粗体标明了。(不好意思…注释好像比代码都长)

#!/bin/bash
#
# @Description:
#  
#   check_lcc v0.2
#
#   Check Load_Cycle_Count from S.M.A.R.T info of your hard drive
#   when power on and off and Save them to $FILE in following format:
#
#          LCC         TIME
#   ON      110044      18:05:00 2008-09-08
#   OFF     110044      18:10:03 2008-09-08
#
#   "ON" indicates POWER ON while "OFF" indicates POWER OFF, LCC is
#   exactly Load_Cycle_Count of your hard drive at TIME.
#
# @Usages:
#  
#   1. sudo vi /etc/init.d/check_lcc
#   2. copy all the contents of this script to it
#     *** Note that u need to modify "FILE" as what u want.
#      save and quit.
#   3. sudo chmod +x /etc/init.d/check_lcc
#   4. sudo update-rc.d check_lcc start 1 2 . stop 99 0 6 .
#   5. Have fun.
#  
#   This script was tested under Ubuntu 8.04.1.
#
# @Author:
#
#   breaddawson@gmail.com
#   2008/09/07

FILE="/home/bread/lcc_report.txt"
STAT=`smartctl -a /dev/sda | grep 193 | sed -nr "s/.*[[:space:]]([[:digit:]]{1,})$/\1/p"`"\t "`date +’%T  %F’`

case "$1" in
start)
    STAT="ON \t"$STAT
    ;;
stop)
    STAT="OFF\t"$STAT
    ;;
*)
    echo "Usages: $0 {start|stop}" >&2
    exit 2
    ;;
esac

echo -e $STAT >> $FILE

按照上面的说明操作之后,LCC的结果就会存在你定义的log文件里面了。可以定期打开查看。

4. 最后附上之前的两种方法,启用laptop mode和添加acpi脚本。

4.1. 加入acpi脚本

1) 为使用电源和电池的时候定制不同的hdparm参数。你可能需要把/dev/sda修改成你的硬盘设备。

$sudo vi 99-hdd-ugly-fix.sh

#!/bin/bash
if on_ac_power; then
  # on AC so don’t do any head parking
  hdparm -B 254 /dev/sda # you might need 255 or a different value
else
  # either on battery or power status could not be determined
  # so quickly park the head to protect the disk
  hdparm -B 192 /dev/sda
fi

2) 将如上脚本安装到如下4个地方

$sudo install 99-hdd-ugly-fix.sh  /etc/acpi/resume.d/
$sudo install 99-hdd-ugly-fix.sh  /etc/acpi/start.d/
$sudo install 99-hdd-ugly-fix.sh  /etc/acpi/ac.d/
$sudo install 99-hdd-ugly-fix.sh /etc/acpi/battery.d/

这个方案比开启laptop简单且方便。因此如果你实在是不想用pm.utils的时候,推荐使用这种方法。

4.2. 启用laptop mode

Ubuntu 8.04测试有效,但是待机/休眠唤醒之后设置会丢失。laptop mode 模块在Ubuntu 8.04中是默认包含的,只是没有启用。下面是具体的设置方法。

1) /etc/default/acpi-support中修改

# 启用laptop模式
ENABLE_LAPTOP_MODE=true

# 将spin down 时间改成 (241-240)*30min = 30min
# spin down time决定硬盘闲置多久以后关闭主轴电动机以节省功耗,0表示永远不关闭
# 具体的解释看 man hdparm的-S部分
SPINDOWN_TIME=241

2) /etc/laptop-mode/laptop-mode.conf中修改

# 即便是接上电源也用laptop mode
ENABLE_LAPTOP_MODE_ON_AC=1

# 显示器关闭的时候也用laptop mode
ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED=1

# 让laptop mode控制硬盘闲置多长时间才卸载
CONTROL_HD_IDLE_TIMEOUT=1

# 改成半小时
LM_AC_HD_IDLE_TIMEOUT_SECONDS=1800
LM_BATT_HD_IDLE_TIMEOUT_SECONDS=1800
NOLM_HD_IDLE_TIMEOUT_SECONDS=7200

# 让laptop mode来控制硬盘的电源管理
CONTROL_HD_POWERMGMT=1

# 192表示不关闭,从128-254都表示不关闭,越大能耗越大
# 具体可以 man hdparm 看-B
BATT_HD_POWERMGMT=192
LM_AC_HD_POWERMGMT=254
NOLM_AC_HD_POWERMGMT=254

3) /etc/acpi/power.sh中

把 "$HDPARM -B 1 /dev/$drive 2>/dev/null"
修改为 "$HDPARM -B 192 /dev/$drive 2>/dev/null"

4) 禁用pm.utils的部分功能

$ sudo chmod -x /usr/lib/pm-utils/power.d/laptop-tools

否则laptop-mode不会随机启动。

5) 重启后,cat /proc/sys/vm/laptop_mode

结果是2表示laptop-mode已经启动,是0表示还未启动,请仔细检查上面的设置是否有遗漏。

###############我是很郁闷的分割线############### 

附:关于为啥要禁用pm.utils,具体的解释如下:

我从网上找到了laptop mode的解决方案之后,按照说明一步步操作,但是重新启动之后,查看cat /proc/sys/vm/laptop_mode,发现仍然是0.(是2才表示已经启动)。查看/etc/rc2.d/目录下确实有S99laptop-mode,这说明系统确实会加载这个服务(这个目录下的文件都是个符号链接,会链接到/etc/init.d目录下的同名脚本)。后来Google了一下发现这样的解释:

首先来说/proc/sys/vm/laptop_mode这个变量和初始化进程laptop-mode并不是一个意思。前者是个内核控制的变量,作用是将磁盘写操作聚簇,后者是一个脚本。

其次,Hardy加入了pm-utils,会覆盖或忽略一部分根据linux传统的配置。为了解决这个问题,可以修改/usr/lib/pm-utils/power.d/laptop-tools中相关的内容或者运行下述命令:

$ sudo chmod -x /usr/lib/pm-utils/power.d/laptop-tools

这条命令会禁用pm-utils的部分功能,从而修复你所遇到的问题(指laptop-mode不会随机启动)。注意得重启以后设置才会生效。

实际上pm-utils盲目地覆盖掉laptop-mode或者是/etc/sysctl.conf中的配置,所以chmod -x禁用相关脚本后, 在从AC POWER转到电池供电的时候,pm-utils就不会执行相关的脚本(laptop-tools),从而也就不会覆盖相关的设置。这种做法改动最小,如果之后你想重新启用pm-utils的这部分功能,只需要chmod +x就可以了。

###############很郁闷的分割线又来啦############### 

5. 最后是References:

日立关于Load/Unload技术的解释: Load/Unload白皮书下载
StorageView关于Load/Unload技术的解释:http://www.storagereview.com/guide2000/ref/hdd/perf/qual/featuresHead.html
西部数据的规格:http://www.wdc.com/en/library/portable/2879-001121.pdf
日立的Feature Tool下载:http://www.hitachigst.com/hdd/support/download.htm#FeatureTool
Bug报告页面: https://launchpad.net/bug59695.html
Ubuntu Wiki的Bug总结:https://wiki.ubuntu.com/DanielHahler/Bug59695
Ubuntu Dev解释:http://www.advogato.org/person/mjg59/diary/82.html
laptop mode的解决方案:https://launchpad.net/ubuntu/+source/acpi-support/+bug/59695/comments/63
acpi的解决方案:http://ubuntuforums.org/showthread.php?p=5031046
Suse的解决方案:http://en.opensuse.org/Disk_Power_Management
休眠后重新设置pm.utils的方案:https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/235105
pm.utils的wiki:http://pm-utils.freedesktop.org/wiki/
Suse的pm.utils介绍:http://en.opensuse.org/Pm-utils
CnBeta的报道:http://www.cnbeta.com/articles/42191.htm
CnBeta的一个总结:http://www.cnbeta.com/articles/42421.htm
国内用户的一个解决方案: http://lymanrb.blogspot.com/2008/01/loadunload-bug.html
Ubuntu中文论坛的讨论: http://forum.ubuntu.org.cn/viewtopic.php?p=555500
关于laptop mode和pm.utils冲突的解释:http://ubuntuforums.org/showthread.php?t=867728
Windows下修改硬盘APM和Spin-down time的工具:http://hddscan.com/

6. 最最后

折腾这个问题费了我一整天(实际上是半天,不过那天我中午才起…),总结这些破烂方法,再加上反复试验确定某个方法是否有效,硬着头皮分析脚本的功能,零零碎碎加起来也有一整天的时间,再加上写这篇总结,又花去一整天加上两节入学教育的时间(罪过啊罪过啊)。到此为止距离开始解决这个问题就已经过去整整一周了。就这还没总结全,好多东西都还没有写上来。不过倒是学到了不少东西,硬盘的原理是确确实实复习了一遍了又,另外学了些写Shell脚本的技巧以及ACPI和pm.utils的机制。感觉系统里面同时有俩搞电源管理的东西实在是太FT了,因为会有冲突的部分,好在据说Intrepid要搞掉acpi只用pm.utils,也许会清净一些。

无论如何总算是写完了。一边实验一边记录,一度想放弃了(实在是太费时间,感觉也没太大的意义),但一个是为了我的宝贝硬盘考虑(钱啊钱啊!),又觉得折腾了那么多不记录下来,功夫不就都白费了。正好今天有两节入学教育,于是就勇敢地抱本过去,写完最后一段哈哈哈。

贴到这里,希望会对遇到这个问题的人有所帮助。

发表在 Uncategorized | 7条评论

我愛你

最近看到GF频频发blog
才意识到自己的space几乎快要荒废掉了
本打算暑假玩玩wordpress然后买个空间的
照现在的计划看来已经是不可能了

转一篇吧,来自女主人的沙龙,权当拔草了
是个在国外生活的台湾女人,
写东西的风格又彪悍又细腻,
所以有时看得很爽有时也会看得胸口堵得不行
似乎现实生活中也是如此,总之是很有个性
喜欢的人顺便收藏了吧

好了,开始。

——————我是牛B的分割线—————–

我愛你

睡覺之前,小法把手臂環繞著我的脖子:「媽媽我愛你。」

我輕輕吻一下這個小痞子的臉頰,小孩光滑結實的皮膚碰到嘴唇的感覺非常的好。「I LOVE YOU TOO.」

痞子忽然的張大眼睛:「AND I LOVE YOU THREE AND FOUR AND FIVE AND SIX AND SEVEN EIGHT NINE TEN,」連珠砲一樣的數下去,得意非凡。「I WIN。」

我笑了,於那笑容中,一股深沉的近乎悲愴的情緒湧上來,我無聲的在心裡頭輕輕的說:「AND I LOVE YOU INFINITE。」

但是小子,你贏。

发表在 Uncategorized | 4条评论

Bread’s Home

Special thanks to Jackey,Snoopy and Xiaonan.

发表在 Uncategorized | 15条评论

慰寂寥

得不到的,日后统统会变成最好的。

“我年轻时也像你,热情过度,来不及地付出,一次又一次地付出,震惊痛苦,渐渐学乖,南薇,没想到我在程序调校上没做对,以致你犯同样错误。”
“现在学乖了?”
“现在终于比较聪明”
“可是你快乐么?”

世事往往如此,没有成功之前,一百个人当中一百个人都不看好,
成功以后,大家一定满面笑容前来祝贺。

这是一个讲效率讲成绩的年月,炽热的心,累己累人。

“没有牺牲,不算爱,佳良,你的思想直回到数百年前去。”

没有人可以拥有一切,你必须做出选择。

世上没有如同身受这回事,当事人心如刀割,旁观者再关心,也隔着一层皮肉。

睡吧,一觉醒来,世界不一样。

——亦舒

发表在 Uncategorized | 9条评论

「转载」我们都老得太快,却聪明得太迟

 

把钱省下来,等待退休后再去享受。
结果退休后,因为年纪大,身体差,行动不方便,哪里也去不成。
钱存下来等养老,结果孩子长大了,要出国留学,要创业做生意,要花钱娶老婆,
自己的退休金都被拗走了。

当自己有足够的能力善待自己时,就立刻去做,
老年人有时候是无法做中年人或是青少年人可以做的事,
年纪和健康就是一大因素。
小孩子从小就告诉他,养你到高中,大学以后就要自立更生,
要留学,创业,娶老婆,自己想办法,自己要留多一点钱,
不要为了小孩子而活。

我们都老得太快却聪明得太迟,我的学长去年丧妻。
这突如其来的事故,实在叫人难以接受,
但是死亡的到来不总是如此。
学长说他太太最希望他能送鲜花给他,
但是他觉得太浪费,总推说等到下次再买,
结果却是在她死后,用鲜花布置她的灵堂。
这不是太愚蠢了吗?!
等到……、等到…..,似乎我们所有的生命,都用在等待。

「等到我大学毕业以后,我就会如何如何」我们对自己说
「等到我买房子以后!」
「等我最小的孩子结婚之后!」
「等我把这笔生意谈成之后!」
人人都很愿意牺牲当下,去换取未知的等待;
牺牲今生今世的辛苦钱,去购买后世的安逸。

在台湾只要往有山的道路上走一走,
就随处都可看到「农舍」变「精舍」,山坡地变灵塔,
无非也是为了等到死后,能图个保障,不必再受苦。
许多人认为必须等到某时或某事完成之后再采取行动。
「明天我就开始运动」
「明天我就会对他好一点」
「下星期我们就找时间出去走走」
「退休后,我们就要好好享受一下」

然而,生活总是一直变动,环境总是不可预知,
在现实生活中,各种突发状况总是层出不穷。
身为一个医生,我所见过的死人,比一般人要来得多。
这些人早上醒来时,原本预期过的,
是另一个平凡无奇的日子,没想到一个意料之外的事;
交通意外、 脑溢血、心脏病发作等等。
剎那间生命的巨轮倾覆离轨,突然闯进一片黑暗之中。
那么我们要如何面对生命呢?我们毋需等到生活完美无瑕,
也毋需等到一切都平稳,
想做什么,现在就可以开始做起。
一个人永远也无法预料未来,所以不要延缓想过的生活,
不要吝于表达心中的话, 因为生命只在一瞬间。

记住!
给活人送一朵鲜花,强过给死人送贵重的花圈
每个人的生命都有尽头,许多人经常在生命即将结束时,
才发现自己还有很多事没有做,
有许多话来不及说,
这实在是人生最大的遗憾。
别让自己徒留「为时已晚」的空余恨。
逝者不可追,来者犹未卜,最珍贵、最需要实时掌握的「当下」,
往往在这两者蹉跎间,转眼错失。

人生短暂飘忽,包得有一首小诗这样写:
高天与原地,悠悠人生路;
行行向何方,转眼即长暮。
正是道尽了人生如寄,转眼即逝的惶恐。
有许多事,在你还不懂得珍惜之前,已成旧事;
有许多人,在你还来不及用心之前,已成旧人。
遗憾的事一再发生,
过后再追悔「早知道如何如何」是没有用的,
「那时候」已经过去,
你追念的人也已走过了你

一句瑞典格言说:「我们老得太快,却聪明得太迟」
不管你是否察觉,生命都一直在前进
人生并未售来回票,失去的便永远不再
将希望寄予「等到方便的时间才享受」
我们不知失去了多少可能的幸福
不要再等待有一天你「可以松口气」,或是「麻烦都过去了」。
生命中大部分的美好事物都是短暂易逝的,
享受它们、品尝它们,
善待你周围的每一个人,
别把时间浪费在等待所有难题的「完满结局」上。

找回迷失的生命。
死亡也许是免费的 ─ 但是,
却要付出生命的代价。
劝大家一句话:把握当下,莫等待

说的很有道理,所以现在就开始对所有的朋友(包括我)好一点吧,哈哈!

另:感谢热心的柳MM发这篇文给大家。挺好玩的哈哈。尤其是这些图片,哪位大哥大姐懂外国话的,能给咱翻译一下就最好不过了。会画画就是爽啊,拍个饺子都能画,真是牛叉。
再另:这篇文章真的是很有意义的说。其中有句话怎么说来着?把握当下,莫等待。所以,咳咳,打算请我吃饭的同志们,行动起来吧!我手机一如既往D保持24小时开机状态,随时接受预约。为了省电省时省话费,在blog留言也行。总之赶快赶快。千万不能留下遗憾啊。

发表在 Uncategorized | 6条评论

忙啊忙啊,转篇文章

  女人入洞房那天,早早收起了自己的鞋,等男人脱鞋上炕,女人却双脚踩在男人的鞋上。男人见了,“嘿嘿”笑着说,还挺迷信。女人却认真地说,俺娘说了,踩了男人的鞋,一辈子不受男人的气。男人说,俺娘也说了,女人踩了男人的鞋,那是一辈子要跟男人吃苦受罪的。
  女人开始试探着管男人,先从生活小事儿开始,支使男人拿尿盆倒尿罐,男人全干了。地里的庄稼女人说种啥,男人就种啥。左邻右舍女人说跟谁走近点跟谁走远点,男人全听女人的。男人正跟人闲侃,女人一声喊,男人像被牵了鼻子的牛,乖乖就回去了。男人正跟人喝酒,女人上前只扯一下耳朵,就被拽进家。有人激男人,这女人三天不打,她就上房揭瓦。你也算个男人,怎能让女人管得没有一点男人的气概?若是我的女人,非扇她两鞋底不可。男人不急不慌地说:把你的女人叫来,我也舍得扇她两鞋底子。那人急了,你懂个好赖话不?上辈子老和尚托生的没见过女人!真不像你爹的种,怕老婆!   
  村里人再有大事商量,男人一出场,人们就说,这商量大事你也做不了主,还是把你家女人请来吧。男人还真把女人叫来了。  
  女人能管住男人觉着很得意,直到有一天女人在男人耳边说起了婆婆的不是。男人红了眼,一声吼,想知道我为啥不打你吗?就因为我老娘。我娘一辈子不容易,我爹脾性暴躁,稍有不顺心,张口就骂举手就打,我爹打断过胳膊粗的棍子,打散过椅子。我娘为了我们几个孩子,竟熬了一辈子。每次见娘挨打,我都发誓,我娶了女人决不捅他一指头。不是我怕你,是我忘不了我老娘说的话,她说女人是被男人疼的,不是被男人打的。    
  女人惊呆了,她没想到男人的胸怀竟这样宽广。    
  男人在外再同人神吹海喝,女人不喊也不再拽耳朵,有时会端碗水递给男人。有人问男人,咋调教的?男人却一本正经地说:打出来的女人嘴服,疼出来的女人心服。
  看完了,你从中领悟到了那个朴实的道理了吗?

高中的时候特喜欢这种情节简单人物特征模糊道理浅显但你总会被触动一下的故事
记得当时看到自己喜欢的故事还要找个本子记下来,当宝贝一样
习惯于被挂掉的罗密欧朱丽叶沉底的titanic以及被冻死的jack所感动
那时不屑于平淡宁静的生活,想要并相信凄美和轰轰烈烈,
当然偶尔也会向往一下宁静的夏日稀落的树荫相互依偎的白衬衫和粉红色蝴蝶结
哈哈天真的小男生,那时真好
后来上了大学,就不再倾心于这种故事了
因为我发现曾经让你忍不住落泪的故事终于在现实中发生
会产生更大的杀伤力
并且万分无奈的处境往往会压抑得会让你哭都哭不出来
终于明白故事中的不幸不过是用来赚取稿费和眼泪
如此那般凄美的情节一旦溢到了现实中
就只会荡漾着凄惨,美丽根本不会有一滴
咳咳,又开始酸了,打住…..
总之,看完这个故事后有点汗….
这些狗P道理是一个人懂就可以的么?

头次转载东西到blog。
因为我一直觉得自己的博,就是用来写自己心情的。
从LQQM上转过来这篇文,据说也是篇老文。
所以最初的源头我是没功夫去找了。
最近忙得晕头转向。
有太多的事情等着你去做,
所以没有时间更没有心情去静下心来思考问题。
因此很多时候比较浮躁。
这样不行。。。。。。
好后悔啊…我想生一场过了考试才会好的大病…

废话不说了。。。还要去看组合数学。。。

发表在 Uncategorized | 4条评论