分析the address already in use产生的原因

Posted: 2009年11月5日星期四
最近在维护一个别人写的程序时遇到一个它的程序启动不了的情况,具体情况如下,服务器端按照它的方式不能正常关闭,就用kill命令把它的程序结束掉了,再启动程序时报如下的错误:the address already in use,问了公司的同事,同事也告诉不可能啊,要不就是由什么链入了系统库了,当时我也不懂也没有在意,没有去查,这几天我刚好在学习linux网络编程,也碰到了这个问题,通过查找资料,了解到,并不是什么链入了系统库了,而是下面将要说明的情况:
一般情况认为,server socket已经关闭,理应释放了ip address及port资源,但是事实并不一定是这样的,因为大部分socket连接是基于tcp协议多次握手连接的基础上,以保证数据传输的完整性,我们都知道tcp协议是一种可靠的协议,它有3次握手,四次挥手过程,下面是4次挥手过程
(1)当服务器端处于ESTABLISHED状态时, 服务器欲主动关闭连线,发送FIN至客户端,进入到FIN-WAIT-1状态,等待客户端回应ACK,表示等待确认客户端得知服务器端要关闭连线
(2)当客户端收到服务器端的FIN时,立即回应ACK给服务器端,进入到CLOSE-WAIT状态,表示须等到应用程序没有任何资料要传送给服务器端, 客户端才决定关闭连线
(3)服务器端收到客户端回应的ACK,表示确认客户端得知服务器要关闭连线,此刻等待客户端发送FIN
(4)客户端决定关闭连线,发送FIN至服务器端,进入到LAST-ACK状态,等待服务器端回应ACK,表示等待确认服务器端得知客户端要关闭连线
(5)服务器端收到客户端的FIN后,随即回应ACK,进入TIME_WAIT状态,表示服务器端得知客户端要关闭连线,且等待2MSL时间,以防客户端再次发送FIN
(6)客户端收到服务器端回应的ACK后,进入CLOSED状态,表示客户端已确认服务器端已得知它要关闭连线,才进行关闭连线
(7)服务器端等待2MSL时间,才进入CLOSED状态,关闭连线,并自连线表中移除
在上述的第(5),(7)点,此刻TIME-WAIT的用意在于,虽服务器已确认客户端要关闭连线,且回应了ACK给客户端,但不保证客户端会收到ACK ,一旦ACK遗漏,
客户端会再次发送FIN给服务器端,再次进行确认,所以服务器端须进入TIME-WAIT状态,等待2MSL时间,预防客户端会再次发送FIN,进行连线关闭的确认。

从上面的4次挥手过程得知,我们直接结束游戏的服务器端,它并没有马上释放它的ip及port资源就在于服务器端进入了time_wait状态,有可能客户端没有
收到ack资源,导致不停的发送fin请求,导致直接结束服务器端,导致重启起不来,下面是我用C写的一个程序怎么重现上面所出现的实例,以及怎么避免上面所
出现的情况
1.客户端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
struct sockaddr_in server;
int sock;
char buf[32];
int n;

sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(1);
}

server.sin_family = AF_INET;
server.sin_port = htons(1234);
server.sin_addr.s_addr = inet_addr("127.0.0.1");

if(connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0){
perror("connetc");
exit(1);
}

n = read(sock, buf, sizeof(buf));
if(n < 0) {
perror("read");
return 1;
}

printf("%d, %s\n", n, buf);

close(sock);

return 0;
}

帮上面的代码保存为一个client.c文件,通过下面命令进行编译
gcc -o client client.c
2.重现出现情况的服务器端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;

sock0 = socket(AF_INET, SOCK_STREAM, 0);
if (sock0 < 0) {
perror("socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("bind");
return 1;
}

if (listen(sock0, 5) !=0) {
perror("listen:");
return 1;
}
while (1) {
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
if (sock < 0) {
perror("accept:");
break;
}

printf("accepted connection from %s\n",
inet_ntoa(client.sin_addr));
if(send(sock, "Hello", 5, 0) < 1) {
perror("write:");
return 1;
}

close(sock);
}
close(sock0);

return 0;
}

把上面的程序保存为一个server1.c文件,执行下面的命令进行编译
gcc -o server1 server1.c
3.避免上述情况发生的服务器端

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
int sock0;
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
int sock;
int yes = 1;

