STUN协议,探测NAT类型

1, STUN客户端(101:10)向STUN服务器(404:40)发送请求,要求得到自身经NAT映射后的地址(202:20):
      a,收不到服务器回复,则认为UDP被防火墙阻断,不能通信,网络类型:Blocked.
      b,收到服务器回复(地址要嘛是映射地址要嘛就是源地址),对比本地地址,如果相同(直接返回的就是源地址101:10),则认为无NAT设备(没经过NAT映射转换),进入第2步,否则认为有NAT设备,进入3步.
   2,(已确认无NAT设备)STUN客户端向STUN服务器发送请求,要求服务器从其他IP和PORT(505:50)向客户端回复包:
      a,收不到服务器从其他IP地址的回复,认为包被前置防火墙阻断,网络类型:Symmetric UDPFirewall.(如果没有NAT的话是无论如何都能收到回复的,只有一点受到防火墙的阻断,有时候杀毒软件也阻断)

b,收到则认为客户端处在一个开放的网络上,网络类型:Opened.

   3,(已确认存在NAT设备)STUN客户端(101:10)向STUN服务器(404:40)发送请求,要求服务器从其他IP和PORT(505:50)向客户端回复包:

a,收不到服务器从其他IP地址(包括IP和Port)的回复,认为包被前置NAT设备阻断,进入第4步.(如果不是前置的就相当于全开Opened或Full ConeNat类型,无论那个IP和端口都能接收到回复)
      b,收到则认为NAT设备类型为Full Cone,即网络类型:Full Cone NAT.(此没有什么限制的基本和没有NAT一样Opened)
  4, STUN客户端(101:10)向STUN服务器(404:40)的另外一个IP地址(505:40,端口不能改变)发送请求,要求得到自身经NAT映射后的地址(202:20,如果不是对称的应该返回这个映射地址),并对比之(与第一步中返回的映射地址比对)
      a,地址不相同,则网络类型:Symmetric NAT.(如果是对称类型,则101:10在向一个不同的IP地址(505,端口不变)发送请求时会映射一个新的端口,此处相当于生成一个202:21,比对不相同)
      b,相同则认为是Restricted NAT(受限的),进入第5步,进一步确认类型.
5, (已确认RestrictedNAT设备)STUN客户端(101:10)向STUN服务器(404:40)发送请求,要求服务器从相同IP(404)的其他PORT(41)向客户端回复包:
      a,收不到服务器从其他PORT地址的回复,认为包被前置NAT设备阻断,网络类型:Port Restricted coneNAT.(端口改变了,端口受限相当于我一个人A(101)向B(404)要右手(40端口)的苹果,而B左手(41端口)有个香蕉,A只要B的右手苹果,而B给了A一个左手的香蕉,A肯定是不要的,相当于收不到回复)
      b,收到则认为网络类型: Restricted cone NAT.(IP受限,对端口没什么要求,只要是404

这个IP就行,无论用那个端口都行)

以上实现过程作为服务器只有双IP的服务器才能实现,针对我们这些电脑只有一个IP的开发者来说,我们就得通过两台电脑来模拟实现双IP了,这地方上面请求的服务器地址就得改变一下了,个人认为这里的Server1:404地址的端口必须和Server2:505地址的端口一样,才能实现,端口相同也不会影响测试效果的.

来源:http://blog.csdn.net/hack8/article/details/6593768

客户端主机所在网络可以分为以下类型:

1, Opened: 即主机拥有公网IP,并且没有防火墙,可自由与外部通信.

2, Full Cone NAT: 主机前有NAT设备,

NAT规则如下:从主机UDP端口A发出的数据包都会对应到NAT设备出口IP的端口B,并且从任意外部地址发送到该NAT设备UDP端口B的包都会被转到主机端口A.

3, Restricted cone NAT: 主机前有NAT设备,

 NAT规则如下:从主机UDP端口A发出的数据包都会对应到NAT设备出口IP的端口B,但只有从之前该主机发出包的目的IP发出到该NAT设备UDP端口B的包才会被转到主机端口A.

4, Port Restricted cone NAT: 主机前有NAT设备,

  NAT规则如下:从主机UDP端口A发出的数据包都会对应到NAT设备出口IP的端口B,但只有从之前该主机发出包的目的IP/

解读Linux启动过程

转自:https://my.oschina.net/macwe/blog/1531024

解读Linux启动过程

 

1. 概述

本文解读一下从CPU加电自检到启动init进程的过程, 先通过下面这张图大致看一下Linux启动的整个过程。

本文的分析环境是GRUB 0.97 + Linux 2.6.18。

2. BIOS

CPU加电后首先工作在实模式并初始化CS:IP=FFFF:FFF0,BIOS的入口代码必须从该地址开始。BIOS完成相应的硬件检查并提供一系列中断服务例程,这些中断服务提供给系统软件访问硬件资源(比如磁盘、显示器等),最后选择一个启动盘加载第一个扇区(即:MBR,共512字节)数据到内存0x7C00处,并从这里开始执行指令(CS:IP=0000:7C00),对于笔者的电脑来说这就是GRUB的Stage1部分。

3. GRUB

GRUB的作用是bootloader,用来引导各种OS的。

3.1. Stage1

Stage1就是MBR,由BIOS把它从磁盘的0扇区加载到0x7c00处,大小固定位512字节,此时的CPU上下文如下:

eax=0011aa55 ebx=00000080 ecx=00000000 edx=00000080 esi=0000f4a0 edi=0000fff0
eip=00007c00 esp=00007800 ebp=00000000 iopl=0 nv up ei pl zr na po nc
cs=0000 ds=0000 es=0000 fs=0000 gs=0000 ss=0000 eflags=00000246
// 注: dl=启动磁盘号, 00H~7FH是软盘, 80H~FFH是硬盘。

因为只能是512字节,大小受限,它就干一件事,把Stage2的第一个512字节读取到0x8000,然后jmp到0x8000继续执行。

3.1.1. 读磁盘

磁盘扇区寻址有两种方式:

  • CHS方式:传统的方式,使用三元组(10位Cylinder, 8位Head, 6位Sector)来寻找扇区,最大只能找到(2^10) * (2^8) * (2^6) * 512 = 8GB的硬盘容量,现在的硬盘明显不够用了。
  • LBA方式:现在的方式,使用48位线性地址来寻找扇区,最大支持(2^48) * 512 = 128PB的硬盘空间。虽然机械上还是CHS的结构,不过磁盘的固件会自动完成LBA到CHS的转换。

因为CHS明显不适合现在的硬盘,所以LBA模式寻址是现在的PC的标配了吧!万一磁盘不支持LBA或者是软盘,需要我们手工转换成CHS模式。转换公式如下(就是三维空间定位一个点的问题):

磁道号C = LBA / 每磁道的扇区数SPT / 盘面总HPC
磁头号H = (LBA / 每磁道的扇区数SPT) mod HPC
扇区号S = (LBA mod SPT) + 1

判断是否支持LBA模式

/* check if LBA is supported */
movb	$0x41, %ah
movw	$0x55aa, %bx
int	$0x13

如果返回成功(CF=1)并且BX值是0xAA55表示支持LBA寻址(用Extensions方法)。

注意:3.5英寸软盘需要使用CHS方式寻址,它的CHS参数是80个柱面、2个磁头、每个磁道18个扇区,每扇区512字节,共1.44MB容量。

LBA模式读的功能号是AH=42h,DL参数是磁盘号,DS:SI参数是Disk Address Packet(DAP)结构体的内存地址,定义如下:

struct DAP {
    uint8_t sz; // 结构体大小
    uint8_t unused;
    uint16_t sector_cnt; // 需要都的扇区总数
    struct dst_addr { // 内存地址,读到这里
        uint16_t offset;
        uint16_t segment;
    };
    uint64_t lba_addr;  // 磁盘的LBA地址
};

参考:

  • https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
  • https://en.wikipedia.org/wiki/INT_13H

3.2. Stage2

Stage2就是GRUB剩下的全部的代码了,包括BIOS中断服务的封装给C代码使用、键盘驱动、文件系统驱动、串口、网络驱动等等,它提供了一个小型的命令行环境,可以解析用户输入命令并执行对OS的启动。

3.2.1. start.S

首先Stage2的头512字节(start.S)被加载到0x8000,并在这里开始执行,此时的CPU上下文如下:

eax=00000000 ebx=00007000 ecx=00646165 edx=00000080 esi=00007c05 edi=0000fff0
eip=00008000 esp=00001ffe ebp=00000000 iopl=0 nv up ei pl zr na po nc
cs=0000 ds=0000 es=0800 fs=0000 gs=0000 ss=0000 eflags=00000246

start.S的工作是把Stage2的后续部分全部加载到内存中(从0x8200开始),有103KB大小。

3.2.2. asm.S

asm.S是0x8200处的代码,先看一下CPU上下文环境:

eax=00000e00 ebx=00000001 ecx=00646165 edx=00000080 esi=00008116 edi=000081e8
eip=00008200 esp=00001ffe ebp=000062d8 iopl=0 nv up ei pl zr na po nc
cs=0000 ds=0000 es=1ae0 fs=0000 gs=0000 ss=0000 eflags=00000246
3.2.2.1. 最开始的代码应该设置好段寄存器和栈
cli
/* set up %ds, %ss, and %es */
/* cs=0000 ds=0000 es=0000 fs=0000 gs=0000 ss=0000 */
xorw	%ax, %ax
movw	%ax, %ds
movw	%ax, %ss
movw	%ax, %es

/* set up the real mode/BIOS stack */
movl	$STACKOFF, %ebp
movl	%ebp, %esp
sti

此时:

cs=0000 ds=0000 es=0000 ss=0000 esp=00001ff0 ebp=00001ff0。
3.2.2.2. 保护模式和实模式

因为GRUB没有实现自己的中断服务,所以访问硬件资源还是使用BIOS的中断服务例程(实模式)。GRUB的命令行环境是工作在保护模式下的,所以当GRUB需要访问BIOS中断的时候需要切换回实模式,于是在GRUB执行过程中会有频繁的实模式和保护模式的互相切换操作,当切换回实模式后别忘了保存保护模式下的栈指针

(1) 实模式进入保护模式

/* transition to protected mode */
DATA32	call EXT_C(real_to_prot)

/* The ".code32" directive takes GAS out of 16-bit mode. */
.code32

下图是实模式到保护模式的切换步骤:

GRUB没有设置分页机制和新的中断,所以GRUB的保护模式访问的是物理内存且是不能使用INT指令,不过对于bootloader来说够用了。因为需要切换到保护模式栈,原来的返回地址要放到新的栈上,以保证能够正常ret:

ENTRY(real_to_prot)
	...
	/* put the return address in a known safe location */
	movl	(%esp), %eax
	movl	%eax, STACKOFF  ; 把返回地址保存起来备用

	/* get protected mode stack */
	movl	protstack, %eax
	movl	%eax, %esp
	movl	%eax, %ebp      ; 设置保护模式的栈

	/* get return address onto the right stack */
	movl	STACKOFF, %eax
	movl	%eax, (%esp)    ; 把返回地址重新放到栈上
	
	/* zero %eax */
	xorl	%eax, %eax

	/* return on the old (or initialized) stack! */
	ret                     ; 正常返回

(2) 保护模式切换回实模式

	/* enter real mode */
	call	EXT_C(prot_to_real)
	
	.code16

下图说明了保护模式切换回实模式的步骤:

保护模式的栈需要保存起来以便恢复现场,让C代码正确运行,实模式的栈每次都重置为STACKOFF即可,和(1)一样,也要设置好返回地址:

ENTRY(prot_to_real)
	...
	/* save the protected mode stack */
	movl	%esp, %eax
	movl	%eax, protstack  ; 把栈保存起来

	/* get the return address */
	movl	(%esp), %eax
	movl	%eax, STACKOFF   ; 返回地址放到实模式栈里

	/* set up new stack */
	movl	$STACKOFF, %eax  ; 设置实模式的栈
	movl	%eax, %esp
	movl	%eax, %ebp
	... 
3.2.2.3. 创建C运行时环境

C的运行环境主要包括栈、bss数据区、代码区。随着切换到保护模式,栈已经设置好了;随着Stage2从磁盘加载到内存,代码区和bss区都已经在内存了,最后还需要把bss区给初始化一下(清0),接下来即可愉快的执行C代码了。

3.2.2.4. 执行cmain()

先执行一个init_bios_info()获取BIOS的信息,比如被BIOS使用的内存空间(影响我们Linux映像加载的位置)、磁盘信息、ROM信息、APM信息,最后调用cmain()。 cmain()函数在stage2.c文件中,其中最主要的函数run_menu()是启动一个死循环来提供命令行解析执行环境。

3.2.2.5. load_image()

如果grub.cfg或者用户执行kenrel命令,会调用load_image()函数来将内核加载到内存中。至于如何加载linux镜像在Documentation的boot.txt和zero-page.txt有详细说明。

load_image()是一个非常长的函数,它要处理支持的各种内核镜像格式。Linux镜像vmlinuz文件头是struct linux_kernel_header结构体,该结构体里头说明了这个镜像使用的boot协议版本、实模式大小、加载标记位和需要GRUB填写的一些参数(比如:内核启动参数地址)。

  • 实模式部分:始终被加载到0x90000位置,并从0x90200开始执行(linux 0.11就这样做了)。
  • 保护模式部分:我们现在使用的内核比较大(大于512KB),叫做bzImage,加载到0x100000(高位地址,1MB)开始的位置,可以任意大小了。否则小内核zImage放在0x10000到mbi.mem_lower * 1024(一般是0x90000)区域。
3.2.2.6. linux_boot()

我们正常的启动过程调用的是big_linux_boot()函数,把实模式部分copy到0x90000后,设置其他段寄存器值位0x9000, 设置CS:IP=9020:0000开始执行(使用far jmp)。

至此GRUB的工作完成,接下来执行权交给Linux了。

4. setup.S

该文件在arch/i386/boot/setup.S,主要作用是收集硬件信息并进入保护模式head.S。初始的CPU上下文如下:

eax=00000000 ebx=00009000 ecx=00000000 edx=00000003 esi=002d8b54 edi=0009a000
eip=00000000 esp=00009000 ebp=00001ff0 iopl=0 nv up di pl zr na po nc
cs=9020 ds=9000 es=9000 fs=9000 gs=9000 ss=9000  eflags=00000046

4.1. 自身检查

先检查自己setup.S是否合法,主要是检查末尾的两个magic是否一致

# Setup signature -- must be last
setup_sig1:	.word	SIG1
setup_sig2:	.word	SIG2

4.2. 收集硬件信息

主要是通过BIOS中断来收集硬件信息。收集的信息包括内存大小、键盘、鼠标、显卡、硬盘、APM等等。收集的硬件信息保存在0x9000处:

# 设置ds = 0x9000,用来保存硬件信息
movw	%cs, %ax			# aka SETUPSEG
subw	$DELTA_INITSEG, %ax 		# aka INITSEG
movw	%ax, %ds

这里看一下如何获取内存大小,这样OS才能进行内存管理。这里用三种方法获取内存信息:

  1. e820h:请求中断INT 15H,AX=E820H时返回可用的物理内存信息,e820由此得名,参考http://www.uruk.org/orig-grub/mem64mb.html。由于内存的使用是不连续的,通过连续调用INT 15H得到所有可用的内存区域,每次查询得到的结果ES:DI是个struct address_range_descriptor结构体,返回的结果都是64位的,完全能够满足目前PC的需求了。
     struct address_range_descriptor {
     	uint32_t base_addr_low;   // 起始物理地址
     	uint32_t base_addr_high;
     	uint32_t length_low;      // 长度
     	uint32_t length_high;
     	uint8_t type;             // 1=OS可用的, 2=保留的,OS不可用
     };
    
  2. e801h:通过请求中断INT15h,AX=e801H返回结果,最高只能得到4GB内存结果。
  3. 88h:古老的办法,通过请求中断INT15h,AH=88H返回结果。最高只能得到16MB或者64MB的内存,现在的电脑不适用了。

