qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

操作系统:进程管理和IO控制

 一、进程管理
  进程管理包括进程控制,进程调度,进程同步与通信,死锁控制四个内容。
  (一)进程控制
  进程是操作系统中运行的基本单位,包括程序段,数据段和进程控制段。操作系统通过进程控制块(PCB)管理进程。每一个PCB唯一标示一个进程。它存储进程的PID,UID,当前状态等信息,以及进程执行某一时刻的寄存器值,并且指向进程的数据段和程序段。OS把所有PCB链接为一个链表。
  进程在刚刚被创建时出于new状态。OS负责申请一块存储空间作为该进程的PCB,在其中填上进程的信息,标示为ready,链接到PCB队列和就绪队列中,此时进程进入"就绪"态。进程调度程序在未来某一时刻将其分配给处理机执行,该进程出于"执行"态,OS将根据PCB中的内容初始化处理机的各个寄存器值,之后进入用户态执行;进程请求I/O或系统调用时将放弃处理机,进入"活动阻塞"态,进程调度程序将当前处理机各寄存器值压栈(存入PCB中,或存入核心栈),将该进程PCB运行队列中移入阻塞队列;当系统调用或I/O完成时被唤醒再次进入活动就绪态,即PCB由阻塞队列链入就绪队列(操作系统如何实现唤醒进程??)。如果在系统调用完成之前,进程被换到外存,相应内存空间被释放,则进程进入"静止阻塞态",这里中级调度程序将和磁盘驱动程序一起,把相应内容拷贝到对换空间,同时释放内存;系统调用完成时若仍在外存中而为未被换入,则出于"静止就绪"态,之后被换入内存可直接进入执行态,或动态就绪态。进程执行完毕后进入"僵死"态,系统将释放内存空间,回收所有资源。
  UNIX系统中提供的系统调用中,fork产生一个与父进程完全相同的子进程,在子进程的地址空间中运行;spawn则从文件中装入一个文件作为子进程,在其地址空间运行;exec则从文件中装一个进程到当前进程地址空间,覆盖当前进程执行。
  (二)进程调度
  这里说的进程调度主要指低级调度,即由调度程序负责完成的,在内存和处理机之间的调度。在文件和内存之间的调度成为高级调度,又叫作业调度,在内存和对换空间的调度称为中级调度,又称对换。低级调度算法主要有:先来先服务FIFO;.短作业优先SJF;.时间片轮转Round Robin;静态优先级调度;多级队列反馈轮转调度。UNIX系统中采用的动态优先级+多级队列反馈轮转调度。具体内容可参见前面的文章
  (三)进程通信
  进程通信有直接通信和间接通信,直接通信是两进程直接交换数据或发送信息,间接通信则是把信息发送到一个中间实体中。根据同步方式,可分为:1.发送进程阻塞,接受进程不阻塞;2.发送进程不阻塞,接受进程阻塞;3.发送进程和接收进程都阻塞。
  进程通信也分为高级通信和低级通信。
  高级通信通常传输数据量较大,包括:消息机制,共享存储区,管道。
  1、消息机制:
  在消息机制里值得注意的是消息缓冲队列方式。发送进程首先申请一块缓冲区,把消息内容和消息首部填入其中,之后调用send系统调用。send将把该进程的消息内容拷贝到消息缓冲队列中;接受进程首先申请接受缓冲区,之后调用receive系统调用,OS负责把消息缓冲队列中的消息拷贝到该进程空间的缓冲区中。
  2、管道通信:
  相当于一个队列形式的一个进程在管道尾写,另一个进程在管道头取,管道分为无名管道和有名管道。无名管道是用pipe函数创建的,只能用于子进程之间的通信,有名管道用mkfifo函数创建用于任意两个进程之间通信,对管道的操作相当于对文件的操作比如open函数打开管道close函数关闭管道等。
  低级通信包括同步和互斥两种,通过信号量实现,主要针对临界区问题。临界区的互斥访问要遵循4个原则:空闲让进,忙则等待,有限等待,让权等待。主要信号量分为0/1信号量,整型信号量,记录型信号量,AND型信号量和信号量集。经典同步问题有生产者-消费者问题,读者-写者问题,哲学家进餐问题。
  信号量:信号量是操作系统提供的管理公有资源的手段,即PV操作。对于独享设备有驱动程序做PV操作。
  p操作的过程是:s=s-1;if(s<0){进入等待队列,自己阻塞进程}
  v操作的过程是:s=s+1;if(s<0){从等待队列取一个进程;取出的进程进入就绪队列,当前进程该干嘛干嘛}
  pv原语不能次序错误,而且必须成对出现。信号量的定义是semaphore mutex;经典同步问题有生产者-消费者问题;读者-写者问题;哲学家进餐问题。
  (四)死锁控制
  由于进程竞争资源或推进顺序非法,可能会造成进程死锁。死锁有四个必要条件:互斥访问,请求保持,非剥夺,环路等待。打破其中任何一个都可以不出现死锁。对于死锁的处理有三类方法:
  1.死锁预防:主要是打破死锁必要条件中的一个。打破请求保持条件,则要求进程在执行之前一次性请求到所有资源才可以执行;打破非剥夺条件,要求进程执行过程中因为缺少资源无法执行时,剥夺所有资源,将其阻塞;打破环路等待条件,则给资源编号,要求进程请求资源的顺序依照编号由高到低进行。
  2.死锁避免:指Dijkstra的银行家算法;
  3.死锁检测消除:指在发生死锁时检测资源分配图中是否有子环,然后将一个或多个进程挂起,消除死锁;检测算法是NP完全问题,CPU代价较大。
  二、输入输出(I/O)管理
  (一)   I/O管理概述
  1.  I/O管理功能
  (1)   动态的纪录各种设备的状态
  (2)   设备分配与回收
  (3)   实施设备驱动和中段处理的工作
  2.  I/O应用接口
  (1)  设备和设备控制器的接口:设备和cpu之间不是直接通信的而是夹着一个设备控制器,设备与设备控制器是靠三根线相连的,数据信号线,控制信号线和状态信号线,数据信号线用于在设备和设备控制器之间传送数据信号,控制信号线传送由设备控制器向I/O设备发送控制信号,状态信号线用于传送设备当前状态的信号。
  (2)  设备控制器:控制一个或多个I/O设备,其基本功能有接收和识别(cpu发的)命令,数据交换(与cpu或与设备数据交换),标示和报告设备的状态(给cpu发)地址识别,数据缓冲,差错控制。
  (3)  设备控制器由三部分组成:设备控制器与处理器的接口(由数据线连接DMR和控制状态寄存器,控制线,和地址线组成),设备控制器与设备的接口(多个设备接口,每个设备接口由数据控制和状态三种信号),I/O逻辑(当cpu启动一个设备时,将启动命令发给I/O逻辑同时通过地址线给I/O逻辑由它进行译码。。译出命令后对所选设备进行控制。所以地址线和控制线是直接跟I/O逻辑相连的。

  3.  I/O通道
  I/O通道是特殊的处理机。它具有执行I/O指令的能力,并通过执行通道程序来控制I/O操作,它的指令单一主要与I/O操作相关的指令,通道没有自己的内存,它和CPU共享内存。通道又分为字节多路通道,数组选择通道,和数组多路通道。
  (1)  数组选择通道:又称告诉通道,在物理上可以连接多个设备,但某一段时间内通道只能选择一个设备进行工作
  (2)  数组多路通道:当某设备进行数据传送时,通道只为该设备服务,当设备在执行寻址等控制性动作时,通道挂起该设备的通道程序,去为其他设备服务。
  (3)  字节多路通道:用于大量低速设备,与设备之间数据传送的基本单位是字节,为一个设备传送一个字节后,又可以为另个设备传送一个字节。数组多路通道传输的基本单位是块。而且一次只能有一个设备在传输数据。
  4.  I/O控制方式
  (1)  程序I/O方式:忙等方式。
  (2)  中段驱动I/O方式:当某进程要启动某个I/O设备工作时,便由cpu向相应的设备控制器发出一条I/O命令,然后立即返回继续执行原来的任务,此时,CPU和 I/O设备并行操作。以字节为单位进行I/O。
  (3)  DMA I/O方式:直接存储器访问方式数据传输的单位是块,数据之间在设备和内存中进行交换,仅块传输的开始和结束时才需要CPU干预。(替代了设备控制 器)。DMA控制器中有四类寄存器:命令寄存器(存cpu发的控制命令或设备的状态),内存地址寄存器,数据寄存器(缓冲数据作用),数据计数器(存本次要读的字节数)。
  (4)  I/O通道控制方式:通道是通过执行通道程序,并与设备控制器共同实现对I/O设备的控制的。通道指令格式为命令
  1)操作码:规定了指令所执行的操作。
  2)内存地址:标明读操作和写操作时的内存首址
  3)计数:表示本条占领所要读或写数据的字节数
  4)通道程序结束位P:用于表示通道程序是否结束
  5)记录结束标志R。
  三、设备管理
  设备管理包括:缓冲管理,设备分配和设备处理三个内容。
  进程I/O一共有四种方式:程序查询方式,程序中断方式,DMA方式,通道控制方式。通道处理器也是一个处理机,但是与CPU相比,它指令较为单一,没有独立的存储空间(使用内存空间)。通道分为字节多路通道,数组多路通道和数组选择通道。
  (一)缓冲管理
  缓冲管理包括单缓冲,双缓冲,循环缓冲和缓冲池。这里需要注意的是缓冲池。它包括输入队列,输出队列和空闲队列,有四种工作方式:
  1.收容输入方式:进程请求向缓冲区输入,从空闲缓冲队列中申请一个缓冲区输入,将之链接在输入队列尾;
  2.提取输入方式:进程要从缓冲区中提取输入,则从输入队列尾部缓冲区中拷贝信息,同时释放空间,将之重新链入空闲队列中;
  3.收入输出方式:进程要想缓冲区输出,从空闲缓冲区队列中申请一个缓冲区输出,将之链接在输出队列尾;
  4.提取输出方式:进程要想缓冲区提取输出,则从输出队列尾部缓冲区中拷贝信息,同时释放空间,将之重新链入空闲队列中。
  (二)设备分配
  操作系统为实现设备分配需要配备的数据结构有:设备使用表,控制器使用表,通道使用表,系统设备表。利用设备独立性的思想分配设备。所谓设备独立性,是指使用逻辑设备名称来请求某种设备,而系统通过某种名称转换方式将其转换成物理设备名称进行分配。实现设备独立性需要再驱动程序之上再增加一层设备独立性软件,负责实现名称和地址的转换,这种转换信息使用逻辑设备表LUT实现,每一个表项存储逻辑设备名,物理设备名,驱动程序入口地址。使用设备独立性方法实现设备分配,具有灵活性和易于设备重定向。
  SPOOLing是设备分配的一个重要例子。它实现了独占设备的共享。SPOOLing系统包括输入井,输出井,输入缓冲,输出缓冲,输入进程和输出进程。进程在请求独占设备输出时,OS要求它先把要输出的内容输入到输入缓冲,之后有输入进程将其输入到输入井中,此时如果处理机要处理这些数据,则从输入井中取数据;独占设备要输出时,由输出进程将输出井中的信息送至输出缓冲,由输出设备输出。这种方式实现了独占设备的共享,如网络打印机。
  (三)设备处理
  设备处理指的是一系列驱动程序。它们是OS与外设的接口,把OS进程发来的抽象的要求转化为对硬件的具体的控制命令,如设置命令控制字,读取设备状态信息,启动设备等。它们对操作系统提供统一的接口,方便用户调用。

