使用反向ssh从外网访问内网主机的方法详解

由于我们自己使用的电脑未必有外网ip,因此我们需要一个有固定外网ip的服务器(随便搞个腾讯云,阿里云的小机子就行),然后用这台服务器与内网的机子进行通信,我们到时候要先登陆自己的服务器,然后再利用这个服务器去访问内网的主机。
1、准备好有固定ip的服务器A,以及待访问的内网机器B。两者都开着sshd服务,端口号默认都是22。顺便做好ssh免密码登陆。
2、内网主机B主动连接服务器A,执行以下命令:
 >$ ssh -NfR 10000:localhost:22 username@servername -p 22
这条命令的意思是在后台执行(-f),不实际连接而是做port forwarding(-N),做反向ssh(-R),将远程服务器的10000端口映射成连接本机(B)与该服务器的反向ssh的端口。
附:这里有必要加强一下记忆,这个端口号一不小心就容易搞混

man文档中的参数命令是这样的:

-R [bind_address:]port:host:hostport
-R [bind_address:]port:local_socket
-R remote_socket:host:hostport
-R remote_socket:local_socket

bind_address以及其后面的port是指远程主机的ip以及端口,host以及其后的hostport是指本机的ip和端口。由于ssh命令本身需要远程主机的ip(上上条命令中的servername),因此这个bind_address原则上是可以省略的。
执行完这条命令,我们可以在服务器A上看到他的10000端口已经开始监听:
~]$ ss -ant | grep 10000
LISTEN 0 128 127.0.0.1:10000 *:*
3、在上面的操作中,这个1111端口就已经映射成了内网主机B的22端口了,现在我们只要ssh到自己的这个端口就行了。在服务器A中执行:
 >$ ssh username@localhost -p 10000
这样就成功的登陆了内网的主机了。
功能优化
上面的做法其实有一个问题,就是反向ssh可能会不稳定,主机B对服务器A的端口映射可能会断掉,那么这时候就需要主机B重新链接,而显然远在外地的我无法登陆B

这其实有一个非常简单的解决方案,就是用autossh替代步骤2中的ssh:
C:\ > autossh -M 2222 -NfR 10000:localhost:22 username@servername -p 22
后面的参数跟ssh都一样,只是多了一个-M参数,这个参数的意思就是用本机的2222端口来监听ssh,每当他断了就重新把他连起来。。。不过man文档中也说了,这个端口又叫echo port,他其实是有一对端口的形式出现,第二个端口就是这个端口号加一。因此我们要保证这个端口号和这个端口号加一的端口号不被占用。

有时,我们会想在局域网外访问局域网内的机器。这时,我们可以使用SSH的反向连接来实现。
设备A:位于局域网内,可以访问代理服务器B。 假设该设备IP:A.A.A.A,用户名userA
设备B:位于局域网外,作为访问设备A的代理服务器,不可访问A。假设该设备IP:B.B.B.B,用户名userB
设备C:想要访问A的设备,可以访问B,无法直接访问A。假设该设备IP:C.C.C.C,用户名userC

目标:设备C可以通过SSH访问局域网内设备C

条件:三台设备都需要包含SSH客户端,A,B设备需要包含SSH服务端。

在A设备上建立A设备到B设备的反向代理:

ssh -fCNR <port_b1>:localhost:22 userB@B.B.B.B

例如:ssh -fCNR 10000:localhost:22 userB@B.B.B.B (此时B设备上已经可以通过ssh -p 10000 userA@localhost连接到设备A)

<port_b1>:建立在B机器上,用来代理设备A机器22端口的端口。

userB@B.B.B.B :B机器的用户名和IP地址。

在B设备上建立B设备到A设备的正向代理:(这样做的目的是为了实现和外网的通信)

ssh -fCNL *:<port_b2>:localhost:<port_b1> userB@localhost

例如:ssh -fCNL *:10001:localhost:10000 userB@localhost

<port_b2>:用作本地转发的端口,用来和外网通信,并将数据转发到<port_b1>,实现从其他机器可以访问。

*代表可以接受来自任意机器的访问。

现在C机器上可以通过B机器SSH到A机器

ssh -p<port_b2> userA@B.B.B.B

参数介绍

-f 后台运行-C 允许压缩数据-N 不执行任何命令-R 将端口绑定到远程服务器,反向代理-L 将端口绑定到本地客户端,正向代理

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, 不是段描述符索引了(像实模式的分段)。

历经 4 年半的开发,Sublime Text 3.0 正式版终发布