扩展阅读:http://wiki.osdev.org/Detecting_Memory_(x86)#E820h

4.3. 启用A20

让CPU访问1MB以上的扩展内存,否则访问的是X mod 1MB的地址。下面列举三种开启A20的方法:

  1. 使用I/0端口92H,AL的将1-bit置1
     inb	$0x92, %al			# Configuration Port A
     orb	$0x02, %al			# "fast A20" version
     andb	$0xFE, %al			# don't accidentally reset
     outb	%al, $0x92
    
  2. 使用BIOS中断INT 0x15, AX=0x2401
     movw	$0x2401, %ax
     pushfl					# Be paranoid about flags
     int	$0x15
     popfl
    
  3. 使用键盘控制器
     movb	 $0xD1, %al			# command write
     outb	 %al, $0x64
     call	 empty_8042
    
     movb	 $0xDF, %al			# A20 on
     outb	 %al, $0x60
     call	 empty_8042
    

4.4. 进入保护模式

4.4.1. 临时的GDT和IDT

这里的IDT全部是0;Linux目前使用的GDT如下:

gdt:
	.fill GDT_ENTRY_BOOT_CS,8,0

	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
	.word	0				# base address = 0
	.word	0x9A00				# code read/exec
	.word	0x00CF				# granularity = 4096, 386
						#  (+5th nibble of limit)

	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
	.word	0				# base address = 0
	.word	0x9200				# data read/write
	.word	0x00CF				# granularity = 4096, 386
						#  (+5th nibble of limit)
gdt_end:

这里只定义了两个DPL为0的代码段和数据段,只给内核使用的。

4.4.1. 设置CR0.PE

这里使用lmsw指令,它和mov cr0, X是等价的

movw	$1, %ax				# protected mode (PE) bit
lmsw	%ax				# This is it!
jmp	flush_instr

4.5. 调转到head.S(CS:EIP=0x10:100000)

至此硬件信息就收集完成,这些收集到的硬件信息都保存在0x90000处,后续OS可以使用这些硬件信息来管理了。

5. head.S

该文件位于arch/i386/kernel/head.S,这个是内核保护模式的代码的起点,笔者电脑的位置在0x100000,此时CPU上下文是:

eax=00000001 ebx=00000000 ecx=0000ff03 edx=47530081 esi=00090000 edi=00090000
eip=00100000 esp=00008ffe ebp=00001ff0 iopl=0 nv up di pl nz na pe nc
cs=0010 ds=0018 es=0018 fs=0018 gs=0018 ss=0018               eflags=00000002

注:已经进入保护模式,CS的值是GDT表项的索引。

它的作用就是设置真正的分段机制和分页机制、启动多处理器、设置C运行环境,最后执行start_kernel()函数。

5.1. startup_32

5.1.1. 加载临时的分段机制

boot_gdt_table就是临时的GDT,其实和start.S的一样:

	lgdt boot_gdt_descr - __PAGE_OFFSET
	movl $(__BOOT_DS),%eax
	movl %eax,%ds
	movl %eax,%es
	movl %eax,%fs
	movl %eax,%gs

ENTRY(boot_gdt_table)
	.fill GDT_ENTRY_BOOT_CS,8,0
	.quad 0x00cf9a000000ffff	/* kernel 4GB code at 0x00000000 */
	.quad 0x00cf92000000ffff	/* kernel 4GB data at 0x00000000 */

5.1.2. 初始化内核bss区和内核启动参数

为了让C代码正常运行,bss区全部清0,启动参数需要移动到boot_params位置。

5.1.3. 启动临时分页机制

临时的页表,只要能够满足内核使用就行。页目录表是swapper_pg_dir,它是一个4096大小的内存区域,默认全是0。一般__PAGE_OFFSET=0xC0000000(3GB),这是要把物理地址0x00000000映射到0xc0000000的地址空间(内核地址空间)。下面是页目录表和页表的初始化代码:

page_pde_offset = (__PAGE_OFFSET >> 20); // 3072,页目录的偏移

	// 页目录表存放在pg0位置,arch/i386/kernel/vmlinux.lds中定义
	movl $(pg0 - __PAGE_OFFSET), %edi
	movl $(swapper_pg_dir - __PAGE_OFFSET), %edx  // edx是页目录表的地址
	movl $0x007, %eax			/* 0x007 = PRESENT+RW+USER */
10:
	// 创建一个页目录项
	leal 0x007(%edi),%ecx			/* Create PDE entry */
	movl %ecx,(%edx)			/* Store identity PDE entry */
	movl %ecx,page_pde_offset(%edx)		/* Store kernel PDE entry */
	addl $4,%edx   // 指向swapper_pg_dir的下一个项
	movl $1024, %ecx   // 每个页表1024个项目
11:
	stosl  // eax -> [edi]; edi = edi + 4
	addl $0x1000,%eax // 每次循环,下一个页目录项
	loop 11b
	/* End condition: we must map up to and including INIT_MAP_BEYOND_END */
	/* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
	leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp  // 页表覆盖到这里就终止
	cmpl %ebp,%eax
	jb 10b
	movl %edi,(init_pg_tables_end - __PAGE_OFFSET)

下面是对上面代码的翻译(这样更有利于理解):

extern uint32_t *pg0;  // 初始值全0
extern uint32_t *swapper_pg_dir;  // 初始值全0

void init_page_tables()
{
	uint32_t PAGE_FLAGS = 0x007; // PRESENT+RW+USER
	uint32_t page_pde_offset = (_PAGE_OFFSET >> 20); // 3072
	uint32_t addr = 0 | PAGE_FLAGS;  // 内存地址+页表属性
	uint32_t *pg_dir_ptr = swapper_pg_dir; // 页目录表项指针
	uint32_t *pg0_ptr = pg0;  // 页表项指针
	
	for (;;) {
		// 设置页目录项,同时映射两个地址,让物理地址和虚拟地址都能访问,
		*pg_dir_ptr = pg0 | PAGE_FLAGS;     // 0, 1
		*(uint32_t *)((char *)pg_dir_ptr + page_pde_offset) = pg0 | PAGE_FLAGS;  // 768, 769
		pg_dir_ptr++;
		
		// 设置页表项目
		for (int i = 0; i < 1024; i++) {
			*pg0++ = addr;
			addr += 0x1000;
		}
		// 退出条件,实际上只映射了两个页目录就退出了(0,1,768, 769)
		if (pg0[INIT_MAP_BEYOND_END] | PAGE_FLAGS) >= addr) {
			init_pg_tables_end = pg0_ptr;
			return;
		}
	}	
};

5.1.4. 设置栈

/* Set up the stack pointer */
	lss stack_start,%esp

ENTRY(stack_start)
	.long init_thread_union+THREAD_SIZE
	.long __BOOT_DS

/* arch/i386/kernel/init_task.c
* Initial thread structure.
*
* We need to make sure that this is THREAD_SIZE aligned due to the
* way process stacks are handled. This is done by having a special
* "init_task" linker map entry..
*/
union thread_union init_thread_union 
	__attribute__((__section__(".data.init_task"))) =
		{ INIT_THREAD_INFO(init_task) };

内核最初使用的栈是init_task进程的,也就是0号进程的栈,这个进程是系统唯一一个静态定义而不是通过fork()产生的进程。

5.1.5. 设置真正的IDT和GDT

	lgdt cpu_gdt_descr   // 真正的GDT
	lidt idt_descr    //真正的IDT
	ljmp $(__KERNEL_CS),$1f   // 重置CS
1:	movl $(__KERNEL_DS),%eax	# reload all the segment registers
	movl %eax,%ss			# after changing gdt.  // 重置SS

	movl $(__USER_DS),%eax		# DS/ES contains default USER segment
	movl %eax,%ds
	movl %eax,%es

	xorl %eax,%eax			# Clear FS/GS and LDT
	movl %eax,%fs
	movl %eax,%gs
	lldt %ax
	cld			# gcc2 wants the direction flag cleared at all times
	// push一个假的返回地址以满足 start_kernel()函数return的要求
	pushl %eax		# fake return address  

对于IDT先全部初始化成ignore_int例程:

setup_idt:
	lea ignore_int,%edx
	movl $(__KERNEL_CS << 16),%eax
	movw %dx,%ax		/* selector = 0x0010 = cs */
	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */

	lea idt_table,%edi
	mov $256,%ecx
rp_sidt:
	movl %eax,(%edi)
	movl %edx,4(%edi)
	addl $8,%edi
	dec %ecx
	jne rp_sidt
	ret

ignore_int例程就干一件事,打印一个错误信息"Unknown interrupt or fault at EIP %p %p %p\n"

对于GDT我们最关心的__KERNEL_CS、__KERNEL_DS、__USER_CS、__USER_DS这4个段描述符:

.quad 0x00cf9a000000ffff	/* 0x60 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff	/* 0x68 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff	/* 0x73 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff	/* 0x7b user 4GB data at 0x00000000 */

至此分段机制、分页机制、栈都设置好了,接下去可以开心的jmp start_kernel了。

6. start_kernel

该函数在linux/init/main.c文件里。我们可以认为start_kernel是0号进程init_task的入口函数,0号进程代表整个linux内核且每个CPU有一个。 这个函数开始做一系列的内核功能初始化,我们重点看rest_init()函数。

6.1.rest_init

这是start_kernel的最后一行,它启动一个内核线程运行init函数后就什么事情也不做了(死循环,始终交出CPU使用权)。

static void noinline rest_init(void)
{
	kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND); // 启动init
	……
	/* Call into cpu_idle with preempt disabled */
	cpu_idle();  // 0号进程什么事也不做
}

6.2. init()

该函数的末尾fork了”/bin/init”进程。这样1号进程init就启动了,接下去就交给init进程去做应用层该做的事情了!

// 以下进程启动后父进程都是0号进程
if (ramdisk_execute_command) {
	run_init_process(ramdisk_execute_command);
	printk(KERN_WARNING "Failed to execute %s\n",
			ramdisk_execute_command);
}

/*
 * We try each of these until one succeeds.
 *
 * The Bourne shell can be used instead of init if we are 
 * trying to recover a really broken machine.
 */
