Linux_Note
Last edited September 25, 2006
More by wanyancan »
[精华] gentoo简单安装手册(stage3) - ChinaUnix.net
www.chinaunix.net/jh/4/514650.html

[精华] gentoo简单安装手册(stage3)


http://www.chinaunix.net 作者:wingger  发表于:2005-03-24 12:10:39
发表评论】 【查看原文】 【Linux讨论区】【关闭

一、安装前准备

1、选择安装方式:我由于条件限制,因此不能使用网络安装,我选择无网络安装
因此,首先到gentoo下载Universal LiveCD的镜像文件ISO并烧制光盘,在刻录的时候刻录映像档案既可制成LiveCD启动光盘了

2、我的系统硬件信息,LINUX下可使用lspci或lsmod查看
CPU:PIII
SCSI硬盘:Adaptec AIC-7896U2/7897U2
E100及VIA_rshine网卡
ACPI电源

二、安装gentoo基础系统。

1、我选用了局域网的ssh方式安装,因此,必须在liveCD方式下设定网络,以便安装
1)更改root密码:#passwd
2)设定网络:#net-setup eth1或eth0,根据提示设定IP,掩码,网关:
3)启动sshd服务#/etc/init.d/sshd start
4)连接ssh服务器
提示:如果liveCD不能认到你的网卡,那就必须手动加载了模组了

2、分区建立文件系统并挂载
1)分区:
用fdisk /dev/sda分区,如果是IDE就用fdisk /dev/hda,下面是我的分区信息:
/dev/sda1 boot分区 32M              ext2
/dev/sda2 Swap 分区 384M swap
/dev/sda3 主分区(Root) 其余的 ext3

2)建立文件系统
mke2fs /dev/sda1
mke2fs -j /dev/sda3
mkswap /dev/sda2
启用swap
swapon /dev/sda2

3)挂载分区
# mount /dev/sda3 /mnt/gentoo
# mkdir /mnt/gentoo/boot
# mount /dev/sda1 /mnt/gentoo/boot

3、安装stage
安装前查先用ls /mnet/cdrom/stages查看LiveCD提供的stages包,根据需要选择自己的包。
我选用了stage3-pentium3-2004.3.tar.bz2,因为我的CPU是pentium3
#cd /mnt/gentoo
#ls /mnt/cdrom/stages
#tar -xvjpf /mnt/cdrom/stages/stage3-pentium3-2004.3.tar.bz2

4、安装快照
# tar -xvjf /mnt/cdrom/snapshots/portage-200401022.tar.bz2 -C /mnt/gentoo/usr

5、设定编译参数
# nano -w /mnt/gentoo/etc/make.conf
CFLAGS="-O2 -march=pentium3 -fomit-frame-pointer"
CHOST="i686-pc-linux-gnu"
CXXFLAGS="${CFLAGS}"
MAKEOPTS="-j2"

-march选择使用自己的CPU型号
MAKEOPTS 一般为自己的CPU个数加1

6、挂载proc档案系统
# mount -t proc none /mnt/gentoo/proc

7、进入新的系统环境 
# chroot /mnt/gentoo /bin/bash
# env-update
 * Caching service dependencies...
# source /etc/profile

8、设定DNS
nano -w /etc/resolv.conf
如下内容
nameserver xx.xx.xx.xx
nameserver xx.xx.xx.xx

xx.xx.xx.xx为DNS服务器地址。

三、安装核心
1、安装核心源代码,这里我选用了2.6.9的核心
安装完后查看/usr/src,应该有有一个 linux 的連接連到您所選的核心
# emerge gentoo-dev-sources
# ls -l /usr/src/linux
lrwxrwxrwx    1 root     root           12 Oct 13 11:04 /usr/src/linux ->;
linux-2.6.9-gentoo-r1
如果這並沒有指向你所選擇的核心,你要把他改成正確的核心: 
# rm /usr/src/linux
# cd /usr/src
# ln -s linux-2.4.26-gentoo-r9 linux

2、手动预设,这里除了必须要选的一些选项,其它我都按照预设
#cd /usr/src/linux
# make menuconfig

参照官方文档选择必须开启的选项
1) Code maturity level options --->;
  [*] Prompt for development and/or incomplete code/drivers

2)选择处理类别,请选择你的处理器类别,我的是pIII
Processor type and features --->;
Processor family (Pentium-III/Celeron(Coppermine)/Pentium-III Xeon
(X) Pentium-III/Celeron(Coppermine)/Pentium-III Xeon

3)文件系统
File systems --->;
  Pseudo Filesystems --->;
    [*] /proc file system support
    [*] /dev file system support (OBSOLETE)
    [*]   Automatically mount at boot
    [*] Virtual memory file system support (former shm fs)
根据需要选择,在上面的分区中,使用了ext2,ext3,所以在这里应当选上ext2,ext3文件系统的支持,我还选了quota support
当然你也可以想要的,如NTFS等,还有网络文件系统NFS之类的

4)选择网络驱动,我使用的是e100及via_rhine,
Ethernet (10 or 100Mbit)  --->;
<*>;   Intel(R) PRO/100+ support 
<*>;   VIA Rhine support 
我选了QoS and/or fair queueing  --->;里的很多选项,其它可根据需要选择如PPP支持等  


5)选择scsi卡驱动,我的是aic7XXX,old driver,就选这个,下面是我的scsi清单
 --- SCSI device support                                             
   [*]   legacy /proc/scsi/ support                                    
   <*>;   SCSI disk support                                             
   <*>;   SCSI CDROM support                                            
   [*]     Enable vendor-specific extensions (for SCSI CDROM)          
   <*>;   SCSI generic support                                          
         SCSI Transport Attributes  --->;                               
         SCSI low-level drivers  --->;   
           <*>; Adaptec AIC7xxx support (old driver) 

3、编译
(核心 2.4)# make dep && make bzImage modules modules_install
(核心 2.6)# make && make modules_install

4、安装
# cp arch/i386/boot/bzImage /boot/kernel-2.6.9
# cp System.map /boot/System.map-2.6.9

5、备份核心设定
# cp .config /boot/config-2.6.9

总之在核心设定的时间最好多看看参考手册和help,方便自己确定需要哪一些模块
以上我的所有选项除了网络的一些不必须选项外,都是使用核心安装,而不是module方式

