Author Archives: Gery

Fedora16上装kvm Fedora17 Guest

一直想搞个虚拟机做测试用,换内核啊重启啊什么的也方便,趁五一宅家,折腾一下。

1. 环境设置

要用kvm需要有相应软硬件的支持。硬件上来说就是要CPU支持虚拟化。目前CPU基本都支持了,只是有的时候BIOS里会把虚拟化支持给关掉,这时候就需要手动打开。比如我的T400上虚拟化支持默认就是关掉的,导致我modprobe kvm-intel的时候返回ENOTSUPP错误。重启进入BIOS,找到CPU设置,有虚拟化设置相关的选项,选择ENABLE,然后完全关机(重启不管用),再重新上电设置才能生效。

软件上的支持,首先是kernel要支持。Fedora16的kernel肯定没问题。其次是安装kvm相关的软件,比如qemu-kvm qemu-img qemu-kvm-tools qemu-system-x86_64。根据自己需要吧。最后别忘了加载内核,对Intel CPU来说就是

modprobe kvm-intel

2. 安装虚拟机

首先用qemu-img生成一个文件,用作虚拟机的磁盘文件。

qemu-img create -f qcow2 f17.img 30G

这里是生成一个qcow2格式的30G大小的文件,用作Fedora 17的镜像。关于格式,可以参看qemu-img –help最后的输出,关于区别还是google吧。

然后就可以用Fedora 17的iso镜像进行安装了。

qemu-kvm -m 1024 -smp 2 -hda ./f17.img -net nic -net user -vnc localhost:1 -cdrom ./Fedora-17-TC1-x86_64-DVD.iso -boot d
 
  • -m 1024: 给虚拟机分配1024MB内存
  • -smp 2: 分配2CPU
  • -hda ./f17.img: 硬盘为f17.img
  • -net nic -net user: user mode网络,虚拟机ip是10.0.2.15,host的ip是10.0.2.2,guest用NAT模式通过host上网
  • -vnc localhost:1 :开启vnc server,让host可以连接
  • -cdrom: 指定安装iso镜像
  • -boot d: 指定从cd启动

关于guest网络设置可以有很复杂的配置,比如guest通过bridge直接从dhcp server获取ip地址,而且这个ip是可以从其他主机访问的。但这种模式需要有线连接,一般无线网卡都不支持bridge,所以我就没用这种方法。(而且这种方法配置起来也有点麻烦,对网络一直不好的我来说,有点困难……)

其实不使用vnc也是完全可以的,qemu会自动启一个图形显示。总之无论是否通过vnc,都可以正常安装系统了。我这里用了Minimal模式安装,最小系统,没有图形化界面,因为我只想通过ssh登录到虚拟机里操作。

3. 启动Guest

Guest安装好了之后就可以启动了,我用了以下启动参数

qemu-kvm -m 1024 -smp 2 -name f17 -hda ./f17.img -net nic -net user -redir tcp:2222::22 -vnc 127.0.0.1:1 -daemonize -serial tcp:localhost:4445,server,nowait -monitor tcp:localhost:4444,server,nowait
 
  • -redir tcp:2222:22 — 把host的2222端口转发到guest的22端口,这样就可以在host里 ssh -p 2222 root@localhost来登录guest了,很方便
  • -daemonize — 让qemu-kvm在后台运行
  • -serial tcp:localhost:4445,server,nowait — 把guest的serial转发到host的4445端口,并且不用等待连接,这样在host上就可以用 telnet localhost 4445来看guest的console输出了,前提是guest的kernel commandline里要加上console=ttyS0这个参数
  • -monitor tcp:localhost:4444,server,nowait — 跟上一个类似,只是把qemu的monitor输出重定向,telnet之后是qemu的控制台。注意在控制台里不要直接quit,那样会把guest直接杀掉,而是要先ctrl-]回到telnet提示符,再quit

关于serial和monitor的设置,主要参考了这里这里

4. 编译最新的kernel

由于想测试最新的upstream的kernel,那就要自己编译了

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
make localmodconfig  # 根据当前系统进行最小模块化编译,参考这里
make -j 4  # 由于 -smp 2,这里就 -j 4
make modules_install
make install