if (execute_command) {
	run_init_process(execute_command);
	printk(KERN_WARNING "Failed to execute %s.  Attempting "
				"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

附录1. 启动多核CPU

以上解读的内容只在0号CPU上执行,如果是多CPU的环境,还要初始化其他的CPU。多CPU启动的起点是start_kernel()->rest_init()>init()->smp_init()。而smp_init()函数给每个CPU上调用cpu_up()do_boot_cpu()函数,每个CPU都要再走一遍head.S的流程,然后启动自己的idle进程(内核态0号进程)。

附录2. x64的不同

i386和x64的启动代码主要区别在head.S中。

  • 页表格式不同,i386使用两级页表,x64使用4级页表。
  • 多了兼容32位的代码段和数据段__USER32_CS、__USER32_DS和__KERNEL32_CS
  • x64段寄存器用法和i386的不同:x64下面CS、DS、ES、SS不用了,始终为0。而FS、GS寄存器的用法倒像是实模式下的,主要考虑是保留两个作为基地址好让线性地址计算方便。FS:XX = MSR_FS_BASE + XXGS:XX = MSR_GS_BASE + XX, 不是段描述符索引了(像实模式的分段)。

婚姻周年纪念日

第1年:纸婚
第2年:棉婚 第3年:皮革婚 第4年:水果婚 第5年:木婚 第6年:铁婚
第7年:铜婚
第8年:陶婚 第9年:柳婚
第10年:铝婚
第11年:钢婚 第12年:丝婚 第13年:丝带婚 第14年:象牙婚
第15年:水晶婚 第20年:瓷婚
第25年:银婚
第30年:珍珠婚 第35年:珊瑚婚 第40年:红宝石婚 第45年:蓝宝石婚
第50年:金婚
第55年:绿宝石婚
第60年:钻石婚
第70年:白金婚
第75年:白石婚
第80年:   橡树婚


结婚后的每一个纪念日都有不同的名称,不同的象征意义:
第一年是 纸婚(意思是一张纸印的婚姻关系,比喻最初结合薄如纸,要小心保护!)Paper wedding
第二年 棉婚(加厚一点,尚须磨炼!)Cotton wedding
第三年 皮革婚(开始有点韧性)Leather wedding
第四年 丝婚(缠紧,如丝般柔韧,你浓我浓。)Silk wedding
第五年 木婚(硬了心,已经坚韧起来)Wood wedding
第六年 铁婚(夫妇感情如铁般坚硬永固) Iron or Sugar Candy wedding
第七年 铜婚(比铁更不会生锈,坚不可摧) Copper wedding
第八年 陶婚 (如陶瓷般美丽,并须呵护)Pottery wedding
第九年 柳婚 (像垂柳一样,风吹雨打都不怕。)Willow wedding
第十年 锡婚 (锡器般坚固,不易跌破。)Tin wedding
第十一年 钢婚 (如钢铁般坚硬,今生不变。)Steel wedding
第十二年 链婚 (像铁链一样,心心相扣)Linen wedding
第十三年 花边婚 (多姿多彩,多样化的生活)Lace wedding
第十四年 象牙婚 (时间愈久,色泽愈光亮美丽)Ivory wedding
第十五年 水晶婚 (透明清澈而光彩夺目)Crystal wedding


以后每5年一个名称:
第二十年 瓷婚 (光滑无暇,需呵护,不让跌破)China wedding
第二十五年 银婚 (已有恒久价值,是婚后第一个大庆典)Silver wedding
第三十年 珍珠婚 (像珍珠般浑圆,美丽和珍贵)Pearls wedding
第三十五年 珊瑚婚 (嫣红而宝贵,生色出众)Coral wedding
第四十年 红宝石婚 (名贵难得,色泽永恒)Ruby wedding
第四十五年 蓝宝石婚 (珍贵灿烂,值得珍惜)Sapphire wedding
第五十年 金婚 (至高无上,婚后第二大庆典,情如金坚,爱情历久弥新)Golden wedding
第五十五年 翡翠婚 (如翡翠玉石,人生难求)Emerald wedding
第六十年 钻石婚(夫妻一生中最大的一次结婚典庆,珍奇罕有,今生无悔,是最隆重庆典) Diamond wedding (Diamond Jubilee)
凡六十一七十结婚周年纪念,中国人统称为“福禄寿婚”。

linux 常见终端热键以及Ctrl+C、Ctrl+Z比较 [转]

linux中存在一些按键,那么如何查阅目前的一些按键内容了?可以利用stty(setting tty 终端机的意思)。stty也可以帮助设置终端机的输入按键代表意义。

 >$ stty -a
speed 38400 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ;
eol2 = ; swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke

需要注意的是特殊字体那几个,此外^表示[Ctrl]那个按键的意思,如:intr = ^C表示利用【Ctrl】+c来完成的。几个重要的代表意义是:

eof:End of file的意思,代表结束输入;

erase:向前删除一个字符;

intr:送出一个interrupt(中断)的信号给目前正在运行的程序;
kill:删除在目前命令行上的所有字符;

quit:送出一个quit的信号给目前正在运行的进程;

start:在某个进程停止后,重新启动它的输出;

stop:停止目前屏幕的输出;

susp:送出一个terminal stop的信号给正在运行的进程;

如果你想要执行[ctrl]+h来进行字符的删除,那么可以执行:

root@mycomputer:~# stty erase ^h
Ctrl+C终止目前的命令

Ctrl+D输入结束(eof),例如邮件结束的时候

eof代表End of file的意思,代表结束输入

Ctrl+M就是Enter

Ctrl+S暂停屏幕的输出

Ctrl+Q恢复屏幕的输出

Ctrl+U在提示符下,将整行命令删除

Ctrl+Z暂停目前的命令

Ctrl+Z和Ctrl+C都是中断命令,但是它们的作用却不一样。

Ctrl+C是强制中断程序的执行,而Ctrl+Z是将任务中断,但是此任务并没有结束,还是在进程中只是保持挂起的状态,用户可以使用fg/bg操作继续前台或后台飞任务,fg命令重新启动前台被中断的任务。bg命令把被中断的任务放在后台执行。

来源:http://blog.sina.com.cn/s/blog_14ecbe4520102wrmv.html

 

信号具有平台相关性,不同平台下能使用的信号种类是有差异的。

Linux下支持的信号:

SEGV, ILL, FPE, BUS, SYS, CPU, FSZ, ABRT, INT, TERM, HUP, USR1, USR2, QUIT, BREAK, TRAP, PIPE

Windows下支持的信号:

SEGV, ILL, FPE, ABRT, INT, TERM, BREAK

React 定义组件的参数-生命周期

定义组件的参数-生命周期
创建期:getDefaultProps
创建期:getInitialState
创建期:componentWillMount
创建期:componentDidMount
存在期:componentWillReceiveProps
存在期:shouldComponentUpdate
存在期:componentWillUpdate
存在期:componentDidUpdate
销毁&清理期:componentWillUnmount

定义组件的参数-生命周期
生命周期相关参数,是React定义组件时提供的一系列处理函数(钓子函数),这些函数会在组件生命周期的某个阶段调用。

创建期:getDefaultProps

object getDefaultProps()

创建期:getInitialState

object getInitialState()
在组件挂载前(即:创建期)调用一次,其返回值将做为this.state的初始值。

getInitialState()方法会组件类创建的时候调用一次,其返回值会被缓存下来。该方法用于设置props属性的默认值,但仅对于非必须属性。如果父组件没有指定props中的某个值,此返回对象中的相应属性将会合并到this.props。

getInitialState()方法会在组件实例创建前调用,这时还不能使用this.props属性,且其返回对象是在所有实例间共享的。

创建期:componentWillMount

componentWillMount()
componentWillMount()服务器端和客户端都只调用一次,在初始化渲染执行之前被调用。如果在这个方法内调用setState()方法,render()方法将会收到更新后的state,也就是说这是我做在组件渲染前最后一个修改state的机会。

创建期:componentDidMount

componentDidMount()
componentDidMount()会在组件初始化(渲染完成)后立即调用一次,我们一般在这个方法中使用this.getDOMNode()方法访问原始DOM。

存在期:componentWillReceiveProps

componentWillReceiveProps(object nextProps)

componentWillReceiveProps在将要接受新的props时被调用
componentWillReceiveProps()方法会在组件生命周期的存在期调用,当组件感知到props属性改变,会调用此方法。render()方法将会在其后调用,这时我们可以通过this.setState()来阻止组件的再次渲染。

存在期:shouldComponentUpdate

boolean shouldComponentUpdate(object nextProps, object nextState)
shouldComponentUpdate()方法发生在组件生命周期的存在器,在组件收到新的props或state。在这个方法中,我们可以访问组件的props和state属性,通过这两个属性可以确认组件是否需要更新,如果不需要更新,则返回false,则其后的方法将不会在执行。如:

shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

存在期:componentWillUpdate

componentWillUpdate(object nextProps, object nextState)
componentWillUpdate()会在收到新的props或state后调用,类似componentWillMount()。

存在期:componentDidUpdate

componentDidUpdate(object prevProps, object prevState)
componentDidUpdate()会在组件重新渲染后立即被调用,当我们需要在组件重新渲染后操作DOM则需要使用这个方法。

销毁&清理期:componentWillUnmount

componentWillUnmount()
componentWillUnmount()是组件销毁&清理期唯一调用的方法,它会在组件从DOM中移除时被调用,这时我们可以清理一些状态或清理在componentDidMount中创建的DOM元素。

优秀程序员眼中的整洁代码[转]

有多少程序员,就有多少定义。所以我只询问了一些非常知名且经验丰富的程序员。

image.php_-57.gif

Bjarne Stroustrup,C++语言发明者,C++ Programming Language(中译版《C++程序设计语言》)一书作者。

我喜欢优雅和高效的代码。代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。

Bjarne用了“优雅”一词。说得好!我MacBook上的词典提供了如下定义:外表或举止上令人愉悦的优美和雅观;令人愉悦的精致和简单。注意对“愉悦”一词的强调。Bjarne显然认为整洁的代码读起来令人愉悦。读这种代码,就像见到手工精美的音乐盒或者设计精良的汽车一般,让你会心一笑。

Bjarne也提到效率——而且两次提及。这话出自C++发明者之口,或许并不出奇;不过我认为并非是在单纯追求速度。被浪费掉的运算周期并不雅观,并不令人愉悦。留意Bjarne怎么描述那种不雅观的结果。他用了“引诱”这个词。诚哉斯言。糟糕的代码引发混乱!别人修改糟糕的代码时,往往会越改越烂。

务实的Dave Thomas和Andy Hunt从另一角度阐述了这种情况。他们提到破窗理论4。窗户破损了的建筑让人觉得似乎无人照管。于是别人也再不关心。他们放任窗户继续破损。最终自己也参加破坏活动,在外墙上涂鸦,任垃圾堆积。一扇破损的窗户开辟了大厦走向倾颓的道路。

Bjarne也提到完善错误处理代码。往深处说就是在细节上花心思。敷衍了事的错误处理代码只是程序员忽视细节的一种表现。此外还有内存泄漏,还有竞态条件代码。还有前后不一致的命名方式。结果就是凸现出整洁代码对细节的重视。

Bjarne以“整洁的代码只做好一件事”结束论断。毋庸置疑,软件设计的许多原则最终都会归结为这句警语。有那么多人发表过类似的言论。糟糕的代码想做太多事,它意图混乱、目的含混。整洁的代码力求集中。每个函数、每个类和每个模块都全神贯注于一事,完全不受四周细节的干扰和污染。

image.php_-58.gif

Grady Booch,Object Oriented Analysis and Design with Applications(中译版《面向对象分析与设计》)一书作者。

整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。

Grady的观点与Bjarne的观点有类似之处,但他从可读性的角度来定义。我特别喜欢“整洁的代码如同优美的散文”这种看法。想想你读过的某本好书。回忆一下,那些文字是如何在脑中形成影像!就像是看了场电影,对吧?还不止!你还看到那些人物,听到那些声音,体验到那些喜怒哀乐。

阅读整洁的代码和阅读Lord of the Rings(中译版《指环王》)自然不同。不过,仍有可类比之处。如同一本好的小说般,整洁的代码应当明确地展现出要解决问题的张力。它应当将这种张力推至高潮,以某种显而易见的方案解决问题和张力,使读者发出“啊哈!本当如此!”的感叹。

窃以为Grady所谓“干净利落的抽象”(crisp abstraction),乃是绝妙的矛盾修辞法。毕竟crisp几乎就是“具体”(concrete)的同义词。我MacBook上的词典这样定义crisp一词:果断决绝,就事论事,没有犹豫或不必要的细节。尽管有两种不同的定义,该词还是承载了有力的信息。代码应当讲述事实,不引人猜测。它只该包含必需之物。读者应当感受到我们的果断决绝。

image.php_-59.gif

“老大”Dave Thomas,OTI公司创始人,Eclipse战略教父

整洁的代码应可由作者之外的开发者阅读和增补。它应当有单元测试和验收测试。它使用有意义的命名。它只提供一种而非多种做一件事的途径。它只有尽量少的依赖关系,而且要明确地定义和提供清晰、尽量少的API。代码应通过其字面表达含义,因为不同的语言导致并非所有必需信息均可通过代码自身清晰表达。

Dave老大在可读性上和Grady持相同观点,但有一个重要的不同之处。Dave断言,整洁的代码便于其他人加以增补。这看似显而易见,但亦不可过分强调。毕竟易读的代码和易修改的代码之间还是有区别的。

Dave将整洁系于测试之上!要在十年之前,这会让人大跌眼镜。但测试驱动开发(Test Driven Development)已在行业中造成了深远影响,成为基础规程之一。Dave说得对。没有测试的代码不干净。不管它有多优雅,不管有多可读、多易理解,微乎测试,其不洁亦可知也。

Dave两次提及“尽量少”。显然,他推崇小块的代码。实际上,从有软件起人们就在反复强调这一点。越小越好。

Dave也提到,代码应在字面上表达其含义。这一观点源自Knuth的“字面编程”(literate programming)5。结论就是应当用人类可读的方式来写代码。

image.php_-60.gif

Michael Feathers,Working Effectively with Legacy Code(中译版《修改代码的艺术》)一书作者。

我可以列出我留意到的整洁代码的所有特点,但其中有一条是根本性的。整洁的代码总是看起来像是某位特别在意它的人写的。几乎没有改进的余地。代码作者什么都想到了,如果你企图改进它,总会回到原点,赞叹某人留给你的代码——全心投入的某人留下的代码。

一言以蔽之:在意。这就是本书的题旨所在。或许该加个副标题,如何在意代码。

Michael一针见血。整洁代码就是作者着力照料的代码。有人曾花时间让它保持简单有序。他们适当地关注到了细节。他们在意过。

image.php_-61.gif

Ron Jeffries,Extreme Programming Installed(中译版《极限编程实施》)以及Extreme Programming Adventures in C#(中译版《C#极限编程探险》)作者。

Ron初入行就在战略空军司令部(Strategic Air Command)编写Fortran程序,此后几乎在每种机器上编写过每种语言的代码。他的言论值得咀嚼。

近年来,我开始研究贝克的简单代码规则,差不多也都琢磨透了。简单代码,依其重要顺序:

能通过所有测试;

没有重复代码;

体现系统中的全部设计理念;

包括尽量少的实体,比如类、方法、函数等。

在以上诸项中,我最在意代码重复。如果同一段代码反复出现,就表示某种想法未在代码中得到良好的体现。我尽力去找出到底那是什么,然后再尽力更清晰地表达出来。

在我看来,有意义的命名是体现表达力的一种方式,我往往会修改好几次才会定下名字来。借助Eclipse这样的现代编码工具,重命名代价极低,所以我无所顾忌。然而,表达力还不只体现在命名上。我也会检查对象或方法是否想做的事太多。如果对象功能太多,最好是切分为两个或多个对象。如果方法功能太多,我总是使用抽取手段(Extract Method)重构之,从而得到一个能较为清晰地说明自身功能的方法,以及另外数个说明如何实现这些功能的方法。

消除重复和提高表达力让我在整洁代码方面获益良多,只要铭记这两点,改进脏代码时就会大有不同。不过,我时常关注的另一规则就不太好解释了。

这么多年下来,我发现所有程序都由极为相似的元素构成。例如“在集合中查找某物”。不管是雇员记录数据库还是名-值对哈希表,或者某类条目的数组,我们都会发现自己想要从集合中找到某一特定条目。一旦出现这种情况,我通常会把实现手段封装到更抽象的方法或类中。这样做好处多多。

可以先用某种简单的手段,比如哈希表来实现这一功能,由于对搜索功能的引用指向了我那个小小的抽象,就能随需应变,修改实现手段。这样就既能快速前进,又能为未来的修改预留余地。

另外,该集合抽象常常提醒我留意“真正”在发生的事,避免随意实现集合行为,因为我真正需要的不过是某种简单的查找手段。

减少重复代码,提高表达力,提早构建简单抽象。这就是我写整洁代码的方法。

Ron以寥寥数段文字概括了本书的全部内容。不要重复代码,只做一件事,表达力,小规模抽象。该有的都有了。

image.php_-62.gif

Ward Cunningham,Wiki发明者,eXtreme Programming(极限编程)的创始人之一,Smalltalk语言和面向对象的思想领袖。所有在意代码者的教父。

如果每个例程都让你感到深合己意,那就是整洁代码。如果代码让编程语言看起来像是专为解决那个问题而存在,就可以称之为漂亮的代码。

这种说法很Ward。它教你听了之后就点头,然后继续听下去。如此在理,如此浅显,绝不故作高深。你大概以为此言深合己意吧。再走近点看看。

“……深合己意”。你最近一次看到深合己意的模块是什么时候?模块多半都繁复难解吧?难道没有触犯规则吗?你不是也曾挣扎着想抓住些从整个系统中散落而出的线索,编织进你在读的那个模块吗?你最近一次读到某段代码、并且如同对Ward的说法点头一般对这段代码点头,是什么时候的事了?

Ward期望你不会为整洁代码所震惊。你无需花太多力气。那代码就是深合你意。它明确、简单、有力。每个模块都为下一个模块做好准备。每个模块都告诉你下一个模块会是怎样的。整洁的程序好到你根本不会注意到它。设计者把它做得像一切其他设计般简单。

那Ward有关“美”的说法又如何呢?我们都曾面临语言不是为要解决的问题所设计的困境。但Ward的说法又把球踢回我们这边。他说,漂亮的代码让编程语言像是专为解决那个问题而存在!所以,让语言变得简单的责任就在我们身上了!当心,语言是冥顽不化的!是程序员让语言显得简单。

稿源:代码湾

转自:https://www.oschina.net/news/87473/good-programmers-clean-code

mac os x10.12 安装thrift0.8 源码

参考:http://www.cnblogs.com/peterpanzsy/p/4210127.html

http://thrift.apache.org/docs/install/

一:安装最新版(自动安装)

最简单的是用homebrew进行安装

  • 安装homebrew 在终端输入ruby -e “$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)”
  • 安装thrift   brew install thrift

用brew安装的thrift版本是0.9的,but,我们项目中得thrift版本是0.8的,所以果断卸载掉。

brew uninstall thrift。转到下面第二种方法安装thrift

二:手动安装0.8.0

先安装依赖。

http://www.jattcode.com/installing-autoconf-automake-libtool-on-mac-osx-mountain-lion/

  • 安装BOOST

下载:http://www.boost.org/

命令:./bootstrap.sh,该命令用于生成bjam可执行文件,这个东西就是用来编译boost库

命令:sudo ./b2 threading=multi address-model=64 variant=release stage install

  • 安装 libevent

下载:http://libevent.org/

命令:./configure –prefix=/usr/local

命令:make

命令:sudo make install

  • 安装 Apache Thrift

下载:http://thrift.apache.org/

编译命令:./configure –prefix=/usr/local/ –with-boost=/usr/local/lib –with-libevent=/usr/local/lib –without-ruby –without-python –without-perl –without-php

有一些不相关的code genorater可以不要了,不然又得会报错。。。

安装命令:sudo make install

thrift -version 可以查看安装是否成功和版本

thrift-0.8.0.tar.gz

安装8的时候会遇到如下问题,并按如下解决:

以下摘自美团 Created by 曹继光, last modified by 严鑫 on 十月 10, 2014

Osx 10.9 是比较新的系统, 从这个版本开始, 系统默认编译器从GCC 改为 Clang(GCC -v 一下, 给的是clang 的提示).

Thrift 0.8  代码相对又比较老, 在新的编译器上遇到了下列一些问题.

问题1 :

多个源码文件报错 # include <tr1/functional>, file not found, 改为 # include <boost/tr1/functional.hpp> 解决.

需要修改以下文件:

  • lib/cpp/src/concurrency/ThreadManager.h  line:24
  • lib/cpp/src/async/TAsyncChannel.h line:23
  • lib/cpp/src/async/TAsyncChannel.cpp line:21
  • lib/cpp/src/async/TAsyncProcessor.h line:23
  • lib/cpp/src/async/TAsyncBufferProcessor.h line:23

原因:  在我试验的环境下, 此头文件有后缀名/usr/local/include/boost/ tr1/functional.hpp

问题2:

cpp 测试代码 lib/cpp/test/Benchmark.cpp 编译失败,  shared_ptr  ambigous , 用 boost::shared_ptr 替换 shared_ptr,

错误消失.

原因: Clang 支持 C++ 11, 其 标准库自带 shared_ptr(std::shared_ptr, 参考 http://zh.cppreference.com/w/cpp/memory/shared_ptr)

故需显式指定名字空间.

三:测试

下面编写一个HelloWorld.thrift 来测试一下,内容如下:

namespace java com.xx.mobile.hotel.sc.demo

service HelloWorldService {
string sayHello(1:string username)
}

执行:thrift -gen java HelloWorld.thrift 将在同级目录下生成gen-java/com/xx/mobile/hotel/sc/demo/HelloWorldService.java文件。

Hibernate5 注解 命名策略

对于hibernate注解实体中属性对应数据库表的列名,怎么命名的问题,我们肯定不愿一个个属性去配置

Hibernaet5.1 之前  在applicationContext.xml中的sessionFactory中配置

<property name=”namingStrategy”>
<bean class=”org.hibernate.cfg.ImprovedNamingStrategy”></bean>
</property>

5.1开始

hibernate.ejb.naming_strategy将不再被支持,而是被替换成了两个属性:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy

对于physical_naming_strategy有两个常用的配置:

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

对于PhysicalNamingStrategyStandardImplDefaultNamingStrategy的效果,

对于SpringPhysicalNamingStrategyImprovedNamingStrategy的效果。

法1:在sessionFactory的bean里配置

<property name=”PhysicalNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl”></bean>
</property>

法2:在sessionFactory bean的hibernateProperties property中配置

<prop key=”hibernate.physical_naming_strategy”>org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl</prop>

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy 是 spring boot 包提供地

<property name=”ImplicitNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl” />
</property>

或是

<prop key=”hibernate.implicit_naming_strategy”>org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl</prop>

centos7 安装 sqlserver

安装可参考:https://docs.microsoft.com/zh-cn/sql/linux/sql-server-linux-setup-red-hat

1.下载sql server的源,便于通过yum命令来安装

curl https://packages.microsoft.com/config/rhel/7/mssql-server.repo > /etc/yum.repos.d/mssql-server.repo

2.安装

yum install -y mssql-server

3.配置

sqlservr-setup 或 mssql-conf setup

安装客户端工具

可以参考:https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools

1.设置防火墙

要连接数据库,首先要打开防火墙上1433端口,也就是,增加tcp端口1433到公共区域,并且永久生效

2.下载客户端工具的源

curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/msprod.repo

3.安装客户端工具

yum install mssql-tools unixODBC-devel

4.连接sql sever

sqlcmd -S localhost -U sa

 

chrome 谷歌浏览器出现 错误代码:ERR_UNSAFE_PORT 的解决办法

1、问题描述:

使用谷歌浏览器访问一个WEB项目,该项目设置的端口号为6000,结果不能访问:
 
2、问题所在:
出现此类问题的原因不是服务器端的问题,而是谷歌浏览器(FF浏览器也有)对一些特殊的端口进行了限制,具体有哪些端口进行了访问限制,请参见本文末。
 
3、问题解决:
最简单的办法就是直接修改搭建项目的端口号,避开这些谷歌限制的端口号。
 
谷歌浏览器限制的一些端口号:
1:    // tcpmux
7:    // echo
9:    // discard
11:   // systat
13:   // daytime
15:   // netstat
17:   // qotd
19:   // chargen
20:   // ftp data
21:   // ftp access
22:   // ssh
23:   // telnet
25:   // smtp
37:   // time
42:   // name
43:   // nicname
53:   // domain
77:   // priv-rjs
79:   // finger
87:   // ttylink
95:   // supdup
101:  // hostriame
102:  // iso-tsap
103:  // gppitnp
104:  // acr-nema
109:  // pop2
110:  // pop3
111:  // sunrpc
113:  // auth
115:  // sftp
117:  // uucp-path
119:  // nntp
123:  // NTP
135:  // loc-srv /epmap
139:  // netbios
143:  // imap2
179:  // BGP
389:  // ldap
465:  // smtp+ssl
512:  // print / exec
513:  // login
514:  // shell
515:  // printer
526:  // tempo
530:  // courier
531:  // chat
532:  // netnews
540:  // uucp
556:  // remotefs
563:  // nntp+ssl
587:  // stmp?
601:  // ??
636:  // ldap+ssl
993:  // ldap+ssl
995:  // pop3+ssl
2049: // nfs
3659: // apple-sasl / PasswordServer
4045: // lockd
6000: // X11
6665: // Alternate IRC [Apple addition]
6666: // Alternate IRC [Apple addition]
6667: // Standard IRC [Apple addition]
6668: // Alternate IRC [Apple addition]
6669: // Alternate IRC [Apple addition]

struts2 upgrade 2.3 to 2.5 migration 升级向导

Dependencies

Update Struts dependencies to 2.5.

Remove the following plugin dependencies because they were dropped and aren’t supported anymore.

  • Dojo Plugin
  • Codebehind Plugin
  • JSF Plugin
  • Struts1 Plugin

StrutsPrepareAndExecuteFilter

The org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter was moved to org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.

In web.xml replace this:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

with that:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

There were other package changes, please read Version Notes 2.5 for more details.

DTD

Struts DTD was updated to 2.5 version.

In struts.xml replace 2.3 DTD version:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

with 2.5:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"

Tags attributes

The id attribute was replaced with var attribute in the following tags.

  • <s:action>
  • <s:append>
  • <s:bean>
  • <s:date>
  • <s:generator>
  • <s:iterator>
  • <s:merge>
  • <s:number>
  • <s:set>
  • <s:sort>
  • <s:subset>
  • <s:text>
  • <s:url>

If you have something like that in your code:

<s:url id="url" action="login">

change it to:

<s:url var="url" action="login">

The <s:set> tag name attribute is replaced with var attribute.

From:

<s:set id="str1" value="'string1 value'" />
<s:set name="str2" value="'string2 value'" />

to:

<s:set var="str1" value="'string1 value'" />
<s:set var="str2" value="'string2 value'" />

Also escape attribute was renamed to escapeHtml attribute.

From:

<s:property escape="true" var="someProperty"/>

to:

<s:property escapeHtml="true" var="someProperty"/>

Div tag

The <s:div> tag was dropped.

Replace <s:div> with plain HTML <div> tag.

Field names

If you have field names which starts with single lower case letter, for example:

private String sTrng;
public String getSTrng() {...}
public void setSTrng(String str) {...}

change accessors to getsTrng and setsTrng.

Or better yet, change field names to not contain single lower case letter:

private String strng;
public String getStrng() {...}
public void setStrng(String str) {...}

For additional info see WW-3909.

Tiles

Depending on from which version of struts you upgrade and whether you used tiles-plugin or tiles3-plugin you may need to do different steps.

Struts 2.5 just provides a tiles-plugin which uses Tiles3. So support for Tiles2 has been dropped as well as the name tiles3-plugin.

Now the only maven dependency looks like this:

maven dependecy for tiles-plugin
<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-tiles-plugin</artifactId>
    <version>${struts2.version}</version>
</dependency>

You may need to update DTD in your tiles.xml files to Tiles3:

tiles3 dtd
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"

A Listener in web.xml is required. It is not necessary to configure paths to tiles.xml files here as they are picked up automatically.

StrutsTilesListener in web.xml
<listener>
  <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>

Optionally you may remove TilesDefinitions from XML and annotate actions instead. See Tiles Plugin for more details.

Temp/Work directory of ApplicationServer/ServletContainer

Users reported it was necessary for them to remove temp/work directory of their ApplicationServer/ServletContainer. Likely to force server to recompile JSPs.

来源:https://cwiki.apache.org/confluence/display/WW/Struts%202.3%20to%202.5%20migration

Java的基本数据类型

变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。

内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。

Java的两大数据类型:

  • 内置数据类型
  • 引用数据类型

内置数据类型

Java语言提供了八种基本类型。六种数字类型(四个整数型(默认是int 型),两个浮点型(默认是double 型)),一种字符类型,还有一种布尔型。

byte:

  • byte数据类型是8位、有符号的,以二进制补码表示的整数;(256个数字),占1字节
  • 最小值是-128(-2^7);
  • 最大值是127(2^7-1);
  • 默认值是0;
  • byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一;
  • 例子:byte a = 100,byte b = -50。

short:

  • short数据类型是16位、有符号的以二进制补码表示的整数,占2字节
  • 最小值是-32768(-2^15);
  • 最大值是32767(2^15 – 1);
  • Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一;
  • 默认值是0;

int:

  • int数据类型是32位、有符号的以二进制补码表示的整数;占3字节
  • 最小值是-2,147,483,648(-2^31);
  • 最大值是2,147,485,647(2^31 – 1);
  • 一般地整型变量默认为int类型;
  • 默认值是0;
  • 例子:int a = 100000, int b = -200000。

long:

  • long数据类型是64位、有符号的以二进制补码表示的整数;占4字节
  • 最小值是-9,223,372,036,854,775,808(-2^63);
  • 最大值是9,223,372,036,854,775,807(2^63 -1);
  • 这种类型主要使用在需要比较大整数的系统上;
  • 默认值是0L;
  • char数据类型可以储存任何字符;
  • 例子:char letter = ‘A’。

char 类型可以参与整型计算,然后转换成字符型

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:

public class PrimitiveTypeTest {  
    public static void main(String[] args) {  
        // byte  
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);  
        System.out.println("包装类:java.lang.Byte");  
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);  
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);  
        System.out.println();  
  
        // short  
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);  
        System.out.println("包装类:java.lang.Short");  
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);  
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);  
        System.out.println();  
  
        // int  
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);  
        System.out.println("包装类:java.lang.Integer");  
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);  
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);  
        System.out.println();  
  
        // long  
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);  
        System.out.println("包装类:java.lang.Long");  
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);  
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);  
        System.out.println();  
  
        // float  
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);  
        System.out.println("包装类:java.lang.Float");  
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);  
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);  
        System.out.println();  
  
        // double  
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);  
        System.out.println("包装类:java.lang.Double");  
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);  
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);  
        System.out.println();  
  
        // char  
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);  
        System.out.println("包装类:java.lang.Character");  
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台  
        System.out.println("最小值:Character.MIN_VALUE="  
                + (int) Character.MIN_VALUE);  
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台  
        System.out.println("最大值:Character.MAX_VALUE="  
                + (int) Character.MAX_VALUE);  
    }  
}  