四、设定系统
1、/etc/fstab设定
/dev/sda1   /boot     ext2    defaults,noatime     1 2
/dev/sda2   none      swap    sw                0 0
/dev/sda3   /         ext3    noatime           0 1
none        /proc     proc    defaults          0 0
none        /dev/shm  tmpfs   nodev,nosuid,noexec  0 0
/dev/cdroms/cdrom0    /mnt/cdrom    auto      noauto,user    0 0

2、网络设定
1)
、主机名:echo gentoo >; /etc/hostname
、域:echo mynetwork >; /etc/dnsdomainname
、加入runnel中:rc-update add domainname default

2)网络,我暂时不想使用 eth0这块网卡
# nano -w /etc/conf.d/net
iface_eth0="10.0.0.137 broadcast 192.168.0.255 netmask 255.255.255.0"
iface_eth1="192.168.1.201 broadcast 192.168.1.255 netmask 255.255.255.0"
gateway="eth1/192.168.1.1"

3)开机自动启动网络
# rc-update add net.eth0 default
若有多块网卡,如下设定
# cd /etc/init.d
# ln -s net.eth0 net.eth1
# rc-update add net.eth1 default

4)# nano -w /etc/hosts
127.0.0.1     localhost  gentoo(后面加上主机名既可)

3、更改root密码
#passwd

4、安装系统工具
# emerge syslog-ng
# rc-update add syslog-ng default

# emerge vixie-cron
# rc-update add vixie-cron default
(只有在安裝 dcron 或 fcron) # crontab /etc/crontab

# emerge slocate

五、安装grub引导程序
安装设定
# emerge grub
# nano -w /boot/grub/grub.conf
default 0
timeout 30
title=Gentoo Linux 2.6.9
root (hd0,0)
kernel /kernel-2.6.9 root=/dev/sda3 doscsi nousb nodevfs

将grub安装到mbR
# cp /proc/mounts /etc/mtab
# grub-install --root-directory=/boot /dev/sda

重开机
# exit
cdimage ~# cd
cdimage ~# umount /mnt/gentoo/boot /mnt/gentoo/proc /mnt/gentoo
cdimage ~# reboot
Linux 远程登录Windows图形界面 - 红联门户-您身边的Linux学习社区-本站新域名:www.linux110.com-Linux技术文章-Linux教程下载-电脑技术-红旗Linux -
www.linuxdiyf.com/viewarticle.php?id=9626
rdesktop 的使用

  1.远程Windows 系统的设置

  这里以连接Windows XP Professional的远程桌面服务为例。首先在WindowsXP 下启用远程桌面服务(注意,XP 的HomeEdition 没有远程桌面服务),右键点击“我的电脑”,选择“属性”,查看“远程”选项,选择“允许用户远程连接到这台计算机”即可。

  2.Linux 下rdesktop 的使用

  rdesktop 的使用很简单,可通过#rdesktop-h得到使用的帮助。一般常用的登录命令为:

  #rdesktop -g 1024x768 -d 24hostname

  其中 “g 1024×768”设置分辨率为1024×768,“d 24”设置真彩24 位,hostname为 Windows 机器的主机名或者IP 地址。在输入了Windows XP的用户名和密码后,就可以登录并操作远程的Windows系统

6.5.2. DNAT target

这个target是用来做目的网络地址转换的,就是重写包的目的IP地址。如果一个包被匹配了,那么和它 属于同一个流的所有的包都会被自动转换,然后就可以被路由到正确的主机或网络。DNAT target是非常有 用的。比如,你的Web服务器在LAN内部,而且没有可在Internet上使用的真实IP地址,那就可以使用这个 target让防火墙把所有到它自己HTTP端口的包转发给LAN内部真正的Web服务器。目的地址也可以是一个范 围,这样的话,DNAT会为每一个流随机分配一个地址。因此,我们可以用这个target做某种类型地负载平 衡。

注意,DANT target只能用在nat表的PREROUTING和OUTPUT链中,或者是被这两条链调用的链里。但还要 注意的是,包含DANT target的链不能被除此之外的其他链调用,如POSTROUTING。

Table 6-16. DNAT target

Option --to-destination
Example iptables -t nat -A PREROUTING -p tcp -d 15.45.23.67 --dport 80 -j DNAT --to-destination 192.168.1.1-192.168.1.10
Explanation 指定要写入IP头的地址,这也是包要被转发到的地方。上面 的例子就是把所有发往地址15.45.23.67的包都转发到一段LAN使用的私有地址中,即192.168.1.1到 192.168.1.10。如前所述,在这种情况下,每个流都会被随机分配一个要转发到的地址,但同一个流总是使 用同一个地址。我们也可以只指定一个IP地址作为参数,这样所有的包都被转发到同一台机子。我们还可以 在地址后指定一个或一个范围的端口。比如:--to-destination 192.168.1.1:80--to-destination 192.168.1.1:80-100SNAT的语法和这 个target的一样,只是目的不同罢了。要注意,只有先用--protocol指定了TCP或 UDP协议,才能使用端口。

因为DNAT要做很多工作,所以我要再罗嗦一点。我们通过一个例子来大致理解一 下它是如何工作的。比如,我想通过Internet连接发布我们的网站,但是HTTP server在我们的内网里,而 且我们对外只有一个合法的IP,就是防火墙那个对外的IP——$INET_IP。防火墙还 有一个内网的IP——$LAN_IP,HTTP server的IP是$HTTP_IP (这当然是内网的了)。为了完成我们的设想,要做的第一件事就是把下面的这个简单的规则加入到nat表 的PREROUTING链中:

iptables -t nat -A PREROUTING --dst $INET_IP -p tcp --dport 80 -j DNAT \ --to-destination $HTTP_IP