sock0 = socket(AF_INET, SOCK_STREAM, 0);
if (sock0 < 0) {
perror("socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
setsockopt(sock0,
SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
if(bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("bind");
return 1;
}

if (listen(sock0, 5) !=0) {
perror("listen:");
return 1;
}
while (1) {
len = sizeof(client);
sock = accept(sock0, (struct sockaddr *)&client, &len);
if (sock < 0) {
perror("accept:");
break;
}

printf("accepted connection from %s\n",
inet_ntoa(client.sin_addr));
if(send(sock, "Hello", 5, 0) < 1) {
perror("write:");
return 1;
}

close(sock);
}
close(sock0);

return 0;
}

把上面的程序保存为一个server2.c文件,执行下面的命令进行编译
gcc -o server2 server2.c

启动服务器端
./server1
在另外一个终端中启动客户端
./clinet
得到如下的内容
5, Hello
在服务器的终端通过ctrl + c结束刚刚起来的服务器端,在一分钟内再启动服务器端,你就可以发现出现下面的错误信息
bind: Address already in use
过1分钟后(Linux系统默认time_wait的时间为60秒)就可以发现可以起来了。
为规避上面的情况,再启动server2,再重复上面的步骤,发现已经没有这种情况了。

上面只是一个具体的实例,并不保证数据的完整性,具体到它们的程序中,具体进行分析。

c开发的中的man使用技巧

Posted: 2009年11月2日星期一
在c语言开发过程中,经常需要进行查找函数的说明,在linux下有man命令,比较容易获取c的标准库函数的说明,在ubuntu下若要查看C语言的函数说明,需要安装manpages-dev包,具体安装方法如下
sudo apt-get install manpages-dev
若对于英文不好的同学,可以再安装一个中文的语言包:manpages-zh,这个语言包并不一定包含所有的函数说明,只是其中的一部分,安装方法如下:
sudo apt-get install manpages-zh
通常对于一些不常用的函数可以直接通过man 函数名,可以得到所需的函数说明,对于函数名跟系统命令相同时,就不会得到你想要的函数说明,具体怎么区分怎么才能得到了我想要的函数说明呢?
通常做法如下所示
1. man -k 函数名 |grep ^函数名
2.通过查到的结果,一般会在后面加一个括号带一个数字,再通过man 刚刚得到的数字 函数名 得到我们想要的函数帮助文档
譬如查看c语言read函数说明
man read |grep ^read
得到的结果如下:
zhou@lingjie:~$ man -k read |grep ^read
read (2) - read from a file descriptor
readahead (2) - perform file readahead into page cache
readahead-list (8) - manual page for readahead-list: 0.20050517.0220
readahead-watch (8) - manual page for readahead-watch: 0.20050517.0220
readdir (2) - read directory entry
readdir (3) - read a directory
readdir_r (3) - read a directory
readelf (1) - Displays information about ELF files.
readline (3readline) - get a line from a user with editing
readlink (1) - display value of a symbolic link
readlink (2) - read value of a symbolic link
readlinkat (2) - read value of a symbolic link relative to a directory ...
readom (1) - read or write data Compact Discs
readprofile (1) - a tool to read kernel profiling information
readv (2) - read or write data into multiple buffers

那么我查看C的函数说明就是如下的命令
man 2 read
就可以得到我们想要的函数说明了。

利用iptables端口映射实现跨多机房内网访问

Posted: 2009年10月9日星期五
公司在外地的移动机房,由于公司采用的电信通网络,公司的机房跟外地的移动机房连接及其不稳定,丢包以及连接中断是家常便饭,由于需要程序需要数据交换,这种网络状况基本上不可能,公司放了一个机器放在一个双线机房用iptables端口转发实现数据交换,进行服务器维护,但是最近公司有个需求需要直接用公司的机器访问,外地移动机房内的内网DB,这台DB没有外网地址就得进行多次转发。由于放在移动机房的只有一个linux机器具有外网IP,而这台机器上也有mysql,占用了3306端口,这就需要把移动机房中的转发机器的其它端口转发到内网DB的3306上,同时为安全起见只允许机房的ip地址访问转发的端口,具体需求拓扑如下所示:

下面是实现过程
在中转机房的机器上执行

iptables -A INPUT -p tcp -s 219.x.x.x -d 220.x.x.x --dport 5300 -j ACCEPT
iptables -A PREROUTING -s 219.x.x.x -d 220.x.x.x -p tcp --dport 5300 -j DNAT --to-destination 221.x.x.x
iptables -A POSTROUTING -p tcp --dport 5300 -j SNAT --to-source 220.x.x.x


在移动机房的机器上执行下面的语句

iptables -A PREROUTING -d 221.x.x.x -p tcp --dport 5300 -j DNAT --to-destination 192.168.0.6:3306
iptables -A POSTROUTING -p tcp --dport 3306 -j SNAT --to-source 192.168.0.5


通过执行上面的语句,公司的机器可以通过直接访问中转机房的220.x.x.x的5300直接访问到移动机房内网的机器

搭建linux ris服务器批量在dell服务器上安装windows 2003

Posted: 2009年10月7日星期三
以前写了一个《使用kickstart批量安装Linux》对于sa来说节省了很大部分时间,现在又一批机器需要安装windows 2003由于有一台linux安装服务器,为节约服务器着想,决定在这台服务器上面安装一个linux ris服务器,其实在网上有一堆文章介绍搭建一个linux ris服务器批量安装windows,对于一般的pc来说照着他们的做也许没有问题很简单就能搭建出来,但是对于服务器而言就比较的麻烦了,首先我们的这批服务器是dell的服务器,安装windows 2003需要dell的安装引导盘,引导盘的作用就是用来加载驱动程序,而网上的文章基本没有介绍有关如何加载驱动?加载驱动有什么技巧以及注意的地方。我也问了我的一些sa的朋友,几个朋友都告诉我他们是一台台手动去安装,问他们原因,他们说配置驱动很麻烦。事实证明他们说的确实是这样,如果你安装的机器是10台以内,我建议你还是别照着我的做了,一台台手动安装比较的快。

本文的中演示的是安装windows 2003 i386版本。并不适用其它版本,对于window 2003之前的版本可以参照linux ris安装文档进行相应的更改,对于vista之后的windows更不适用,vista之后的版本都改成了wds进行批量部署。
实施方案的拓扑图如下所示
          ┌────────┐
          │   Boot Server   │
          │(DHCP TFTP samba)│
          └───┬────┘
                  │
          ┌───┴───┐
          │     Switch    │
          └───┬───┘
                   │
      ┌─────┴──────┐
      │                         │
┌──┴───┐        ┌───┴───┐
│Client(PxE)  │        │Client(PxE)    │
└──────┘        └───────┘
实施步骤
1.配置启动服务器
2.配置安装方案
3.启动待安装服务器,进行系统安装

启动服务器就一个普通的linux pc能够连接上网络,下面说说我要安装的服务器配置
dell 1950:
CPU: E5410*2(2330MHZ)
mem: 2G*4
raid控制卡: PERC 6/i
网卡: NetXtreme II Broadcom
配置启动服务器
启动服务器的目的是帮助待安装机器上启动Red Hat Linux安装程序。启动服务器上需要搭建一个DHCP 服务器和一个TFTP 服务器,一个nfs用来传输网络镜像文件。前者是为了给待安装机器分配IP地址,后者则是提供了一个让待安装机器下载启动镜像的途径。
首先配置DHCP服务
1) 安装DHCP服务器包(RPM包名:dhcpd)。
2)配置服务器IP地址
3) 编辑DHCP服务器配置文件 /etc/dhcp.conf。

