“自己动手写操作系统”学习笔记(一)

(一)

1. 在virtual box内安装ubuntu 12.04LTS版本,设定共享文件夹。

virtual box设置共享文件夹

在ubuntu的/media文件夹下可见到共享的文件夹,以sf_共享名为文件夹名。


ubuntu下可见的共享文件夹.JPG

2. 共享文件夹的用户和组都是root,复制时要使用sudo,将D盘下ainbed/os文件夹拷贝到用户当前目录

sudo cp -r /media/sf_D_DRIVE/ainbed/os .(-r代表将目录下的文件及子目录全部复制)


复制共享文件夹到当前目录.JPG

3. 为了操作该目录下的文件,将用户和组改为当前用户。

sudo chown -R os waj:waj (-R也是必不可少)


改变目录所属用户和组.JPG

4. 安装了bochs.

命令:sudo apt-get install bochs
使用命令 bochs -q -f boshsrc仿真时出现


dlopen failed for module 'x'.JPG

百度搜索一下,原因是bochs-x模块没有安装。
命令:sudo apt-get install bochs-x
紧接着又出了问题:bochs-bin: symbol lookup error: /usr/lib/bochs/plugins/libbx_x.so: undefined symbol: XpmCreatePixmap
解决办法:sudo apt-get install bochs-sdl


bochs-sdl.JPG

进入配置文件:bochsrc,注释掉keyboard_mapping:,加入:display_library:sdl后恢复正常。


bochsrc.JPG
Hello OS World!.JPG

(二))

1. 在window主机和virtual box内ubuntu主机共享文件夹,相互可以更改内容

共享文件夹.JPG

输入命令:
mkdir /home/waj/wind
chmod 777 wind
sudo mount -t vboxsf D_DRIVE /home/waj/wind
cd wind
ls可显示windows主机D盘的内容

2. 软盘镜像操作---写入给定扇区

dd if=/dev/zero of=a.img bs=512 count=2880
可以生成一个空白软盘镜像
dd if=boot.bin of=a.img bs=512 count=1 seek=0 conv=notrunc
在第一扇区写入boot.bin(512个字节),seek指明开始写的扇区,count指明写多少扇区,notrunc指明文件不截断。

    org 07c00h          ; 编译器将程序定位到此,写入引导扇区,BIOS将引导
    mov ax, cs          ; 扇区加载到此位置,然后跳转执行第一条指令
    mov ds, ax
    mov es, ax
    call    DispStr         ; 调用显示字符串例程
    jmp $           ; 无限循环
DispStr:
    mov ax, BootMessage
    mov bp, ax          ; ES:BP = 串地址
    mov cx, 17          ; CX = 串长度
    mov ax, 01301h      ; AH = 13,  AL = 01h
    mov bx, 000ch       ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
    mov dl, 0
    int 10h         ; 10h 号中断
    ret
BootMessage:        db  "Hello, Wangxiyue!"
times   510-($-$$)  db  0   ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw  0xaa55              ; 结束标志


3. 软盘镜像操作---通过文件系统写入

dd if=/dev/zero of=a.img bs=512 count=2880
先生成一个空白镜像。
sudo mkfs -t msdos a.img
将软盘镜像以FAT12文件系统格式化
sudo mount -t msdos -o loop a.img ./fd
将软盘镜像以fat12格式挂载到当前fd目录下
sudo cp boot.com ./fd
可以将boot.com复制到fd目录即为a.img的内部
sudo umount fd
将挂载到fd的软盘镜像卸载,a.img内部已经有了boot.com文件,可以在虚拟机中使用

org 0100h           ; 此时,nasm boot.asm -o boot.com
mov ax, cs          ; 第一个软盘放DOS系统,第二个软盘放此程序,
mov ds, ax                      ; 可在DOS下运行程序
mov es, ax
call    DispStr         ; 调用显示字符串例程
jmp $           ; 无限循环

4. linux系统的软盘操作

当系统有软驱时,可在系统中找到/dev/fd0这个块设备。
sudo mount -t msdos /dev/fd0 /mnt/floppy
将设备挂载到软盘,可以通过fat12文件系统操作访问
注意:软驱必须有软盘才能挂载。

5. 测试软盘启动和通过dos调试程序

以下是直接启动时的bochsrc配置文件

###############################################################
# Configuration file for Bochs
###############################################################

# how much memory the emulated machine will have
megs: 32