现在,所有从Internet来的、到防火墙的80端口去的包都会被转发(或称做被DNAT )到在内网的HTTP服务器上。如果你在Internet上试验一下,一切正常吧。再从内网里试验一下,完全 不能用吧。这其实是路由的问题。下面我们来好好分析这个问题。为了容易阅读,我们把在外网上访问我们 服务器的那台机子的IP地址记为$EXT_BOX

  1. 包从地址为$EXT_BOX的机子出发,去往地址为$INET_IP 的机子。

  2. 包到达防火墙。

  3. 防火墙DNAT(也就是转发)这个包,而且包会经过很多其他的链检验及处理。

  4. 包离开防火墙向$HTTP_IP前进。

  5. 包到达HTTP服务器,服务器就会通过防火墙给以回应,当然,这要求把防火墙作为HTTP到达$EXT_BOX的网关。一般情况下,防火墙就是HTTP服务器的缺省网关。

  6. 防火墙再对返回包做Un-DNAT(就是照着DNAT的步骤反过来做一遍),这样就 好像是防火墙自己回复了那个来自外网的请求包。

  7. 返回包好象没经过这么复杂的处理、没事一样回到$EXT_BOX

现在,我们来考虑和HTTP服务器在同一个内网(这里是指所有机子不需要经过路由器而可以直接互相访 问的网络,不是那种把服务器和客户机又分在不同子网的情况)的客户访问它时会发生什么。我们假设客户 机的IP为$LAN_BOX,其他设置同上。

  1. 包离开$LAN_BOX,去往$INET_IP

  2. 包到达防火墙。

  3. 包被DNAT,而且还会经过其他的处理。但是包没有经过SNAT 的处理,所以包还是使用它自己的源地址,就是$LAN_BOX(译者注:这就是IP 传输包的特点,只根据目的地的不同改变目的地址,但不因传输过程中要经过很多路由器而随着路由器改变 其源地址,除非你单独进行源地址的改变。其实这一步的处理和对外来包的处理是一样的,只不过内网包的 问题就在于此,所以这里交待一下原因)。

  4. 包离开防火墙,到达HTTP服务器。

  5. HTTP服务器试图回复这个包。它在路由数据库中看到包是来自同一个网络的一台机子,因此它会把回 复包直接发送到请求包的源地址(现在是回复包的目的地址),也就是$LAN_BOX

  6. 回复包到达客户机,但它会很困惑,因为这个包不是来自它访问的那台机子。这样,它就会把这个包 扔掉而去等待“真正”的回复包。

针对这个问题有个简单的解决办法,因为这些包都要进入防火墙,而且它们都去往需要做DNAT才能到达 的那个地址,所以我们只要对这些包做SNAT操作即可。比如,我们来考虑上面的例子,如果对那些进入防火 墙而且是去往地址为$HTTP_IP、端口为80的包做SNAT操作,那么这些包就好象是从$LAN_IP来的了,也就是 说,这些包的源地址被改为$LAN_IP了。这样,HTTP服务器就会把回复包发给防火墙,而防火墙会再对包做 Un-DNAT操作,并把包发送到客户机。解决问题的规则如下:

iptables -t nat -A POSTROUTING -p tcp --dst $HTTP_IP --dport 80 -j SNAT \ --to-source $LAN_IP

要记住,按运行的顺序POSTROUTING链是所有链中最后一个,因此包到 达这条链时,已经被做过DNAT操作了,所以我们在规则里要基于内网的地址$HTTP_IP(包的目的地)来匹配 包。

警告:我们刚才写的这条规则会对日志产生很大影响,这种影响应该说是很不好的。因为来自 Internet包在防火墙内先后经过了DNAT和SNAT处理,才能到达HTTP服务器(上面的例子),所以HTTP服务器 就认为包是防火墙发来的,而不知道真正的源头是其他的IP。这样,当它记录服务情况时,所有访问记录的 源地址都是防火墙的IP而不是真正的访问源。我们如果想根据这些记录来了解访问情况就不可能了。因此上 面提供的“简单办法”并不是一个明智的选择,但它确实可以解决“能够访问”的问题,只是没有考虑到日 志而已。

其他的服务也有类似的问题。比如,你在LAN内建立了SMTP服务器,那你就要设置防火墙以便能转 发SMTP的数据流。这样你就创建了一个开放的SMTP中继服务器,随之而来的就是日志的问题了。

一定要注意,这里所说的问题只是针对没有建立DMZ或类似结构的网络,并且内网的用户访问的是 服务器的外网地址而言的。(译者注:因为如果建立了DMZ,或者服务器和客户机又被分在不同的子网里, 那就不需要这么麻烦了。因为所有访问的源头都不在服务器所在的网里,所以就没必要做SNAT去改变包的源 地址了,从而记录也就不是问题了。如果内网客户是直接访问服务器的内网地址那就更没事了)

较好的解决办法是为你的LAN在内网建立一台单独的DNS服务器(译者注:这样,内网的客户使用网站名 访问HTTP服务器时,DNS就可以把它解析成内网地址。客户机就可以直接去访问HTTP服务器的内网地址了, 从而避免了通过防火墙的操作,而且包的源地址也可以被HTTP服务器的日志使用,也就没有上面说的日志问 题了。),或者干脆建立DMZ得了(这是最好的办法,但你要有钱哦,因为用的设备多啊)。

对上面的例子应该考虑再全面些,现在还有一个问题没解决,就是防火墙自己要访问HTTP服务器时会发 生什么,能正常访问吗?你觉得呢:)很可惜,现在的配置还是不行,仔细想想就明白了。我们这里讨论的基 础都是假设机子访问的是HTTP服务器的外网地址,但这个外网地址其实就是防火墙对外的地址,所以当防火 墙访问这个外网地址时,就是访问它自己。防火墙上如果有HTTP服务,那客户机就会看到页面内容,不过这 不是它想看到的(它想要的在DNAT上了),如果没有HTTP服务,客户就只能收到错 误信息了。前面给出的规则之所以不起作用是因为从防火墙发出的请求包不会经过那两条链。还记得防火墙 自己发出的包经过哪些链吧:)我们要在nat表的OUTPUT链中添加下面的规则:

iptables -t nat -A OUTPUT --dst $INET_IP -p tcp --dport 80 -j DNAT \ --to-destination $HTTP_IP

有了最后这条规则,一切都正常了。和HTTP服务器不在同一个网的机子能正常访问服务了,和它在一个 网内的机子也可以正常访问服务了,防火墙本身也能正常访问服务了,没有什么问题了。这种心情,套用 《大话西游》里的一句话,就是“世界又清净了”。(不要说你不知道什么是《大话西游》)