编译以上代码输出结果如下所示:

基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127

基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767

基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647

基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807

基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38

基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308

基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的”E+数字”表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。

实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。


引用类型

  • 引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
  • 对象、数组都是引用数据类型。
  • 所有引用类型的默认值都是null。
  • 一个引用变量可以用来引用与任何与之兼容的类型。
  • 例子:Animal animal = new Animal(“giraffe”)。

Java常量

常量就是一个固定值。它们不需要计算,直接代表相应的值。

常量指不能改变的量。 在Java中用final标志,声明方式和变量类似:

final double PI = 3.1415927;

虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。

字面量可以赋给任何内置类型的变量。例如:

byte a = 68;
char a = 'A'

byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。

当使用常量的时候,前缀0表示8进制,而前缀0x代表16进制。例如:

int decimal = 100;
int octal = 0144;
int hexa =  0x64;

和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:

"Hello World"
"two\nlines"
"\"This is in quotes\""

字符串常量和字符常量都可以包含任何Unicode字符。例如:

char a = '\u0001';
String a = "\u0001";

Java语言支持一些特殊的转义字符序列。

符号 字符含义
\n 换行 (0x0a)
\r 回车 (0x0d)
\f 换页符(0x0c)
\b 退格 (0x08)
\s 空格 (0x20)
\t 制表符
\” 双引号
\’ 单引号
\\ 反斜杠
\ddd 八进制字符 (ddd)
\uxxxx 16进制Unicode字符 (xxxx)

WordPress错误:无法安装这个包。PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file

WordPress更新或是上传插件或主题错误时出现“无法安装这个包。PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file”错误在某些配置不够完善的主机中可能会出现这种情况。这是因为空间中temp目录没有设置访问权限的问题,需要空间商为你设置目录访问权限,一般这种要求他们是不会理的,所以我们只能改变WordPress的上传临时目录。

解决方法如下

1. 通过网站FTP打开WordPress根目录下的 wp-config.php 文件,找到如下代码:

/** WordPress 目录的绝对路径。 */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');

2. 在下面增加如下代码即可:

/** 指定WordPress的临时目录 */
define('WP_TEMP_DIR', ABSPATH . 'wp-content/temp');

3. 最后在 /wp-content/ 文件夹下新建个一个名为 temp 的文件夹,然后再重新上传或者更新下载插件和主题就可以了。

最好叫 temp 权限和系统中一致为777

Spring Data JPA 查询方法支持的关键字

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = 1?
Between findByStartDateBetween … where x.startDate between 1? and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> age) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

开发高效率 Git命令 Cherry-Pick 摘樱桃

在实际的项目开发中(使用Git版本控制),在所难免会遇到没有切换分支开发、需要在另一个分支修改bug然后合并到当前分支的情况。之前遇到这种第一反应就是将分支合并过去来解决问题。如果你那些提交当中也穿插了其他人的提交而且他们的提交不可以合并到另一个分支,那么使用分支的合并将明显变得困难。下面分享给大家一个非常好用Git的命令Cherry-Pick来处理这些情况,从而提高开发的效率。

git Cherry-Pick 命令可以选择某一个分支中的一个或几个commit(s)来进行操作。你可以理解merge的个性定制版本

多次使用时要按提交的顺序进行合并,不然会导致某些文件发生冲突。这也是 容易 踩的坑。

  1. 当你的需求还没有完成的时候,其他人应该切换到另一分支开发的时候,你可以先在当前分支继续开发完,然后再选择Cherry-Pick命令合并过去就可以了。
  2. 当你需要将某个人的commits合并到另一开分时候,可以选择Cherry-Pick命令。(在实际的项目开发中,在所难免有人会提交错分支)
  3. 当你切换到某条分支修改Bug后,需要将修改提交合并另一分支,可以选择Cherry-Pick命令。

大型网站架构演化

原创地址:https://my.oschina.net/u/3039671/blog/836750

1. 最初始的网站架构

就像我们在自己电脑上搭建了一个论坛的网站,应用程序(例如Apache服务器)、数据库等都部署在我们自己的电脑上的。就可以正常运行了。

2. 应用服务和数据服务分离

我们的论坛越来越受欢迎,用户越来越多,论坛也十分越活。但是面临的问题是数据库中的信息越来越多,存储不够了。这个时候我们又多弄了几台服务器,应用程序(Apache服务器)、数据库和保存用户上传的文件(图片)单独部署在不同的服务器上。