由于用了make localmodconfig,这里编译速度很快。如果想把自己关注的模块编译进去,就要事先加载模块,比如 modprobe btrfs,之后再make localmodconfig。

make install会自动设置并更新grub,重启就可以进入新内核了。不过这里为了方便调试,并把serial重定向,还需要更新一下grub的配置。因为Fedora 17里就用的是grub2了,我不太熟悉,就用了grubby这个工具来更新grub配置了。我增加了两个参数,一个是serial的,一个是kdump的

grubby --update-kernel=/boot/vmlinuz-3.4.0-rc5 --args="console=ttyS0 crashkernel=128M"

这个工具还是很好用的,具体就man吧。重启之后别忘了配置kdump并chkconfig kdump on && service kdump start

5. 开始折腾

以上都设置好之后,就可以开始折腾了,比如做一些测试,改一些代码,重新编译测试新kernel等等。我是通过从host ssh到guest来做的,并通过telnet看guest的console信息

# On host
ssh -p 2222 root@localhost  # ssh登录guest
telnet localhost 4445   # telnet看guest的console

这下能方便点折腾内核了,作为初学者就得不厌其烦慢慢折腾吧。

配置mutt收发邮件

(这篇blog的草稿已经在草稿箱里呆了一年了,趁这次五一赶紧写完吧,乱七八糟胡说一通,算是给自己留个参考)