我想大家应该能明白这些规则只说明了数据包是如何恰当地被DNAT和SNAT的。除此之外,在 filter表中还需要其他的规则(在FORWARD链里),以允许特定的包也能经过前面写的(在POSTROUTING链和 OUTPUT链里的)规则。千万不要忘了,那些包在到达FORWARD链之前已经在PREROUTING链里被DNAT过了,也 就是说它们的目的地址已被改写,在写规则时要注意这一点。

$RANDOM: generate random integer
www.tldp.org/LDP/abs/html/randomvar.html
Advanced Bash-Scripting Guide:
PrevChapter 9. Variables RevisitedNext

9.6. $RANDOM: generate random integer

$RANDOM is an internal Bash function (not a constant) that returns a pseudorandom [1] integer in the range 0 - 32767. It should not be used to generate an encryption key.

Example 9-25. Generating random numbers

#!/bin/bash

# $RANDOM returns a different random integer at each invocation.
# Nominal range: 0 - 32767 (signed 16-bit integer).

MAXCOUNT=10
count=1

echo
echo "$MAXCOUNT random numbers:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ]      # Generate 10 ($MAXCOUNT) random integers.
do
  number=$RANDOM
  echo $number
  let "count += 1"  # Increment count.
done
echo "-----------------"

# If you need a random int within a certain range, use the 'modulo' operator.
# This returns the remainder of a division operation.

RANGE=500

echo

number=$RANDOM
let "number %= $RANGE"
#           ^^
echo "Random number less than $RANGE  ---  $number"

echo



#  If you need a random integer greater than a lower bound,
#+ then set up a test to discard all numbers below that.

FLOOR=200

number=0   #initialize
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
done
echo "Random number greater than $FLOOR ---  $number"
echo

   # Let's examine a simple alternative to the above loop, namely
   #       let "number = $RANDOM + $FLOOR"
   # That would eliminate the while-loop and run faster.
   # But, there might be a problem with that. What is it?



# Combine above two techniques to retrieve random number between two limits.
number=0   #initialize
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
  let "number %= $RANGE"  # Scales $number down within $RANGE.
done
echo "Random number between $FLOOR and $RANGE ---  $number"
echo



# Generate binary choice, that is, "true" or "false" value.
BINARY=2
T=1
number=$RANDOM

let "number %= $BINARY"
#  Note that    let "number >>= 14"    gives a better random distribution
#+ (right shifts out everything except last binary digit).
if [ "$number" -eq $T ]
then
  echo "TRUE"
else
  echo "FALSE"
fi  

echo


# Generate a toss of the dice.
SPOTS=6   # Modulo 6 gives range 0 - 5.
          # Incrementing by 1 gives desired range of 1 - 6.
          # Thanks, Paulo Marcel Coelho Aragao, for the simplification.
die1=0
die2=0
# Would it be better to just set SPOTS=7 and not add 1? Why or why not?

# Tosses each die separately, and so gives correct odds.

    let "die1 = $RANDOM % $SPOTS +1" # Roll first one.
    let "die2 = $RANDOM % $SPOTS +1" # Roll second one.
    #  Which arithmetic operation, above, has greater precedence --
    #+ modulo (%) or addition (+)?


let "throw = $die1 + $die2"
echo "Throw of the dice = $throw"
echo


exit 0

Example 9-26. Picking a random card from a deck

#!/bin/bash
# pick-card.sh

# This is an example of choosing random elements of an array.


# Pick a card, any card.

Suites="Clubs
Diamonds
Hearts
Spades"

Denominations="2
3
4
5
6
7
8
9
10
Jack
Queen
King
Ace"

# Note variables spread over multiple lines.


suite=($Suites)                # Read into array variable.
denomination=($Denominations)

num_suites=${#suite[*]}        # Count how many elements.
num_denominations=${#denomination[*]}

echo -n "${denomination[$((RANDOM%num_denominations))]} of "
echo ${suite[$((RANDOM%num_suites))]}


# $bozo sh pick-cards.sh
# Jack of Clubs


# Thank you, "jipe," for pointing out this use of $RANDOM.
exit 0

Jipe points out a set of techniques for generating random numbers within a range.
#  Generate random number between 6 and 30.
   rnumber=$((RANDOM%25+6))	

#  Generate random number in the same 6 - 30 range,
#+ but the number must be evenly divisible by 3.
   rnumber=$(((RANDOM%30/3+1)*3))

#  Note that this will not work all the time.
#  It fails if $RANDOM returns 0.

#  Frank Wang suggests the following alternative:
   rnumber=$(( RANDOM%27/3*3+6 ))

Bill Gradwohl came up with an improved formula that works for positive numbers.
rnumber=$(((RANDOM%(max-min+divisibleBy))/divisibleBy*divisibleBy+min))

Here Bill presents a versatile function that returns a random number between two specified values.

Example 9-27. Random between values

#!/bin/bash
# random-between.sh
# Random number between two specified values. 
# Script by Bill Gradwohl, with minor modifications by the document author.
# Used with permission.


randomBetween() {
   #  Generates a positive or negative random number
   #+ between $min and $max
   #+ and divisible by $divisibleBy.
   #  Gives a "reasonably random" distribution of return values.
   #
   #  Bill Gradwohl - Oct 1, 2003

   syntax() {
   # Function embedded within function.
      echo
      echo    "Syntax: randomBetween [min] [max] [multiple]"
      echo
      echo    "Expects up to 3 passed parameters, but all are completely optional."
      echo    "min is the minimum value"
      echo    "max is the maximum value"
      echo    "multiple specifies that the answer must be a multiple of this value."
      echo    "    i.e. answer must be evenly divisible by this number."
      echo    
      echo    "If any value is missing, defaults area supplied as: 0 32767 1"
      echo    "Successful completion returns 0, unsuccessful completion returns"
      echo    "function syntax and 1."
      echo    "The answer is returned in the global variable randomBetweenAnswer"
      echo    "Negative values for any passed parameter are handled correctly."
   }

   local min=${1:-0}
   local max=${2:-32767}
   local divisibleBy=${3:-1}
   # Default values assigned, in case parameters not passed to function.

   local x
   local spread

   # Let's make sure the divisibleBy value is positive.
   [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy))

   # Sanity check.
   if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o  ${min} -eq ${max} ]; then 
      syntax
      return 1
   fi

   # See if the min and max are reversed.
   if [ ${min} -gt ${max} ]; then
      # Swap them.
      x=${min}
      min=${max}
      max=${x}
   fi

   #  If min is itself not evenly divisible by $divisibleBy,
   #+ then fix the min to be within range.
   if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then 
      if [ ${min} -lt 0 ]; then
         min=$((min/divisibleBy*divisibleBy))
      else
         min=$((((min/divisibleBy)+1)*divisibleBy))
      fi
   fi

   #  If max is itself not evenly divisible by $divisibleBy,
   #+ then fix the max to be within range.
   if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then 
      if [ ${max} -lt 0 ]; then
         max=$((((max/divisibleBy)-1)*divisibleBy))
      else
         max=$((max/divisibleBy*divisibleBy))
      fi
   fi

   #  ---------------------------------------------------------------------
   #  Now, to do the real work.

   #  Note that to get a proper distribution for the end points,
   #+ the range of random values has to be allowed to go between
   #+ 0 and abs(max-min)+divisibleBy, not just abs(max-min)+1.

   #  The slight increase will produce the proper distribution for the
   #+ end points.

   #  Changing the formula to use abs(max-min)+1 will still produce
   #+ correct answers, but the randomness of those answers is faulty in
   #+ that the number of times the end points ($min and $max) are returned
   #+ is considerably lower than when the correct formula is used.
   #  ---------------------------------------------------------------------

   spread=$((max-min))
   [ ${spread} -lt 0 ] && spread=$((0-spread))
   let spread+=divisibleBy
   randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min))   

   return 0

   #  However, Paulo Marcel Coelho Aragao points out that
   #+ when $max and $min are not divisible by $divisibleBy,
   #+ the formula fails.
   #
   #  He suggests instead the following formula:
   #    rnumber = $(((RANDOM%(max-min+1)+min)/divisibleBy*divisibleBy))

}