ddns-update-style interim;
ignore client-updates;
allow booting;
allow bootp;

subnet 192.168.0.0 netmask 255.255.255.0 {
option routers 192.168.0.1;
option subnet-mask 255.255.255.0;
option domain-name-servers 192.168.0.1;
range dynamic-bootp 192.168.0.128 192.168.0.254;
default-lease-time 21600;
max-lease-time 43200;
next-server 192.168.0.4;
filename "pxelinux.0";
}
}

注:
一般参数基本一样,下面我说以下几个重要,平时作为dhcp服务器,不常用的几个参数
allow booting;allow bootp;这两句话的意思是能作为pxe的启动服务器,给他们分配ip地址,next-server作用是指定tftp服务器的地址,filename,找到tftp服务器后找下载那个启动文件,搭建tftp服务器
4) 给启动服务器配置一个IP,这个IP必须在DHCP服务器定义的子网内。上例中配置的启动服务器的ip地址为192.168.0.4。
5) 启动DHCP服务。
service dhcpd start
配置TFTP服务器(如没有安装xinetd软件包,先安装这个软件包)

1) 安装TFTP服务器包(RPM包名:tftpd)。
2) 编辑TFTP 服务器的配置文件 /etc/xinetd.d/tftp。配置文件如下:
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = dpan
server = /usr/sbin/in.tftpd
server_args = -s /tftpboot -m /tftpboot/rules -vv
disable = no
}

注:
这里选定了/tftpboot为TFTP 服务器的根目录位置。
-m /tftpboot/rules 这里指定一个规则文件,这个文件的作用下文中指出,
-vv的作用是把tftp的log详细输出,log文件为/var/log/message,作用为便于调试。
3)建立/tftpboot/rules文件,文件内容如下:
ri ^[a-z]: # Remove “drive letters”
rg \\ / # Convert backslashes to slashes
rg \# @ # Convert hash marks to @ signs
rg /../ /..no../ # Convert /../ to /..no../
rg A a
rg B b
rg C c
rg D d
rg E e
rg F f
rg G g
rg H h
rg I i
rg J j
rg K k
rg L l
rg M m
rg N n
rg O o
rg P p
rg Q q
rg R r
rg S s
rg T t
rg U u
rg V v
rg W w
rg X x
rg Y y
rg Z z
r ^/(.*) \1

这个文件的作用是把所有的查找大写的内容转为小写,由于windows不区分大小写,故统一改成小写。
4)启动TFTP服务
service xinetd start

由于windows的ris安装是通过windows共享文件的方式进行文件传输后,在linux下可以通过samba实现与windows进行文件共享,进而实现了ris安装
配置samba服务器
1) 安装samba服务器包(RPM包名:samba)。
2)配置服务器IP地址
3) 编辑samba服务器配置文件 /etc/samba/smb.conf

[global]
null passwords = true
workgroup = MYGROUP
server string = Samba Server
security = share
load printers = no
disable spoolss = yes
debug level = 2
log file = /var/log/samba/%m.log
max log size = 0
dns proxy = no
[wininstall]
path = /tftpboot
browsable = true
read only = No
writable = yes
guest ok = Yes