应用服务器处理大量的业务逻辑,所以需要更好的CPU

数据库服务器需要完成数据的快速查询,所以需要更大的硬盘和内存

文件服务器保存用户上传的图片等文件,所以需要更大的硬盘

3. 使用缓存改善网站性能

我们的论坛用户继续快速增涨,我们发现访问速度越来越慢,原因就是很多请求都要访问数据库(例如,读取用户的个人信息,打个不恰当的比喻,每次进入一个话题,该话题中的每一个发言用户的信息都要从数据库中读取)。这个时候如果我们能缓存这些用户信息,每次从缓存中读取,这样对数据库的压力会大大降低,并且读取的性能也提升了很多。

4. 使用应用服务器集群改善网站的并发处理能力。

使用缓存后,又出现问题了,在论坛使用高峰的时候,单一应用服务器处理请求连接有限,这个时候就需要部署应用服务器集群,然后在使用一个负载均衡服务器(例如Nginx,apache Server)。

5. 数据库读写分离

用户继续增加,使用缓存后,虽然大部分读的操作都不会直接访问数据库,但是还是有一些读操作(缓存未命中,缓存过期)和全部的写操作还是必须操作数据库。当用户达到一定规模,数据库又成了系统的性能瓶颈。

在原来单一数据库的模式上,设置一个主数据库和从数据库,写操作的时候写入主数据库,然后从主数据库同步到从数据库中。读操作就在从数据库中读。当然我们需要一个数据访问的模块来处理这些逻辑

6. 使用反向代理和CDN加速网站响应

论坛用户反应,打开你们的论坛速度太慢了,再不改善我就不用了。

原因也很简单,一个访问请求中,也许存在很多静态资源(CSS,图片)等,又或者用户的使用的联通,我们的服务器在电信。适应反向代理和CDN技术可以大大改善用户请求的响应速度

7. 使用分布式数据库和分布式文件系统

虽然数据库进行读写分离以后,但是在我们论坛疯狂增涨下,任何强大的单一服务器的性能都是有限的,只有使用分布式系统,才能在业务不断增涨进行横向扩展。这个是我们最后手段了,使用之前应该先考虑能否根据业务不同来拆分数据库。例如我们论坛的包括了不同主题(汽车、房子、以及你懂的话题),如果按照这些主题来区分数据库,也是好的选择。(注意这个虽然也是要使用多个数据库,但和分布式数据库的概念是有很大区别的)

8. 使用NoSql和搜索引擎

论坛中,要搜索一些帖子,如果每次进行数据库查询,在数据量十分大的情况,显然是不可取的。还有就是对数据存储的要求,需要使用NoSql。

9. 业务拆分

为了以后以后的发展,我们的业务需要扩展,我们需要增加即时通讯的业务(类似微信),知识问答(类似知乎)。但是这些业务是分开开发的,如果和原有的论坛业务耦合在一起,在代码发布的时候,就会十分麻烦。这个时候,根据不同的业务,分别进行部署和发布。

10. 分布式服务

虽然按照业务进行拆分以后,虽然不同业务之间的管理隔离开来,但是问题又出现了,但我们部署了上万台服务器的时候,每个服务器都保持有与数据库的连接,这样会导致数据集的连接资源不够。而且还有个问题就是对一些基础功能每个业务之间有重复开发的问题。

所以,我们把一些基础业务功能提取出来,做成基础服务独立部署(登录服务、用户信息管理,日志功能等)。这样上层只用关系自己的业务逻辑,调用底层统一的基础服务。

一份简单实用的微交互设计指南

好的产品往往做好了两点:功能和细节设计。

功能吸引用户使用你的产品,细节设计将你的用户留下。优秀的细节设计能够使你的产品在众多竞品中脱颖而出,优秀的微交互设计往往能够让用户在初次使用产品时就能够留下深刻的印象。作为一个交互设计师,在设计微交互方案的时候不仅要考虑视觉上的冲击力,还要想办法赋予其信息传递的功能。

 什么是微交互?

微交互是产品中存在的某一个时刻,它完成了某一个小的任务。Dan Saffer在他的书中(Microinteractions)第一次描述了微交互的概念,这些小细节专注于服务这些必要的功能:

 交流回馈或者动作的结果回馈

完成某个单独的任务

增强直接操作的感觉

帮助用户在视觉上展示操作的结果,以及避免错误

一些明显的微交互例子包括:

当你将iPhone设置成静音时伴随出现的的震动提醒、屏幕上的静音icon。

7yw20160316
 

界面动画提示是否能够点击(当鼠标移到按钮上方时按钮的颜色改变)。

6yw20160316
 

  为什么使用微交互?

微交互是在对用户一些自然的需求/欲望的认知和反馈。用户从微交互提供的视觉、触觉等反馈中确认他们的行为被接受。微交互还可以引导用户正确的使用系统/产品。

  定义微交互的使用情景

微交互的一个特点是它可以被放置在很多的场景下,辅助不同的动作行为。总体来说,微交互的使用场景包括:

 展示系统/产品的状态

Jacob Nielsen在“可用性启发原则”中指出:让你的用户时刻了解发生的事情,用户期望对自己的行为立即得到反馈。但是有些情况下,app需要时间来等待行为处理完成后才能向用户发出反馈。因此,产品界面需要向用户指明此刻正在发生的事。

5yw20160316
 

▲ 下载进度表

  或者标明用户所在的位置:

4yw20160316
 

Tips:不要让你的用户感到无助,让你的用户了解实时的状态并且向其展示进度(比如进度条能够让用户了解进度,消除疑惑)

 提示更新

我们有时需要向用户推送通知来保证用户了解到事态的更新。动画可以做到这一点,动画可以吸引用户的注意力,避免用户忽视掉重要的信息。

3yw20160316
 

Tips:微交互里的动效应该遵循KISS原则(keep it simple, stupid),应该尽量简单直接。

关联上下文

使用动效来将用户的注意力平滑的在导航页面间切换,向用户解释页面里元素之间的关系,以及页面跳转的来龙去脉。这对于移动设备非常有用,因为屏幕的尺寸限制,移动界面中每一页的内容都很紧凑,使用动效来阐述内容之间的联系非常实用。

Tips:尽量让每个页面的导航简洁,这样可以有效避免用户在页面跳转中迷失。两个状态之间的却换应该清晰、平滑、快捷。在视觉上统一所有的交互形式,降低用户的学习成本。

 输入可视化

数据输入是应用中非常重要的环节,微交互可以使用现有的元素来展示数据输入的反馈,从而将这步操作变得更加高效。

2yw20160316
 

Tips:微交互能够帮助用户理解信息格式,来源,帮助用户便捷输入信息。

  引导互动

微交互可以鼓励用户、吸引用户与产品交互。它可以在用户体验中产生同理心。但是需要谨慎使用微交互,保证其在感官上不会冒犯你的用户。

时刻谨记:不要让用户感到厌烦,Keep it simple, stupid.

1yw20160316
 

Tips:关注用户的情感反馈,因为它在用户体验中起到很重要的作用。多做用户研究和情景调研,设计能被用户频繁使用的微交互方案。

  值得谨记的

微交互向用户展示动作反馈,通知以及信息框架结构

微交互应该通过转移用户的注意力、愉悦用户等来达到加快/缩短信息数据的传输

了解你的用户以及使用微交互的背景,能够让你的微交互方案更加的精准与高效

微交互必须能够支持长时效的使用,在第一次使用时感到惊喜的方案,可能在第一百次使用时就变成了困扰。

微交互方案应该人性化一些,并且在视觉上保持和谐。用户在使用的时候应该感到流畅,微交互的方案应该尽可能的从现实生活中获得启发,比如使用拟物化等手段,从而降低学习成本。

  总结

用心设计,思考用户使用产品的情景,再设计这些微交互时多运用一些生活中常见的操作模式、物体的运动轨迹、常见的行为方式等。产品的易用性来源于对细节的打磨,伟大的设计不仅仅在功能上满足用户的需求,还要在微交互的设计上打动人心。

Mac升级为macOS Sierra系统后xcode项目报错[转]

元旦升级了macOS Sierra系统,顿时感觉入坑了,本来好好的项目报如下错误:

resource fork, Finder information, or similar detritus not allowed
Command /usr/bin/codesign failed with exit code 1

在网上找了很多方法,都不使用我的项目,或者说网上的说法太模糊,现给出解决办法:

  1. 关闭Xcode,打开终端;
  2. 进入项目的文件夹, 有.xcodeproj文件

    ll

    发现有 drwxr-xr-x@ 7 liyunde staff 238B 1 3 12:36 iosapp.xcodeproj
    多了个@符号

  3. 输入指令

    xattr -rc .

  4.  打开项目重新运行,OK了!

网上说的太模糊,资料也少,自己浪费了很长时间,现在给出详细的解决办法,避免同行入坑,节省时间。

参考:http://blog.csdn.net/benpaofengling/article/details/52680542

iOS获取当前app的名称和版本号[转]

NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
CFShow(infoDictionary);
// app名称
NSString *app_Name = [infoDictionary objectForKey:@”CFBundleDisplayName”];
// app版本
NSString *app_Version = [infoDictionary objectForKey:@”CFBundleShortVersionString”];
// app build版本
NSString *app_build = [infoDictionary objectForKey:@”CFBundleVersion”];

//手机序列号
NSString* identifierNumber = [[UIDevice currentDevice] uniqueIdentifier];
NSLog(@”手机序列号: %@”,identifierNumber);
//手机别名: 用户定义的名称
NSString* userPhoneName = [[UIDevice currentDevice] name];
NSLog(@”手机别名: %@”, userPhoneName);
//设备名称
NSString* deviceName = [[UIDevice currentDevice] systemName];
NSLog(@”设备名称: %@”,deviceName );
//手机系统版本
NSString* phoneVersion = [[UIDevice currentDevice] systemVersion];
NSLog(@”手机系统版本: %@”, phoneVersion);
//手机型号
NSString* phoneModel = [[UIDevice currentDevice] model];
NSLog(@”手机型号: %@”,phoneModel );
//地方型号  (国际化区域名称)
NSString* localPhoneModel = [[UIDevice currentDevice] localizedModel];
NSLog(@”国际化区域名称: %@”,localPhoneModel );

NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
// 当前应用名称
NSString *appCurName = [infoDictionary objectForKey:@”CFBundleDisplayName”];
NSLog(@”当前应用名称:%@”,appCurName);
// 当前应用软件版本  比如:1.0.1
NSString *appCurVersion = [infoDictionary objectForKey:@”CFBundleShortVersionString”];
NSLog(@”当前应用软件版本:%@”,appCurVersion);
// 当前应用版本号码   int类型
NSString *appCurVersionNum = [infoDictionary objectForKey:@”CFBundleVersion”];
NSLog(@”当前应用版本号码:%@”,appCurVersionNum);

jstat命令使用

jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下:

jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]

jstat -class

Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间

编译统计

jstat -compiler

Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法

垃圾回收统计
jstat -gc

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

堆内存统计
jstat -gccapacity

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数

新生代垃圾回收统计
jstat -gcnew

S0C:第一个幸存区大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间

新生代内存统计
jstat -gcnewcapacity

NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数

老年代垃圾回收统计
jstat -gcold

MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

老年代内存统计
jstat -gcoldcapacity

OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
元数据空间统计

jstat -gcmetacapacity

MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

总结垃圾回收统计
jstat -gcutil

S0:幸存1区当前使用比例
S1:幸存2区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元数据区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

JVM编译方法统计
jstat -printcompilation

Compiled:最近编译方法的数量
Size:最近编译方法的字节码数量
Type:最近编译方法的编译类型。
Method:方法名标识。

Java进程线程 CPU 占用高负载高问题排查

java -jar 运行应用,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高。

问题分析:
1,程序属于CPU密集型,和开发沟通过,排除此类情况。
2,程序代码有问题,出现死循环,或是死锁, 可能性极大。

过程:

1.代码是不能定位,从日志上也无法分析得出。
2.top,发现PID,83021 的Java进程占用CPU高达900%,出现故障。
3.找到该进程后,如何定位具体线程或代码呢,首先显示线程列表,并按照CPU占用高的线程排序:ps -mp 83021 -o THREAD,tid,time | sort -rn | head -n 10

USER     %CPU PRI SCNT WCHAN  USER SYSTEM    TID     TIME
ubox     88.9  19    – futex_    –      –  83032 08:21:49
ubox     88.9  19    – –         –      –  83031 08:21:49
ubox     88.9  19    – –         –      –  83030 08:21:49
ubox     88.9  19    – –         –      –  83028 08:21:49
ubox     88.9  19    – –         –      –  83027 08:21:49
ubox     88.9  19    – –         –      –  83025 08:21:49
ubox     88.9  19    – –         –      –  83024 08:21:49
ubox     88.9  19    – –         –      –  83023 08:21:49
ubox      712   –    – –         –      –      – 2-18:57:53

找到了耗时最高的线程83032,占用CPU时间超过8小时了!
4.将需要的线程ID转换为16进制格式:

printf “%x\n” 83032
14458

5.最后打印线程的堆栈信息:jstack 83021 | grep 14458 -A 5

6.将输出的信息给开发部进行确认,这样就能找出有问题的代码。
通过最近几天的监控,CPU已经安静下来了。

php-fpm添加service服务

Nginx通过FastCGI运行PHP比Apache包含PHP环境有明显的优势,最近有消息称,PHP5.4将很有可能把PHP-FPM补丁包含在内核里,nginx服务器平台上运行PHP将更加轻松,下面我们就来看一篇php-fpm平滑启动并配置服务例子。

我的PHP是源码安装的。php-fpm在PHP 5.3.2以后的版本不支持以前的php-fpm (start|restart|stop|reload) ,那么如果将php-fpm配置成服务,并添加平滑启动/重启。

配置php-fpm.conf(vi  php-7.1.0/etc/php-fpm.conf),将pid(;pid = run/php-fpm.pid)前的;去掉。

因为编译安装php的,所以会在php目录生成很多二进制文件,找到init.d.php-fpm,拷贝到init.d下。

cp  php-7.1.0/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

设置权限,并添加服务

chmod +x /etc/init.d/php-fpm
chkconfig –add php-fpm

以后可以使用如下命令管理php-fpm了

service php-fpm status

service php-fpm start
service php-fpm restart
service php-fpm reload

service php-fpm stop

不管你在哪里上班,请记住以下黄金定律!

01. 不为模糊不清的未来担忧,只为清清楚楚的现在努力。

02. 只有先改变自己的态度,才能改变人生的高度。

03. 在抱怨自己赚钱少之前,先努力,学着让自己值钱。

04. 学历代表过去,学习能力才代表将来。

05. 耐得住寂寞才能守得住繁华,该奋斗的年龄不要选择了安逸。

06. 有些事情不是看到希望才去坚持,而是坚持了才看得到希望。

07. 压力不是有人比你努力,而是比你牛几倍的人依然在努力。

08. 你所做的事情,也许暂时看不到成功,但不要灰心,你不是没有成长,而是在扎根。

09. 现实和理想之间,不变的是跋涉,暗淡与辉煌之间,不变的是开拓。

跳槽穷半年,改行穷三年。献给每天喊辞职、想辞职的人或周期性喊辞职、想辞职的人!

1,不要轻易离开团队,否则你要从零做起 。

2,不要老想着做不顺就放弃,哪个团队都有问题,哪个团队都有优点。

3.跟对领导很重要,愿意教你的,放手让你做的领导,绝对要珍惜。

4.团队的问题就是你脱颖而出的机会,抱怨和埋怨团队就是打自己耳光,说自己无能,更是在放弃机会 !