# filename of ROM images
romimage: file=BIOS-bochs-latest
vgaromimage: file=VGABIOS-lgpl-latest

# what disk images will be used
#floppya: 1_44=D:\ainbed\os\freedos.img, status=inserted
floppya: 1_44=D:\ainbed\os\windbg\a.img, status=inserted

# hard disk
#ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
#ata0-master: type=disk, path="c.img", cylinders=306, heads=4, spt=17

# choose the boot disk.
boot: a

# where do we send log messages?
# log: bochsout.txt

# disable the mouse
mouse: enabled=0

# enable key mapping, using US layout as default.
keyboard: keymap=keymaps/x11-pc-us.map

如果需要在dos下运行,则a盘放freedos.img,b盘放boot.com程序

利用bochsdbg -q -f bochsrc调试程序
b 0x7c00设置断点
sreg查看寄存器的值
s单步执行(进入子函数)、n单步(跳过子函数)、

(三)

1. 在调试com程序的时候,如何设置断点?

在com程序开头,都有org 0100h这句话,但是断点设置b 0x0100b并没有用。
在网上找的方法,程序里加上xchg bx,bx,然后再配置文件加上magic_break:enanbled=1

magic_break.JPG

2. 进入保护模式代码分析

; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================

%include    "pm.inc"    ; 常量, 宏, 以及一些说明

org 07c00h
    00007c00(初始地址):jmp  LABEL_BEGIN(7c24)

[SECTION .gdt]
; GDT
;                              段基址,       段界限     , 属性
00007c04(GDT基地址):LABEL_GDT:    Descriptor       0,                0, 0           ; 空描述符
00007c0c:LABEL_DESC_CODE32: Descriptor       0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
00007c14:LABEL_DESC_VIDEO:  Descriptor 0B8000h,           0ffffh, DA_DRW         ; 显存首地址
; GDT 结束

GdtLen      equ $ - LABEL_GDT   ; GDT长度
00007c1c:GdtPtr     dw  GdtLen - 1  ; GDT界限
00007c1e:       dd  0       ; GDT基地址

; GDT 选择子
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .s16]
[BITS   16]
00007c24:LABEL_BEGIN:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0100h

    ; 初始化 32 位代码段描述符
    (7c2f)xor   eax, eax
    mov ax, cs
    shl eax, 4
    add eax, LABEL_SEG_CODE32(7c80)
    mov word [LABEL_DESC_CODE32 + 2](7c0e), ax(0)
    shr eax, 16
    mov byte [LABEL_DESC_CODE32 + 4](7c10), al
    mov byte [LABEL_DESC_CODE32 + 7](7c13), ah

    ; 为加载 GDTR 作准备
    (7c4c)xor   eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_GDT(7c04)        ; eax <- gdt 基地址
    mov dword [GdtPtr + 2](7c1e), eax   ; [GdtPtr + 2] <- gdt 基地址

    ; 加载 GDTR
    lgdt    [GdtPtr](00007c04,0x17=24-1)

    ; 关中断
    cli

    ; 打开地址线A20
    in  al, 92h
    or  al, 00000010b
    out 92h, al

    ; 准备切换到保护模式
    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    ; 真正进入保护模式
    (7c77)jmp   dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs,
                    ; 并跳转到 Code32Selector:0  处
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS   32]

LABEL_SEG_CODE32:
    (00007c80)mov   ax, SelectorVideo(0x10第二个选择子)
    mov gs, ax          ; 视频段选择子(目的)

    mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, 'P'
    mov [gs:edi], ax

    ; 到此停止
    jmp $

SegCode32Len    equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

(四)

1. LDT的使用调试