posted @ 2014-06-20 13:24 顺其自然EVO 阅读(669) | 评论 (0)编辑 收藏

Linux下LVM的配置详解

 LVM是Logical Volume Manager(逻辑卷管理器)的简写,它为主机提供了更高层次的磁盘存储管理能力。LVM可以帮助系统管理员为应用与用户方便地分配存储空间。在LVM管理下的逻辑卷可以按需改变大小或添加移除。另外,LVM可以为所管理的逻辑卷提供定制的命名标识。因此,使用LVM主要是方便了对存储系统的管理,增加了系统的扩展性。
  一、准备lvm环境
  1.硬盘的准备
  添加了一块硬盘/dev/hdb。
  准备了三个分区,方案如下:容量为100M,仅为了实验准备。
  /dev/hdb1
  /dev/hdb2
  /dev/hdb3
  2.转换分区类型为lvm卷
  fdisk /dev/hdb
  t转换为lvm卷类型
  Device Boot      Start    End      Blocks  Id  System
  /dev/hdb1        1         208      98248+  8e  Linux LVM
  /dev/hdb2        209      416      98280   8e  Linux LVM
  /dev/hdb3        417      624      98280   8e  Linux LVM
  然后w保存并且
  #partprobe       /*使用磁盘分区生效*/
  二、lvm创建过程
  1.从硬盘驱动器分区中创建物理卷(physical volumes-PV)。
  2.从物理卷中创建卷组(volume groups-VG)
  3.从卷组中创建逻辑卷(logical volumes-LV),并分派逻辑卷挂载点,其中只有逻辑卷才可以写数据。
  lvm的最大的特点就是可以动态的调整分区的大小,并且可以随着分区容量的增长而增加磁盘空间的容量。
  LVM配置与创建
  三、LVM的物理卷PV
  1.相关命令
  pvcreate  创建PV
  pvscan    扫描PV
  pvdisplay 显示PV
  pvremove  删除PV
  partprobe
  2.创建物理卷
  如果以上容量不够,可以再添加其它分区到物理卷中。
[root@redhat ~]# pvcreate /dev/hdb1 /dev/hdb2
Physical volume “/dev/hdb1″ successfully created
Physical volume “/dev/hdb2″ successfully created
[root@redhat ~]# pvscan
PV /dev/hdb1         lvm2 [95.95 MB]
PV /dev/hdb2         lvm2 [95.98 MB]
Total: 2 [191.92 MB] / in use: 0 [0   ] / in no VG: 2 [191.92 MB]
[root@redhat ~]# pvdisplay
— NEW Physical volume —
PV Name               /dev/hdb1
VG Name
PV Size               95.95 MB
Allocatable           NO
PE Size (KByte)       0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               2Ni0Tx-oeSy-zGUP-t7KG-Fh22-0BUi-iyPhhQ
— NEW Physical volume —
PV Name               /dev/hdb2
VG Name
PV Size               95.98 MB
Allocatable           NO
PE Size (KByte)       0
Total PE                  0
Free PE                  0
Allocated PE           0
PV UUID               2XLXfY-V3L2-Mtsl-79U4-ovuJ-YaQf-YV9qHs
四、创建LVM的卷组VG
  1.相关命令
  vgcreate   创建VG
  vgscan     扫描VG
  vgdispaly
  vgextend
  vgreduce
  vgchange
  vgremove
  2.创建逻辑卷VG