5.心怀感恩之心,感谢系统给你平台,感谢伙伴给你配合。

6.创造利润是你存在的核心价值,创业不是做慈善 。

7.遇到问题请先思考,只反映问题是初级水平,思考并解决问题才是高级水平。

坚持不一定成功,坚持到底一定成功! 好好工作吧!

学会说话,给人温暖,给人鼓励,给人赞美,

给人信心,给人方便,给人希望,给人智慧!

2016年,不管你在哪里上班,请记住这个字!

第一则:

工作不养闲人,团队不养懒人。

第二则:

入一行, 先别惦记着能赚钱, 先学着让自己值钱。

第三则:

没有哪个行业的钱是好赚的。

第四则:

干工作,没有哪个是顺利的,受点气是正常的。

第五则:

赚不到钱,赚知识;

赚不到知识,赚经历;

赚不到经历,赚阅历;

以上都赚到了 就不可能赚不到钱。

第六则:

只有先改变自己的态度,才能改变人生的高度。

只有先改变自己的工作态度,才能有职业高度。

第七则:

让人迷茫的原因只有一个

——那就是本该拼搏的年纪,却想得太多,做得太少!

送君一个字:干!

本文整理自网络

版权归原作者所有,向原作者致敬!

curl上传文件的用法

用curl下载网页估计大家都会,但是模拟 multipart/form-data 形式的 form 上传文件则稍稍复杂些。命令行如下:

curl  -F "filename=@~/file.tar.gz" http://localhost/action.php

如果使用了-F参数,curl就会以 multipart/form-data 的方式发送POST请求。-F参数以name=value的方式来指定参数内容,如果值是一个文件,则需要以name=@file的方式来指定。

如果通过代理,上面的命令有可能会被代理拒绝,这时需要指定上传文件的MIME类型

curl -x myproxy.com:1080 -F "filename=@~/file.tar.gz;type=application/octet-stream" http://localhost/action.php

另外,如果不上传文件,则可以使用 -d 参数,这时curl会以application/x-www-url-encoded 方式发送 POST 请求。

url -d "action=del&name=archive" -d "id=12" http://localhost/action.php

[原创] Maven Surefire Plugin Using POJO Tests 使用 Junit 或 TestNg 测试类默认命名规范

开始时候 运行测试用例发现只有部分运行了,始终找不到原因

原来在单元测试时候默认 规范可以使用三种,但是网上找到的都是互相抄袭地错误方法

JUnit 4以及TestNG。

在默认情况下,maven-surefire-plugin的test目标会自动执行测试源码路径

(默认为src/test/java/)下所有符合一组命名模式的测试类。这组模式为:

**/Test*.java:任何子目录下所有命名以Test开关的Java类。

**/*TestCase.java:任何子目录下所有命名以TestCase结尾的Java类。

**/*Test.java:任何子目录下所有命名以Test结尾的Java类。

但是 Maven Surefire 只支持一种: 如果需要其他规则要手动写到配置文件里

POJO tests look very much like JUnit or TestNG tests, though they do not require dependencies on these artifacts. A test class should be named **/*Test and should contain test* methods which will each be executed by Surefire.

https://maven.apache.org/surefire/maven-surefire-plugin/examples/pojo-test.html

另外有返回值的方法, TestNG 需要使用 <suite allow-return-values=”true”> 配置,默认会跳过

[转][Google Guava] 强大的集合工具类:java.util.Collections中未包含的集合工具

原文链接 译文链接 译者:沈义扬,校对:丁一

尚未完成: Queues, Tables工具类

任何对JDK集合框架有经验的程序员都熟悉和喜欢java.util.Collections包含的工具方法。Guava沿着这些路线提供了更多的工具方法:适用于所有集合的静态方法。这是Guava最流行和成熟的部分之一。

我们用相对直观的方式把工具类与特定集合接口的对应关系归纳如下:

集合接口 属于JDK还是Guava 对应的Guava工具类
Collection JDK Collections2:不要和java.util.Collections混淆
List JDK Lists
Set JDK Sets
SortedSet JDK Sets
Map JDK Maps
SortedMap JDK Maps
Queue JDK Queues
Multiset Guava Multisets
Multimap Guava Multimaps
BiMap Guava Maps
Table Guava Tables

在找类似转化、过滤的方法?请看第四章,函数式风格。

静态工厂方法

在JDK 7之前,构造新的范型集合时要讨厌地重复声明范型:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();

我想我们都认为这很讨厌。因此Guava提供了能够推断范型的静态工厂方法:

List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

可以肯定的是,JDK7版本的钻石操作符(<>)没有这样的麻烦:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();

但Guava的静态工厂方法远不止这么简单。用工厂方法模式,我们可以方便地在初始化时就指定起始元素。

Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

此外,通过为工厂方法命名(Effective Java第一条),我们可以提高集合初始化大小的可读性:

List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);

确切的静态工厂方法和相应的工具类一起罗列在下面的章节。

注意:Guava引入的新集合类型没有暴露原始构造器,也没有在工具类中提供初始化方法。而是直接在集合类中提供了静态工厂方法,例如:

Multiset<String> multiset = HashMultiset.create();

Iterables

在可能的情况下,Guava提供的工具方法更偏向于接受Iterable而不是Collection类型。在Google,对于不存放在主存的集合 ——比如从数据库或其他数据中心收集的结果集,因为实际上还没有攫取全部数据,这类结果集都不能支持类似size()的操作 ——通常都不会用Collection类型来表示。

因此,很多你期望的支持所有集合的操作都在Iterables类中。大多数Iterables方法有一个在Iterators类中的对应版本,用来处理Iterator。

截至Guava 1.2版本,Iterables使用FluentIterable进行了补充,它包装了一个Iterable实例,并对许多操作提供了”fluent”(链式调用)语法。

下面列出了一些最常用的工具方法,但更多Iterables的函数式方法将在第四章讨论。

常规方法

concat(Iterable<Iterable>) 串联多个iterables的懒视图* concat(Iterable...)
frequency(Iterable, Object) 返回对象在iterable中出现的次数 与Collections.frequency (Collection,   Object)比较;Multiset
partition(Iterable, int) 把iterable按指定大小分割,得到的子集都不能进行修改操作 Lists.partition(List, int)paddedPartition(Iterable, int)
getFirst(Iterable, T default) 返回iterable的第一个元素,若iterable为空则返回默认值 与Iterable.iterator(). next()比较;FluentIterable.first()
getLast(Iterable) 返回iterable的最后一个元素,若iterable为空则抛出NoSuchElementException getLast(Iterable, T default)
FluentIterable.last()
elementsEqual(Iterable, Iterable) 如果两个iterable中的所有元素相等且顺序一致,返回true 与List.equals(Object)比较
unmodifiableIterable(Iterable) 返回iterable的不可变视图 与Collections. unmodifiableCollection(Collection)比较
limit(Iterable, int) 限制iterable的元素个数限制给定值 FluentIterable.limit(int)
getOnlyElement(Iterable) 获取iterable中唯一的元素,如果iterable为空或有多个元素,则快速失败 getOnlyElement(Iterable, T default)

*译者注:懒视图意味着如果还没访问到某个iterable中的元素,则不会对它进行串联操作。

Iterable<Integer> concatenated = Iterables.concat(
        Ints.asList(1, 2, 3),
        Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6
String lastAdded = Iterables.getLast(myLinkedHashSet);
String theElement = Iterables.getOnlyElement(thisSetIsDefinitelyASingleton);
//如果set不是单元素集,就会出错了!

与Collection方法相似的工具方法

通常来说,Collection的实现天然支持操作其他Collection,但却不能操作Iterable。

下面的方法中,如果传入的Iterable是一个Collection实例,则实际操作将会委托给相应的Collection接口方法。例如,往 Iterables.size方法传入是一个Collection实例,它不会真的遍历iterator获取大小,而是直接调用 Collection.size。

方法 类似的Collection方法 等价的FluentIterable方法
addAll(Collection addTo,   Iterable toAdd) Collection.addAll(Collection)
contains(Iterable, Object) Collection.contains(Object) FluentIterable.contains(Object)
removeAll(Iterable   removeFrom, Collection toRemove) Collection.removeAll(Collection)
retainAll(Iterable   removeFrom, Collection toRetain) Collection.retainAll(Collection)
size(Iterable) Collection.size() FluentIterable.size()
toArray(Iterable, Class) Collection.toArray(T[]) FluentIterable.toArray(Class)
isEmpty(Iterable) Collection.isEmpty() FluentIterable.isEmpty()
get(Iterable, int) List.get(int) FluentIterable.get(int)
toString(Iterable) Collection.toString() FluentIterable.toString()

FluentIterable

除了上面和第四章提到的方法,FluentIterable还有一些便利方法用来把自己拷贝到不可变集合

ImmutableList
ImmutableSet toImmutableSet()
ImmutableSortedSet toImmutableSortedSet(Comparator)

Lists

除了静态工厂方法和函数式编程方法,Lists为List类型的对象提供了若干工具方法。

方法 描述
partition(List, int) 把List按指定大小分割
reverse(List) 返回给定List的反转视图。注: 如果List是不可变的,考虑改用ImmutableList.reverse()
List countUp = Ints.asList(1, 2, 3, 4, 5);
List countDown = Lists.reverse(theList); // {5, 4, 3, 2, 1}
List<List> parts = Lists.partition(countUp, 2);//{{1,2}, {3,4}, {5}}

静态工厂方法

Lists提供如下静态工厂方法:

具体实现类型 工厂方法
ArrayList basic, with elements, from Iterable, with exact capacity, with expected size, from Iterator
LinkedList basic, from Iterable

Sets

Sets工具类包含了若干好用的方法。

集合理论方法

我们提供了很多标准的集合运算(Set-Theoretic)方法,这些方法接受Set参数并返回SetView,可用于:

  • 直接当作Set使用,因为SetView也实现了Set接口;
  • copyInto(Set)拷贝进另一个可变集合;
  • immutableCopy()对自己做不可变拷贝。
方法
union(Set, Set)
intersection(Set, Set)
difference(Set, Set)
symmetricDifference(Set,   Set)

使用范例:

Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
Set<String> primes = ImmutableSet.of("two", "three", "five", "seven");
SetView<String> intersection = Sets.intersection(primes,wordsWithPrimeLength);
// intersection包含"two", "three", "seven"
return intersection.immutableCopy();//可以使用交集,但不可变拷贝的读取效率更高

其他Set工具方法

方法 描述 另请参见
cartesianProduct(List<Set>) 返回所有集合的笛卡儿积 cartesianProduct(Set...)
powerSet(Set) 返回给定集合的所有子集
Set<String> animals = ImmutableSet.of("gerbil", "hamster");
Set<String> fruits = ImmutableSet.of("apple", "orange", "banana");

Set<List<String>> product = Sets.cartesianProduct(animals, fruits);
// {{"gerbil", "apple"}, {"gerbil", "orange"}, {"gerbil", "banana"},
//  {"hamster", "apple"}, {"hamster", "orange"}, {"hamster", "banana"}}

Set<Set<String>> animalSets = Sets.powerSet(animals);
// {{}, {"gerbil"}, {"hamster"}, {"gerbil", "hamster"}}

静态工厂方法

Sets提供如下静态工厂方法:

具体实现类型 工厂方法
HashSet basic, with elements, from Iterable, with expected size, from Iterator
LinkedHashSet basic, from Iterable, with expected size
TreeSet basic, with Comparator, from Iterable

Maps

Maps类有若干值得单独说明的、很酷的方法。

uniqueIndex

Maps.uniqueIndex(Iterable,Function)通常针对的场景是:有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象——译者注:这个方法返回一个Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。

比方说,我们有一堆字符串,这些字符串的长度都是独一无二的,而我们希望能够按照特定长度查找字符串:

ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings,
    new Function<String, Integer> () {
        public Integer apply(String string) {
            return string.length();
        }
    });

如果索引值不是独一无二的,请参见下面的Multimaps.index方法。

difference

Maps.difference(Map, Map)用来比较两个Map以获取所有不同点。该方法返回MapDifference对象,把不同点的维恩图分解为:

entriesInCommon() 两个Map中都有的映射项,包括匹配的键与值
entriesDiffering() 键相同但是值不同值映射项。返回的Map的值类型为MapDifference.ValueDifference,以表示左右两个不同的值
entriesOnlyOnLeft() 键只存在于左边Map的映射项
entriesOnlyOnRight() 键只存在于右边Map的映射项
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
MapDifference<String, Integer> diff = Maps.difference(left, right);

diff.entriesInCommon(); // {"b" => 2}
diff.entriesInCommon(); // {"b" => 2}
diff.entriesOnlyOnLeft(); // {"a" => 1}
diff.entriesOnlyOnRight(); // {"d" => 5}

处理BiMap的工具方法

Guava中处理BiMap的工具方法在Maps类中,因为BiMap也是一种Map实现。

BiMap工具方法 相应的Map工具方法
synchronizedBiMap(BiMap) Collections.synchronizedMap(Map)
unmodifiableBiMap(BiMap) Collections.unmodifiableMap(Map)

静态工厂方法

Maps提供如下静态工厂方法:

具体实现类型 工厂方法
HashMap basic, from Map, with expected size
LinkedHashMap basic, from Map
TreeMap basic, from Comparator, from SortedMap
EnumMap from Class, from Map
ConcurrentMap:支持所有操作 basic
IdentityHashMap basic

Multisets

标准的Collection操作会忽略Multiset重复元素的个数,而只关心元素是否存在于Multiset中,如containsAll方法。为此,Multisets提供了若干方法,以顾及Multiset元素的重复性:

方法 说明 Collection方法的区别
containsOccurrences(Multiset   sup, Multiset sub) 对任意o,如果sub.count(o)<=super.count(o),返回true Collection.containsAll忽略个数,而只关心sub的元素是否都在super中
removeOccurrences(Multiset   removeFrom, Multiset toRemove) 对toRemove中的重复元素,仅在removeFrom中删除相同个数。 Collection.removeAll移除所有出现在toRemove的元素
retainOccurrences(Multiset   removeFrom, Multiset toRetain) 修改removeFrom,以保证任意o都符合removeFrom.count(o)<=toRetain.count(o) Collection.retainAll保留所有出现在toRetain的元素
intersection(Multiset,   Multiset) 返回两个multiset的交集; 没有类似方法
Multiset<String> multiset1 = HashMultiset.create();
multiset1.add("a", 2);

Multiset<String> multiset2 = HashMultiset.create();
multiset2.add("a", 5);

multiset1.containsAll(multiset2); //返回true;因为包含了所有不重复元素,
//虽然multiset1实际上包含2个"a",而multiset2包含5个"a"
Multisets.containsOccurrences(multiset1, multiset2); // returns false

multiset2.removeOccurrences(multiset1); // multiset2 现在包含3个"a"
multiset2.removeAll(multiset1);//multiset2移除所有"a",虽然multiset1只有2个"a"
multiset2.isEmpty(); // returns true

Multisets中的其他工具方法还包括:

copyHighestCountFirst(Multiset) 返回Multiset的不可变拷贝,并将元素按重复出现的次数做降序排列
unmodifiableMultiset(Multiset) 返回Multiset的只读视图
unmodifiableSortedMultiset(SortedMultiset) 返回SortedMultiset的只读视图
Multiset<String> multiset = HashMultiset.create();
multiset.add("a", 3);
multiset.add("b", 5);
multiset.add("c", 1);

ImmutableMultiset highestCountFirst = Multisets.copyHighestCountFirst(multiset);
//highestCountFirst,包括它的entrySet和elementSet,按{"b", "a", "c"}排列元素

Multimaps

Multimaps提供了若干值得单独说明的通用工具方法

index

作为Maps.uniqueIndex的兄弟方法,Multimaps.index(Iterable, Function)通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。

比方说,我们想把字符串按长度分组。

ImmutableSet digits = ImmutableSet.of("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine");
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
    public Integer apply(String string) {
        return string.length();
    }
};

ImmutableListMultimap<Integer, String> digitsByLength= Multimaps.index(digits, lengthFunction);
/*
*  digitsByLength maps:
*  3 => {"one", "two", "six"}
*  4 => {"zero", "four", "five", "nine"}
*  5 => {"three", "seven", "eight"}
*/