配置启动信息
在windows下可以通过expand解压缩光盘中的文件,由于linux下面没有这个工具,但是有人写了一个叫cabextract的工具可以解压windows光盘中的压缩文件,具体可以在http://www.cabextract.org.uk/下载到你所需的版本,
由于我采用的centos 5.2 32位版,为简单着想我安装的是rpm包,具体操作如下
1)下载
wget http://www.cabextract.org.uk/cabextract-1.2-1.i386.rpm
2)安装
rpm -ivh cabextract-1.2-1.i386.rpm
上传windows 2003的iso镜像或者光盘中的内容到服务器的一个目录,这里我采用上传一个windows 2003的iso镜像到dpan目录下。并把光盘挂载到/tftpboot的win2k3目录下
1)建立win2k3目录
mkdir /tftpboot/win2k3
2)挂载iso文件到/mnt目录下
mount -o loop /dpan/windows2003.iso /mnt
3)为方便我们往后面往里面添加驱动或者添加启动后的脚本文件,我们把光盘中的内容复制到/tftpboot下的win2k3目录下
cp -r /mnt/* /tftpboot/win2k3
4)进入/tftpboot目录,配置windows ris启动文件信息(每行为一个命令)

cd /tftpboot
cabextract win2k3/i386/startrom.n1_
sed -i -e 's/NTLDR/W2K3L/gi' startrom.n12
mv startrom.n12 /tftpdpan/w2k3.0
cabextract win2k3/i386/setupldr.ex_
sed -i -e 's/winnt\.sif/wi2k3\.sif/gi' setupldr.exe
sed -i -e 's/ntdetect\.com/ntdetect\.2k3/gi' setupldr.exe
mv setupldr.exe /tftpdpan/w2k3l
cp win2k3/i386/ntdetect.com /tftpdpan/ntdetect.2k3

5)复制pxe的启动镜像到/tftpboot目录
启动镜像pxelinux.0可以在syslinux安装包里获得。安装好syslinux安装包后,将pxelinux.0复制到/tftpboot
cp /usr/lib/syslinux/pxelinux.0 /tftpboot
6)启动镜像pxelinux.0文件在执行过程中,会读取同目录下pxelinux.cfg文件夹下的配置文件以确定它应该加载什么启动文件。
建立启动配置文件存放文件夹
mkdir /tftpboot/pxelinux.cfg
建立启动配置文件default
default win2k3
label win2k3
kernel w2k3.0

建立应答文件/tftpboot/wi2k3.sif,文件内容如下所示

[data]
AutoPartition = "0"
floppyless = "1"
msdosinitiated = "1"
UnattendedInstall = "Yes"
OriSrc = "\\192.168.0.4\wininstall\win2k3\i386"
OriTyp = "4"
LocalSourceOnCD = "1"
DisableAdminAccountOnDomainJoin = "1"

[SetupData]
OsLoadOptions = "/fastdetect"
SetupSourceDevice = "\Device\LanmanRedirector\192.168.0.4\wininstall\win2k3"

[Unattended]
UnattendMode = "FullUnattended"
FileSystem = "LeaveAlone"
NtUpgrade = "No"
OverwriteOemFilesOnUpgrade = "No"
DriverSigningPolicy = "Ignore"
UpdateInstalledDrivers = "Yes"
ConfirmHardware = "No"
ExtendOEMPartition = "0"
TargetPath = "\WINDOWS"
UnattendSwitch = "Yes"
WaitForReboot = "No"
CrashDumpSetting = "0"
OemSkipEula = "Yes"
OEMSkipWelcome = "1"
InstallFilesPath = "\\192.168.0.4\wininstall\win2k3\i386"
LegacyNIC = "1"

[UserData]
ProductKey = "11111-11111-11111-11111-11111"
FullName = "abc"
OrgName = "xyz"
ComputerName = *

[GuiUnattended]
AdminPassword = 123456
EncryptedAdminPassword = "No"
OEMSkipRegional = "1"
TimeZone = "220"
OemSkipWelcome = "1"

[Identification]
JoinWorkgroup = WORKGROUP

[RemoteInstall]
Repartition = "No"
UseWholeDisk = "No"

[Networking]
InstallDefaultComponents = "Yes"


完成后目录结构如下所示
|-- w2k3l
|-- ntdetect.2k3
|-- pxelinux.0
|-- pxelinux.cfg
|  `-- default
|-- win2k3
|  |-- i386
|  |-- autorun.inf
|  |-- bootfont.bin
|  |-- docs
|  |-- i386
|  |-- $oem$
|  |-- printers
|  |-- readme.htm
|  |-- setup.exe
|  |-- support
|  |-- win51
|  |-- win51ia
|  `-- win51ia.sp1
|-- rules
|-- w2k3.0
`-- wi2k3.sif

经过以上的操作基本可以把dell 1950起来到蓝色的安装界面,要是有不需要任何驱动的机器,基本可以完成安装。由于是服务器,有些特定的硬件需要提前加载好驱动,安装起来比较的麻烦,下面告诉你怎样加载驱动,
首先是NetXtreme II Broadcom网卡驱动,既然是网络安装,那么首先需要加载的一定是网卡驱动,由于Broadcom公司提供了ris的驱动(不是系统下的驱动),具体这可在Broadcom驱动下载页中下载到相应的驱动,我需要的驱动下载地址为:http://www.broadcom.com/docs/driver_download/NXII/win_2k3_RIS-5.0.0.zip
下载解压
wget http://www.broadcom.com/docs/driver_download/NXII/win_2k3_RIS-5.0.0.zip
mkdir -p /tftpdpan/nicdriver
unzip win_2k3_RIS-5.0.0.zip -d /tftpdpan/nicdriver


由于要驱动网卡,需要一个binlserver, BINL读取所有新的与网络接口卡相关的 .inf 文件,并在映像中创建 .pnf 文件。
配置binl server,用来加载网卡驱动
1)服务器下载
wget http://oss.netfarm.it/guides/ris-linux-0.4.tar.gz
2)解压
tar xf ris-linux-0.4.tar.gz
3)执行
cd ris-linux-0.4
./infparser.py /tftpdpan/nicdrivers/Server/W2K3_W2K8/RIS/
./binlsrv.py -d
cp /tftpdpan/nicdrivers/Server/W2K3_W2K8/RIS/b06nd51x.sys /tftpdpan/win2k3/i386
启动被安装服务器,现在就可以加载到网卡驱动了

加载完网卡驱动还有找不到磁盘,需要加载raid磁盘驱动:
通过光盘安装过windows的用户知道在开始时蓝色屏幕上有一个按F6的提示,这个提示是用来通过软盘加载磁盘驱动,这类驱动是windows系统中的驱动,可以下载好windows驱动,解压好驱动放在一个指定的目录,再通过应答文件,一般可以加载上这类驱动,这类驱动加载完毕后不必在系统中再安装这类驱动。加载这类驱动有注意点:文件要有正确的oem数字签名(一般提供驱动的厂商都带数字签名),乱改动这类文件,同时不在配置文件中写明去掉数字签名认证,是不能加载的。
下面通过一个实例说明怎么加载dell的PERC 6/i raid卡的驱动,完成ris的驱动加载
1)下载dell的perc 6/i驱动
wget ftp://ftp.us.dell.com/SAS-RAID/DELL_MULTI-DEVICE_A07_R211422.exe
2)建立加载驱动的目录
mkdir -p /tftpboot/win2k3/\$oem\$/textmode
3)解压驱动文件
unzip DELL_MULTI-DEVICE_A07_R211422.exe -d /tftpboot/win2k3/\$oem\$/textmode
4)配置自动应答文件wi2k3.sif,在文件的末尾加上如下内容
[MassStorageDrivers]
"DELL PERC5 and PERC6/CERC6 RAID Controller Driver (Server 2003 32 bit)"=OEM

[OEMBootFiles]
nodev.inf
oemsetup.inf
percsas.cat
percsas.sys
txtsetup.oem
同时在wi2k3.sif中的[Unattended]段添加如下字段
OemPreInstall = "Yes"
注意了[MassStorageDrivers]段中的字符是编辑txtsetup.oem文件查看PERC_32中后面的字符段得到的结果,[OEMBootFiles]段中下面的配置就是刚刚解压后的所有文件,这样就完成了对raid卡驱动的加载
完成后目录结构如下所示
|-- w2k3l
|-- ntdetect.2k3
|-- pxelinux.0
|-- pxelinux.cfg
|  `-- default
|-- win2k3
|  |-- i386
|  |-- autorun.inf
|  |-- bootfont.bin
|  |-- docs
|  |-- i386
|  |-- $oem$
|    |-- textmode
|    |-- nodev.inf
|    |-- oemsetup.inf
|    |-- percsas.cat
|    |-- percsas.sys
|    `-- txtsetup.oem
|  |-- printers
|  |-- readme.htm
|  |-- setup.exe
|  |-- support
|  |-- win51
|  |-- win51ia
|  `-- win51ia.sp1
|-- rules
|-- w2k3.0
`-- wi2k3.sif
以上就可以完成对raid卡为PERC 6/i 网卡为:NetXtreme II Broadcom的机器批量安装。但安装完成后还需手动打补丁包、安装简单的软件如rar,以及执行一写安全脚本命令等。这样如果一台台去操作,工作量大。
学习了以下某些盗版光盘的制作方法,完成该系统的优化操作(由于这部分已经快是一个成型的windows系统,故建立的这部分文件千万注意了为windows文件格式,最好用windows下的文本编辑器进行编辑操作,用vi编辑的话会有两个后果,一是文件保存是两者的换行符不一致,而如果写了中文的话,两者保存了文件的编码方式不一致,导致执行不了这部分的命令)。
CMDLINES.TXT 这个文件位于$OEM$目录下,安装是当进行到注册组件(也就安装到剩余13分钟)时这个特殊的脚本被安装程序调用
下面是一个典型的
[COMMANDS]
"install.cmd"
"REGEDIT32.EXE /S youhua.reg"
"update.cmd"
这个文件的内容总是以" [COMMANDS]"为起头;总是使用引号(")来标记下面需要执行的每条命令CMDLINES.TXT是一个比较受限制的命令行接口,这就意味着很多传统DOS命令将不能由它进行调用,但是外部命令(比如REGEDIT32.EXE)还是可以调用的.
注意路径问题,这里的youhua.reg、update.cmd和install.cmd的路径为cmdlines.txt的同级目录.在$oem$目录下建立一个youhua.reg和install.cmd文件,youhua.reg文件为注册表优化的一部分参数,install.cmd为打补丁和安装网卡驱动的一些操作,install.cmd为添加网卡驱动的操作,下载网卡驱动,放置在$oem$目录下的DrvInst下,下面为几个文件的具体实例
install.cmd

@ echo off
@ color 0a

@ echo 安装网卡驱动中。。。。。 2>>%HOMEDRIVE%/errlog.txt

@ cd DrvInst 2>>%HOMEDRIVE%/errlog.txt

BDrv5706.msi /qn 2>>%HOMEDRIVE%/errlog.txt

if %errorlevel% EQU 0 (
@ echo 网卡驱动安装成功 2>>%HOMEDRIVE%/errlog.txt
@ cd ..
@ goto win2k3sp2 2>>%HOMEDRIVE%/errlog.txt
) else (
@ echo 网卡驱动安装失败 继续安装WINDOWS_2003_SP2 补丁 2>>%HOMEDRIVE%/errlog.txt
@ cd ..
@ goto win2k3sp2 2>>%HOMEDRIVE%/errlog.txt
)

:win2k3sp2

@echo 安装WINDOWS_2003_SP2补丁中。。。。 2>>%HOMEDRIVE%/errlog.txt

sp2.exe /passive /norestart 2>>%HOMEDRIVE%/errlog.txt

if %errorlevel% EQU 0 (
@ echo WINDOWS_2003_SP2补丁安装成功 2>>%HOMEDRIVE%/errlog.txt
) else (
@ echo WINDOWS_2003_SP2补丁安装失败 详细见errlog日志 2>>%HOMEDRIVE%/errlog.txt
)

youhua.reg

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\netBT\Parameters]
"SMBDeviceEnabled"=dword:0
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]
"RestrictAnonymous"=dword:1
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"SynAttackProtect"=dword:1
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DisableCAD"=dword:1
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DontDisplayLastUserName"=dword:1
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DontDisplayLockedUserId"=dword:3
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
"DontDisplayLastUserName"=dword:1
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp]
"PortNumber"=dword:12345
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp
"PortNumber"=dword:12345

完成后目录结构如下所示
|-- w2k3l
|-- ntdetect.2k3
|-- pxelinux.0
|-- pxelinux.cfg
|  `-- default
|-- win2k3
|  |-- i386
|  |-- autorun.inf
|  |-- bootfont.bin
|  |-- docs
|  |-- i386
|  |-- $oem$
|    |-- install.cmd
|    |-- youhua.reg
|    |-- cmdlines.txt
|    |-- DrvInst
|      |-- BDrv5706.msi
|      |-- 0x0409.ini
|      |-- DrvFiles.cab
|      |-- instmsia.exe
|      |-- instmsiw.exe
|      |-- setup.exe
|      |-- Setup.ini
|  |-- textmode
|      |-- nodev.inf
|      |-- oemsetup.inf
|      |-- percsas.cat
|      |-- percsas.sys
|      `-- txtsetup.oem
|  |-- printers
|  |-- readme.htm
|  |-- setup.exe
|  |-- support
|  |-- win51
|  |-- win51ia
|  `-- win51ia.sp1
|-- rules
|-- w2k3.0
`-- wi2k3.sif
上一步部分不是说有一部分命令不能执行吗,还有办法在应答文件中添加即自动应答的GuiRunOnce段中添加执行脚本,具体就是在第一次登录系统时执行脚本,脚本放置在$oem$下的$1目录里,这个目录里的内容会放在系统安装盘下根目录下,由于windows自动安装中不能自动分区通过GuiRunOnce,使用系统自带的diskpart命令实现自动分区,使用系统自带的cacls等命令实现分区及系统安全
在wi2k3.sif文件中添加如下内容,实现分区和系统安全及其其它命令
[GuiRunOnce]
%systemdrive%\diskpart.cmd
%systemdrive%\setup.cmd
下面是实现后的目录结构
|-- w2k3l
|-- ntdetect.2k3
|-- pxelinux.0
|-- pxelinux.cfg
|  `-- default
|-- win2k3
|  |-- i386
|  |-- autorun.inf
|  |-- bootfont.bin
|  |-- docs
|  |-- i386
|  |-- $oem$
|    |-- install.cmd
|    |-- youhua.reg
|    |-- cmdlines.txt
|    |-- DrvInst
|      |-- BDrv5706.msi
|      |-- 0x0409.ini
|      |-- DrvFiles.cab
|      |-- instmsia.exe
|      |-- instmsiw.exe
|      |-- setup.exe
|      |-- Setup.ini
|  |-- textmode
|      |-- nodev.inf
|      |-- oemsetup.inf
|      |-- percsas.cat
|      |-- percsas.sys
|      `-- txtsetup.oem
|  |-- $1
|      |-- diskpart.cmd
|      |-- diskpart.txt
|      |-- setup.cmd
|  |-- printers
|  |-- readme.htm
|  |-- setup.exe
|  |-- support
|  |-- win51
|  |-- win51ia
|  `-- win51ia.sp1
|-- rules
|-- w2k3.0
`-- wi2k3.sif

下面是几个文件的具体内容
diskpart.cmd

@ echo off
for %%i in (d,e,f,g,h) do mountvol %%i: /D > nul
%windir%\system32\diskpart /s %systemdrive%\diskpart.txt
echo y | format D: /FS:NTFS /V:GAME /Q /X /A:4096
if %errorlevel% EQU 0 (
@ echo format D: success....
) else (
@ echo format D: fail....
)
echo y | format E: /FS:NTFS /V:OTHER /Q /X /A:4096
if %errorlevel% EQU 0 (
@ echo format E: success....
) else (
@ echo format E: fail....
)


diskpart.txt

select disk 0
create partition extended
create partition logical size=30800
assign letter=D
create partition logical
assign letter=E


setup.cmd

@ echo off

@del /f /q %WINDIR%\Web\printers
@del /f /q %WINDIR%\Web\printers\images
@del /f /q %WINDIR%\Web\printers\PrtCabs
@del /f /q %WINDIR%\Help\iisHelp

wmic useraccount where name='administrator' call Rename test
net user dpan 123456 /add /passwordchg:yes /expires:never
net localgroup "Remote Desktop Users" dpan /add
net user epan 654321 /add /passwordchg:yes /expires:never
net localgroup "Remote Desktop Users" epan /add
net localgroup Users dpan /del
Net user epan 654321

Cacls D:\ /e /c /d epan
Cacls C:\ /t /e /c /g epan:f
Cacls E:\ /t /e /c /g epan:f
Cacls C:\ /e /c /d dpan
Cacls D:\ /t /e /c /g dpan:f
Cacls E:\ /e /c /d dpan
Cacls C:\ /e /c /d everyone
Cacls D:\ /e /c /d everyone
Cacls E:\ /e /c /d everyone
Cacls C:\ /e /c /r everyone
Cacls D:\ /e /c /r everyone
Cacls E:\ /e /c /r everyone

del %systemdrive%\diskpart.cmd
del %systemdrive%\diskpart.txt
del %systemdrive%\%0

eaccelerator真的能加速你的php吗?

Posted: 2009年8月3日星期一
经常看到很多介绍安装php的文章中,提到安装一个eaccelerator加速php程序,我按照他们的需求安装了,确实能用了,可是经过我的压力测试,发现他确实没有实现那些文章所谓的加速功能,是我配置错了,还是具体哪有问题,下面介绍一下我的配置情况,对于php的安装,搜索引擎一搜索网上一大堆,我就不累赘了,现在说说我的安装和eaccelerator的过程
php安装软件及版本
nginx/0.8.7
php-5.2.8
eaccelerator-0.9.5.3
eaccelerator安装

/usr/local/bin/phpize
./configure --enable-eaccelerator=shared --with-php-config=/usr/local/php/bin/php-config
make
sudo make install

eaccelerator配置
分成两块,一块是采用eaccelerator加速的就在php.ini文件中加入如下内容

[eaccelerator]
extension_dir = "/usr/local/lib/php/extensions"
extension="/no-debug-non-zts-20060613/eaccelerator.so"
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator_cache"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"

添加cache目录

mkdir /tmp/eaccelerator_cache

另一块就是初始化安装不配置任何参数进行测试
eaccelerator加速测试
ab -c 200 -n 10000 http://www.test.com/phpinfo.php
测试结果

Server Software: nginx/0.8.7
Server Hostname: www.test.com
Server Port: 80

Document Path: /test.php
Document Length: 25 bytes

Concurrency Level: 200
Time taken for tests: 1.523543 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Non-2xx responses: 10000
Total transferred: 2160000 bytes
HTML transferred: 250000 bytes
Requests per second: 6563.65 [#/sec] (mean)
Time per request: 30.471 [ms] (mean)
Time per request: 0.152 [ms] (mean, across all concurrent requests)
Transfer rate: 1384.27 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 1.8 0 15
Processing: 4 22 7.9 24 67
Waiting: 1 18 9.7 20 66
Total: 6 23 7.4 24 67

Percentage of the requests served within a certain time (ms)
50% 24
66% 24
75% 24
80% 25
90% 31
95% 32
98% 41
99% 61
100% 67 (longest request)

不采用eaccelerator加速测试
ab -c 200 -n 10000 http://www.test.com/phpinfo.php
测试结果

Server Software: nginx/0.8.7
Server Hostname: www.test.com
Server Port: 80

Document Path: /test.php
Document Length: 25 bytes

Concurrency Level: 200
Time taken for tests: 1.373767 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Non-2xx responses: 10011
Total transferred: 2162376 bytes
HTML transferred: 250275 bytes
Requests per second: 7279.25 [#/sec] (mean)
Time per request: 27.475 [ms] (mean)
Time per request: 0.137 [ms] (mean, across all concurrent requests)
Transfer rate: 1536.65 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 3.0 2 21
Processing: 4 24 11.4 23 94
Waiting: 2 17 12.9 15 93
Total: 5 26 11.1 26 94

Percentage of the requests served within a certain time (ms)
50% 26
66% 29
75% 31
80% 31
90% 32
95% 34
98% 83
99% 92
100% 94 (longest request)


结果说明:
通过ab的测试,从结果来看(只需要看黑体部分的内容就OK了),Requests per second(每秒事务数),Time per request(平均事务响应时间),Time per request(每个请求实际运行时间的平均值),这3个值在不采用eaccelerator加速的情况下,性能更强
总结:
不知是我的配置和测试的问题?还是在新的php版本中eaccelerator确实出现兼容性问题?

mysql数据类型区别之BLOB与TEXT

Posted: 2009年7月17日星期五
当数据量比较小时我们会选择使用占用空间相对较小的char或varchar数据类型,当数据量相对较大时通常会选择blob或text数据类型来存储他们。他们都可以存储可变数量的文本数据,在TEXT或BLOB列的存储或检索过程中,不存在大小写转换。两者的区别在于,blob能保存存储二进制数据,比如相片,blob也没有字符集概念,并且排序和比较基于列值字节的数值值。TEXT列有一个字符集,并且根据字符集的校对规则对值进行排序和比较。有4种blob类型,分别为:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它们只是可容纳值的最大长度不同。text类型也有相对应的4种数据类型,分别为:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。在大多数方面,可以将BLOB列视为能够足够大的VARBINARY列。同样,可以将TEXT列视为VARCHAR列。BLOB和TEXT在以下几个方面不同于VARBINARY和VARCHAR:
1、当保存或检索BLOB和TEXT列的值时不删除尾部空格。(这与VARBINARY和VARCHAR列相同)。
2、请注意比较时将用空格对TEXT进行扩充以适合比较的对象,正如CHAR和VARCHAR。
3、对于BLOB和TEXT列的索引,必须指定索引前缀的长度。对于CHAR和VARCHAR,前缀长度是可选的。
4、BLOB和TEXT列不能有 默认值。
当对blob和text数据类型执行大量删除操作时存在很到的性能问题,删除操作能给数据表格留下很大的的空洞,以后填入这些空洞的插入操作存在性能问题,下面通过一个具体事例来说明这个空洞问题。

mysql> create table t(id varchar(100), context text);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t values(1, repeat('beijing', 100)),(2, repeat('beijing', 100)),(3, repeat('beijing', 100));
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

再连续执行如下语句16次

mysql> insert into t select * from t;
Query OK, 196608 rows affected (1.20 sec)
Records: 196608 Duplicates: 0 Warnings: 0

退出mysql,在操作系统下查看mysql数据文件夹下的,t.MYD文件大小

[root@backup ~]# du -sh /usr/local/mysql/data/test/t*
12K /usr/local/mysql/data/test/t.frm
268M /usr/local/mysql/data/test/t.MYD
4.0K /usr/local/mysql/data/test/t.MYI

从上面看到数据文件大小为268M。
进入mysql中删除id为1的数据,通过以上数据插入操作,我们知道id为1的数据应该为总数据的1/3。

mysql> delete from t where id=1;
Query OK, 131072 rows affected (1.11 sec)

退出mysql,继续在操作系统下查看mysql数据文件夹下的,t.MYD文件大小

[root@backup ~]# du -sh /usr/local/mysql/data/test/t*
12K /usr/local/mysql/data/test/t.frm
268M /usr/local/mysql/data/test/t.MYD
4.0K /usr/local/mysql/data/test/t.MYI

从上面可以看到数据大小并没有发生变化,这就产生了上面所说的空洞,接下来通过optimize table对表进行碎片整理,避免空洞造成的性能问题

mysql> optimize table t;
+--------+----------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------+----------+----------+----------+
| test.t | optimize | status | OK |
+--------+----------+----------+----------+
1 row in set (0.99 sec)

再次退出mysql,在操作系统下查看mysql数据文件夹下的,t.MYD文件大小

[root@backup ~]# du -sh /usr/local/mysql/data/test/t*
12K /usr/local/mysql/data/test/t.frm
179M /usr/local/mysql/data/test/t.MYD
4.0K /usr/local/mysql/data/test/t.MYI

可以发现表的数据文件变小了,说明空洞所占领的空间已经被回收。对于采用了blob字段或text字段的数据表格,建议定期对表格进行optimize table操作,避免因空洞带来的性能问题。
由于text和blob数据量大,对于text和blob数据类型的检索,一般不建议直接采用mysql对他们进行检索。同时建议吧text和blob数据列分离到单独的表中,这样避免text或blob数据删除所带来的性能问题。

mysql数据类型区别之char与varchar

Posted: 2009年7月15日星期三
也许在使用mysql创建数据表时经常有这样的疑问,是选择char还是varchar,text还是blob数据类型呢?其实选择什么都行,都能完成我们的需求,但是,选择好合适的数据类型不仅能节约磁盘空间还能提高访问速度达到mysql调优的目的。
1、char和varchar
char和varchar都能存储字符串,其中char是固定长度,varchar是可变长度字符类型。下面通过一个实例来来说明两者的数据存储的区别

mysql> create table c(c char(4), v varchar(4));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into c values('ab ', 'ab ');
Query OK, 1 row affected(0.00 sec)

通过在mysql函数cancat,在字符的末尾添加字符的功能可以看出他的存储情况

mysql> select concat(c, '+'), concat(v, '+') from c;
+----------------+----------------+
| concat(c, '+') | concat(v, '+') |
+----------------+----------------+
| ab+ | ab + |
+----------------+----------------+
1 row in set (0.00 sec)

从上可以看出在mysql检索时通常会删除掉char数据类型的空格。
由于char是固定长度,所以他的处理速度比varchar要快得多,但是他比较消耗存储空间,程序还得考虑他末尾的空格问题对于那些长度变化不大,并且对查询速度有较高要求的数据可以考虑使用char数据类型来存储。
下面来说说在不同的存储引擎条件下两者的使用原则
1、myisam存储引擎:建议使用固定长度的char数据类型来代替可变长度的varchar数据类型
2、memory存储引擎:都使用固定长度的数据行存储,因此使用char或varchar都没有关系,两者都被当作char数据类型进行处理
3、innodb存储引擎:建议使用可变的varchar数据类型,理由:对于innodb数据表,内部的行存储格式没有区分固定长度和可变长度列(所有的数据行都指向数据列值的头指针),因此使用固定长度的char数据类型不一定比使用可变数据长度的数据类型性能好,因而,主要的性能因素是数据行使用的存储总量,由于char数据列占用的空间比varchar数据列要大,因此varchar存储的空间要小,对于磁盘io来说越小的空间,处理数据能力越强。