Sublime Text 3.0 正式发布:提供 Linux 软件包仓库支持

历经 4 年半的开发,Sublime Text 3.0  正式版终于发布了!3.0 带来了崭新的 UI 主题,新的颜色主题以及新的图标。此外,在格式高亮方面有较大改进,也支持 Windows 上的触摸板输入、支持 macOS 的 Touch Bar,以及为 Linux 提供了软件包仓库支持!

相对于 Sublime Text 2 而言,新版编辑器几乎每一个方面都有改进,所以即便是主要变更列表也显得太长了,具体可以关注这个页面

3.0 版的新特性包括:定义跳转、新的语义高亮引擎、新的用户界面和丰富的 API。改进拼写检查和自动缩进,自动换行能也更好的处理源代码,对高分屏支持更好,任意跳转也更加智能。

在 Sublime Text 3 中最令人骄傲的一点是性能:它比历史上发布过的任何一个 Sublime Text 2 版本都要快得多,启动快、打开文件快、甚至内容滚动都快。虽然它的体积比 2 要大,但是却更轻快。

J2EE框架 Spring

J2EE框架 Spring 推荐

J2EE框架 面向方面AOP/IoC Web框架
Apache
Java
跨平台

Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache许可证形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由 Rod Johnson 和 Juergen Hoeller等开发。Spring Framework 提供了一个简易的开发方式,这种开发方式,将避免那些可能致使底层代码变得繁杂混乱的大量的属性文件和帮助类。

Spring 中包含的关键特性:

  • 强大的基于 JavaBeans 的采用控制翻转(Inversion of Control,IoC)原则的配置管理,使得应用程序的组建更加快捷简易。
  • 一个可用于从 applet 到 Java EE 等不同运行环境的核心 Bean 工厂。
  • 数据库事务的一般化抽象层,允许宣告式(Declarative)事务管理器,简化事务的划分使之与底层无关。
  • 内建的针对 JTA 和 单个 JDBC 数据源的一般化策略,使 Spring 的事务支持不要求 Java EE 环境,这与一般的 JTA 或者 EJB CMT 相反。
  • JDBC 抽象层提供了有针对性的异常等级(不再从SQL异常中提取原始代码), 简化了错误处理, 大大减少了程序员的编码量. 再次利用JDBC时,你无需再写出另一个 ‘终止’ (finally) 模块. 并且面向JDBC的异常与Spring 通用数据访问对象 (Data Access Object) 异常等级相一致.
  • 以资源容器,DAO 实现和事务策略等形式与 Hibernate,JDO 和 iBATIS SQL Maps 集成。利用众多的翻转控制方便特性来全面支持, 解决了许多典型的Hibernate集成问题. 所有这些全部遵从Spring通用事务处理和通用数据访问对象异常等级规范.
  • 灵活的基于核心 Spring 功能的 MVC 网页应用程序框架。开发者通过策略接口将拥有对该框架的高度控制,因而该框架将适应于多种呈现(View)技术,例如 JSP,FreeMarker,Velocity,Tiles,iText 以及 POI。值得注意的是,Spring 中间层可以轻易地结合于任何基于 MVC 框架的网页层,例如 Struts,WebWork,或 Tapestry。
  • 提供诸如事务管理等服务的面向方面编程框架。

在设计应用程序Model时,MVC 模式(例如Struts)通常难于给出一个简洁明了的框架结构。Spring却具有能够让这部分工作变得简单的能力。程序开发员们可以使用Spring的 JDBC 抽象层重新设计那些复杂的框架结构。

高性能PHP框架 Phalcon

BSD
PHP,C
跨平台

PhalconPHP 是一个使用 C 扩展开发的 PHP Web 框架,提供高性能和低资源占用。

Phalcon 是一个开源的、全堆栈的 PHP 5 框架,使用 C 扩展编写,专门为高性能优化。无需学习和使用 C 语言,所有函数都以 PHP 类的方式曾现。Phalcon 是一个松耦合的框架。

使用时需在 php.ini 中添加:extension=phalcon.so

参考:https://www.oschina.net/p/phalcon

雨中的旋律

《雨中的旋律》(Rhythm of the Rain)是欧美经典歌曲,由瀑布合唱团演唱。

英文歌词