invertFrom

鉴于Multimap可以把多个键映射到同一个值(译者注:实际上这是任何map都有的特性),也可以把一个键映射到多个值,反转Multimap也会很有用。Guava 提供了invertFrom(Multimap toInvert,
Multimap dest)
做这个操作,并且你可以自由选择反转后的Multimap实现。

注:如果你使用的是ImmutableMultimap,考虑改用ImmutableMultimap.inverse()做反转。

ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.putAll("b", Ints.asList(2, 4, 6));
multimap.putAll("a", Ints.asList(4, 2, 1));
multimap.putAll("c", Ints.asList(2, 5, 3));

TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(multimap, TreeMultimap<String, Integer>.create());
//注意我们选择的实现,因为选了TreeMultimap,得到的反转结果是有序的
/*
* inverse maps:
*  1 => {"a"}
*  2 => {"a", "b", "c"}
*  3 => {"c"}
*  4 => {"a", "b"}
*  5 => {"c"}
*  6 => {"b"}
*/

forMap

想在Map对象上使用Multimap的方法吗?forMap(Map)把Map包装成SetMultimap。这个方法特别有用,例如,与Multimaps.invertFrom结合使用,可以把多对一的Map反转为一对多的Multimap。

Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2);
SetMultimap<String, Integer> multimap = Multimaps.forMap(map);
// multimap:["a" => {1}, "b" => {1}, "c" => {2}]
Multimap<Integer, String> inverse = Multimaps.invertFrom(multimap, HashMultimap<Integer, String>.create());
// inverse:[1 => {"a","b"}, 2 => {"c"}]

包装器

Multimaps提供了传统的包装方法,以及让你选择Map和Collection类型以自定义Multimap实现的工具方法。

只读包装 Multimap ListMultimap SetMultimap SortedSetMultimap
同步包装 Multimap ListMultimap SetMultimap SortedSetMultimap
自定义实现 Multimap ListMultimap SetMultimap SortedSetMultimap

自定义Multimap的方法允许你指定Multimap中的特定实现。但要注意的是:

  • Multimap假设对Map和Supplier产生的集合对象有完全所有权。这些自定义对象应避免手动更新,并且在提供给Multimap时应该是空的,此外还不应该使用软引用、弱引用或虚引用。
  • 无法保证修改了Multimap以后,底层Map的内容是什么样的。
  • 即使Map和Supplier产生的集合都是线程安全的,它们组成的Multimap也不能保证并发操作的线程安全性。并发读操作是工作正常的,但需要保证并发读写的话,请考虑用同步包装器解决。
  • 只有当Map、Supplier、Supplier产生的集合对象、以及Multimap存放的键值类型都是可序列化的,Multimap才是可序列化的。
  • Multimap.get(key)返回的集合对象和Supplier返回的集合对象并不是同一类型。但如果Supplier返回的是随机访问集合,那么Multimap.get(key)返回的集合也是可随机访问的。

请注意,用来自定义Multimap的方法需要一个Supplier参数,以创建崭新的集合。下面有个实现ListMultimap的例子——用TreeMap做映射,而每个键对应的多个值用LinkedList存储。

ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap(
    Maps.<String, Collection>newTreeMap(),
    new Supplier<LinkedList>() {
        public LinkedList get() {
            return Lists.newLinkedList();
        }
    });

Tables

Tables类提供了若干称手的工具方法。

自定义Table

堪比Multimaps.newXXXMultimap(Map, Supplier)工具方法,Tables.newCustomTable(Map, Supplier<Map>)允许你指定Table用什么样的map实现行和列。

// 使用LinkedHashMaps替代HashMaps
Table<String, Character, Integer> table = Tables.newCustomTable(
Maps.<String, Map<Character, Integer>>newLinkedHashMap(),
new Supplier<Map<Character, Integer>> () {
public Map<Character, Integer> get() {
return Maps.newLinkedHashMap();
}
});

transpose

transpose(Table<R, C, V>)方法允许你把Table<C, R, V>转置成Table<R, C, V>。例如,如果你在用Table构建加权有向图,这个方法就可以把有向图反转。

包装器

还有很多你熟悉和喜欢的Table包装类。然而,在大多数情况下还请使用ImmutableTable

Unmodifiable Table RowSortedTable

免费好用的MySQL数据库客户端图形界面管理工具

1. Workbench
MySQL Workbench — 可视化数据库设计
MySQLWorkbench 是一款可视化的数据库设计工具,它将SQL开发、管理和数据库设计、创建和维护融合到一个简单的开发环境中,它源自fabFORCE.net,是 DBDesigner4的接班人,并且更换了mysql GUI工具绑定。当前的版本是5.2,最开始的版本是5.0, 需要强调的 是,MySQLWorkbench是被视为DBDesinger 4的继承者来开发的。

下载地址:http://dev.mysql.com/downloads/workbench/

2. HeidiSQL

ConnectionHeidiSQL 之前被称为mySQL-Front,是由德国程序员AnsgarBecker和一些在Delhi领域有突出贡献的人开发的一款免费的开源客户端。用 HeidiSQL管理数据库,用户必须通过验证登录一个本地或者远程的MySQL服务器,创建一个会话(session). 在这个会话里,用户可以通过 连接MySQL服务器管理MySQL 数据库,会话结束会断开与服务器的连接。它的特征集对于大部分普通和高级的数据库,表和数据操作来说已经足够了,但 是为了让它趋向完美,它依然保持积极的开发状态。

一个由Java, jHeidi编写的版本,被设计用来工作在Mac和Linux电脑上,不幸的是,这个项目在2010年三月被叫停了。

官方网站:http://www.heidisql.com/

3.phpMyAdmin10-best-mysql-gui-tools5

PhpMyAdmin是由PHP编写的,目的在于通过Internet管理MySQL,PhpMyAdmin支持对MySQL的大范围的操作,最频繁的使用是数据库的管理、表格、字段、关系、索引、用户、权限。你也可以直接执行SQL语句。

官方网站:http://www.phpmyadmin.net/home_page/

4. Sequel Pro

10-best-mysql-gui-tools6Sequel Pro是一款免费的开源项目,它是CocoaMySQL 数据库管理系统的继承者, CocoaMySQL是LorenzTextor脑力劳动的产物, 而Lorenz Textor是从2003年开始,CocoaMySQL主要的开发者。

SequelPro用来管理MySQL数据库(本地或者internet),它允许你增加、删除数据库和表,更改字段、索引和视图,过滤table内容,增加、编辑和删除列,执行自定义查询和转存table和整个数据库
它兼容MySQL 3.x, 4 or 5.

官方网站:http://www.sequelpro.com/

5. MySQL Browser

MySQLBrowser的优点是简单,极其简单,安装之后能够立刻上手,马上就能使用的那种,布局也很简陋,功能也很简陋,简单使用没有问题,尤其是刚开始学习mysql的童鞋,可以尝试一下。

官方下载地址: http://dev.mysql.com/downloads/gui-tools/5.0.html

linux 查看系统信息命令

下面给除了各linux发行版比较常用的系统信息查询的命令

#  man  # 使用man命令 查看命令手册

# uname -a # 查看内核/操作系统/CPU信息
# head -n 1 /etc/issue # 查看操作系统版本
# cat /proc/cpuinfo # 查看CPU信息
# hostname # 查看计算机名
# lspci -tv # 列出所有PCI设备
# lsusb -tv # 列出所有USB设备
# lsmod # 列出加载的内核模块
# env # 查看环境变量资源
# free -m # 查看内存使用量和交换区使用量
# df -h # 查看各分区使用情况
# du -sh <目录名> # 查看指定目录的大小
# grep MemTotal /proc/meminfo # 查看内存总量
# grep MemFree /proc/meminfo # 查看空闲内存量
# uptime # 查看系统运行时间、用户数、负载
# cat /proc/loadavg # 查看系统负载磁盘和分区
# mount | column -t # 查看挂接的分区状态
# fdisk -l # 查看所有分区
# swapon -s # 查看所有交换分区
# hdparm -i /dev/hda # 查看磁盘参数(仅适用于IDE设备)
# dmesg | grep IDE # 查看启动时IDE设备检测状况网络
# ifconfig # 查看所有网络接口的属性
# iptables -L # 查看防火墙设置
# route -n # 查看路由表
# netstat -lntp # 查看所有监听端口
# netstat -antp # 查看所有已经建立的连接
# netstat -s # 查看网络统计信息进程
# ps -ef # 查看所有进程
# top # 实时显示进程状态用户
# w # 查看活动用户
# id <用户名> # 查看指定用户信息
# last # 查看用户登录日志
# cut -d: -f1 /etc/passwd # 查看系统所有用户
# cut -d: -f1 /etc/group # 查看系统所有组
# crontab -l # 查看当前用户的计划任务服务
# chkconfig –list # 列出所有系统服务
# chkconfig –list | grep on # 列出所有启动的系统服务程序
# rpm -qa # 查看所有安装的软件包

PHP跳出多层循环

使用下面方法可以直接跳出循环(推荐):

$http = 0;

foreach($arr as $v){

    for($i=1;$i<=3;$i++){
        
        if($http == 200) break 2;

        echo $i,"\n";

        $http = 200;
    }

    echo $v,'这里不会输出';
}

echo 'END';

但是有些特殊情况使 层数不确定,或是很多层时(一般不会有这种情况):可以使用goto;

foreach($arr as $v){
    for($a=1;$a<=3;$a++)
    for($b=1;$b<=3;$b++)
    for($c=1;$c<=3;$c++)
    for($x=1;$x<=3;$x++)
    for($d=1;$d<=3;$d++)
    for($v=1;$v<=3;$v++)
    for($z=1;$z<=3;$z++)
    for($n=1;$n<=3;$n++){

//        if($http == 200) break ??;
        if($http == 200) goto end;

        echo $n,"\n";

        $http = 200;
    }

    echo $v,'这里不会输出';
}
end:

echo 'END';

C语言产生满足正态分布的随机数

C语言中可以通过rand函数生成满足均匀分布的随机数,但是生成满足正太分布的随机数就没有那么简单了,下面对常用的几种方法进行总结并用C++编程实现。

方法一:由均匀分布的随机数来产生

一个简单可行的并且容易编程的方法是:求12个在(0,1)上均匀分布的和,然后减6(12的一半)。这种方法可以用在很多应用中,这12个数的和是Irwin-Hall分布;选择一个方差12。但此推导的结果限制在(-6,6)之间,并且密度为12。

方法二:Box-Muller方法

Box-Muller方法是以两组独立的随机数U和V,这两组数在(0,1]上均匀分布,用U和V生成两组独立的标准常态分布随机变量X和Y:

正态分布曲线 2

方法三:由正态分布曲线图形得到的直观结果

图1 正态分布曲线

正态分布曲线

从上图可以看出,在μ附近的概率密度大,远离μ的地方概率密度小,我们要产生的随 机数要服从这种分布,就是要使产生的随机数在μ附近的概率要大,远离μ处小。算法的主要思想是:在上图的大矩形中随机产生点,这些点是平均分布的,如果产生的点落在概率密度曲线的下方,则认为产生的点是符合要求的,将它们保留,如果在概率密度曲线的上方, 则认为这些点不合格,将它们去除。如果随机产生了一大批在整个矩形中均匀分布的点,那 么被保留下来的点的横坐标就服从了正态分布。可以设想,由于在μ处的 f(x)的值比较大,理所当然的在μ附近的点个数要多,远离μ处的少,这从面积上就可以看出来。我们要产生的随机数就是这里的横坐标。

根据以上所述三种方法,编写C++测试代码如下:

#include

#include

using namespace std;

#define pi 3.1415926

#define rd (rand()/(RAND_MAX+1.0))

//区间[min,max]上的均匀分布,min和max要求传入的参数类型一致

template <<span style=”color:blue”>class T>

T rand(T min, T max)

{

return min+(max-min)*rand()/(RAND_MAX+1.0);

}

//求均值为miu,方差为sigma的正太分布函数在x处的函数值

double normal(double x, double miu,double sigma)

{

return 1.0/sqrt(2*pi)/sigma*exp(-1*(x-miu)*(x-miu)/(2*sigma*sigma));

}

//按照矩形区域在函数值曲线上下位置分布情况得到正太函数x值

double randn(double miu,double sigma, double min ,double max)

{

double x,y,dScope;

do{

x=rand(min,max);

y=normal(x,miu,sigma);

dScope=rand(0.0,normal(miu,miu,sigma));

}while(dScope>y);

return x;

}

double randn(int type)

{

//按照12个均匀分布之和减去6得到正态分布函数的x值

if (type==1)

return rd+rd+rd+rd+rd+rd+rd+rd+rd+rd+rd+rd-6.0;

//按照计算公式y=sqrt(-2*ln(U))*cos(2*PI*V)计算得到x

else if(type==2)

return sqrt(-2*log(rand()/(RAND_MAX+1.0)))*cos(2*pi*rand()/(RAND_MAX+1.0));

else

return randn(0.0,1.0,-10.0,10.0);

}

int main(int argc,char* argv[])

{

srand((unsigned)time( NULL ));

ofstream outfile(“321.txt”);

for (int i=0;i<100;i++)

{

//randn(1)、randn(2)和randn(3)效果差不多

outfile << randn(3) << endl;

}

return 0;

}

参考:

[1] http://zh.wikipedia.org/wiki/正态分布

[2] http://en.wikipedia.org/wiki/Normal_distribution

[3] http://wenku.baidu.com/view/e9de620d7cd184254b3535c9?pn=2&ssid=&from=&bd_page_type=1&uid=bd_1332071259_725&pu=sl@1,pw@1000,sz@224_220,pd@1,fz@2,lp@0,tpl@color,&st=1&wk=rd&maxpage=3

转载:http://blog.sina.com.cn/s/blog_70a14458010155b8.html

高富帅们的Git技巧

Git是一个分布式版本控制系统,拥有许多神奇而易用的特性(比如:分支),这让它可以轻松适应各种工作流程。这篇文章不涉及Git的基本使用,而是介绍了一些高级却有用的小技巧。让我们一起来看看高富帅们的Git技巧,准备好逆袭吧!

以“块”形式暂存你的改动

你肯定已经很熟悉的使用git add命令来将改动暂存到暂存区(staging area)了。你可能也会偶然因为两个不同的原因而做了一次改动,却没有分别提交(仅仅提交了一次),所以,当你执行git log时,会看到诸如这样的提交信息:“修改X,改动无关的Y”。如果这看起来像是你的工作方式,交互式add将是你的有力工具。

交互式add(或者叫add块),将会一个块一个快的循环你的改动。使用命令git add -p时,你可以在每个改动“块”(即:连续的改动会被组织到一起)时进行一些选择,比如:切分当前块为更小的块、跳过一个改动块、甚至手动的编辑该块,你 可以敲入?来查看所有该命令提供的选项。