# Let's test the function.
min=-14
max=20
divisibleBy=3


#  Generate an array of expected answers and check to make sure we get
#+ at least one of each answer if we loop long enough.

declare -a answer
minimum=${min}
maximum=${max}
   if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then 
      if [ ${minimum} -lt 0 ]; then
         minimum=$((minimum/divisibleBy*divisibleBy))
      else
         minimum=$((((minimum/divisibleBy)+1)*divisibleBy))
      fi
   fi


   #  If max is itself not evenly divisible by $divisibleBy,
   #+ then fix the max to be within range.

   if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then 
      if [ ${maximum} -lt 0 ]; then
         maximum=$((((maximum/divisibleBy)-1)*divisibleBy))
      else
         maximum=$((maximum/divisibleBy*divisibleBy))
      fi
   fi


#  We need to generate only positive array subscripts,
#+ so we need a displacement that that will guarantee
#+ positive results.

displacement=$((0-minimum))
for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
   answer[i+displacement]=0
done


# Now loop a large number of times to see what we get.
loopIt=1000   #  The script author suggests 100000,
              #+ but that takes a good long while.

for ((i=0; i<${loopIt}; ++i)); do

   #  Note that we are specifying min and max in reversed order here to
   #+ make the function correct for this case.

   randomBetween ${max} ${min} ${divisibleBy}

   # Report an error if an answer is unexpected.
   [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] && echo MIN or MAX error - ${randomBetweenAnswer}!
   [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] && echo DIVISIBLE BY error - ${randomBetweenAnswer}!

   # Store the answer away statistically.
   answer[randomBetweenAnswer+displacement]=$((answer[randomBetweenAnswer+displacement]+1))
done



# Let's check the results

for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do
   [ ${answer[i+displacement]} -eq 0 ] && echo "We never got an answer of $i." || echo "${i} occurred ${answer[i+displacement]} times."
done


exit 0

Just how random is $RANDOM? The best way to test this is to write a script that tracks the distribution of "random" numbers generated by $RANDOM. Let's roll a $RANDOM die a few times . . .

Example 9-28. Rolling a single die with RANDOM

#!/bin/bash
# How random is RANDOM?

RANDOM=$$       # Reseed the random number generator using script process ID.

PIPS=6          # A die has 6 pips.
MAXTHROWS=600   # Increase this if you have nothing better to do with your time.
throw=0         # Throw count.

ones=0          #  Must initialize counts to zero,
twos=0          #+ since an uninitialized variable is null, not zero.
threes=0
fours=0
fives=0
sixes=0

print_result ()
{
echo
echo "ones =   $ones"
echo "twos =   $twos"
echo "threes = $threes"
echo "fours =  $fours"
echo "fives =  $fives"
echo "sixes =  $sixes"
echo
}

update_count()
{
case "$1" in
  0) let "ones += 1";;   # Since die has no "zero", this corresponds to 1.
  1) let "twos += 1";;   # And this to 2, etc.
  2) let "threes += 1";;
  3) let "fours += 1";;
  4) let "fives += 1";;
  5) let "sixes += 1";;
esac
}

echo


while [ "$throw" -lt "$MAXTHROWS" ]
do
  let "die1 = RANDOM % $PIPS"
  update_count $die1
  let "throw += 1"
done  

print_result

exit 0

#  The scores should distribute fairly evenly, assuming RANDOM is fairly random.
#  With $MAXTHROWS at 600, all should cluster around 100, plus-or-minus 20 or so.
#
#  Keep in mind that RANDOM is a pseudorandom generator,
#+ and not a spectacularly good one at that.

#  Randomness is a deep and complex subject.
#  Sufficiently long "random" sequences may exhibit
#+ chaotic and other "non-random" behavior.

# Exercise (easy):
# ---------------
# Rewrite this script to flip a coin 1000 times.
# Choices are "HEADS" and "TAILS".

As we have seen in the last example, it is best to "reseed" the RANDOM generator each time it is invoked. Using the same seed for RANDOM repeats the same series of numbers. [2] (This mirrors the behavior of the random() function in C.)

Example 9-29. Reseeding RANDOM

#!/bin/bash
# seeding-random.sh: Seeding the RANDOM variable.

MAXCOUNT=25       # How many numbers to generate.

random_numbers ()
{
count=0
while [ "$count" -lt "$MAXCOUNT" ]
do
  number=$RANDOM
  echo -n "$number "
  let "count += 1"
done  
}

echo; echo

RANDOM=1          # Setting RANDOM seeds the random number generator.
random_numbers

echo; echo