[root@redhat ~]# vgcreate vg0 /dev/hdb1 /dev/hdb2
Volume group “vg0″ successfully created
[root@redhat ~]# vgscan
Reading all physical volumes.  This may take a while…
Found volume group “vg0″ using metadata type lvm2
[root@redhat ~]# vgdisplay
— Volume group —
VG Name               vg0
System ID
Format                lvm2
Metadata Areas        2
Metadata Sequence No  1
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                0
Open LV               0
Max PV                0
Cur PV                2
Act PV                2
VG Size               184.00 MB
PE Size               4.00 MB   /*分配的块的大小默认为4M*/
Total PE              46
Alloc PE / Size       0 / 0
Free  PE / Size       46 / 184.00 MB
VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH增加VG容量到1TB的方法:
vgcreate -s 16M vg0 /dev/hdb1 /dev/hdb2
  3.删除与添加逻辑卷
  [root@redhat ~]# vgreduce vg0 /dev/hdb2
  Removed “/dev/hdb2″ from volume group “vg0″
  [root@redhat ~]# vgextend vg0 /dev/hdb2
  Volume group “vg0″ successfully extended
  五、创建LVM的逻辑卷LV
  1.相关命令
  lvcreate
  lvscan
  lvdisplay
  lvextend
  lvreduce
  lvremove
  lvresize
  2.创建逻辑卷LV
[root@redhat ~]# lvcreate -L 184M -n data vg0
Logical volume “data” created
[root@redhat ~]# lvscan
ACTIVE     ‘/dev/vg0/data’ [184.00 MB] inherit
[root@redhat ~]# lvdisplay
— Logical volume —
LV Name                /dev/vg0/data
VG Name                vg0
LV UUID                HNKO5d-yRre-qVnP-ZT8D-fXir-XTeM-r6WjDX
LV Write Access        read/write
LV Status              available
# open                 0
LV Size                184.00 MB
Current LE             46
Segments               2
Allocation             inherit
Read ahead sectors     0
Block device           253:0
  六、挂载LVM的逻辑卷LV
  lv的格式化:
  mkfs.ext3 /dev/vg0/data
  mdkir /mnt/lvm
  mount /dev/vg0/data /mnt/lvm
  [root@redhat ~]# ls /mnt/lvm
  lost+found
  [root@redhat ~]# df -T
  文件系统      类型     1K-块        已用     可用 已用% 挂载点
  /dev/hda3     ext3     7625092   2219460   5012040  31% /
  /dev/hda1     ext3      101086     10006     85861  11% /boot
  tmpfs        tmpfs      150108         0    150108   0% /dev/shm
  /dev/mapper/vg0-data
  ext3      182469      5664    167385   4% /mnt/lvm
  七、LVM的容量调整
  LVM的容量调整可以在多个环节进行调整,比如:可以在物理卷上,VG上,以及LV上,都可以进行容量的扩展,这也是LVM它的一个优势所在。
  1.添加物理卷
  首先应卸载在使用过程中的LV,然后必须保证该磁盘的类型是lvm类型,才能添加进来。
[root@redhat ~]# umount /dev/vg0/data
[root@redhat ~]# pvcreate /dev/hdb3
Physical volume “/dev/hdb3″ successfully created
[root@redhat ~]# pvscan
PV /dev/hdb1   VG vg0   lvm2 [92.00 MB / 0    free]
PV /dev/hdb2   VG vg0   lvm2 [92.00 MB / 0    free]
PV /dev/hdb3            lvm2 [95.98 MB]
Total: 3 [279.98 MB] / in use: 2 [184.00 MB] / in no VG: 1 [95.98 MB]
  2.添加VG的容量
  把上面新添加的LVM磁盘加入到vg0卷组中。
[root@redhat ~]# vgextend vg0 /dev/hdb3
Volume group “vg0″ successfully extended
[root@redhat ~]# vgdisplay
— Volume group —
VG Name               vg0
System ID
Format                lvm2
Metadata Areas        3
Metadata Sequence No  5
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                1
Open LV               0
Max PV                0
Cur PV                3
Act PV                3
VG Size               276.00 MB
PE Size               4.00 MB
Total PE              69
Alloc PE / Size       46 / 184.00 MB
Free  PE / Size       23 / 92.00 MB
VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH
  3.添加入LV中VG增珈的容量
  把新加入LVM磁盘的容量加入LV中。
  [root@redhat ~]# lvextend -L +92M /dev/vg0/data
  Extending logical volume data to 276.00 MB
  Logical volume data successfully resized
  [root@redhat ~]# lvscan
  ACTIVE      ‘/dev/vg0/data’ [276.00 MB] inherit
  [root@redhat ~]# resize2fs -f /dev/vg0/data
  resize2fs 1.39 (29-May-2006)
  Resizing the filesystem on /dev/vg0/data to 282624 (1k) blocks.
  The filesystem on /dev/vg0/data is now 282624 blocks long.
  如果不做这一步的话,在实现挂载的时候,发现LV的容量没有真正的加入进LV卷中,因为相关信息写入到了磁盘超级块中。
  4.挂载使用
  [root@redhat ~]# mount /dev/vg0/data /mnt/lvm
  [root@redhat ~]# df
  文件系统               1K-块        已用     可用 已用% 挂载点
  /dev/hda3              7625092   2219468   5012032  31% /
  /dev/hda1               101086     10006     85861  11% /boot
  tmpfs                   150108         0    150108   0% /dev/shm
  /dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
  LVM的卸载
  八、LVM的卸载方法
  如果不想使用LVM的话,可以卸载它, 卸载的方法与分区的删除方法类似,就是最后创建的最先删除。顺序如下:
  先删除LV
  再删除VG
  最后PV
  以前的LVM的分区应用fdisk转换成其它类型的文件系统,当普通分区使用。
  九、LVM的卸载过程
  1.umount取消挂载
[root@redhat ~]# df
文件系统               1K-块        已用     可用 已用% 挂载点
/dev/hda3              7625092   2219468   5012032  31% /
/dev/hda1               101086     10006     85861  11% /boot
tmpfs                   150108         0    150108   0% /dev/shm
/dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
[root@redhat ~]# umount /mnt/lvm
  2.删除LV逻辑卷
  [root@redhat ~]# lvremove /dev/vg0/data
  Do you really want to remove active logical volume “data”? [y/n]: y
  Logical volume “data” successfully removed
  3.删除VG卷组
  [root@redhat ~]# vgchange -a n vg0
  0 logical volume(s) in volume group “vg0″ now active
  说明:把vg0转换成休眠状态,实验中这一步可以不用。
  [root@redhat ~]# vgremove vg0
  Volume group “vg0″ successfully removed
  4.删除PV
[root@redhat ~]# pvscan 查看pv的情况
PV /dev/hdb1         lvm2 [95.95 MB]
PV /dev/hdb2         lvm2 [95.98 MB]
PV /dev/hdb3         lvm2 [95.98 MB]
Total: 3 [287.90 MB] / in use: 0 [0   ] / in no VG: 3 [287.90 MB]
[root@redhat ~]# pvremove /dev/hdb1 /dev/hdb2 /dev/hdb3
Attempt to close device ‘/dev/cdrom’ which is not open.
Labels on physical volume “/dev/hdb1″ successfully wiped
Labels on physical volume “/dev/hdb2″ successfully wiped
Labels on physical volume “/dev/hdb3″ successfully wiped
  5.最后就是用fdisk修改磁盘的类型了。