开始以“块”形式暂存改动简单到只需一条命令(括号部分替换为特定文件):

git add -p (path/file)

译者注:感觉这条命令平常用的较少,我遇到需要分别提交的情况时,都是手动来add然后提交,该命令是这种方法的高级版本。我们平常可能对提交历史的重视比较低,常常出现一些无用的、无意义的提交信息,可以试试这条命令。

切换到最后所在分支

作为一个善良的码农,当你需要快速做些修正或是清理工作时,你都应该花些时间来对待。如果你的工作流程是十分依赖分支的话(译者注:强烈建议如 此),你可能不希望无关的修正影响到现在正在进行功能开发的分支。这意味着,你应该使用git stash命令来暂时存放你的改动,然后切到master分支(译者注:或是其它啥分支,我一般是取名为fix),在那个分支进行修正。(译者注:修正完 了,可以切回正在进行功能开发的分支,执行git stash pop来弹出之前暂存的改动,继续进行开发)。在不同分支间切换很乏味,幸好这里有个快捷命令可以切换到你最后所在的分支:

git checkout -

这个语法对于使用linux的高富帅们来说一定不陌生,cd命令有个类似的缩写cd -,表示切换到你最后所在的目录。当你需要切回功能开发分支时,你根本不用关心那个分支是啥名,只需git checkout -。

译者注:感觉tab键的自动补全也挺好用的,不过这条命令可以少敲点字。有了这条命令,妈妈再也不用担心我的分支切换了。

显示哪些分支被合并了(或是哪些没有被合并)

在使用git时,你可能会创建许多分支,导致执行git branch命令列出分支时变得有些杂乱。于是,你想处理那些已经合并到master分支的无用分支,但是,当你执行git checkout -d 来删除分支时可能会遇到“麻烦”(译者注:git会拒绝删除未合并的分支并提示你),如果使用以下命令,你将不再需要三思而后删,可以自信的处理那些已经 合并了的分支。

如果你想要看看你的本地分支里哪些分支是已经合并进你当前所在的分支时,可以使用:

git branch --merged

反过来,如果需要查看哪些分支还没有合并进当前所在的分支,可以使用:

git branch --no-merged

结合高富帅的UNIX工具,你可以轻松的删除那些已经合并了的分支:

git branch --merged | xargs git branch -d

译者注:xargs是UNIX平台的一个工具,它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。如果git branch –merged显示的是a,b,c三个分支已经合并,上面的命令会转换为:git branch -d a b c。更多xargs的信息:http://zh.wikipedia.org/wiki/Xargs

从另一分支获取文件内容而不用切换分支

设想你正在进行重构,你创建了好几个分支并在各分支下进行改动。这时,你想把另一个分支里某一个文件的改动引入到当前工作的分支里,为了达到目的你 可能需要好几步:git stash你的改动;切换到那个分支;获取文件的改动;切回工作分支(当然是使用git checkout -);继续进行编辑(译者注:别忘了git stash pop)。但是,你也可以直接检出另一分支的文件,并且合并到你当前所在的工作分支,使用命令(括号部分替换为对应的分支和文件):

git checkout (branch) -- (path/file)

以最后提交排序的Git分支

想必你已经使用上面的tip处理了杂乱的分支,有一些是用–merged选项标志来清理的吧。那其它的分支咋办呢?你咋知道哪些是有用的,哪些是 完全过期无用的呢?git for-each-ref命令可以打印出一个列表,该列表显示每个分支最后一次提交的引用(reference)信息。我们可以自定义输出来包含一些有用 的信息,更重要的是我们还可以按日期排序。可以使用下面的命令来输出一个列表,该表将显示按时间先后排序的每个分支的最后提交信息、提交者等信息:

git for-each-ref --sort=-committerdate --format="%(committername)@%(refname:short) [%(committerdate:short)] %(contents)"

还可以把它定义在gitconfig里:

[alias]
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"

译者注:定义后就只需执行git latest了。注意双引号需要转义!

在玻璃房内的人们别用git blame

或者说,在玻璃房内的人们不应该直接使用git blame而不带下文的选项标志。(译者注:玻璃房内的人是完全能被别人看到的人。这里的意思应该是想说,你每一次提交的变动都会被记录到git仓库的历 史,对于git仓库来说,你就像是住在玻璃房里的人,没有任何秘密,你根本逃不过git的”责问“)git blame是很有用的命令,它就像使用科学来证明你是正确的!但是请注意,许多文件的变动是很表面的,发现问题的来源需要更多的探索。像是移除空白、移动 内容到新行、移动内容到另一文件等动作都可以使用选项来忽略掉,以便更容易的找到代码变动的始作俑者。

在你blame(责备)他人前,记得用以下命令看看结果:

git blame -w  # 忽略移除空白这类改动
git blame -M  # 忽略移动文本内容这类改动
git blame -C  # 忽略移动文本内容到其它文件这类改动

译者注:git blame用来显示一份文件每一行的最近一次提交的提交hash值和提交者。当你跟别人说“我真的没改过这个文件啊”之前,就得git blame下。

在整个git仓库提交历史中找寻内容(然后删掉它)

你有时可能需要查找一行你写的代码,但是就是无法找到。它可能安放在了一些已经被遗忘的分支,或是删除了很久,又或是就在那显而易见的地方。无论哪种方式,你都可以通过一些命令在整个git仓库的历史中搜寻特定的字符串。

首先,我们需要拿到所有的提交,然后,使用git grep来搜寻特定的字符串。如下:

git rev-list --all | xargs git grep -F '搜寻的字符串'

你可能有一个粗心的朋友不小心在仓库里提交了诸如,用户名、密码、外婆的大蒜食谱等敏感信息。首先,他们得更改用户名、密码(并向外婆道歉)。然 后,你需要搜寻这些得罪人的文件,并将他们从整个仓库的历史里抹去(这听起来好像很容易)。经过这个处理,那些执行git pull的伙计们就会发现所有提交中包含的敏感信息都被清理干净了,而那些没有合并你的远程改动的家伙还是拥有敏感信息(所以,千万别忘记先改用户名和密 码)。我们来看看怎么操作。

首先,重写每个分支的历史,移除敏感信息:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch (filename)' --prune-empty --tag-name-filter cat -- --all

然后,将记录敏感信息的文件增加到.gitignore文件,并提交(括号部分替换为对应文件名):

echo (filename) >> .gitignore
git add .gitignore
git commit -m "Add sensitive (filename) file to gitignore"

接着,由于我们改写了历史,我们需要“强制”的将改动推到远程:

git push origin master --force
# 译者注:还可以使用命令
git push origin +master

最后,这个文件还在你的本地仓库里,还需要将它完全抹除:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

你这粗心的朋友从敏感文件的危机中解脱,而你用你高超的git知识成功逆袭,成为了他的英雄!

译者注:一天,妹子叫我去她家帮她把她的三围信息从git仓库的历史里完全删除,我研究了很久不得要领。妹子说,不如我们做点其它的事吧。我觉得我的git知识被她鄙视了,坚定的说,我一定要把它删掉!然后,就没有然后了… …

忽略文件跟踪

在和他人合作时可能常常意味着你需要更改一些配置才能让应用在环境里跑起来,这时,常常会不小心把这些只对你有意义的配置文件也给提交了。为了不再 常常关注这些文件,看着它们在git status时放肆的显示“modified”,你可以告诉git忽略它们的改动。这种方式,可以看成是一种和仓库绑定的gitignore文件(括号部 分替换为对应文件):

  git update-index --assume-unchanged (path/file)

译者注:感觉,.gitignore文件更方便和好理解。

让分支的历史归零

不管出于啥理由,有时从头开始正是你需要的。也许是你接手了一个不确信能安全开源的代码仓库;也许是你要着手做些全新的事情;也许是你想创建用于其 它目的一个新分支,又希望继续在仓库里维护它(比如:github页面,项目的文档一类的东西)。上述的情形下,你可以非常简单的创建一个没有提交历史的 分支(括号部分替换为对应分支):

  git checkout --orphan (branch)

译者注:我们知道,分支只是对提交的一个引用,所以,每当从当前分支创建一个分支时,被创建的分支都会延续之前的历史,但是这种方式却不会,是一个完完全全干净的git分支,没有任何的提交!

你一定离不开的别名

不讨论能节省大量敲击时间的“git别名(git alias)”技巧的git文章一定都是在耍流氓。停止输入冗长的命令,使用超级有用的别名吧!git别名可以加到.gitconfig文件里,或是使用 命令(译者注:本质就是改写.gitconfig命令)来增加(括号部分替换为别名和对应的命令):

    git config --global alias.(name) "(command)"
  1. 在依赖分支的工作流程中,你常常要在不同分支间切换,每次敲击节约你6个字母。
    co = checkout
    
  2. 在提交前瞧瞧你将要提交的都有什么改动是一个好习惯,这可以帮助你发现拼写错误、不小心的提交敏感信息、将代码组织成符合逻辑的组。使用git add暂存你的改动,然后使用git ds查看你将要提交的改动动。
    ds = diff --staged
    
  3. 你可能十分熟悉git输出的详细状态信息了,当到达一定境界时,你可能需要忽略所有那些描述,直击问题的核心。这个别名输出将输出git status的简短形式和分支的详细信息。
    st = status -sb
    
  4. 你是否在提交后才发现忘记git add某个文件了,或是提交了才想再改动些啥?amend(修正)暂存区到最近的一次提交吧。(译者注:这个命令不太好理解,–amend是重写提交历 史,-C是重用某次提交的提交信息。场景是当你提交完了发现还有些改动没提交,又不想写什么“改动了X,再次提交”这种狗血的提交信息。重新git add并git amend后,重用上次的提交信息再次提交,替换上次的不完整提交。特别注意–amend重写了提交,如果你已经push到远程了,慎用这条命令!)
    amend = commit --amend -C HEAD
    
  5. 有时上面的修正可能不好使了,你需要undo(撤销)。undo会回退到上次提交,暂存区也会回退到那次提交时的状态。你可以进行额外的改动,用新的提交信息来再次进行提交。
    undo = reset --soft HEAD^
    
  6. 维护一个多人编辑的代码仓库常常意味着试着发现何人在改动什么,这个别名可以输出提交者和提交日期的log信息。
    ls = log --pretty=format:'%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green) [%cn]' --decorate --date=short
    
  7. 这个别名用来在一天的开启时回顾你昨天做了啥,或是在早晨刷新你的记忆(括号内替换为自己的email)。
    standup = log --since '1 day ago' --oneline --author (YOUREMAIL)
    
  8. 一个复杂的仓库可能很难用直线式的输出来查看,这个别名可以用图表的形式向你展示提交是怎样及何时被加到当前分支的。
    graph = log --graph --pretty=format:'%C(yellow)%h %C(blue)%d %C(reset)%s %C(white)%an, %ar%C(reset)'
    

译者注:我根据上面的别名进行了一些整理修改,这是我现在的.gitconfig里的别名配置:

[alias]
  st = status -sb
  co = checkout
  br = branch
  mg = merge
  ci = commit
  ds = diff --staged
  dt = difftool
  mt = mergetool
  last = log -1 HEAD
  latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
  ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
  hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
  type = cat-file -t
  dump = cat-file -p

via alimama mux
作者:Chris Kelly 译者:栖邀
英文原文

来源:http://segmentfault.com/a/1190000002448847

unauthenticated user mysql 原因分析

最近常出现 客户端连接远程服务器失败问题

show processlist;

出现 unauthenticated user,一直到超时;

网上查了下,到处都是互相抄袭文章,类似:

解决的方案很简单,结束这个反查的过程,禁止任何解析。

打开mysql的配置文件(my.cnf),在[mysqld]下面增加一行:

skip-name-resolve

。。。

实际上我都用IP连接的,不会出现这种问题

最后网上有个说是网络慢也会影响连接,ping 测试了下果然慢

ping liyunde.com
PING liyunde.com (112.126.64.59): 56 data bytes
64 bytes from 112.126.64.59: icmp_seq=0 ttl=51 time=41.072 ms
64 bytes from 112.126.64.59: icmp_seq=1 ttl=51 time=40.965 ms
64 bytes from 112.126.64.59: icmp_seq=2 ttl=51 time=41.036 ms
64 bytes from 112.126.64.59: icmp_seq=3 ttl=51 time=42.662 ms
64 bytes from 112.126.64.59: icmp_seq=4 ttl=51 time=41.681 ms
64 bytes from 112.126.64.59: icmp_seq=5 ttl=51 time=41.701 ms
64 bytes from 112.126.64.59: icmp_seq=6 ttl=51 time=41.859 ms
64 bytes from 112.126.64.59: icmp_seq=7 ttl=51 time=43.713 ms

网络响应过慢,配置服务器不能解决这个问题,换个好点的网络,问题自然解决。

服务器的瞬时 Diffie-Hellman 公共密钥过弱

服务器的瞬时 Diffie-Hellman 公共密钥过弱

ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY

该错误会在连接到安全 (HTTPS) 服务器时发生。 这意味着服务器正在尝试建立安全连接, 但由于严重的配置错误,连接会很不安全!

在这种情况下, 服务器需要进行修复。 为了保护您的隐私, “Google Chrome”不会使用不安全的连接。

以前可以跳过,现在不行了

方法:

1.更改服务器加密协议为TSL,chrome 45 版本后不再支持SSL v2 v3了

Firefox 可以使用 禁止dhe,不过不推荐

2.使用更高强度的证书,可以自签,也可使用免费证书(不过都有时间限制)

当然如果不差钱,可以购买一个证书,这样解决问题彻底

PHP7 Support PThread

In PHP7 etc/standard/php_smart_str.h has been renamed to etc/standard/php_smart_string.h.

错误描述:

php-7.0.0beta3/ext/pthreads/php_pthreads.c:22:
./src/pthreads.h:41:10: fatal error: ‘ext/standard/php_smart_str.h’ file not
found
#include <ext/standard/php_smart_str.h>
^
1 error generated.
make: *** [php_pthreads.lo] Error 1

https://github.com/gophp7/gophp7-ext/wiki/extensions-catalog

https://github.com/krakjoe/pthreads/tree/seven

今天在github 上发现一个新的分支seven 可用在php7

下载之

使用当前最新php版本 php-7.0.0RC2

➜  /Users/liyunde/Downloads/pthreads-seven  >phpize7
Configuring for:
PHP Api Version:         20131218
Zend Module Api No:      20141001
Zend Extension Api No:   320140815

配置

➜  /Users/liyunde/Downloads/pthreads-seven  >./configure –with-php-config=php-config7 –enable-pthreads

…..

creating libtool
appending configuration tag “CXX” to libtool
configure: creating ./config.status
config.status: creating config.h

编译,安装

make -j2

make test

sudo make install

make 通过,工作正常,enjoy !

 

Excel最多可存多少行,多少列

使用Excel2007或Excel2010,在“另存为” 菜单中可以选择为“Excel 07-2003 工作薄”,从中我们可以看出,到了2007版以后,存储格式变了,简单一点从扩展名便可以看出,一个是.xls,一个是.xlsx,差异自己去体会,因为 对于我来说,Excel用得相当少。

平时多是用Number,不过一般为能用 都导出为excel格式,演示文档,内容相同的PPT2007 相对PPT2003要小很多,更方便携带。

Excel最多可存多少行,多少列?查到的资料如下:

Excel 07-2003一个工作表最多可有65536行,行用数字1—65536表示;最多可有256列,一个工作簿中最多含有255个工作表,默认情况下是三个工作表;

Excel 2007及以后版本,一个工作表最多可有1048576行,16384列;