[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:         Descriptor       0,                 0, 0         ; 空描述符
LABEL_DESC_NORMAL: Descriptor       0,            0ffffh, DA_DRW    ; Normal 描述符
LABEL_DESC_CODE32: Descriptor       0,  SegCode32Len - 1, DA_C + DA_32  ; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor       0,            0ffffh, DA_C      ; 非一致代码段, 16
LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1    ; Data
LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位
LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT    ; LDT
LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW    ; 显存首地址
; GDT 结束

GdtLen      equ $ - LABEL_GDT   ; GDT长度
GdtPtr      dw  GdtLen - 1  ; GDT界限
        dd  0       ; GDT基地址

此处共有8个描述符,共占据0x40字节,从0x3224:0x0104开始(这个是.com程序分配到的段地址和0x0100的开始地址决定的,一个jmp指令占据4个字节。
下面有6个字节存储GDT表长度和基地址。
数据段从0x3224:0x160开始,共60个字节(32字节对齐)。
堆栈段从0x3224:0x1a0开始,共0x200个字节。

16位代码段从0x3224:0x3a0开始,程序开始的jmp就跳转到这里。

2. 16位实模式向保护模式的转移

[SECTION .s16](3224:03a0)
[BITS   16]
LABEL_BEGIN:
    xchg    bx, bx(bochs调试的magic_break)
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0100h

    mov [LABEL_GO_BACK_TO_REAL+3], ax(3224:0538,在16位段里面,用于返回实模式)
    mov [SPValueInRealMode], sp(3224:0160)

    ; 初始化 16 位代码段描述符
    mov ax, cs
    movzx   eax, ax
    shl eax, 4
    add eax, LABEL_SEG_CODE16(3224:0520)
    mov word [LABEL_DESC_CODE16 + 2], ax(3224:011e第3个描述符)
    shr eax, 16
    mov byte [LABEL_DESC_CODE16 + 4], al
    mov byte [LABEL_DESC_CODE16 + 7], ah

    ; 初始化 32 位代码段描述符
    xor eax, eax
    mov ax, cs
    shl eax, 4
    add eax, LABEL_SEG_CODE32(3224:04ac)
    mov word [LABEL_DESC_CODE32 + 2], ax(3224:0116第2个描述符)
    shr eax, 16
    mov byte [LABEL_DESC_CODE32 + 4], al
    mov byte [LABEL_DESC_CODE32 + 7], ah

    ; 初始化数据段描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_DATA(3224:0160,数据段的基地址)
    mov word [LABEL_DESC_DATA + 2], ax(3224:0126第4个描述符)
    shr eax, 16
    mov byte [LABEL_DESC_DATA + 4], al
    mov byte [LABEL_DESC_DATA + 7], ah

    ; 初始化堆栈段描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_STACK(3224:01a0,堆栈的基地址)
    mov word [LABEL_DESC_STACK + 2], ax(3224:012e第5个描述符)
    shr eax, 16
    mov byte [LABEL_DESC_STACK + 4], al
    mov byte [LABEL_DESC_STACK + 7], ah

    ; 初始化 LDT 在 GDT 中的描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_LDT(3224:0540,LDT表的基地址,在GDT表中占据一个描述符)
    mov word [LABEL_DESC_LDT + 2], ax(3224:0136第6个描述符)
    shr eax, 16
    mov byte [LABEL_DESC_LDT + 4], al
    mov byte [LABEL_DESC_LDT + 7], ah

    ; 初始化 LDT 中的描述符
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_CODE_A(3224:0560,局部段的物理地址,描述符在LDT表中,在LDTR中会存储LDT段的选择子)
    mov word [LABEL_LDT_DESC_CODEA + 2], ax(3224:0542,LDT中第一个描述符)
    shr eax, 16
    mov byte [LABEL_LDT_DESC_CODEA + 4], al
    mov byte [LABEL_LDT_DESC_CODEA + 7], ah

    ; 为加载 GDTR 作准备
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_GDT(0x3220:0104)     ; eax <- gdt 基地址
    mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址

    ; 加载 GDTR
    lgdt    [GdtPtr]

    ; 关中断
    cli

    ; 打开地址线A20
    in  al, 92h
    or  al, 00000010b
    out 92h, al

    ; 准备切换到保护模式
    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    ; 真正进入保护模式
    jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

3. 在32位代码段跳入局部段执行代码

    mov ax, SelectorData
    mov ds, ax          ; 数据段选择子
    mov ax, SelectorVideo
    mov gs, ax          ; 视频段选择子
    mov ax, SelectorStack
    mov ss, ax          ; 堆栈段选择子
    mov esp, TopOfStack
    ; Load LDT
    mov ax, SelectorLDT
    lldt    ax                            ;这一句是执行局部任务的关键,首先找到该LDT在GDT中的描述符,得到基地址,------然后根据跳转选择子和第2位在LDT表找到局部段的描述符
    jmp SelectorLDTCodeA:0  ; 根据局部描述符的基地址和跳转目标偏移跳入局部任务

(五)

1. 由0级跳往3级(高到低)

用返回指令retf、iret都可以实现。
返回时,要在内核堆栈顶含有3级代码的入口地址、选择子、3级堆栈地址、选择子,然后用retf指令可进入3级代码。

    mov ax, SelectorTSS
    ltr ax  ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。

    push    SelectorStack3
    push    TopOfStack3
    push    SelectorCodeRing3
    push    0
    retf        ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

2. 由3级进入0级

特别要注意3级进入0级,需要切换堆栈,要准备TSS段、TSS描述符、在tr寄存器加载TSS选择子,TSS段里准备好0级堆栈。
使用调用门由3级进入0级,跳转代码是:

; CodeRing3
[SECTION .ring3]
ALIGN   32
[BITS   32]
LABEL_CODE_RING3:    ;cs=0x2b,base=32ae0,limit=0x1b;ss=0x43,base=32600,limit=0x1ff,ldtr=0x50,gdtr= 0x32444,tr=0x50
    mov ax, SelectorVideo
    mov gs, ax          ; 视频段选择子(目的)

    mov edi, (80 * 14 + 0) * 2  ; 屏幕第 14 行, 第 0 列。
    mov ah, 0Ch         ; 0000: 黑底    1100: 红字
    mov al, '3'
    mov [gs:edi], ax

    call    SelectorCallGateTest:0  ; 测试调用门(有特权级变换),将打印字母 'C'。
    jmp $
SegCodeRing3Len equ $ - LABEL_CODE_RING3
; END of [SECTION .ring3]

SelectorCallGateTest:0前面是调用门选择子,3级RPL,调用门的DPL是3极,选择子是目标代码段的选择子,目标代码段的DPL是0级,中间的特权检验由硬件完成。

调用门工作时,发现特权级变化,要暂存当前SS和SP,然后从TSS段加载新的0级SS和SP,在0级堆栈保持3级SS和SP,保存参数,保持CS和IP。如果从0级返回,可原样恢复SS、SP、CS、IP都是由硬件自动完成。

(六)

1. 如何开启分页

需要的东西:页目录、页表、CR3存放页目录地址、CR0最高位置1
通过段:偏移得到的线性地址分为三部分:头十位是页目录索引、页目录一项代表4M,一个目录项占据4k页表、通过索引乘以4k加上页表基地址,可得对应页表地址。中间十位是页表项的偏移索引,乘以4后可得偏移字节,在前22位写入实际物理页面(4k对齐)与属性。最后十位是页内偏移。

2. 代码分析

[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:      Descriptor         0,                 0, 0              ; 空描述符
LABEL_DESC_NORMAL:  Descriptor         0,            0ffffh, DA_DRW         ; Normal 描述符
LABEL_DESC_FLAT_C:  Descriptor             0,           0fffffh, DA_CR | DA_32 | DA_LIMIT_4K; 0 ~ 4G
LABEL_DESC_FLAT_RW: Descriptor             0,           0fffffh, DA_DRW | DA_LIMIT_4K   ; 0 ~ 4G
LABEL_DESC_CODE32:  Descriptor         0,  SegCode32Len - 1, DA_CR | DA_32      ; 非一致代码段, 32
LABEL_DESC_CODE16:  Descriptor         0,            0ffffh, DA_C           ; 非一致代码段, 16
LABEL_DESC_DATA:    Descriptor         0,   DataLen - 1, DA_DRW         ; Data
LABEL_DESC_STACK:   Descriptor         0,        TopOfStack, DA_DRWA | DA_32        ; Stack, 32 位
LABEL_DESC_VIDEO:   Descriptor   0B8000h,            0ffffh, DA_DRW         ; 显存首地址
; GDT 结束

现代操作系统使用平坦模式:即逻辑地址=线性地址,基本的代码段和数据段都是偏移为0,段界限为4G。

PagingDemo:
    mov ax, cs
    mov ds, ax
    mov ax, SelectorFlatRW
    mov es, ax

    push    LenFoo
    push    OffsetFoo
    push    ProcFoo
    call    MemCpy
    add esp, 12

    push    LenBar
    push    OffsetBar
    push    ProcBar
    call    MemCpy
    add esp, 12

    push    LenPagingDemoAll
    push    OffsetPagingDemoProc
    push    ProcPagingDemo
    call    MemCpy
    add esp, 12

    mov ax, SelectorData
    mov ds, ax          ; 数据段选择子
    mov es, ax

    call    SetupPaging     ; 启动分页

    call    SelectorFlatC:ProcPagingDemo
    call    PSwitch         ; 切换页目录,改变地址映射关系
    call    SelectorFlatC:ProcPagingDemo

    ret

主代码要掌握实际的物理内存布局:
call SetpupPaging调用时,线性地址=物理地址,因此开启分页对代码的运行没有影响
call SelectorFlatC:ProcPagingDemo使用线性地址0x401000调用一个函数,这个线性地址对应Foo函数,显示Foo,
call PSwitch切换页表,改变一个4k页面的映射,线性地址0x401000映射到物理地址0x301000,这个物理地址是函数bar,
因此,再次调用SelectorFlatC:ProcPagingDemo将调用函数bar,显示bar。

3. PSwitch代码分析

    mov eax, LinearAddrDemo    ;0x4010000
    shr eax, 22                          ;右移22位得到最高十位,页目录项索引
    mov ebx, 4096                      ;乘以4k得到该页表相对于页表基地址的偏移
    mul ebx
    mov ecx, eax                        ;偏移存储在ecx
    mov eax, LinearAddrDemo
    shr eax, 12  
    and eax, 03FFh  ; 1111111111b (10 bits)  ;获取线性地址中间十位,为页表索引
    mov ebx, 4                  ;乘以4得到页表项相对于该页表的偏移
    mul ebx
    add eax, ecx                ;相加得到页表项相对于页表基地址的偏移
    add eax, PageTblBase1  ;得到该页表项的物理地址。
    mov dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW    ;将该表项映射的物理地址和属性填入(物理地址是4k页面,只占据前20位,最后12位填写属性。

    mov eax, PageDirBase1
    mov cr3, eax
    jmp short .3
.3:
    nop

(七)

1. 保护模式下的中断

必备条件:内存中有IDT表、每个IDT表项为中断门描述符指明GDT选择子和偏移(内存平坦模式)、将IDT表基地址加载到IDTR寄存器。
当中断发生时,通过中断向量和IDT基地址找到中断门描述符、通过选择子找到对应的段基址(一般为0,中断门主要完成特权级变换)、通过偏移找到对应的中断处理程序、通过TR寄存器和TSS段找到当前进程的进程栈、将用户态的SS和SP压栈、EFLAGS压栈、CS和IP压栈,然后跳转到对用的中断处理程序。
以下为中断描述符表:

; IDT
[SECTION .idt]
ALIGN   32
[BITS   32]
LABEL_IDT:
; 门                        目标选择子,            偏移, DCount, 属性
%rep 32
        Gate    SelectorCode32, SpuriousHandler,      0, DA_386IGate
%endrep
.020h:      Gate    SelectorCode32,    ClockHandler,      0, DA_386IGate
%rep 95
        Gate    SelectorCode32, SpuriousHandler,      0, DA_386IGate
%endrep
.080h:      Gate    SelectorCode32,  UserIntHandler,      0, DA_386IGate

IdtLen      equ $ - LABEL_IDT
IdtPtr      dw  IdtLen - 1  ; 段界限
        dd  0       ; 基地址
; END of [SECTION .idt]

以下为初始化IDTR寄存器:

    ; 为加载 IDTR 作准备
    xor eax, eax
    mov ax, ds
    shl eax, 4
    add eax, LABEL_IDT      ; eax <- idt 基地址
    mov dword [IdtPtr + 2], eax ; [IdtPtr + 2] <- idt 基地址
    ; 关中断
    ;cli
    ; 加载 IDTR
    lidt    [IdtPtr]

2. 中断的返回

中断返回所做的事情:iret指令执行后LDT寄存器保持当前进程的选择子、TSS段保存当前进程的进程栈SP、依次弹出用户态CS、IP、EFLAGS、SS、SP恢复用户态程序的运行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,464评论 25 707
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,138评论 9 467
  • Linux习惯问题: 在vim编辑时,按了ctrl + s后,再按ctrl + q就可以继续执行了。ctrl + ...
    光着脚的鞋阅读 4,482评论 0 16
  • 黑夜 总想着 把一些碎片 揉进 稀稀散散的梦里 而我 总想着 把最爱的你 揉进 我的心里面
    冬屿阅读 73评论 0 0
  • 转眼间,高考都过去两个多月了。今天我格外认真地看我的衣橱;明天,那些曾经朝夕相处的朋友就可以再相见了。你们,好吗?...
    殇璃ZYX阅读 275评论 0 0