RANDOM=1          # Same seed for RANDOM...
random_numbers    # ...reproduces the exact same number series.
                  #
                  # When is it useful to duplicate a "random" number series?

echo; echo

RANDOM=2          # Trying again, but with a different seed...
random_numbers    # gives a different number series.

echo; echo

# RANDOM=$$  seeds RANDOM from process id of script.
# It is also possible to seed RANDOM from 'time' or 'date' commands.

# Getting fancy...
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
#  Pseudo-random output fetched
#+ from /dev/urandom (system pseudo-random device-file),
#+ then converted to line of printable (octal) numbers by "od",
#+ finally "awk" retrieves just one number for SEED.
RANDOM=$SEED
random_numbers

echo; echo

exit 0

The /dev/urandom device-file provides a method of generating much more "random" pseudorandom numbers than the $RANDOM variable. dd if=/dev/urandom of=targetfile bs=1 count=XX creates a file of well-scattered pseudorandom numbers. However, assigning these numbers to a variable in a script requires a workaround, such as filtering through od (as in above example and Example 12-13), or using dd (see Example 12-55), or even piping to md5sum (see Example 33-14).

There are also other ways to generate pseudorandom numbers in a script. Awk provides a convenient means of doing this.

Example 9-30. Pseudorandom numbers, using awk

#!/bin/bash
# random2.sh: Returns a pseudorandom number in the range 0 - 1.
# Uses the awk rand() function.

AWKSCRIPT=' { srand(); print rand() } '
#            Command(s) / parameters passed to awk
# Note that srand() reseeds awk's random number generator.


echo -n "Random number between 0 and 1 = "

echo | awk "$AWKSCRIPT"
# What happens if you leave out the 'echo'?

exit 0


# Exercises:
# ---------

# 1) Using a loop construct, print out 10 different random numbers.
#      (Hint: you must reseed the "srand()" function with a different seed
#+     in each pass through the loop. What happens if you fail to do this?)

# 2) Using an integer multiplier as a scaling factor, generate random numbers 
#+   in the range between 10 and 100.

# 3) Same as exercise #2, above, but generate random integers this time.

The date command also lends itself to generating pseudorandom integer sequences.

Notes

[1]

True "randomness," insofar as it exists at all, can only be found in certain incompletely understood natural phenomena such as radioactive decay. Computers can only simulate randomness, and computer-generated sequences of "random" numbers are therefore referred to as pseudorandom.

[2]

The seed of a computer-generated pseudorandom number series can be considered an identification label. For example, think of the pseudorandom series with a seed of 23 as series #23.

A property of a pseurandom number series is the length of the cycle before it starts repeating itself. A good pseurandom generator will produce series with very long cycles.


PrevHomeNext
Indirect References to VariablesUpThe Double Parentheses Construct