Rhythm of the Rain
Listen to the rhythm of the falling rain,
Telling me just what a fool I’ve been.
I wish that it would go and let me cry in vain,
And let me be alone again.
The only girl I care about has gone away.
Looking for a brand new start!
But little does she know that when she left that day.
Along with her she took my heart.
Rain, please tell me now. Does that seem fair for her
To steal my heart away when she don’t care
I can’t love another, when my heart’s somewhere far away.
The only girl I care about has gone away.
Looking for a brand new start!
But little does she know that when she left that day.
Along with her she took my heart.
Rain, won’t you tell her that I love her so
Please ask the sun to set her heart aglow
Rain in her heart and let the love we knew start to grow.
Listen to the rhythm of the falling rain,
Telling me just what a fool I’ve been.
I wish that it would go and let me cry in vain,
And let me be alone again.
Oh, listen to the falling rain–pitter-patter…

歌词大意

听那淅淅沥沥的雨声,它好像在说,我是个傻瓜。 我真希望雨停下来。 让我无望地哭泣, 让我再次孤身单影。
我唯一在乎的姑娘已经离去, 去寻找她的新生活。 然而,她不知道, 当她离去的那天, 也将我的心带走。
雨呀,请告诉我,那样公平吗?她偷走了我的心, 居然毫不在乎。 我无法再爱别人, 我的心已远远漂向异地。
雨呀,你就不能告诉她 我多么地爱她, 请让太阳燃起她心中的爱苗, 让雨滋润她的心田。 让我们爱情之花重新盛开。
哦,听那雨声, 噼哩啪啦。
歌词、句型分析
1、Listen to the rhythm Of the falling rain,telling me just what a fool I have been.
= As I listen to the rhythm of the falling rain, it seems that it is telling me that I’ve been a stupid guy.
听那淅淅沥沥的雨声,它好像在说,我是个傻瓜。
2、I wish that it would go and let me cry in vain….
=I wish that the rain would stop and let me cry in vain…
我真希望雨停下来,让我无望地哭泣,
in vain 徒然,白费,无效,徒劳的
eg: All our efforts were in vain, which made us very angry.
我们所有的努力都白费了,真令人生气。
3、The only girl I care about has gone away, looking for a brand-new start!
=The only girl that I love has left me and is looking for a new life!
我唯一在乎的姑娘已经离去,去寻找她的新生活。
Care about 喜欢,在乎
eg: She doesn’t care about being ladylike
她不在乎淑女仪态
brand-new a. 全新的
eg: Peter is showing off his brand-new motorcycle to his friends
彼得正在对他的朋友们炫耀他的新摩托车。
4、But little does she know that when she left that day,along with her she took my heart.
=But she doesn’t know that when she left that day,she also took my heart with her.
然而,她不知道,当她离去的那天,也将我的心带走。
But little does she know…此为否定倒装句型。
eg: He was hardly able to tell right from wrong
=Hardly was he able to tell right from wrong

中文版

(红苹果演唱)
歌词:
大雨哗啦啦淋湿我的梦
你我曾经相遇在雨中
那青春年少无知可爱的笑容
像雨后美丽的彩虹
等大雨再次击醒你我的美梦
你却消失无影踪
等心中攒成点点滴滴的感动
有星光闪烁在夜空
也许你我生来太轻松
初恋故事短暂而冲动
我也幻想成为你的大英雄 谁能够
等大雨再次击醒你我的美梦
你却消失无影踪
等心中攒成点点滴滴的感动
有星光闪烁在夜空
等大雨再次击醒你我的美梦
你却消失无影踪
等心中攒成点点滴滴的感动
有星光闪烁在夜空
也许你我生来太轻松
初恋故事短暂而冲动
我也幻想成为你的大英雄 谁能够
噢 听那大雨还在下
哗啦啦啦哗啦啦啦噢
啦啦啦啦啦啦还在下
啦啦啦啦啦啦啦.
啦啦啦啦还在下.
啦啦啦啦啦啦啦
噢 听那大雨还在下
啦啦啦啦啦啦啦啦噢
啦啦啦啦啦啦还在下

婚姻周年纪念日

第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信号Signal信号

信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念、Linux对信号机制的大致实现方法、如何使用信号,以及有关信号的几个系统调用。

信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分。

软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。

收 到信号的进程对各种信号有不同的处理方法。

处理方法可以分为三类:

  1. 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处 理。
  2. 第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
  3. 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信 号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。

在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。

发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:

  1. 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
  2. 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
  3. 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
  4. 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
  5. 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
  6. 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
  7. 跟踪进程执行的信号。

Linux支持的信号列表如下。很多信号是与机器的体系结构相关的