posted @ 2014-06-20 13:23 顺其自然EVO 阅读(2991) | 评论 (0)编辑 收藏

Mybatis通用操作数据库方法类总结

 在项目中用到myBatis作为orm框架,与spring结合,通常的做法是写一个通用的数据库操作类,包括对数据库的增、删、改、查操作。
  具体的实现类如下:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.SqlSessionUtils;
import org.railway.com.trainplan.entity.QueryResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
/**
* 基础框架的数据访问层抽象实现类myBatis orm框架,
* 所有模块的数据访问层实现类均继承该类。<br>
*
* JDK版本:JDK1.6
* @version 1.0
*/
@Repository
public class BaseDao {
private static final Logger log = Logger.getLogger(BaseDao.class);
@Autowired
public SqlSessionTemplate sqlSession;
/**
* 获取数据库连接对象
* @return
*/
private Connection getConnection(){
Connection connection = SqlSessionUtils.getSqlSession(
sqlSession.getSqlSessionFactory(), sqlSession.getExecutorType(),
sqlSession.getPersistenceExceptionTranslator()).getConnection();
return connection;
}
//=================================以下代码为myBatis实现的常用方法(后缀加BySql)===========================//
/**
* 往库表中插入记录
* @param statementId 调用myBatis的mapper文件的声明段名,
* 规则名:mapper的namespace+"." + 该mapper文件某片段的id
* @param value 要操作的对象
* @return 插入成功的记录数
*/
public int insertBySql(String statementId, Object value) {
return sqlSession.insert(statementId, value);
}
/**
* 删除库表中的记录(可批量删除),返回删除成功的记录数。
*
* @param statementId 调用myBatis的mapper文件的声明段名,
* 规则名:mapper的namespace+"." + 该mapper文件某片段的id
* @param value  删除条件值
* @return 删除成功的记录数
*/
public int deleteBySql(String statementId, Object value) {
return sqlSession.delete(statementId, value);
}
/**
* 更新库表中的记录(可批量更新),返回更新成功的记录数。
*
* @param statementId 调用myBatis的mapper文件的声明段名,
* 规则名:mapper的namespace+"." + 该mapper文件某片段的id
* @param value       更新条件值值
* @return 更新成功的记录数
*/
public int updateBySql(String statementId, Object value) {
return sqlSession.update(statementId, value);
}
/**
* 查询符合条件的记录,生成List返回。
*
* @param statementId 调用myBatis的mapper文件的声明段名,
* 规则名:mapper的namespace+"." + 该mapper文件某片段的id
* @param value       查询条件值
* @return list       找到的记录
*/
public List selectListBySql(String statementId, Object value) {
List list = sqlSession.selectList(statementId, value);
//数据库含有空格的字段值(char类型),装载到myBatis生成的持久对象属性中,是否自动去掉属性值左右两边的空格
//如果需要去掉空格,则使用如下方式
//list = Config.getPropertyBool("po.propertyvalue.trimable", true) ? BeanUtil.trim(list) : list
return list;
}
/**
* 查询单个符合条件的记录,生成Object返回</br>
* @param statementId 调用myBatis的mapper文件的声明段名,
* 规则名:mapper的namespace+"." + 该mapper文件某片段的id
* @param parameter   查询条件值
* @return list 找到的记录
*/
public Object selectOneBySql(String statementId, Object parameter) {
Object bean = sqlSession.selectOne(statementId, parameter);
//数据库含有空格的字段值(char类型),装载到myBatis生成的持久对象属性中,是否自动去掉属性值左右两边的空格
//如果需要去掉空格,则使用如下方式
//bean = Config.getPropertyBool("po.propertyvalue.trimable", true) ? BeanUtil.trim(bean) : bean
return bean;
}
/**
* myBatis分页查询符合条件的记录
* <p>
* 注意:调用该方法,必须在myBatis的mapper文件中存在statementId_COUNT片段</br>
* <b>特别说明:由于该命令采用的分页技术不是数据库本身的分页技术,而是采用ResultSet的absolute定位技术,<br>
* 需要把查询结果全部装入ResultSet再定位。如果查询结果较大(1万条记录以上),效率会很低。<br>
* 建议使用Hibernate的query方法或在mapper的XML中使用数据库内部分页技术<br>
* (即把pageId,pageSize作为参数传入SQL语句的类似limit n,m中)来查询。
* </b>
* </p>
* @param statementId 调用myBatis的mapper文件的声明段名,规则名:mapper的namespace+"." + 该sqlMap文件某片段的id
* @param parameter   查询条件对象
* @param offset       返回查询结果的起始行,从0开始
* @param pageSize        返回查询结果的最大行数
* @throws com.fbd.crm.exception.BaseUncheckedException
* @return com.fbd.crm.common.QueryResult
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public QueryResult selectListForPagingBySql(String statementId, Object parameter) throws Exception{
Assert.hasText(statementId, "传入的SQL配置ID不能为空.");
//计算总页数
Integer count = null;
try {
count = (Integer)sqlSession.selectOne(statementId + "_COUNT", parameter);
} catch (Exception e) {
String msg = "配置文件中不存在COUNT语句或发生其它例外,无法执行总数统计. SqlMap id:" + statementId;
log.error(msg, e);
throw new Exception(e);
}
if ((count == null) || (count.intValue() <= 0)) {
log.info("执行COUNT后,返回的结果数为0,表示该SQL执行无结果数据返回.因此提前终止其数据查询并立即返回空集.");
return new QueryResult(new ArrayList(), 0);
}
List resultList = sqlSession.selectList(statementId, parameter);
QueryResult result = new QueryResult(resultList, count);
return result;
}
/**
* myBatis带汇总查询
* </b>
* </p>
* @param statementId 调用myBatis的mapper文件的声明段名,规则名:mapper的namespace+"." + 该sqlMap文件某片段的id
* @param parameter   查询条件对象
* @throws com.fbd.crm.exception.BaseUncheckedException
* @return com.fbd.crm.common.QueryResult
*/
public QueryResult selectListWithTotal(String statementId, Object parameter) throws Exception{
Assert.hasText(statementId, "传入的SQL配置ID不能为空.");
//计算总页数
Integer count = null;
try {
count = (Integer)sqlSession.selectOne(statementId + "_COUNT", parameter);
} catch (Exception e) {
String msg = "配置文件中不存在COUNT语句或发生其它例外,无法执行总数统计. SqlMap id:" + statementId;
log.error(msg, e);
throw new Exception(e);
}
if ((count == null) || (count.intValue() <= 0)) {
log.info("执行COUNT后,返回的结果数为0,表示该SQL执行无结果数据返回.因此提前终止其数据查询并立即返回空集.");
return new QueryResult(new ArrayList(),new ArrayList(), 0);
}
List resultList = sqlSession.selectList(statementId, parameter);
List totalList = sqlSession.selectList(statementId + "_TOTAL", parameter);
QueryResult result = new QueryResult(resultList, totalList, count);
return result;
}
/**
* 执行sql
* @category 该事务不在spring事务管理机制内
* @param sql
* @return List
* @throws SQLException
*/
public List executeQueryBySql(String sql) throws SQLException{
Connection con = null;
Statement stmt = null;
List list = new ArrayList();
try {
con = this.getConnection();//sqlSession.getConnection();
con.setAutoCommit(false);
stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet rSet = stmt.executeQuery(sql);
ResultSetMetaData md = rSet.getMetaData();
int num = md.getColumnCount();
while (rSet.next()) {
Map mapOfColValues = new HashMap(num);
for (int i = 1; i <= num; i++) {
mapOfColValues.put(md.getColumnName(i), rSet.getObject(i));
}
list.add(mapOfColValues);
}
con.commit();
} catch (Exception e) {
e.printStackTrace();
throw new SQLException("sql执行失败");
}finally {
if (stmt != null) {
stmt.close();
}
// 此处不关闭,连接关闭由org.springframework.jdbc.datasource.DataSourceTransactionManager自动完成
if (con != null) {
con.close();
}
}
return list;
}
}
采用的myBatis版本和Maven配置:
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
  3.怎么调用
  在service中将上面的BaseDao类注入就可以调用了它的方法了。比如:
@Autowired
private BaseDao baseDao;

posted @ 2014-06-20 13:22 顺其自然EVO 阅读(789) | 评论 (0)编辑 收藏

数据库同步和数据库复制技术分析

如何针对不同的应用选择不同的产品,需要我们必须清楚数据库同步和数据库复制的具体含义。
  无论概念如何定义,我们都必须清楚,这两种操作的基础是数据库中的数据,但是包含的数据内容却有所不同
  数据库同步,顾名思义,就是把数据中的交易数据(包含更改的数据以及相关的DDL数据描述语句)实时的同步到另外一个数据库;而数据库复制则针对的是数据库中的数据,同时他对实时性要求不高,重点关注的是某一时间点的数据一致性,同时也不需要同步DDL语句,只要把数据同步过去就可以了。
  综上所述,数据库复制只是对某一时刻的数据的同步,而不是数据库同步那样的实时同步,并且不包含DDL语句同步。
  现在市场上有很多复制产品,比如VARITAS、LEGATO、SYMANTEC、EMC 、IBM等阵列厂商都有各自的备份复制产品,都满足定期备份数据;而数据库同步产品则独立于硬件厂商,都是通过数据库日志的技术来达到数据同步,比如ORACLE、QUEST、WOXINTECH等厂家,他们都是通过软件来捕捉数据库改变来达到数据同步的目的。
  但是这些产品如何能满足我们的系统需求呢?我们要从应用的目标来进行分析就很容易选择了。
  目前的应用主要分为5类:
  1、  双机热备:由于数据库只有一份,数据做冗余,那么硬件厂家的产品更满足
  2、  异地容灾:对于大企业来说,采用硬件厂家的复制产品+软件同步产品是个最佳选择;对于中小企业来说,软件同步产品应该更加合适,带宽和硬件等成本最低
  3、  负载分担:这类应用需要让备库能够实时查询,分担主数据库查询的压力,所以对实时要求最高,当主库产生一笔交易,必须实时能在备库进行查询,所以必须选择实时同步软件
  4、  数据分发/集中:这类应用一般是总/分公司模式,有些计划数据必须能从总部传到分公司,同时分公司也会将生产情况传回总部,这类需求一般对实时性要求不是很高,一般每天传一次,所以目前很多系统通过自己的应用系统产生文件之后进行定时FTP发送,当然通过数据库同步软件也可以达到此目的,不过企业需要考虑成本问题
  5、  重要数据实时安全查询系统VIDRSQS(Very Important Data Realtime Safety Query System):目前这类应用已经大范围开展,主要集中在政府政务公开、医疗卫生系统、社保、商保系统,这类系统的最大特点是,需要实时同步交易系统的某 些数据,并且这些数据能够对敏感信息进行过滤,从而达到实时公开的效果,这也是目前同步复制软件正在经历的新功能,不但要求对OWNER、TABLE甚至对字段都能够进行过滤和操作
  通过以上的分析,大家可以看到,数据库同步和数据库复制还是有很多区别的,同时对应的产品也很多,只有仔细分辨,认真把握,才能在众多的产品中找到最合适的,保证性假比最高,同时又能够最大限度满足需求。

posted @ 2014-06-20 13:22 顺其自然EVO 阅读(160) | 评论 (0)编辑 收藏

一个简单的Java服务器