mesa compile fails: glxcmds.c: In function `glXBindTexImageEXT':

Emerge =app-emulation/emul-linux-x86-xlibs-7.0-r1, then

eselect opengl set ati

(for details please see http://forums.gentoo.org/viewtopic-p-3151471.html#3151471 and http://bugs.gentoo.org/show_bug.cgi?id=109922)

 LumaQQ 开发者文档

纯真IP数据库格式详解

摘要
网络上的IP数据库以纯真版的最为流行,LumaQQ也采用了纯真版IP数据库做为IP查询功能的基础。不过关于其格式的文档却非常之少,后来终于在网上找到了一份文档,得以了解其内幕,不过那份文档寥寥数语,也是颇为耐心才读明白。在这里我重写一份,以此做为LumaQQ开发者文档的一部分,我想还是必要的。本文详细介绍了纯真IP数据库的格式,并且给出了一些Demo以供参考。

Luma, 清华大学
修改日期: 2005/01/14

Note: 在此感谢纯真IP数据库作者金狐和那唯一一份文档的作者。

修改历史:
2005-01-14 修改了原来一些表达不清和错误的地方


自从有了IP数据库这种东西,QQ外挂的显示IP功能也随之而生,本人见识颇窄,是否还有其他应用不得而知,不过,IP数据库确实是个不错的东西。如今网络上最流行的IP数据库我想应该是纯真版的(说错了也不要扁我),迄今为止其IP记录条数已经接近30000,对于有些IP甚至能精确到楼层,不亦快哉。2004年4、5月间,正逢LumaQQ破土动工,为了加上这个人人都喜欢,但是好像人人都不知道为什么喜欢的显IP功能,我也采用了纯真版IP数据库,它的优点是记录多,查询速度快,它只用一个文件QQWry.dat就包含了所有记录,方便嵌入到其他程序中,也方便升级。

基本结构

QQWry.dat文件在结构上分为3块:文件头,记录区,索引区。一般我们要查找IP时,先在索引区查找记录偏移,然后再到记录区读出信息。由于记录区的记录是不定长的,所以直接在记录区中搜索是不可能的。由于记录数比较多,如果我们遍历索引区也会是有点慢的,一般来说,我们可以用二分查找法搜索索引区,其速度比遍历索引区快若干数量级。图1是QQWry.dat的文件结构图。



图1. QQWry.dat文件结构

要注意的是,QQWry.dat里面全部采用了little-endian字节序

一. 了解文件头

QQWry.dat的文件头只有8个字节,其结构非常简单,首四个字节是第一条索引的绝对偏移,后四个字节是最后一条索引的绝对偏移。

二. 了解记录区

每条IP记录都由国家和地区名组成,国家地区在这里并不是太确切,因为可能会查出来“清华大学计算机系”之类的,这里清华大学就成了国家名了,所以这个国家地区名和IP数据库制作的时候有关系。所以记录的格式有点像QName,有一个全局部分和局部部分组成,我们这里还是沿用国家名和地区名的说法。

于是我们想象着一条记录的格式应该是: [IP地址][国家名][地区名],当然,这个没有什么问题,但是这只是最简单的情况。很显然,国家名和地区名可能会有很多的重复,如果每条记录都保存一个完整的名称拷贝是非常不理想的,所以我们就需要重定向以节省空间。所以为了得到一个国家名或者地区名,我们就有了两个可能:第一就是直接的字符串表示的国家名,第二就是一个4字节的结构,第一个字节表明了重定向的模式,后面3个字节是国家名或者地区名的实际偏移位置。对于国家名来说,情况还可能更复杂些,因为这样的重定向最多可能有两次。

那么什么是重定向模式?根据上面所说,一条记录的格式是[IP地址][国家记录][地区记录],如果国家记录是重定向的话,那么地区记录是有可能没有的,于是就有了两种情况,我管他叫做模式1和模式2。我们对这些格式的情况举图说明:



图2. IP记录的最简单形式

图2表示了最简单的IP记录格式,我想没有什么可以解释的



图3. 重定向模式1

图3演示了重定向模式1的情况。我们看到在模式1的情况下,地区记录也跟着国家记录走了,在IP地址之后只剩下了国家记录的4字节,后面3个字节构成了一个指针,指向了实际的国家名,然后又跟着地址名。模式1的标识字节是0x01。



图4. 重定向模式2

图4演示了重定向模式2的情况。我们看到了在模式2的情况下(其标识字节是0x02),地区记录没有跟着国家记录走,因此在国家记录之后4个字节之后还是有地区记录。我想你已经明白了模式1和模式2的区别,即:模式1的国家记录后面不会再有地区记录,模式2的国家记录后会有地区记录。下面我们来看一下更复杂的情况。



图5. 混和情况1

图5演示了当国家记录为模式1的时候可能出现的更复杂情况,在这种情况下,重定向指向的位置仍然是个重定向,不过第二次重定向为模式2。大家不用担心,没有模式3了,这个重定向也最多只有两次,并且如果发生了第二次重定向,则其一定为模式2,而且这种情况只会发生在国家记录上,对于地区记录,模式1和模式2是一样的,地区记录也不会发生2次重定向。不过,这个图还可以更复杂,如图7:



图6. 混和情况2

图6是模式1下最复杂的混和情况,不过我想应该也很好理解,只不过地区记录也来重定向而已,有一点我要提醒你,如果重定向的地址是0,则表示未知的地区名。

所以我们总结如下:一条IP记录由[IP地址][国家记录][地区记录]组成,对于国家记录,可以有三种表示方式:字符串形式,重定向模式1和重定向模式2。对于地区记录,可以有两种表示方式:字符串形式和重定向,另外有一条规则:重定向模式1的国家记录后不能跟地区记录。按照这个总结,在这些方式中合理组合,就构成了IP记录的所有可能情况。

设计的理由

在我们继续去了解索引区的结构之前,我们先来了解一下为何记录区的结构要如此设计。我想你可能想到了答案:字符串重用。没错,在这种结构下,对于一个国家名和地区名,我只需要保存其一次就可以了。我们举例说明,为了表示方便,我们用小写字母代表IP记录,C表示国家名,A表示地区名:

  1. 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2, A1 = A2,那么我们就可以使用图3显示的结构来实现重用
  2. 有三条记录a(C1, A1), b(C2, A2), c(C3, A3),如果C1 = C2, A2 = A3,现在我们想存储记录b,那么我们可以用图6的结构来实现重用
  3. 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2,现在我们想存储记录b,那么我们可以采用模式2表示C2,用字符串表示A2

你可以举出更多的情况,你也会发现在这种结构下,不同的字符串只需要存储一次。

了解索引区

在"了解文件头"部分,我们说明了文件头实际上是两个指针,分别指向了第一条索引和最后一条索引的绝对偏移。如图8所示:



图8. 文件头指向索引区图示

实在是很简单,不是吗?从文件头你就可以定位到索引区,然后你就可以开始搜索IP了!每条索引长度为7个字节,前4个字节是起始IP地址,后三个字节就指向了IP记录。这里有些概念需要说明一下,什么是起始IP,那么有没有结束IP? 假设有这么一条记录:166.111.0.0 - 166.111.255.255,那么166.111.0.0就是起始IP,166.111.255.255就是结束IP,结束IP就是IP记录中的那头4个字节,这下你应该就清楚了吧。于是乎,每条索引配合一条记录,构成了一个IP范围,如果你要查找166.111.138.138所在的位置,你就会发现166.111.138.138落在了166.111.0.0 - 166.111.255.255 这个范围内,那么你就可以顺着这条索引去读取国家和地区名了。那么我们给出一个最详细的图解吧:



图9. 文件详细结构

现在一切都清楚了是不是?也许还有一点你不清楚,QQWry.dat的版本信息存在哪里呢? 答案是:最后一条IP记录实际上就是版本信息,最后一条记录显示出来就是这样:255.255.255.0 255.255.255.255 纯真网络 2004年6月25日IP数据。OK,到现在你应该全部清楚了。

Demo

下一步:我给出一个读取IP记录的程序片断,此片断摘录自LumaQQ源文件edu.tsinghua.lumaqq.IPSeeker.java,如果你有兴趣,可以下载源代码详细看看。

	/**
	 * 给定一个ip国家地区记录的偏移,返回一个IPLocation结构
	 * @param offset 国家记录的起始偏移
	 * @return IPLocation对象
	 */
	private IPLocation getIPLocation(long offset) {
		try {
			// 跳过4字节ip
			ipFile.seek(offset + 4);
			// 读取第一个字节判断是否标志字节
			byte b = ipFile.readByte();
			if(b == REDIRECT_MODE_1) {
				// 读取国家偏移
				long countryOffset = readLong3();
				// 跳转至偏移处
				ipFile.seek(countryOffset);
				// 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
				b = ipFile.readByte();
				if(b == REDIRECT_MODE_2) {
					loc.country = readString(readLong3());
					ipFile.seek(countryOffset + 4);
				} else
					loc.country = readString(countryOffset);
				// 读取地区标志
				loc.area = readArea(ipFile.getFilePointer());
			} else if(b == REDIRECT_MODE_2) {
				loc.country = readString(readLong3());
				loc.area = readArea(offset + 8);
			} else {
				loc.country = readString(ipFile.getFilePointer() - 1);
				loc.area = readArea(ipFile.getFilePointer());
			}
			return loc;
		} catch (IOException e) {
			return null;
		}
	}	

	/**
	 * 从offset偏移开始解析后面的字节,读出一个地区名
	 * @param offset 地区记录的起始偏移
	 * @return 地区名字符串
	 * @throws IOException 地区名字符串
	 */
	private String readArea(long offset) throws IOException {
		ipFile.seek(offset);
		byte b = ipFile.readByte();
		if(b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) {
			long areaOffset = readLong3(offset + 1);
			if(areaOffset == 0)
				return LumaQQ.getString("unknown.area");
			else
				return readString(areaOffset);
		} else
			return readString(offset);
	}

	/**
	 * 从offset位置读取3个字节为一个long,因为java为big-endian格式,所以没办法
	 * 用了这么一个函数来做转换
	 * @param offset 整数的起始偏移
	 * @return 读取的long值,返回-1表示读取文件失败
	 */
	private long readLong3(long offset) {
		long ret = 0;
		try {
			ipFile.seek(offset);
			ipFile.readFully(b3);
			ret |= (b3[0] & 0xFF);
			ret |= ((b3[1] << 8) & 0xFF00);
			ret |= ((b3[2] << 16) & 0xFF0000);
			return ret;
		} catch (IOException e) {
			return -1;
		}
	}	
	
	/**
	 * 从当前位置读取3个字节转换成long
	 * @return 读取的long值,返回-1表示读取文件失败
	 */
	private long readLong3() {
		long ret = 0;
		try {
			ipFile.readFully(b3);
			ret |= (b3[0] & 0xFF);
			ret |= ((b3[1] << 8) & 0xFF00);
			ret |= ((b3[2] << 16) & 0xFF0000);
			return ret;
		} catch (IOException e) {
			return -1;
		}
	}

	/**
	 * 从offset偏移处读取一个以0结束的字符串
	 * @param offset 字符串起始偏移
	 * @return 读取的字符串,出错返回空字符串
	 */
	private String readString(long offset) {
		try {
			ipFile.seek(offset);
			int i;
			for(i = 0, buf[i] = ipFile.readByte(); buf[i] != 0; buf[++i] = ipFile.readByte());
			if(i != 0) 
			    return Utils.getString(buf, 0, i, "GBK");
		} catch (IOException e) {			
		    log.error(e.getMessage());
		}
		return "";
	}

代码并不复杂,getIPLocation是主要方法,它检查国家记录格式,并针对字符串形式,模式1,模式2采用不同的代码,readArea则相对简单,因为只有字符串和重定向两种情况需要处理。

总结

纯真IP数据库的结构使得查找IP简单迅速,不过你想要编辑它却是比较麻烦的,我想应该需要专门的工具来生成QQWry.dat文件,由于其文件格式的限制,你要直接添加IP记录就不容易了。不过,能查到IP已经很开心了,希望纯真记录越来越多~。

LumaQQ is a Java QQ client which has a reusable pure Java core and SWT-based GUI

黑羽翔天 § Kingda's Weblog: 完美解决MT3.2数据库转移及备份乱码问题!!
www.kingda.org/archives/kingda/2006/04/mt32_1.html

完美解决MT3.2数据库转移及备份乱码问题!!

MovableType 3.2迁移涉及三个部分
1.mysqldump 导出时的charset转换
2.新建数据库时设置默认charset为utf8,导入数据库
3.在MT的cgi-bin的lib中DBI中的mysql.pm中加上set names指定MT默认提交的charset设定。

设计的细节较多,休息一会儿再写。 :)
补完 :)

事件:
将MT3.2从服务器A转移到服务器B。
我从电脑A导出mt数据库为sql文件,导入到电脑B,发现部分出现乱码。乱码频率高,不可忍受。

MT3.2数据库乱码原因:
服务器A和B上的MySql的MT数据库默认编码都是latin1。
意味着我们在MT上提交的数据都从默认的UTF8转成了latin1,并存储到数据库中。读取时,又从latin1转到了utf8,所以转移之前我们感觉不到问题。
但是此时,查看MySql中mt数据库mt_entry等含有中文字的表,都发现将是乱码。
当然除了这个还有更深层的原因。请看解决步骤。

1.导出服务器A上的mt数据库
点击开始, 选择“运行”, 键入cmd,进入命令行模式。
用cd命令进入mysql\bin,执行
mysqldump -uroot -p --default-character-set=latin1 --set-charset=utf8 --skip-opt mt > mt_backup.sql
(此处mt为mt数据库名,如果你的不同,请相应更改)
提示密码,输入,ok.
copy目录下mt_backup.sql到服务器B的mysql\bin目录下。

2.服务器B上新装mt3.2,建出数据库mt。删除数据库mt。新建数据库mt,选择默认编码为utf8。
这样的好处是,即使在phpMyadmin中查看也不是乱码了.
mysqldump -uroot -p --default-character-set=utf8 mt < mt_backup.sql
数据库导入成功。

3.将mt_blog表中一些原服务器A中的路径修改为服务器B中mt blog发布的路径。
这里很重要,注意不要弄错。

4.以上都弄完后,发现mt还是乱码,而且还乱的更厉害了,这是因为mt与后台建立连接时还是使用默认的latin1发起连接。导致了又一次的不必要转换。
解决办法,在mt所在的cgi-bin目录下的\lib\MT\ObjectDriver\DBI里面找到mysql.pm,用编辑器打开
找到sub init,在最后一句$driver;之前加上
$driver->{dbh}->do("SET NAMES 'utf8'");
然后再试试重建blog页面吧。
乌拉,一切OK。 Blog页面&PHPmyadmin中都正常显示中文字了。 ^_^

sub init {
    my $driver = shift;
    $driver->SUPER::init(@_);
    my $cfg = $driver->cfg;
    my $dsn = 'dbi:mysql:database=' . $cfg->Database;
    $dsn .= ';hostname=' . $cfg->DBHost if $cfg->DBHost;
    $dsn .= ';mysql_socket=' . $cfg->DBSocket if $cfg->DBSocket;
    $dsn .= ';port=' . $cfg->DBPort if $cfg->DBPort;
    $driver->{dbh} = DBI->connect($dsn, $cfg->DBUser, $cfg->DBPassword,
        { RaiseError => 0, PrintError => 0 })
        or return $driver->error(MT->translate("Connection error: [_1]",
             $DBI::errstr));
	$driver->{dbh}->do("SET NAMES 'utf8'");
    $driver;
}

参考文献:
http://www.simplicidade.org/notes/archives/2005/12/utf8_and_dbdmys.html
http://www.youthfly.net/blog/blogview.asp?logID=191

The content on this page is provided by a Google Notebook user, and Google assumes no responsibility for this content.