POSIX.1中列出的信号:
信号 值 处理动作 发出信号的原因
———————————————————————-
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写

下面的信号没在POSIX.1中列出,而在SUSv2列出

信号 值 处理动作 发出信号的原因
——————————————————————–
SIGBUS 10,7,10 C 总线错误(错误的内存访问)
SIGPOLL A Sys V定义的Pollable事件,与SIGIO同义
SIGPROF 27,27,29 A Profiling定时器到
SIGSYS 12,-,12 C 无效的系统调用 (SVID)
SIGTRAP 5 C 跟踪/断点捕获
SIGURG 16,23,21 B Socket出现紧急条件(4.2 BSD)
SIGVTALRM 26,26,28 A 实际时间报警时钟信号(4.2 BSD)
SIGXCPU 24,24,30 C 超出设定的CPU时间限制(4.2 BSD)
SIGXFSZ 25,25,31 C 超出设定的文件大小限制(4.2 BSD)

(对于SIGSYS,SIGXCPU,SIGXFSZ,以及某些机器体系结构下的SIGBUS,Linux缺省的动作是A (terminate),SUSv2 是C (terminate and dump core))。

下面是其它的一些信号

信号 值 处理动作 发出信号的原因
———————————————————————-
SIGIOT 6 C IO捕获指令,与SIGABRT同义
SIGEMT 7,-,7
SIGSTKFLT -,16,- A 协处理器堆栈错误
SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD)
SIGCLD -,-,18 A 与SIGCHLD同义
SIGPWR 29,30,19 A 电源故障(System V)
SIGINFO 29,-,- A 与SIGPWR同义
SIGLOST -,-,- A 文件锁丢失
SIGWINCH 28,28,20 B 窗口大小改变(4.3 BSD, Sun)
SIGUNUSED -,31,- A 未使用的信号(will be SIGSYS)

(在这里,- 表示信号没有实现;有三个值给出的含义为,第一个值通常在Alpha和Sparc上有效,中间的值对应i386和ppc以及sh,最后一个值对应mips。信号29在Alpha上为SIGINFO / SIGPWR ,在Sparc上为SIGLOST。)

处理动作一项中的字母含义如下
A 缺省的动作是终止进程
B 缺省的动作是忽略此信号
C 缺省的动作是终止进程并进行内核映像转储(dump core)
D 缺省的动作是停止进程
E 信号不能被捕获
F 信号不能被忽略

上 面介绍的信号是常见系统所支持的。以表格的形式介绍了各种信号的名称、作用及其在默认情况下的处理动作。各种默认处理动作的含义是:终止程序是指进程退 出;忽略该信号是将该信号丢弃,不做处理;停止程序是指程序挂起,进入停止状况以后还能重新进行下去,一般是在调试的过程中(例如ptrace系统调 用);内核映像转储是指将进程数据在内存的映像和进程在内核结构中存储的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员提 供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。

注意 信号SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。信号SIGIOT与SIGABRT是一个信号。可以看出,同一个信号在不同的系统中值可能不一样,所以建议最好使用为信号定义的名字,而不要直接使用信号的值。

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

孙悟空是哪一年出生

以地上时间计算,孙悟空入地狱时生死簿记载其342岁,其后孙悟空在天上呆了三次,计15天(弼马温)+180天左右(齐天大圣)+49天(炼丹炉)=244天左右,即244年,偷了蟠桃后在花果山上呆了一年,则孙悟空被压到五行山时已经587岁左右,而此年书中写明是王莽篡汉之时(第十四回),即公元8年,则猴王出世发生在公元前579年左右——而后贞观十三年(639年)取经,过了十四年取经结束,即653年(当然历史上这年唐太宗已经死了)。

若考虑王莽篡汉一说为误传,即孙悟空确实只是压了五百年,则孙悟空被压五行山为公元139年,东汉汉顺帝永和四年左右,那么再次以贞观十三年(639年)计算, 猴王出世是在公元前448年左右。

以上,孙悟空的出生年份:
1.采取第十四回王莽篡汉之说,孙悟空出生于公元前579年左右,时为春秋时代,周简王在位。
2.采取书中出现的大多数【被压了五百年】之说,孙悟空出生于公元前448年左右,时为战国时代,周贞定王在位。

一想到孙悟空当美猴王之时,那头正春秋五霸纷争,战国七雄裂土——花果山果然是人间福地,世外桃源啊!

作者:黄粱
链接:https://www.zhihu.com/question/35327628/answer/62342484
来源:知乎