/**
*
*/
package iotest.serversocket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author Brandon B. Lin
*
*/
public class SingleFileHttpServer extends Thread {
private byte[] content;
private byte[] header;
private int port = 80;
public SingleFileHttpServer(String data, String encoding, String MIMEType,
int port) throws UnsupportedEncodingException {
this(data.getBytes(encoding), encoding, MIMEType, port);
}
public SingleFileHttpServer(byte[] data, String encoding, String MIMEType,
int port) throws UnsupportedEncodingException {
this.content = data;
this.port = port;
createHeader(encoding, MIMEType);
}
private void createHeader(String encoding, String MIMEType)
throws UnsupportedEncodingException {
String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFIle 1.0\r\n"
+ "Content-length: " + content.length + "\r\n"
+ "Content-type: " + MIMEType + "\r\n\r\n";
this.header = header.getBytes(encoding);
}
@Override
public void run() {
try {
ServerSocket server = new ServerSocket(port);
log(server.getLocalPort());
while (true) {
respond(server);
}
} catch (IOException exception) {
System.err.println("Could not start server. Port Occupied!");
}
}
private void log(int port) {
System.out.println("Accepting connection on port " + port);
System.out.println("Data to be sent: ");
try {
System.out.write(content);
} catch (IOException e) {
e.printStackTrace();
}
}
private void respond(ServerSocket server) {
Socket connection = null;
try {
connection = server.accept();
String request = readRequestFromSocket(connection);
boolean writeHeader = (request.toString().indexOf("HTTP/") != -1);
writeToSocket(connection, writeHeader);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null)
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String readRequestFromSocket(Socket connection) throws IOException {
InputStream in = new BufferedInputStream(connection.getInputStream());
StringBuffer request = new StringBuffer(80);
while (true) {
int readByte = in.read();
if (readByte == '\r' || readByte == '\n' || readByte == -1)
break;
request.append((char) readByte);
}
return request.toString();
}
private void writeToSocket(Socket connection, boolean writeHeader)
throws IOException {
OutputStream out = new BufferedOutputStream(
connection.getOutputStream());
if (writeHeader) {
out.write(header);
}
out.write(content);
out.flush();
}
public static void main(String[] args) {
String fileName = "index.html";
String contentType = "text/html";
String encoding = "ASCII";
int port = 80;
byte[] data = readFileToByteArray(fileName);
try {
new SingleFileHttpServer(data, encoding, contentType, port).start();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private static byte[] readFileToByteArray(String fileName) {
byte[] data = null;
try {
InputStream in = new FileInputStream(fileName);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int readByte;
while ((readByte = in.read()) != -1)
out.write(readByte);
data = out.toByteArray();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
}

posted @ 2014-06-20 13:21 顺其自然EVO 阅读(262) | 评论 (0)编辑 收藏

LoadRunner如何开展性能测试

 最近一直想理清思路,特别是碰到一些业务复杂的项目,要求做性能测试,结果一时就不知道怎么下手了。因为之前面试的时候,也碰到很多面试官对性能测试知识方面的提问,面试多了,就有经验,现在写下来,脑子不会乱,按思路,照说照做吧!!
  使用工具如何开展性能测试工作呢?不多说,自己的思路如下步骤:
  1、接到项目时,首先了解系统架构,是B/S,还是C/S,使用什么应用服务器(即中间件),什么数据库
  2、熟悉系统的功能、业务流程,明确项目的性能需求是什么?有哪些性能指标?
  3、编写性能测试计划。(有些公司不要求写)
  4、设计性能测试用例。(按场景设计思路来写比较清晰)
  5、准备测试数据,如一些业务需要大数据量的,就要先造好数据。
  6、选择录制协议,录制两份业务需求功能一样的脚本。(好处是:一是可以做脚本备份,二是脚本优化查找需要关联的地方)
  7、优化脚本,包括设置参数化,检查点,关联,集合点,事务以及自行编写的函数,日志输出函数等。
  8、创建场景。(创建两份一样的场景,以20/80并发用户原则递增来设计,如并发要100个用户,第一份创建80个用户并发,第二份创建100个用户并发,这样在结果分析中容易发现)
  9、场景设计,添加集合点策略,负载均衡器,对“运行时设置”,如lr_think_time,迭代设置,日志输出控制等。
  10、场景运行,添加监控图表,服务器系统资源监控计数器,数据库系统资源监控等。
  11、性能结果分析,通过监控图表的数据(事务响应时间、点击率、吞吐量)、系统资源分析、web页面诊断分析等。
  12、收集测试结果,编写性能测试报告。
  以上是自己总结的一点经验,有不对的思路请大家多多指教。活到老,学到老!

posted @ 2014-06-20 13:03 顺其自然EVO 阅读(612) | 评论 (0)编辑 收藏

Python Selenium自动化(二)自动化注册流程

需求:使用python selenium来自动测试一个网站注册的流程。
  假设这个网站的注册流程分为三步,需要提供比较多的信息:
  在这个流程里面,需要用户填入信息、在下拉菜单中选择、选择单选的radio button、多选框等等比较复杂的页面元素操作。
  在开始做的时候,我会按照一个用户真正的填写习惯,参照页面,一步步的定义每个步骤应该进行怎样的操作,我需要查看每一个页面元素的属性,决定选择一个合理的选项,然后再用代码自动化这样的操作。但是反思一下,其实这样的实现方法并不符合一个计算机自动测试的习惯,也浪费了许多时间在手动的寻找页面元素的相关属性和合理操作值上。
  所谓的自动化,就是要机器在最大程度上的去代替人进行某些操作。而只有在程序自己没有办法判断如何操作的时候,我们才为这类操作定义更详细的规定。例如:从计算机的角度,并不需要关心radio button要选择的是male或是female,也不关心下拉菜单中我们要选择的具体的值是什么;它只需要确保radio button和下拉菜单中可以顺利的选择到一个元素。因此,在这个注册流程中,我可以简单的定义找到页面上所有的radio button,然后挨个把它们都点击一遍,这样既能保证每个radio button都被点击事件测试过,也能保证在注册流程中必定有一个选项被选中了。同样的,我可以定义对每个下拉菜单都选择第一个字值。这样的general的方法,可以把我从对每个元素都要找到它的属性然后对其指定详细的操作的辛苦中一次性的解放出来:
def click_radio_btn(driver):
radioBtns = driver.find_elements_by_xpath("//input[@type='radio']")
for radioBtn in radioBtns:
if radioBtn.is_displayed():
radioBtn.click()
  这里我多进行了一次radioBtn.is_displayed()的判断,原因是在我的流程中有的radio button是在选择了某些特定选项之后才会显示在页面上的,因此对于没有显示的元素就不必进行点击操作,否则会抛出异常:
  selenium.common.exceptions.ElementNotVisibleException: Message: u'element not visible
  而对于需要填入具体信息的input field来说,我们就必须要给出具体每个输入框的输入值来告诉程序如何处理这些元素了。

  在这个这个注册流程中,address的下拉菜单中市的选项是基于省份的选值的。这里有一个ajax的异步调用,当用户选择了一个省份之后才会对市下拉单中的值进行加载。于是问题出现了:由于网络的原因,加载市的值的速度是不一定的,有时也许在执行对市的下拉菜单进行选值的时候其中的值其实还没有加载完成。这时,虽然我们选择的是第一个选项,还是会抛出异常:
  selenium.common.exceptions.NoSuchElementException: Message: 'Could not locate element with index 1'
  这种情况的解决办法是:使用selenium.webdriver.support.ui.WebDriverWait提供的until来使webdriver等待到指定的条件满足或是规定的timeout time到期:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://www.zhuce.com")
city = WebDriverWait(driver,10).until(EC.element_to_be_clickable((By.NAME,"city")))
Select(city).select_by_index(1)
  这样,webdriver将会等待name为“city”的元素enable了之后才算找到该元素,并继续进行接下来的点击操作。如果在10秒内该元素都没有加载成功的话,才抛出异常:
  raise TimeoutException(message)
  selenium.common.exceptions.TimeoutException: Message: ''
  python selenium中提供了多种元素等待的条件:
title_is title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable #it is Displayed and Enabled.
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be alert_is_present
相关文章:
Python Selenium自动化(一)点击页面链接测试

posted @ 2014-06-20 11:52 顺其自然EVO 阅读(3152) | 评论 (0)编辑 收藏

Python Selenium自动化(三)Chrome Webdriver的兼容

当一个自动化测试被实现在一个浏览器之后,我们会希望我们的测试能够覆盖到尽量多的别的浏览器。通过跨平台的测试来保证我们的程序在多个浏览器下都能正常工作
  在安装了selenium之后,firefox webdriver和IE webdriver就已经是ready to use的了,但是如果想要在Chrome下进行测试的话需要再安装Chrome webdriver。Chrome webdriver是由Chromium项目自己维护的,因此在实现上也和Firefox driver以及IE driver有一些不同之处。这就导致了在将在Firefox上可以正常运行的测试代码运用到chrome时会产生一些兼容性的问题。
  1. WebDriverWait的可靠性
  上一篇博客提到过,当页面上有使用ajax异步加载的元素的时候,为了确保对元素进行操作的时候该元素已经处在一个可用的状态下了,我们可以使用WebDriverWait来使driver等待至目标元素满足给出的条件时才继续下面的操作。但是在Chrome中,这个方法似乎不是总是适用。
  2.一些奇怪的异常
  总的来说,测试代码在Chrome下的运行是非常快的,至少人眼就可以感觉出来比Firefox下的运行速度要快一点,当然这是在不报异常的情况下。而实际情况是,在Firefox下可以正常运行的代码,在Chrome下却会出现问题,并且抛出一些奇怪的异常:
selenium.common.exceptions.WebDriverException: Message: u'unknown error: Element is not clickable at point
selenium.common.exceptions.InvalidElementStateException: Message: u'invalid element state
selenium.common.exceptions.ElementNotVisibleException: Message: u'element not visible
  第一个异常的抛出一般会发生在radio button的点击事件上,但是一般异常抛出的同时,系统会告诉你当前的element不可以被点击,但是另外的某个元素可以接受这个点击事件。这时我们可以查看页面上系统给出的备选元素是否也可以满足我们的点击需求,如果可以,我们可以简单的把radio button换成这个备选元素来回避这个异常。
  另外,这三个异常都与Chrome的运行速度以及WebDriverWait的可靠性有一定关系。因此,我们可以稍显“粗暴”的让webdriver休眠一段时间来slow down测试代码的执行速度,强制driver等待一个固定的时间来让元素加载完成。
  import time
  #some test code
  time.sleep(2)
  #continue test code
  time.sleep(2)会使driver休眠两秒,然后再继续执行后面的测试代码
  3.不在当前视图范围内的元素的操作
  当我们使用Firefox webdriver来测试某个页面的时候,如果我们选取了某个页面元素来对其进行操作,但是这个元素不在当前浏览器显示的视图范围内,Firefox webdriver的做法是自动的将视图调整到该元素显示的区域,然后对这个元素进行操作。也就是说driver自己完成了页面的scroll down or up的操作。
  但是在Chrome webdriver中,如果待操作元素不在视图显示范围内,则会抛出Element is not clickable at point异常。或是如果设置了WebDriverWait并且它正常工作的话会抛出Timeout异常。
  因此,在使用Chrome wbedriver的时候,我们要更加小心,对于需要滚动页面才能显示在视图中的元素,我们需要添加代码使页面滚动至元素显示的范围,然后再对该元素进行操作。使页面滚动的方法是:
  driver.execute_script("window.scrollBy(0,200)","")  #向下滚动200px
  driver.execute_script("window.scrollBy(0,document.body.scrollHeight)","")  #向下滚动到页面底部
相关文章
Python Selenium自动化(一)点击页面链接测试
Python Selenium自动化(二)自动化注册流程

posted @ 2014-06-20 11:52 顺其自然EVO 阅读(1527) | 评论 (0)编辑 收藏

基于XAMPP的Testlink安装方法

  下载一个最新的安装包(也有免安装的ZIP包,不过那个要配置,上面说安装包是最简单的了,咱就奔着简单去吧)
  双击安装包,安装,记得选择安装 Apache和MySQL Service
  安装完后直接运行XAMPP Control Pannel,但是打开的时候看到MySQL的状态是Running了,但是Apache却没有。点击Start,却提示 Error: Apache service not started [-1]
  原因可能是Apache被防火墙阻止了导致它没有启动。
  解决:关掉 XAMPP Control Pannel,到 \xampp\apache目录下,运行脚本apache_uninstallservice.bat, 然后再运行apache_installservice.bat,这个时候什么360啊防火墙啊都跳出来要你确认是否要解除阻止,那是必须的!这下Apache可以运行了。再打开XAMPP Control Pannel,却提示: Busy.....Apache service started。那是因为80端口被占用了,apache启动要占用80端口。
  解决:在cmd中运行 netstat -ano | findstr 80,查看80端口有没有被占用,如果有,那么修改apache的端口。
  修改方法:
  到xampp\apache\conf目录下,打开httpd.conf文件,将Listen 80 改成 Listen 8081, ServerName localhost:80 改成 ServerName localhost:8081, 保存文件,然后重启XAMPP Control Pannel,世界又恢复和平了...
  然而,这样做的好处是端口不会与其他冲突,但坏处也是显而易见的,就是访问的时候在浏览器中输入地址后面也得加上端口号8081,这对客户端用户来说的确是个麻烦事。如果麻烦不可避免的话,那就越少越好吧,在服务器端操作一下,就是:把占用80端口的这个进程干掉(如上图的话,关掉5428这个进程),然后再apache_uninstallservice.bat 一下,apache_installservice.bat一下即可(这些操作的时候不要关掉XAMPP Control Panel)
  这样,我们的服务器就启动起来了。那么让我们登录进去看看吧......
  打开浏览器,输入 http://localhost/xampp/,选择中文(当然,如果你喜欢其他语言的话也无妨,只要看的懂即可,呵哈)
  点击 “安全”, 先来设置一下MySQL密码
  设置完MySQL密码之后,接下来就到我们最终的目标了,配置TestLink
  将TestLink压缩包解压到 xampp\htdocs目录下,比如就testlink,然后在浏览器中输入 http://localhost/test, 正式登陆testlink了
  一路设置基本默认即可,其实就那两个地方要设置而已:


 这样一步步到最后,就基本完成了testlink的配置了,so easy,哈哈~~
  配置好TestLink后,我们就可以登录它,然后创建我们的project, test suite, test case了
  汉化:
  将strings.txt放到D:\xampp\htdocs\TestLink\locale\zh_CN 目录下,替换原来的文件
  解决图表里中文乱码的问题:
  这个问题,曾经测试过多次都未能解决,这次能解决掉,估计原因是部分字体库的问题,现在测试幼圆字体(SIMYOU.TTF)可以完美支持中文图表显示。此字体也可以直接在Windows系统字体库中找到,方便的很。
  方法:
  步骤一:
  将下载或从Windows中提取到的字体SIMYOU.TTF拷贝到Testlink中的Pchat的字体目录,路径为:
  <testlink>\third_party\pchart\Fonts
  步骤二:
  修改config.inc.php:将$tlCfg->charts_font_path = TL_ABS_PATH . "third_**/pchart/Fonts/tahoma.ttf";中的字体重新设置:$tlCfg->charts_font_path = TL_ABS_PATH . "third_party/pchart/Fonts/SIMYOU.TTF";
  注:本次测试用的字体为从系统中提取的,测试过系统中提取的宋体、黑体和微软雅黑都不行,其它字体希望有心的童鞋继续测试。

posted @ 2014-06-20 11:33 顺其自然EVO 阅读(759) | 评论 (0)编辑 收藏

线程上下文切换的性能损耗测试

线程上下文切换的性能损耗到底有多少,一直没有直观的理解,今天写个程序测试一下。先看看下面的程序(点击下载):
  
  ThreadTester是所有Tester的基类。所有的Tester都干的是同样一件事情,把counter增加到100000000,每次只能加1。
1: public abstract class ThreadTester
2:     {
3:         public const long MAX_COUNTER_NUMBER = 100000000;
4:
5:         private long _counter = 0;
6:
7:         //获得计数
8:         public virtual long GetCounter()
9:         {
10:             return this._counter;
11:         }
12:
13:         //增加计数器
14:         protected virtual void IncreaseCounter()
15:         {
16:             this._counter += 1;
17:         }
18:
19:         //启动测试
20:         public abstract void Start();
21:
22:         //获得Counter从开始增加到现在的数字所耗的时间
23:         public abstract long GetElapsedMillisecondsOfIncreaseCounter();
24:
25:         //测试是否正在运行
26:         public abstract bool IsTesterRunning();
27:     }
SingleThreadTester是单线程计数。
1: class SingleThreadTester : ThreadTester
2:     {
3:         private Stopwatch _aStopWatch = new Stopwatch();
4:
5:         public override void Start()
6:         {
7:             _aStopWatch.Start();
8:
9:             Thread aThread = new Thread(() => WorkInThread());
10:             aThread.Start();
11:         }
12:
13:         public override long GetElapsedMillisecondsOfIncreaseCounter()
14:         {
15:             return this._aStopWatch.ElapsedMilliseconds;
16:         }
17:
18:         public override bool IsTesterRunning()
19:         {
20:             return _aStopWatch.IsRunning;
21:         }
22:
23:         private void WorkInThread()
24:         {
25:             while (true)
26:             {
27:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
28:                 {
29:                     _aStopWatch.Stop();
30:                     break;
31:                 }
32:
33:                 this.IncreaseCounter();
34:             }
35:         }
36:     }
  TwoThreadSwitchTester是两个线程交替计数。
1: class TwoThreadSwitchTester : ThreadTester
2:     {
3:         private Stopwatch _aStopWatch = new Stopwatch();
4:         private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
5:
6:         public override void Start()
7:         {
8:             _aStopWatch.Start();
9:
10:             Thread aThread1 = new Thread(() => Work1InThread());
11:             aThread1.Start();
12:
13:             Thread aThread2 = new Thread(() => Work2InThread());
14:             aThread2.Start();
15:         }
16:
17:         public override long GetElapsedMillisecondsOfIncreaseCounter()
18:         {
19:             return this._aStopWatch.ElapsedMilliseconds;
20:         }
21:
22:         public override bool IsTesterRunning()
23:         {
24:             return _aStopWatch.IsRunning;
25:         }
26:
27:         private void Work1InThread()
28:         {
29:             while (true)
30:             {
31:                 _autoResetEvent.WaitOne();
32:
33:                 this.IncreaseCounter();
34:
35:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
36:                 {
37:                     _aStopWatch.Stop();
38:                     break;
39:                 }
40:
41:                 _autoResetEvent.Set();
42:             }
43:         }
44:
45:         private void Work2InThread()
46:         {
47:             while (true)
48:             {
49:                 _autoResetEvent.Set();
50:                 _autoResetEvent.WaitOne();
51:                 this.IncreaseCounter();
52:
53:                 if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
54:                 {
55:                     _aStopWatch.Stop();
56:                     break;
57:                 }
58:             }
59:         }
60:     }
 MultiThreadTester可以指定线程数,多个线程争抢计数。
1: class MultiThreadTester : ThreadTester
2:     {
3:         private Stopwatch _aStopWatch = new Stopwatch();
4:         private readonly int _threadCount = 0;
5:         private readonly object _counterLock = new object();
6:
7:         public MultiThreadTester(int threadCount)
8:         {
9:             this._threadCount = threadCount;
10:         }
11:
12:         public override void Start()
13:         {
14:             _aStopWatch.Start();
15:
16:             for (int i = 0; i < _threadCount; i++)
17:             {
18:                 Thread aThread = new Thread(() => WorkInThread());
19:                 aThread.Start();
20:             }
21:         }
22:
23:         public override long GetElapsedMillisecondsOfIncreaseCounter()
24:         {
25:             return this._aStopWatch.ElapsedMilliseconds;
26:         }
27:
28:         public override bool IsTesterRunning()
29:         {
30:             return _aStopWatch.IsRunning;
31:         }
32:
33:         private void WorkInThread()
34:         {
35:             while (true)
36:             {
37:                 lock (_counterLock)
38:                 {
39:                     if (this.GetCounter() > ThreadTester.MAX_COUNTER_NUMBER)
40:                     {
41:                         _aStopWatch.Stop();
42:                         break;
43:                     }
44:
45:                     this.IncreaseCounter();
46:                 }
47:             }
48:         }
49:     }
  Program的Main函数中,根据用户的选择来决定执行哪个测试类。
1: class Program
2:     {
3:         static void Main(string[] args)
4:         {
5:
6:             string inputText = GetUserChoice();
7:
8:             while (!"4".Equals(inputText))
9:             {
10:                 ThreadTester tester = GreateThreadTesterByInputText(inputText);
11:                 tester.Start();
12:
13:                 while (true)
14:                 {
15:                     Console.WriteLine(GetStatusOfThreadTester(tester));
16:                     if (!tester.IsTesterRunning())
17:                     {
18:                         break;
19:                     }
20:                     Thread.Sleep(100);
21:                 }
22:
23:                 inputText = GetUserChoice();
24:             }
25:
26:             Console.Write("Click enter to exit...");
27:         }
28:
29:         private static string GetStatusOfThreadTester(ThreadTester tester)
30:         {
31:             return string.Format("[耗时{0}ms] counter = {1}, {2}",
32:                     tester.GetElapsedMillisecondsOfIncreaseCounter(), tester.GetCounter(),
33:                     tester.IsTesterRunning() ? "running" : "stopped");
34:         }
35:
36:         private static ThreadTester GreateThreadTesterByInputText(string inputText)
37:         {
38:             switch (inputText)
39:             {
40:                 case "1":
41:                     return new SingleThreadTester();
42:                 case "2":
43:                     return new TwoThreadSwitchTester();
44:                 default:
45:                     return new MultiThreadTester(100);
46:             }
47:         }
48:
49:         private static string GetUserChoice()
50:         {
51:             Console.WriteLine(@"==Please select the option in the following list:==
52: 1. SingleThreadTester
53: 2. TwoThreadSwitchTester
54: 3. MultiThreadTester
55: 4. Exit");
56:
57:             string inputText = Console.ReadLine();
58:
59:             return inputText;
60:         }
61:     }
三个测试类,运行结果如下:
Single Thread:
[耗时407ms] counter = 100000001, stopped
[耗时453ms] counter = 100000001, stopped
[耗时412ms] counter = 100000001, stopped
Two Thread Switch:
[耗时161503ms] counter = 100000001, stopped
[耗时164508ms] counter = 100000001, stopped
[耗时164201ms] counter = 100000001, stopped
Multi Threads - 100 Threads:
[耗时3659ms] counter = 100000001, stopped
[耗时3950ms] counter = 100000001, stopped
[耗时3720ms] counter = 100000001, stopped
Multi Threads - 2 Threads:
[耗时3078ms] counter = 100000001, stopped
[耗时3160ms] counter = 100000001, stopped
[耗时3106ms] counter = 100000001, stopped
  什么是线程上下文切换
  上下文切换的精确定义可以参考: http://www.linfo.org/context_switch.html。多任务系统往往需要同时执行多道作业。作业数往往大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了让用户感觉这些任务正在同时进行,操作系统的设计者巧妙地利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能,但同时也带来了保存现场和加载现场的直接消耗。(Note. 更精确地说, 上下文切换会带来直接和间接两种因素影响程序性能的消耗. 直接消耗包括: CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉; 间接消耗指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小).
  
  根据上面上下文切换的定义,我们做出下面的假设:
  之所以TwoThreadSwitchTester执行速度最慢,因为线程上下文切换的次数最多,时间主要消耗在上下文切换了,两个线程交替计数,每计数一次就要做一次线程切换。
  “Multi Threads - 100 Threads”比“Multi Threads - 2 Threads”开的线程数量要多,导致线程切换次数也比后者多,执行时间也比后者长。
  由于Windows下没有像Linux下的vmstat这样的工具,这里我们使用Process Explorer看看程序执行的时候线程上线文切换的次数。
  Single Thread:
  
  计数期间,线程总共切换了580-548=32次。(548是启动程序后,初始的数值)
  Two Thread Switch:
 
  计数期间,线程总共切换了33673295-124=33673171次。(124是启动程序后,初始的数值)
  Multi Threads - 100 Threads:
  
  计数期间,线程总共切换了846-329=517次。(329是启动程序后,初始的数值)
  Multi Threads - 2 Threads:
  
  计数期间,线程总共切换了295-201=94次。(201是启动程序后,初始的数值)
  从上面收集的数据来看,和我们的判断基本相符。
  干活的其实是CPU,而不是线程
  再想想原来学过的知识,之前一直以为线程多干活就快,简直是把学过的计算机原理都还给老师了。真正干活的不是线程,而是CPU。线程越多,干活不一定越快。
  那么高并发的情况下什么时候适合单线程,什么时候适合多线程呢?
  适合单线程的场景:单个线程的工作逻辑简单,而且速度非常快,比如从内存中读取某个值,或者从Hash表根据key获得某个value。Redis和Node.js这类程序都是单线程,适合单个线程简单快速的场景。
  适合多线程的场景:单个线程的工作逻辑复杂,等待时间较长或者需要消耗大量系统运算资源,比如需要从多个远程服务获得数据并计算,或者图像处理。
  例子程序:http://pan.baidu.com/s/1ntNUPWP

posted @ 2014-06-20 11:30 顺其自然EVO 阅读(431) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 97 98 99 100 101 102 103 104 105 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