对mutt仰望已久,还有那句著名的”All mail clients suck. This one just sucks less.”。由于一开始接触email就是webmail,导致我对email系统极其不熟悉,什么MUA,MTA,MDA,MRA,各种概念完全不清楚。这也导致我前一次尝试mutt失败。(对各种概念的简单解释请看Mutt的Mail concept

这次再次挑战mutt是因为遇到了真实的需求——发patch,webmail或者其他一些mail client对patch都不够友好,可能会自动断行,自动把tab换成空格等等,收到patch也不方便从网页上复制粘贴(虽然我还用不着收patch,没人给我发啊……)。所以这次mutt就真正派上用场了。

网上配置mutt的文章一搜一堆,不过我还是看gentoo的文档写的最清晰也最明了,gentoo的文档果然名不虚传。地址在这里。按照这个文档应该就能配置出能用的mutt了,我也按照自己的理解写一下我的配置。

我使用fetchmail收信,用msmtp发信,用mutt做mail client管理邮件。

首先是MRA(Mail Retrieval Agent),也就是收信的。mutt里支持pop3,但我还是按照gentoo的文档,配置了fetchmail作为我的MRA。

# ~/.fetchmailrc
poll mail.example.com protocol imap authenticate gssapi user eguan keep ssl folder Inbox
mda "/usr/bin/procmail -d %T"
set daemon 180
  • 第一行poll是说从哪里获取邮件,用什么协议,如何认证,keep表示不删除服务器上的邮件,folder后边表示从哪些文件夹里取邮件,这里是Inbox,如果还有其他文件夹都写上。
  • 第二行是指定MDA,负责邮件分发,要不所有邮件都在一个文件里,不方便管理。
  • 第三行设置fetchmail以daemon方式运行,每180秒收一次邮件。

其次是MDA(Mail Delivery Agent),做信件的分发。说白了就是根据不同的邮件列表/收件人/发件人把邮件归类。我使用procmail,以把所有redhat bugzilla的邮件都放入bugzilla这个文件中为例

# ~/.procmailrc
PATH=$HOME/bin:/usr/bin:/usr/ucb:/bin:/usr/local/bin:.
MAILDIR=$HOME/.mail
DEFAULT=$MAILDIR/mbox
LOGFILE=$MAILDIR/from
LOCKFILE=$MAILDIR/.lockmail

:0
* ^(From):.*bugzilla@redhat.com
bugzilla
  • 首先是一些环境变量的设置,DEFAULT是当所有规则都没有匹配的时候邮件的默认归类
  • 然后是把所有由 *bugzilla@redhat.com 发来的信件都放入bugzilla归类(其实就是$MAILDIR/bugzilla这个文件中)。其中的详细语法不清楚,不过根据这个配置改写其他的配置应该还是比较简单的。
  • 再次是MSA(Mail Submission Agent),也就是发送邮件,我用的是msmtp这个程序,感觉很不错。

    # ~/.msmtprc
    defaults
    logfile ~/.msmtp.log
    
    # Gmail account
    account gmail
    host smtp.gmail.com
    port 587
    tls on
    tls_trust_file /etc/pki/tls/certs/ca-bundle.crt
    from yourname@gmail.com
    auth on
    user yourname@gmail.com
    password your_passwd
    

    msmtp的一个好处是可以设置多个account,可以用命令行参数选择想用的帐号。

    最后就是MUA(Mail User Agent)了,这里也就是mutt。这个配置好多,不详细说了,好多东西自己也不清楚……还是多google多看mutt的手册吧。手册内容非常丰富,有特定需求的时候再去看就可以了。这里把配置贴一下,给个参考吧。

    # Set mail from header
    set use_from=yes
    set realname='Your Name Here'
    set from='Your Name '
    
    # Folders
    set spoolfile="~/.mail/mbox"
    set folder="~/.mail"         # Local mailboxes stored here
    set record="+sent"           # Where to store sent messages
    set postponed="+postponed"   # Where to store draft messages
    set mbox_type=mbox           # Mailbox type
    set move=no                  # Don't move mail from spool
    
    # Set mail boxes
    mailboxes =mbox
    mailboxes =postponed
    mailboxes =sent
    mailboxes =bugzilla
    
    # 
    # Some configs
    #
    
    set askcc	# prompt for cc's
    set help	# show help on first line of display
    set pager_stop	# don't go to next message at end of message
    unset mark_old	# don't mark unread messages as Old
    
    set sendmail="/usr/bin/msmtp"	# use msmtp rather than sendmail.
    set editor="vim"		# use vim as editor
    set pager_context=1		# display one line context on pageup/pagedown
    set quit=ask-yes		# confirm quit, default to yes
    set ispell="aspell -e -c"	# spell checker
    set include=yes			# include original message in reply
    
    #set sort='reverse-threads'
    set sort=threads
    set sort_aux='last-date-received'
    
    # Set send charset
    set send_charset="us-ascii:utf-8"
    
    # To recognize reply messages
    set reply_regexp="^(re([[0-9]+])*|aw|回复|答复)(:|:)[ t]*"
    
    # Include attachments in forward emails
    set mime_forward=yes
    set mime_forward_rest=yes
    
    # Traditional Fwd: subject
    set forward_format="Fwd: %s"
    
    # Display settings
    set index_format="%4C %Z %{%b %d} %?X?%X& ? %-18.18L [%4c] %s"
    
    # Alias setup
    set alias_file= ~/.mutt/aliases
    set sort_alias= alias
    set reverse_alias=yes
    source $alias_file
    
    # 
    # Key bindings
    #
    bind index gg first-entry
    bind index G last-entry
    bind index cf next-page
    bind index cb previous-page
    bind index Cx sync-mailbox
    bind index r display-message
    bind index R reply
    bind index = parent-message
    
    bind pager j next-line
    bind pager k previous-line
    bind pager  previous-line
    bind pager  next-line
    bind pager gg top
    bind pager G bottom
    bind pager n next-entry
    bind pager p previous-entry
    bind pager = parent-message
    bind pager N search-next
    
    bind index,pager L group-reply
    bind index,pager S resend-message
    
    bind generic zt current-top
    bind generic zb current-bottom
    bind generic zz current-middle
    bind editor  noop
    
    #
    # Macros
    #
    
    # Mark all as read
    macro index A "T.*n;WN;^t.*n" "Mark all as read"
    
    # Quit from index view and return to mailboxes view
    macro index q '?'
    macro browser q ''
    
    # Run fetchmail by hitting key of ~
    macro index ~ "!fetchmail -k -m 'procmail -d %T'r"
    macro pager ~ "!fetchmail -k -m 'procmail -d %T'r"
    
    #
    # Color setup
    #
    color header brightred black subject
    color hdrdefault brightwhite black
    color quoted brightgreen black
    color status black cyan
    color indicator default green
    # email address
    color body yellow default [-a-z_0-9.]+@[-a-z_0-9.]+
    color header brightyellow default [-a-z_0-9.]+@[-a-z_0-9.]+
    # URLS
    color body brightmagenta default "(ftp|http)://[^ ]+"
    # mails cced myself
    color index brightblue black   '~c yourmail@example.com'
    # mails to myself
    color index brightyellow black   '~t yourmail@example.com'
    # mails sent from myself
    color index cyan black   '~f yourmail@example.com'
    
    #
    # Auto view
    #
    set mailcap_path=~/.mutt/mailcap
    auto_view text/html application/pdf application/msword
    

    最后简单说一下用 git send-email 配合msmtp发送patch。假设已经用git format-patch生成好要发送的patch了,那么

    git format-patch -1 
    git send-email --to xxx@yyy.com --cc sb@ex.com --smtp-server=/usr/bin/msmtp 0001-xxx.patch
    

    主要就是 –smtp-server这个参数指定发送程序。只要配置好了msmtp就可以发patch了,很方便。

C中的移位和undefined behavior

前一阵写一个程序,需要malloc 2G的buffer,于是第一次就这么写了

long size = 1 << 31;

结果malloc返回总是失败,把size打印出来发现是个负数。如果改成以下的写法就没问题了

long size = 1u << 31;

今天正好看到内部邮件列表里有人也在讨论这个问题,贴出了C99对移位操作的规定,也为我解决了这个疑问

ISO C99 6.5.7 Bitwise shift operators, paragraph 4:

 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated
 bits are filled with zeros. If E1 has an unsigned type, the value of
 the result is E1 × 2^E2 , reduced modulo one more than the maximum
 value representable in the result type. If E1 has a signed type and
 nonnegative value, and E1 × 2^E2 is representable in the result
 type, then that is the resulting value; otherwise, the behavior is
 undefined.

也就是说 1 << 31 这种表达式是个未定义行为,因为1是一个signed type,并且 1 x 2^31已经超出了signed int的表达范围,所以gcc中得到负数只是gcc的一个实现。而 1u << 31 能够得到正确的2G数值,是因为1u是一个unsigned type,1u x 2^31 的结果正好能在unsigned int里表示,再赋值给long之后也就没问题了。

公司羽毛球活动 2012-04-24

今天打球偶遇一个中科院计算所的学生来蹭球,由于今天公司来打球的人少,蹭球也就成为了可能。

这小伙子打的很不错,高远很给力,小球也相当彪悍,经常对角勾的我只能看着球落地,吊网前也很好,很多球我都没反映过来就已经落地了。虽然感觉还没有孙同学那么厉害,但我还是赢不了他。

这次打球充分暴露了我对小球的无感,如论是接小球还是主动放网。再加上对方时不时的假动作,我就只能回质量及其恶劣的球了。除此之外,反手依旧很弱,这一方面是步法没跟上导致被动只能用反手,另一方面就是反手本身的弱了。对方的反手球也都能回到接近双打发球线附近。

还要对更多球路进行熟悉,比如这个小伙子就很喜欢压几次后场之后放短、勾对角,我就要对各个方位的来球都要有思想准备。同时还要增加自己的球路,但这个受限于基本功不牢、技术不全面。

发现如果能有一个人来指出我的不足并给出改进方法是目前最好的提高方式。记得之前孙同学说我在网前基本都是挑后场,太单一,还可以放小球,位置好的话还可以推球,自己注意了这方面之后就能有所提高。现在完全处在自己摸索阶段,很多缺陷自己发现不了,错误动作/思想一直重复,直到有一天固化到身体里就糟了。

打球路漫漫,继续努力。

Debian下使用HTTPS访问WordPress

由于未知原因,VPS最近总是被reset, 经过一番探索,发现ssh能连,dig域名解析正确,但只要访问http就会reset, 直接ip访问没有问题,那么只能是域名被干了。按说我就是自己写写博客,顶多发发生活上的牢骚,不知道怎么就触到校长大人的G点了。

忍了几天终于不能忍了,决定设法解决。首先想到换个域名,但上name.com一搜,稍微看上眼的基本都在10刀左右,为了校长已经花了不少钱了,再花钱不值得。然后想到了HTTPS, 实用还安全,就它了。

那么要想强制Wordpress使用HTTPS访问,要经过两个步骤,一是启用HTTPS访问,二是让Wordpress走HTTPS.

第一步是配置Apache2使用HTTPS. 因为我只是想绕过gfw, 就用自签名的SSL吧。主要参考了这篇文章。原文的步骤很清晰,我这里只罗列一下步骤

openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -in server.csr -signkey server.key -out server.crt

以上步骤对应文章里的Step1-Step4, 也就是生成了自签名的key. 接下来要配置debian的Apache2

a2enmod ssl
a2ensite default-ssl
mkdir -p /etc/apache2/ssl
cp server.key /etc/apache2/ssl/ssl.key
cp server.crt /etc/apache2/ssl/ssl.crt
chmod 400 /etc/apache2/ssl/*

a2enmod ssl之后debian会给你一个提示,告诉你去读一下/usr/share/doc/apache2/README.Debian.gz, 建议看一下,那里用的make-ssl-cert命令进行自签名的。

接下来编辑 /etc/apache2/sites-enabled/default-ssl 找到SSLCertificateFile和SSLCertificateKeyFile字段,修改为自己的路径 /etc/apache2/ssl/ssl.xxx, 然后

/etc/init.d/apache2 restart

重启apache就好了。

第二步是让Wordpress使用HTTPS访问。这里用到一个Wordpress插件,WordPress HTTPS ms这个插件没什么好说的,安装后会出现一个HTTPS配置选项,根据自己的需求配置一下,再用https访问就好了……

现在VPS只能全局默认https了,输入http也会自动转为https访问输入http ms还不能自动转到https上来,这个需要再看一下。目前只有两个问题,一个因为是自签名,第一次访问会有警告,据说chrome下问题更大;第二个问题是访问明显变慢,不过没办法了,总比被墙了的好。

Red Hatter 两周年的礼物

今天是清明节后上班第一天,4月1号过去之后我在红帽就满两周年了。今天公司算是给了我一个很好的礼物,给我“首届红帽北京融科办公室季度最佳员工奖”……得了一个奖杯一个机械键盘。算是对我这两年工作的一个肯定吧,很高兴,给自己呱叽呱叽。(幸亏清明节没请假回家,幸亏今天没在家办公,幸亏……)

Red Hatter of the Q

Monthly Pic 2012 03

三月下了一场雪,也算是北京这几年的惯例了,三月雪。月末的时候跟着公司同事又骑了一次十三陵,这次到顶了,并绕水库一周,回来依旧累惨了

教四前小路上看教三

十三陵水库风光

公司羽毛球活动 2012-04-03

清明节的出游活动由于诸多原因没能成行,那只好自娱自乐了。公司订的长期羽毛球场,每周二都可以,无论节假日,所以跟同事约好,又叫上孙同学,周二又好好打了一次球。

记得上次跟孙同学打球的时候被他调动的很惨,放网质量不好,然后被推到后场,我根本来不及后退,甚至来不及举拍球就已经落地了。后来孙同学告诉我,我在网前打法单一,基本都是挑到后场,其实可以挑可以放网,机会好还可以推。这次打球就感觉有些进步。主要体现在两个地方:一是后场球基本都能打到对方底线,无论是正手区击球还是头顶区击球,这主要得益于平时在家里没事练习挥拍,逐渐找到甩动鞭打的击球感觉;二是步法有些提高,感觉启动快了一点,有些球之前是够不到的,现在能勉强挑回去,尤其是头顶击球之后马上跑去接近网的球。还有一点提高就是网前小球和推球的结合多了起来,打法不再单一,现在也能时不时调动一下孙同学跑动。

不过不足依旧明显。反手球打过去基本就再也接不到了。之前一直很奇怪,练习反手击球的力量应该是够的,为什么打球的时候反手总是用不上力量,回来之后一琢磨发现问题不在击球动作上,而在击球点上。反手区跑动不到位,抓不到好的击球点,击球点基本都在身体前方,这个位置当然发不上力量,看来要练习的是后场反手步法。另外失误太多,很多机会很好的球都失误了,比如杀半场球下网,回高远球出边线,扑球下网,基本功还不够扎实。

不过总的来说这次跟孙同学打单还是比较满意的,虽然五局都输了,但比分比之前要接近了,而且孙同学基本也用全力打球了(之前都不全力跟我打……)
第一局:18:21
第二 三局都是不到15分落败,因为实在没体力了
第四局17:21,我16的时候首次反超比分,但没能坚持住,失误过多输掉。
最后一局最有希望赢球,开场一度13:7领先,16 17分的时候被赶上,最后还是以18:21输掉了

晚上回来想想自己打球的线路,基本都是后场压对方反手底线,75%的球是回到了那个区域,有一些接发球直接吊到对方正手网前得分,比较少回到对方正手底线(有一个球突然回到对方很深的正手底线,效果很不错),基本没有对方反手网前。球路还是太单一了,不过业余打球果然压住对方反手底线就很容易取得主动了。

每次跟孙同学打球回来都很累,今天又开始浑身酸疼了。要开始锻炼上肢力量了,上身太单薄,衣服都撑不起来……

ext3/4的data journalling和direct I/O

ext3和ext4有3种日志模式,分别是journal, ordered(default), writeback,可以在mount的时候用 -o data=指定

journal: All data is committed into the journal prior to being written into the main filesystem.
ordered: All data is forced directly out to the main file system prior to its metadata being committed to the journal.
writeback: Data ordering is not preserved.

另外,如果想在没用 -o data=journal的情况下也做data journalling,还可以使用chattr(1)来更改某个文件的属性,比如

chattr +j testfile

用lsattr testfile就能看到属性已经更改,具体都有什么属性可以man chattr。

这几天遇到一个问题,就是当使用 -o data=journal或者给某个文件增加j属性之后,ext3上的文件就不能使用direct I/O了,具体点说就是open(2)的时候只要带O_DIRECT,open(2)就会返回EINVAL,而在ext4上则没问题。

经过搜索发现,ext3和ext4其实是都不支持在data jouralling情况下的direct I/O的。可以看open(2)的代码,在 __dentry_open()函数里有一段代码做了判断

fs/open.c
 706         /* NB: we're sure to have correct a_ops only after f_op->open */
 707         if (f->f_flags & O_DIRECT) {
 708                 if (!f->f_mapping->a_ops ||
 709                     ((!f->f_mapping->a_ops->direct_IO) &&
 710                     (!f->f_mapping->a_ops->get_xip_mem))) {
 711                         fput(f);
 712                         f = ERR_PTR(-EINVAL);
 713                 }
 714         }

如果有O_DIRECT标志,那么检查相应的a_ops(struct address_space_operations)中的direct_IO方法。ext3在data journalling模式下没有设置direct_IO这个方法,所以就返回了EINVAL。可以看ext3相应的代码(给每一种日志模式都设置了不同的aops)

fs/ext3/inode.c
2007 static const struct address_space_operations ext3_journalled_aops = {         
2008         .readpage               = ext3_readpage,                              
2009         .readpages              = ext3_readpages,                             
2010         .writepage              = ext3_journalled_writepage,                  
2011         .write_begin            = ext3_write_begin,                           
2012         .write_end              = ext3_journalled_write_end,                  
2013         .set_page_dirty         = ext3_journalled_set_page_dirty,             
2014         .bmap                   = ext3_bmap,                                  
2015         .invalidatepage         = ext3_invalidatepage,                        
2016         .releasepage            = ext3_releasepage,                           
2017         .is_partially_uptodate  = block_is_partially_uptodate,                
2018         .error_remove_page      = generic_error_remove_page,                  
2019 };

相应的,ext4的ext4_journalled_aops中设置了 .direct_IO = ext4_direct_IO,所以ext4看似可以用direct I/O。可是再看一下ext4_direct_IO()这个函数

fs/ext4/inode.c
2991         /*
2992          * If we are doing data journalling we don't support O_DIRECT
2993          */
2994         if (ext4_should_journal_data(inode))
2995                 return 0;

也就是如果判断是data journalling模式,ext4_direct_IO()直接返回0,表示写了0个字节。看 mm/filemap.c中的 __generic_file_aio_write()可以知道,->direct_IO返回0之后会用generic_file_buffered_write()写入剩余的字节(对dio来说也就是全部字节了)。所以ext4其实是fall back到了buffer I/O去了,实际上不是direct I/O,只不过没有显式的报错。